[
  {
    "path": ".github/pull_request_template.md",
    "content": "First of all, thank you for your interest in help improving this project! However, I normally won't accept PRs for this project because I'm currently the only copyright owner for it (except for translations and maybe my company), which I think would be a nice thing to keep along. On the other hand, forcing people to give up their copyright to me is both awkward and legally-complex. So if you have any suggestions for an improvement, please feel free to comment on existing issues or open a new one!\n"
  },
  {
    "path": ".github/workflows/android.yml",
    "content": "name: Android CI\n\non: [push]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - name: Check out repository\n      uses: actions/checkout@v4\n      with:\n        submodules: true\n    - name: Set up JDK 17\n      uses: actions/setup-java@v4\n      with:\n        distribution: 'temurin'\n        java-version: '21'\n    - name: Build with Gradle\n      run: ./gradlew assembleDebug lintVitalRelease\n    - name: Upload artifact\n      uses: actions/upload-artifact@v4\n      with:\n        name: app-debug.apk\n        path: app/build/outputs/apk/debug/app-debug.apk\n        retention-days: 10\n"
  },
  {
    "path": ".gitignore",
    "content": "/.gradle/\n/.idea/\n/.kotlin/\n/build/\n/captures/\n/local.properties\n/signing.jks\n/signing.properties\n.DS_Store\n*.iml\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://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\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\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\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<https://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<https://www.gnu.org/licenses/why-not-lgpl.html>.\n"
  },
  {
    "path": "PRIVACY.md",
    "content": "# Privacy Policy\n\nHai Zhang built the Material Files app as an Open Source app. This SERVICE is provided by Hai Zhang at no cost and is intended for use as is.\n\nThis page is used to inform visitors regarding my policies with the collection, use, and disclosure of Personal Information if anyone decided to use my Service.\n\nIf you choose to use my Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that I collect is used for providing and improving the Service. I will not use or share your information with anyone except as described in this Privacy Policy.\n\nThe terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, which is accessible at Material Files unless otherwise defined in this Privacy Policy.\n\n## Information Collection and Use\n\nFor a better experience, while using our Service, I may require you to provide us with certain personally identifiable information. The information that I request will be retained on your device and is not collected by me in any way.\n\nThe app does use third party services that may collect information used to identify you.\n\nLink to privacy policy of third party service providers used by the app\n\n- [Google Play Services](https://policies.google.com/privacy)\n- [Google Analytics for Firebase](https://policies.google.com/privacy)\n- [Firebase Crashlytics](https://firebase.google.com/support/privacy)\n\n## Log Data\n\nI want to inform you that whenever you use my Service, in a case of an error in the app I collect data and information (through third party products) on your phone called Log Data. This Log Data may include information such as your device Internet Protocol (“IP”) address, device name, operating system version, the configuration of the app when utilizing my Service, the time and date of your use of the Service, and other statistics.\n\n## Cookies\n\nCookies are files with a small amount of data that are commonly used as anonymous unique identifiers. These are sent to your browser from the websites that you visit and are stored on your device's internal memory.\n\nThis Service does not use these “cookies” explicitly. However, the app may use third party code and libraries that use “cookies” to collect information and improve their services. You have the option to either accept or refuse these cookies and know when a cookie is being sent to your device. If you choose to refuse our cookies, you may not be able to use some portions of this Service.\n\n## Service Providers\n\nI may employ third-party companies and individuals due to the following reasons:\n\n- To facilitate our Service;\n- To provide the Service on our behalf;\n- To perform Service-related services; or\n- To assist us in analyzing how our Service is used.\n\nI want to inform users of this Service that these third parties have access to your Personal Information. The reason is to perform the tasks assigned to them on our behalf. However, they are obligated not to disclose or use the information for any other purpose.\n\n## Security\n\nI value your trust in providing us your Personal Information, thus we are striving to use commercially acceptable means of protecting it. But remember that no method of transmission over the internet, or method of electronic storage is 100% secure and reliable, and I cannot guarantee its absolute security.\n\n## Links to Other Sites\n\nThis Service may contain links to other sites. If you click on a third-party link, you will be directed to that site. Note that these external sites are not operated by me. Therefore, I strongly advise you to review the Privacy Policy of these websites. I have no control over and assume no responsibility for the content, privacy policies, or practices of any third-party sites or services.\n\n## Children’s Privacy\n\nThese Services do not address anyone under the age of 13\\. I do not knowingly collect personally identifiable information from children under 13\\. In the case I discover that a child under 13 has provided me with personal information, I immediately delete this from our servers. If you are a parent or guardian and you are aware that your child has provided us with personal information, please contact me so that I will be able to do necessary actions.\n\n## Changes to This Privacy Policy\n\nI may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes. I will notify you of any changes by posting the new Privacy Policy on this page. These changes are effective immediately after they are posted on this page.\n\n## Contact Us\n\nIf you have any questions or suggestions about my Privacy Policy, do not hesitate to contact me at https://github.com/zhanghai/MaterialFiles.\n\nThis privacy policy page was created at [privacypolicytemplate.net](https://privacypolicytemplate.net) and modified/generated by [App Privacy Policy Generator](https://app-privacy-policy-generator.firebaseapp.com/)\n"
  },
  {
    "path": "README.md",
    "content": "# Material Files\n\n[本文中文版](README_zh-CN.md)\n\n[![Android CI status](https://github.com/zhanghai/MaterialFiles/workflows/Android%20CI/badge.svg)](https://github.com/zhanghai/MaterialFiles/actions) [![GitHub release](https://img.shields.io/github/v/release/zhanghai/MaterialFiles)](https://github.com/zhanghai/MaterialFiles/releases) [![License](https://img.shields.io/github/license/zhanghai/MaterialFiles?color=blue)](LICENSE)\n\nAn open source Material Design file manager, for Android 5.0+.\n\n[<img alt=\"Get it on Google Play\" src=\"https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png\" width=\"240\">](https://play.google.com/store/apps/details?id=me.zhanghai.android.files) [<img alt=\"Get it on F-Droid\" src=\"https://fdroid.gitlab.io/artwork/badge/get-it-on.png\" width=\"240\">](https://f-droid.org/packages/me.zhanghai.android.files) [<img alt=\"Get it on GitHub\" src=\"https://raw.githubusercontent.com/Kunzisoft/Github-badge/main/get-it-on-github.png\" width=\"240\">](https://github.com/zhanghai/MaterialFiles/releases/latest/download/app-release-universal.apk)\n\n[Help translation on Transifex](https://www.transifex.com/zhanghai/MaterialFiles/) ([Search Android & GNOME translations](https://translations.zhanghai.me/), [Microsoft language resources](https://learn.microsoft.com/en-us/globalization/reference/microsoft-language-resources), [MIME type translations](https://gitlab.freedesktop.org/xdg/shared-mime-info/-/tree/master/po))\n\n## Preview\n\n<p><img src=\"fastlane/metadata/android/en-US/images/phoneScreenshots/1.png\" width=\"32%\" /> <img src=\"fastlane/metadata/android/en-US/images/phoneScreenshots/2.png\" width=\"32%\" /> <img src=\"fastlane/metadata/android/en-US/images/phoneScreenshots/3.png\" width=\"32%\" />\n<img src=\"fastlane/metadata/android/en-US/images/phoneScreenshots/4.png\" width=\"32%\" /> <img src=\"fastlane/metadata/android/en-US/images/phoneScreenshots/5.png\" width=\"32%\" /> <img src=\"fastlane/metadata/android/en-US/images/phoneScreenshots/6.png\" width=\"32%\" /></p>\n\n## Features\n\n- Open source: Lightweight, clean and secure.\n- Material Design: Follows Material Design guidelines, with attention into details.\n- Breadcrumbs: Navigate in the filesystem with ease.\n- Root support: View and manage files with root access.\n- Archive support: View, extract and create common compressed files.\n- NAS support: View and manage files on FTP, SFTP, SMB and WebDAV servers.\n- Themes: Customizable UI colors, plus night mode with optional true black.\n- Linux-aware: Like [Nautilus](https://apps.gnome.org/Nautilus/), knows symbolic links, file permissions and SELinux context.\n- Robust: Uses Linux system calls under the hood, not yet another [`ls` parser](https://news.ycombinator.com/item?id=7994720).\n- Well-implemented: Built upon the right things, including [Java NIO2 File API](https://docs.oracle.com/javase/8/docs/api/java/nio/file/package-summary.html) and [LiveData](https://developer.android.com/topic/libraries/architecture/livedata).\n\n## Why Material Files?\n\nBecause I like Material Design, and clean Material Design.\n\nThere are already a handful of powerful file managers, but most of them just aren't Material Design. And even among the ones with Material Design, they usually have various minor design flaws (layout, alignment, padding, icon, font, etc) across the app which makes me uncomfortable, while still being minor enough so that not everybody would care to fix it. So I had to create my own.\n\nBecause I want an open source file manager.\n\nMost of the popular and reliable file managers are just closed source, and I sometimes use them to view and modify files that require root access. But deep down inside, I just feel uneasy with giving any closed source app the root access to my device. After all, that means giving literally full access to my device, which stays with me every day and stores my own information, and what apps do with such access merely depends on their good intent.\n\nBecause I want a file manager that is implemented the right way.\n\n- This app implemented [Java NIO2 File API](https://docs.oracle.com/javase/8/docs/api/java/nio/file/package-summary.html) as its backend, instead of inventing a custom model for file information/operations, which often gets coupled with UI logic and grows into a mixture of everything ([example](https://github.com/TeamAmaze/AmazeFileManager/blob/master/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java)). On the contrary, a decoupled backend allows cleaner code (which means less bugs), and easier addition of support for other file systems.\n\n- This app doesn't use `java.io.File` or parse the output of `ls`, but built bindings to Linux syscalls to properly access the file system. `java.io.File` is an old API missing many features, and just can't handle things like symbolic links correctly, which is the reason why many people rather parse `ls` instead. However parsing the output `ls` is not only slow, but also [unreliable](https://news.ycombinator.com/item?id=7994720), which made [Cabinet](https://github.com/aminb/cabinet/blob/master/app/src/main/java/com/afollestad/cabinet/file/root/LsParser.java) broken on newer Android versions. By virtue of using Linux syscalls, this app is able to be fast and smooth, and handle advanced things like Linux permissions, symbolic links and even SELinux context. It can also handle file names with invalid UTF-8 encoding because paths are not naively stored as Java `String`s, which most file managers does and fails during file operation.\n\n- This app built its frontend upon modern `ViewModel` and `LiveData` which enables a clear code structure and support for rotation. It also properly handles things like errors during file operation, file conflicts and foreground/background state.\n\nIn a word, this app tries to follow the best practices on Android and do the right thing, while keeping its source code clean and maintainable.\n\nBecause I know people can do it right.\n\n[Nautilus](https://wiki.gnome.org/Apps/Files) is a beautifully-designed and user-friendly file manager on Linux desktop, and it's fully Linux-aware. [Phonograph](https://github.com/kabouzeid/Phonograph) is an open source Material Design music player app (which I've been using for years), and it has just the right Material Design and implementation.\n\nSo, it's time for yet another Android file manager.\n\n## Inclusion in custom ROMs\n\nThank you if you choose to include Material Files in your custom ROM! However since I've received several user complaints due to improper inclusion, I'd like to offer some suggestions on including this app properly for the good of end users:\n\n- Please don't replace the AOSP [DocumentsUI](https://android.googlesource.com/platform/packages/apps/DocumentsUI/) app with this app. This app is not designed to replace DocumentsUI and can't handle a number of functionalities in DocumentsUI - in fact, it relies on DocumentsUI to do things like granting external SD card access.\n\n- Please make sure this app can be uninstalled or at least disabled. Some users may not want this app for a variety of reasons, and get very upset when they can't remove it.\n\n- Please avoid conflict with the Play/F-Droid version of this app. App stores cannot update apps signed with a different certificate, so you can either ship an APK that's signed by me (or F-Droid) so that users will be able to update it on Play/F-Droid, or fork this project and rename the package name when you need to sign the APK with a different certificate and potentially making other changes.\n\n## License\n\n    Copyright (C) 2018 Hai Zhang\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"
  },
  {
    "path": "README_zh-CN.md",
    "content": "# Material Files\n\n[![Android CI 状态](https://github.com/zhanghai/MaterialFiles/workflows/Android%20CI/badge.svg)](https://github.com/zhanghai/MaterialFiles/actions) [![GitHub 发行版](https://img.shields.io/github/v/release/zhanghai/MaterialFiles)](https://github.com/zhanghai/MaterialFiles/releases) [![许可证](https://img.shields.io/github/license/zhanghai/MaterialFiles?color=blue)](LICENSE)\n\n一个开源的 Material Design 文件管理器，适用于 Android 5.0+。\n\n[<img alt=\"下载应用，请到 Google Play\" src=\"https://play.google.com/intl/en_us/badges/static/images/badges/zh-cn_badge_web_generic.png\" width=\"240\">](https://play.google.com/store/apps/details?id=me.zhanghai.android.files) [<img alt=\"下载应用，请到 F-Droid\" src=\"https://fdroid.gitlab.io/artwork/badge/get-it-on-zh-cn.png\" width=\"240\">](https://f-droid.org/packages/me.zhanghai.android.files) [<img alt=\"下载应用，请到 GitHub\" src=\"https://raw.githubusercontent.com/Kunzisoft/Github-badge/main/get-it-on-github.png\" width=\"240\">](https://github.com/zhanghai/MaterialFiles/releases/latest/download/app-release-universal.apk)\n\n[在 Transifex 上帮助翻译](https://www.transifex.com/zhanghai/MaterialFiles/)（[搜索 Android 和 GNOME 的翻译](https://translations.zhanghai.me/)、[微软语言资源](https://learn.microsoft.com/en-us/globalization/reference/microsoft-language-resources)、[MIME 类型翻译](https://gitlab.freedesktop.org/xdg/shared-mime-info/-/tree/master/po)）\n\n## 预览\n\n<p><img src=\"fastlane/metadata/android/en-US/images/phoneScreenshots/1.png\" width=\"32%\" /> <img src=\"fastlane/metadata/android/en-US/images/phoneScreenshots/2.png\" width=\"32%\" /> <img src=\"fastlane/metadata/android/en-US/images/phoneScreenshots/3.png\" width=\"32%\" />\n<img src=\"fastlane/metadata/android/en-US/images/phoneScreenshots/4.png\" width=\"32%\" /> <img src=\"fastlane/metadata/android/en-US/images/phoneScreenshots/5.png\" width=\"32%\" /> <img src=\"fastlane/metadata/android/en-US/images/phoneScreenshots/6.png\" width=\"32%\" /></p>\n\n## 特性\n\n- 开源：轻量、简洁并且安全。\n- Material Design：遵循 Material Design 规范，并且注重细节。\n- 面包屑导航栏：点击导航栏所显示路径中的任一文件夹即可快速访问。\n- Root 支持：使用 root 权限查看和管理文件。\n- 压缩文件支持：查看、提取和创建常见的压缩文件。\n- NAS 支持：查看和管理 FTP、SFTP、SMB 和 WebDAV 服务器上的文件。\n- 主题：可定制的界面颜色，以及可选纯黑的夜间模式。\n- Linux 友好：类似 [Nautilus](https://wiki.gnome.org/action/show/Apps/Files)，支持符号链接、文件权限和 SELinux 上下文。\n- 健壮性：使用 Linux 系统调用实现，而不是另一个 [`ls` 解析器](https://news.ycombinator.com/item?id=7994720)。\n- 实现良好：使用正确的方式打造，包括 [Java NIO2 文件 API](https://docs.oracle.com/javase/8/docs/api/java/nio/file/package-summary.html) 和 [LiveData](https://developer.android.com/topic/libraries/architecture/livedata)。\n\n## 为什么要有 Material Files?\n\n因为喜爱 Material Design，并且是整洁、精致的 Material Design。\n\n市面上已经有了许多强大的文件管理器，但它们中的大多数并非 Material Design。而即使在算是 Material Design 的应用之中，它们（或多或少）有着各种设计瑕疵（布局、对齐、留白、图标、字体等等）存在于应用的各个角落，让人难受；然而却又不是特别大的问题，以至于可能没人愿意特意改善。所以还是需要自己编写。\n\n因为想要一个开源的文件管理器。\n\n大多数流行并且可靠的文件管理器都是闭源的，并且我有时会使用它们来查看或修改需要 root 权限的文件，但我心里对于授予 root 权限给闭源应用还是有些不安。毕竟 root 权限意味着对设备的完全访问权限，而这台设备每天跟随着我并且存储着我的个人信息；这样的话，闭源应用实际上做了哪些事情就完全仅仅取决于它们的良心了。\n\n因为想要一个正确实现的文件管理器。\n\n- 这个应用实现了 [Java NIO2 文件 API](https://docs.oracle.com/javase/8/docs/api/java/nio/file/package-summary.html) 作为后端，而不是发明一个自定义的机制来获取文件信息和进行文件操作。后者经常变得与前端逻辑耦合起来，最终成长为一个包含各种东西的混合物（[示例](https://github.com/TeamAmaze/AmazeFileManager/blob/master/app/src/main/java/com/amaze/filemanager/filesystem/HybridFile.java)）。相反地，一个解耦的后端可以使得代码更加干净（更少问题），并且可以更轻松地加入新文件系统的支持。\n\n- 这个应用没有使用 `java.io.File` 或者解析 `ls` 的输出，而是构建了 Linux 系统调用的绑定来正确地访问文件系统。`java.io.File` 是一个陈旧并且缺少许多功能的 API，并且无法正确处理符号链接，因此许多人宁愿解析 `ls` 的输出。然而解析 `ls` 的输出不仅缓慢，而且[不可靠](https://news.ycombinator.com/item?id=7994720)，同时也正是 [Cabinet](https://github.com/aminb/cabinet/blob/master/app/src/main/java/com/afollestad/cabinet/file/root/LsParser.java) 在新版本 Android 上无法正确运行的原因。而通过使用 Linux 系统调用，这个应用可以做到快速流畅，并且能够处理例如 Linux 权限、符号链接以至于 SELinux 上下文等等高级特性。应用也可以正确地处理含有无效 UTF-8 编码的文件名，因为应用中的路径没有简单地使用 Java 的 `String` 存储，而大多数其他文件管理器却并非如此，因此无法正确地进行文件操作。\n\n- 这个应用的前端是基于现代的 `ViewModel` 和 `LiveData` 实现的，使得代码结构更加清晰并且支持转屏。应用也可以正确地处理文件操作中的错误、文件冲突和前台/后台状态。\n\n总而言之，这个应用尽力遵守 Android 上的最佳实践并且做正确的事，同时保持源代码干净和可维护。\n\n因为事情可以被人做好。\n\n[Nautilus](https://wiki.gnome.org/Apps/Files) 是一个设计美观并且用户友好的 Linux 桌面上的文件管理器，并且同时做到了 Linux 友好。[Phonograph](https://github.com/kabouzeid/Phonograph) 是一个开源的 Material Design 音乐播放其应用（我自己已经使用多年），而它也有着绝佳的 Material Design 设计和实现。\n\n所以，是时候再编写一个 Android 文件管理器了。\n\n## 在定制 ROM 中集成\n\n如果您决定在您的定制 ROM 中集成这个应用，十分感谢！但是鉴于我曾收到过多起由不适当的集成导致的用户反馈，请允许我为了更好的用户体验提供一些关于正确集成这个应用的建议：\n\n- 请不要使用这个应用替换 AOSP 的 [DocumentsUI](https://android.googlesource.com/platform/packages/apps/DocumentsUI/) 应用。这个应用没有被设计成 DocumentsUI 的替代品并且缺少许多 DocumentsUI 中的功能——实际上，这个应用需要 DocumentsUI 来授予外置 SD 卡的访问权限。\n\n- 请确保这个应用可以被卸载或至少禁用。某些用户可能出于各种原因不想要这个应用，并且会在发现无法移除这个应用时十分恼火。\n\n- 请避免和这个应用的 Play/F-Droid 版本冲突。应用商店无法更新使用了不同证书进行签名的应用，所以您可以预置一个由我（或 F-Droid）签名的 APK 以使用户能够在 Play 或 F-Droid 上更新这个应用；或者如果您需要使用其他的证书进行签名（并进行其他更改），请复刻本项目并重命名它的软件包名。\n\n## 许可证\n\n    Copyright (C) 2018 Hai Zhang\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"
  },
  {
    "path": "SECURITY.md",
    "content": "# Security Policy\n\n## Supported Versions\n\nOnly the latest version of this app is currently being supported with security updates.\n\n## Reporting a Vulnerability\n\nPlease email dreaming.in.code.zh@gmail.com to report a vulnerability.\n"
  },
  {
    "path": "app/.gitignore",
    "content": "/.cxx/\n/.externalNativeBuild/\n/build/\n/out/\n"
  },
  {
    "path": "app/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.6)\n\nproject(MaterialFiles C)\n\nadd_library(hiddenapi SHARED\n        src/main/jni/hiddenapi.c)\ntarget_compile_options(hiddenapi\n        PRIVATE\n        -Wall\n        -Werror)\n\nfind_library(LOG_LIBRARY log)\nadd_library(syscall SHARED\n        src/main/jni/syscall.c)\ntarget_compile_options(syscall\n        PRIVATE\n        -Wall\n        -Werror)\ntarget_link_libraries(syscall\n        PRIVATE\n        ${LOG_LIBRARY})\n"
  },
  {
    "path": "app/build.gradle",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\napply plugin: 'com.android.application'\napply plugin: 'kotlin-android'\napply plugin: 'kotlin-parcelize'\n\napply from: '../signing.gradle'\n\n//#ifdef NONFREE\nbuildscript {\n    repositories {\n        google()\n        mavenCentral()\n    }\n    dependencies {\n        classpath 'com.google.gms:google-services:4.4.3'\n        classpath 'com.google.firebase:firebase-crashlytics-gradle:3.0.5'\n    }\n}\napply plugin: 'com.google.gms.google-services'\napply plugin: 'com.google.firebase.crashlytics'\n//#endif\n\nandroid {\n    namespace 'me.zhanghai.android.files'\n    buildToolsVersion = '36.0.0'\n    compileSdk 36\n    ndkVersion '28.1.13356709'\n    defaultConfig {\n        applicationId 'me.zhanghai.android.files'\n        minSdk 21\n        // Not supporting foreground service timeout yet.\n        //noinspection OldTargetApi\n        targetSdk 34\n        versionCode 39\n        versionName '1.7.4'\n        resValue 'string', 'app_version', versionName + ' (' + versionCode + ')'\n        buildConfigField 'String', 'FILE_PROVIDIER_AUTHORITY', 'APPLICATION_ID + \".file_provider\"'\n        resValue 'string', 'app_provider_authority', applicationId + '.app_provider'\n        resValue 'string', 'file_provider_authority', applicationId + '.file_provider'\n        externalNativeBuild {\n            cmake {\n                arguments '-DANDROID_STL=none'\n            }\n        }\n    }\n    buildFeatures {\n        aidl true\n        buildConfig true\n        viewBinding true\n    }\n    androidResources {\n        generateLocaleConfig true\n    }\n    compileOptions {\n        coreLibraryDesugaringEnabled true\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n    kotlinOptions {\n        jvmTarget = JavaVersion.VERSION_1_8.toString()\n    }\n    externalNativeBuild {\n        cmake {\n            path 'CMakeLists.txt'\n        }\n    }\n    lint {\n        // For \"Invalid package reference in library; not included in Android: javax.security.sasl.\n        // Referenced from org.apache.mina.proxy.ProxyAuthException.\"\n        warning 'InvalidPackage', 'MissingTranslation'\n    }\n    buildTypes {\n        release {\n            minifyEnabled true\n            shrinkResources true\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n            signingConfig signingConfigs.release\n//#ifdef NONFREE\n            firebaseCrashlytics {\n                nativeSymbolUploadEnabled true\n            }\n//#endif\n        }\n    }\n    packagingOptions {\n        jniLibs {\n            useLegacyPackaging true\n        }\n        resources {\n            excludes += [\n                    'META-INF/DEPENDENCIES',\n                    'org/bouncycastle/pqc/crypto/picnic/*'\n            ]\n        }\n    }\n    bundle {\n        language {\n            enableSplit = false\n        }\n    }\n}\n\nrepositories {\n    maven {\n        url 'https://jitpack.io'\n    }\n}\ndependencies {\n    // We need https://github.com/bitfireAT/dav4jvm/commit/c1bc14348831bcdb00f3a6eec4859b81c7dc3728\n    implementation('com.github.bitfireAT:dav4jvm:ec6264d427') {\n        exclude group: 'org.ogce', module: 'xpp3'\n    }\n    implementation 'com.github.chrisbanes:PhotoView:2.3.0'\n    implementation 'com.github.topjohnwu.libsu:service:5.2.2'\n}\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n\n    coreLibraryDesugaring(\"com.android.tools:desugar_jdk_libs:2.1.5\")\n\n    // kotlinx-coroutines-android depends on kotlin-stdlib-jdk8\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version\"\n    def kotlinx_coroutines_version = '1.10.2'\n    implementation \"org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinx_coroutines_version\"\n    implementation \"org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinx_coroutines_version\"\n\n    implementation 'androidx.activity:activity-ktx:1.10.1'\n    implementation 'androidx.appcompat:appcompat:1.7.1'\n    implementation 'androidx.core:core-ktx:1.16.0'\n    implementation 'androidx.drawerlayout:drawerlayout:1.2.0'\n    implementation 'androidx.exifinterface:exifinterface:1.4.1'\n    implementation 'androidx.fragment:fragment-ktx:1.8.8'\n    def androidx_lifecycle_version = '2.9.2'\n    implementation \"androidx.lifecycle:lifecycle-common-java8:$androidx_lifecycle_version\"\n    implementation \"androidx.lifecycle:lifecycle-livedata-ktx:$androidx_lifecycle_version\"\n    implementation \"androidx.lifecycle:lifecycle-process:$androidx_lifecycle_version\"\n    implementation \"androidx.lifecycle:lifecycle-runtime-ktx:$androidx_lifecycle_version\"\n    implementation \"androidx.lifecycle:lifecycle-viewmodel-ktx:$androidx_lifecycle_version\"\n    implementation 'androidx.preference:preference-ktx:1.2.1'\n    implementation 'androidx.recyclerview:recyclerview:1.4.0'\n    implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'\n    implementation 'com.google.android.material:material:1.12.0'\n\n    implementation 'com.caverock:androidsvg-aar:1.4'\n    implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0'\n    implementation 'com.drakeet.drawer:drawer:1.0.3'\n    implementation 'com.h6ah4i.android.materialshadowninepatch:materialshadowninepatch:1.0.0'\n    implementation 'com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:1.0.0'\n    // SMBJ 0.12.0 breaks anonymous authentication: https://github.com/hierynomus/smbj/issues/792\n    //noinspection GradleDependency\n    implementation ('com.hierynomus:smbj:0.11.5') {\n        // org.bouncycastle:bcprov-jdk15on uses bytecode version unsupported by Jetifier, so use\n        // org.bouncycastle:bcprov-jdk15to18 instead.\n        exclude group: 'org.bouncycastle', module: 'bcprov-jdk15on'\n    }\n    def bouncy_castle_version = '1.81'\n    implementation \"org.bouncycastle:bcprov-jdk15to18:$bouncy_castle_version\"\n    implementation ('com.hierynomus:sshj:0.40.0') {\n        exclude group: 'org.bouncycastle', module: 'bcprov-jdk18on'\n        exclude group: 'org.bouncycastle', module: 'bcpkix-jdk18on'\n    }\n    implementation \"org.bouncycastle:bcpkix-jdk15to18:$bouncy_castle_version\"\n    implementation 'com.leinardi.android:speed-dial:3.3.0'\n    implementation ('com.rapid7.client:dcerpc:0.12.13') {\n        // SMBJ-RPC depends on the JRE flavor of Guava which targets Java 8.\n        exclude group: 'com.google.guava', module: 'guava'\n        // Prevent promotion of SMBJ version to 0.12.2.\n        exclude group: 'com.hierynomus', module: 'smbj'\n        exclude group: 'org.bouncycastle', module: 'bcprov-jdk18on'\n    }\n    implementation 'com.google.guava:guava:33.4.8-android'\n    // Guava conflicts with com.google.guava:listenablefuture:1.0 pulled in by AndroidX Core\n    implementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava'\n    implementation 'com.takisoft.preferencex:preferencex:1.1.0'\n    implementation 'commons-net:commons-net:3.12.0'\n    // LicensesDialog 2.2.0 pulls in androidx.webkit and uses setForceDark() instead of correctly\n    // setting colors.\n    //noinspection GradleDependency\n    implementation 'de.psdev.licensesdialog:licensesdialog:2.1.0'\n    // dev.chrisbanesinsetter:insetter:0.6.0 makes inset unstable when entering immersive.\n    implementation 'dev.chrisbanes:insetter-ktx:0.3.1'\n    implementation 'dev.rikka.rikkax.preference:simplemenu-preference:1.0.3'\n    implementation 'dev.rikka.shizuku:api:13.1.5'\n    implementation ('eu.agno3.jcifs:jcifs-ng:2.1.10') {\n        exclude group: 'org.bouncycastle', module: 'bcprov-jdk18on'\n    }\n    implementation platform('io.coil-kt:coil-bom:2.7.0')\n    implementation 'io.coil-kt:coil'\n    implementation 'io.coil-kt:coil-gif'\n    implementation 'io.coil-kt:coil-svg'\n    implementation 'io.coil-kt:coil-video'\n    implementation 'me.zhanghai.android.appiconloader:appiconloader:1.5.0'\n    implementation 'me.zhanghai.android.fastscroll:library:1.3.0'\n    implementation 'me.zhanghai.android.foregroundcompat:library:1.0.2'\n    implementation 'me.zhanghai.android.libarchive:library:1.1.6'\n    implementation 'me.zhanghai.android.libselinux:library:2.1.1'\n    implementation 'me.zhanghai.android.retrofile:library:1.2.0'\n    implementation 'me.zhanghai.android.systemuihelper:library:1.0.0'\n    implementation 'org.apache.ftpserver:ftpserver-core:1.2.1'\n    // This is a dependency of org.apache.ftpserver:ftpserver-core but org.apache.mina:mina-core\n    // 2.1.4+ became incompatible before API 24 due to dependency on StandardSocketOptions\n    // (DIRMINA-1123) and NetworkChannel.supportedOptions() (DIRMINA-1130).\n    implementation ('org.apache.mina:mina-core') {\n        version {\n            strictly '2.1.3'\n        }\n    }\n    // Also a dependency of jCIFS-NG.\n    implementation 'org.slf4j:slf4j-android:1.7.36'\n\n//#ifdef NONFREE\n    implementation platform('com.google.firebase:firebase-bom:34.0.0')\n    implementation 'com.google.firebase:firebase-analytics'\n    implementation 'com.google.firebase:firebase-crashlytics-ndk'\n//#endif\n}\n"
  },
  {
    "path": "app/google-services.json",
    "content": "{\n  \"project_info\": {\n    \"project_number\": \"728501351509\",\n    \"firebase_url\": \"https://zhanghai-materialfiles.firebaseio.com\",\n    \"project_id\": \"zhanghai-materialfiles\",\n    \"storage_bucket\": \"zhanghai-materialfiles.appspot.com\"\n  },\n  \"client\": [\n    {\n      \"client_info\": {\n        \"mobilesdk_app_id\": \"1:728501351509:android:007cb5eb181702a9\",\n        \"android_client_info\": {\n          \"package_name\": \"me.zhanghai.android.files\"\n        }\n      },\n      \"oauth_client\": [\n        {\n          \"client_id\": \"728501351509-3ul5c5u8jpd9q9gi4e7537bd31r5nkr5.apps.googleusercontent.com\",\n          \"client_type\": 3\n        }\n      ],\n      \"api_key\": [\n        {\n          \"current_key\": \"AIzaSyDUcQAOKRcCjsA-e5-cZHbt4HQQFDP4wA4\"\n        }\n      ],\n      \"services\": {\n        \"appinvite_service\": {\n          \"other_platform_oauth_client\": [\n            {\n              \"client_id\": \"728501351509-3ul5c5u8jpd9q9gi4e7537bd31r5nkr5.apps.googleusercontent.com\",\n              \"client_type\": 3\n            }\n          ]\n        }\n      }\n    }\n  ],\n  \"configuration_version\": \"1\"\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# Native methods\n# https://www.guardsquare.com/en/products/proguard/manual/examples#native\n-keepclasseswithmembernames,includedescriptorclasses class * {\n    native <methods>;\n}\n\n# App\n-keep class me.zhanghai.android.files.** implements androidx.appcompat.view.CollapsibleActionView { *; }\n-keep class me.zhanghai.android.files.provider.common.ByteString { *; }\n-keep class me.zhanghai.android.files.provider.linux.syscall.** { *; }\n-keepnames class * extends java.lang.Exception\n# For Class.getEnumConstants()\n-keepclassmembers enum * {\n    public static **[] values();\n}\n-keepnames class me.zhanghai.android.files.** implements android.os.Parcelable\n\n# Apache FtpServer\n-keepclassmembers class * implements org.apache.mina.core.service.IoProcessor {\n    public <init>(java.util.concurrent.ExecutorService);\n    public <init>(java.util.concurrent.Executor);\n    public <init>();\n}\n\n# Bouncy Castle\n-keep class org.bouncycastle.jcajce.provider.** { *; }\n-keep class org.bouncycastle.jce.provider.** { *; }\n\n# SMBJ\n-dontwarn javax.el.**\n-dontwarn org.ietf.jgss.**\n-dontwarn sun.security.x509.X509Key\n\n# SMBJ-RPC\n-dontwarn java.rmi.UnmarshalException\n"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<manifest\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\">\n\n    <uses-feature android:name=\"android.hardware.touchscreen\" android:required=\"false\" />\n    <uses-feature android:name=\"android.hardware.wifi\" android:required=\"false\" />\n    <uses-feature android:name=\"android.software.leanback\" android:required=\"false\" />\n\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n    <uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" />\n    <uses-permission android:name=\"android.permission.FOREGROUND_SERVICE\" />\n    <uses-permission android:name=\"android.permission.FOREGROUND_SERVICE_DATA_SYNC\" />\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <uses-permission\n        android:name=\"android.permission.MANAGE_EXTERNAL_STORAGE\"\n        tools:ignore=\"ScopedStorage\" />\n    <uses-permission android:name=\"android.permission.POST_NOTIFICATIONS\" />\n    <uses-permission\n        android:name=\"android.permission.QUERY_ALL_PACKAGES\"\n        tools:ignore=\"QueryAllPackagesPermission\" />\n    <uses-permission android:name=\"android.permission.REQUEST_INSTALL_PACKAGES\" />\n    <uses-permission android:name=\"android.permission.WAKE_LOCK\" />\n    <uses-permission\n        android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"\n        android:maxSdkVersion=\"32\" />\n    <uses-permission android:name=\"com.android.launcher.permission.INSTALL_SHORTCUT\" />\n\n    <!-- Crashlytics and Shizuku requires API 23. -->\n    <uses-sdk tools:overrideLibrary=\"com.google.firebase,com.google.firebase.components,com.google.firebase.crashlytics,com.google.firebase.crashlytics.ndk,com.google.firebase.installations,com.google.firebase.sessions,rikka.shizuku.aidl,rikka.shizuku.api,rikka.shizuku.shared\" />\n\n    <!--\n      ~ Samsung DeX requires explicitly setting android:resizeableActivity=\"true\" for the app to be\n      ~ resizable.\n      ~ TODO: Remove this attribute once Samsung respects the default value for it.\n      -->\n    <application\n        android:allowBackup=\"true\"\n        android:banner=\"@drawable/banner\"\n        android:enableOnBackInvokedCallback=\"true\"\n        android:fullBackupContent=\"true\"\n        android:icon=\"@mipmap/launcher_icon\"\n        android:label=\"@string/app_name\"\n        android:networkSecurityConfig=\"@xml/netework_security_config\"\n        android:requestLegacyExternalStorage=\"true\"\n        android:requestRawExternalStorageAccess=\"true\"\n        android:resizeableActivity=\"true\"\n        android:roundIcon=\"@mipmap/launcher_icon\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/Theme.MaterialFiles\"\n        android:usesCleartextTraffic=\"true\"\n        tools:ignore=\"GoogleAppIndexingWarning,UnusedAttribute\">\n\n        <activity\n            android:name=\"me.zhanghai.android.files.filelist.FileListActivity\"\n            android:exported=\"true\"\n            android:visibleToInstantApps=\"true\"\n            tools:ignore=\"UnusedAttribute\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n                <category android:name=\"android.intent.category.LEANBACK_LAUNCHER\" />\n            </intent-filter>\n            <intent-filter tools:ignore=\"AppLinkUrlError\">\n                <action android:name=\"android.intent.action.VIEW\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <data android:mimeType=\"inode/directory\" />\n                <data android:mimeType=\"resource/folder\" />\n                <data android:mimeType=\"vnd.android.document/directory\" />\n            </intent-filter>\n            <!-- @see me.zhanghai.android.files.file.isSupportedArchive -->\n            <intent-filter\n                android:label=\"@string/archive_viewer_title\"\n                tools:ignore=\"AppLinkUrlError\">\n                <action android:name=\"android.intent.action.VIEW\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <data android:mimeType=\"application/gzip\" />\n                <data android:mimeType=\"application/java-archive\" />\n                <data android:mimeType=\"application/rar\" />\n                <data android:mimeType=\"application/zip\" />\n                <data android:mimeType=\"application/zstd\" />\n                <data android:mimeType=\"application/vnd.android.package-archive\" />\n                <data android:mimeType=\"application/vnd.debian.binary-package\" />\n                <data android:mimeType=\"application/vnd.ms-cab-compressed\" />\n                <data android:mimeType=\"application/vnd.rar\" />\n                <data android:mimeType=\"application/x-bzip2\" />\n                <data android:mimeType=\"application/x-compress\" />\n                <data android:mimeType=\"application/x-cpio\" />\n                <data android:mimeType=\"application/x-deb\" />\n                <data android:mimeType=\"application/x-debian-package\" />\n                <data android:mimeType=\"application/x-gtar\" />\n                <data android:mimeType=\"application/x-gtar-compressed\" />\n                <data android:mimeType=\"application/x-java-archive\" />\n                <data android:mimeType=\"application/x-lzma\" />\n                <data android:mimeType=\"application/x-tar\" />\n                <data android:mimeType=\"application/x-xz\" />\n                <data android:mimeType=\"application/x-7z-compressed\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.intent.action.GET_CONTENT\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.OPENABLE\" />\n                <data android:mimeType=\"*/*\" />\n            </intent-filter>\n            <!-- @see https://android.googlesource.com/platform/packages/apps/DocumentsUI/+/main/AndroidManifest.xml -->\n            <intent-filter>\n                <action android:name=\"android.intent.action.OPEN_DOCUMENT\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.OPENABLE\" />\n                <data android:mimeType=\"*/*\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.intent.action.CREATE_DOCUMENT\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.OPENABLE\" />\n                <data android:mimeType=\"*/*\" />\n            </intent-filter>\n            <!--\n              ~ Unusable until we implement DocumentsProvider.\n            <intent-filter>\n                <action android:name=\"android.intent.action.OPEN_DOCUMENT_TREE\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n            </intent-filter>\n            -->\n            <intent-filter>\n                <action android:name=\"me.zhanghai.android.files.intent.action.VIEW_DOWNLOADS\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n            </intent-filter>\n            <meta-data\n                android:name=\"android.app.shortcuts\"\n                android:resource=\"@xml/shortcuts\" />\n        </activity>\n\n        <!--\n          ~ Using android:documentLaunchMode=\"always\" gives a better result than\n          ~ Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK. Not sure why though.\n          -->\n        <activity\n            android:name=\"me.zhanghai.android.files.filelist.OpenFileActivity\"\n            android:documentLaunchMode=\"always\"\n            android:excludeFromRecents=\"true\"\n            android:exported=\"true\"\n            android:theme=\"@style/Theme.MaterialFiles.Translucent\">\n            <intent-filter>\n                <action android:name=\"me.zhanghai.android.files.intent.action.OPEN_FILE\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <data android:mimeType=\"*/*\" />\n            </intent-filter>\n        </activity>\n\n        <activity\n            android:name=\"me.zhanghai.android.files.filelist.EditFileActivity\"\n            android:autoRemoveFromRecents=\"true\"\n            android:icon=\"@drawable/edit_icon\"\n            android:label=\"@string/file_edit_title\"\n            android:theme=\"@style/Theme.MaterialFiles.Translucent\" />\n\n        <activity\n            android:name=\"me.zhanghai.android.files.filelist.OpenFileAsDialogActivity\"\n            android:autoRemoveFromRecents=\"true\"\n            android:icon=\"@drawable/open_as_icon\"\n            android:label=\"@string/file_open_as_title\"\n            android:theme=\"@style/Theme.MaterialFiles.Translucent\" />\n\n        <activity\n            android:name=\"me.zhanghai.android.files.storage.AddStorageDialogActivity\"\n            android:label=\"@string/storage_add_storage_title\"\n            android:theme=\"@style/Theme.MaterialFiles.Translucent\" />\n\n        <activity\n            android:name=\"me.zhanghai.android.files.storage.EditDeviceStorageDialogActivity\"\n            android:label=\"@string/storage_edit_device_storage_title\"\n            android:theme=\"@style/Theme.MaterialFiles.Translucent\" />\n\n        <activity\n            android:name=\"me.zhanghai.android.files.storage.AddExternalStorageShortcutActivity\"\n            android:label=\"@string/storage_add_external_storage_shortcut_title\"\n            android:theme=\"@style/Theme.MaterialFiles.Translucent\" />\n\n        <activity\n            android:name=\"me.zhanghai.android.files.storage.EditExternalStorageShortcutDialogActivity\"\n            android:label=\"@string/storage_edit_external_storage_shortcut_title\"\n            android:theme=\"@style/Theme.MaterialFiles.Translucent\" />\n\n        <activity\n            android:name=\"me.zhanghai.android.files.storage.AddDocumentTreeActivity\"\n            android:label=\"@string/storage_add_document_tree_title\"\n            android:theme=\"@style/Theme.MaterialFiles.Translucent\" />\n\n        <activity\n            android:name=\"me.zhanghai.android.files.storage.EditDocumentTreeDialogActivity\"\n            android:label=\"@string/storage_edit_document_tree_title\"\n            android:theme=\"@style/Theme.MaterialFiles.Translucent\" />\n\n        <activity\n            android:name=\"me.zhanghai.android.files.storage.EditFtpServerActivity\"\n            android:label=\"@string/storage_edit_ftp_server_title_edit\"\n            android:theme=\"@style/Theme.MaterialFiles\" />\n\n        <activity\n            android:name=\"me.zhanghai.android.files.storage.EditSftpServerActivity\"\n            android:label=\"@string/storage_edit_sftp_server_title_edit\"\n            android:theme=\"@style/Theme.MaterialFiles\" />\n\n        <activity\n            android:name=\"me.zhanghai.android.files.storage.AddLanSmbServerActivity\"\n            android:label=\"@string/storage_add_lan_smb_server_title\"\n            android:theme=\"@style/Theme.MaterialFiles\" />\n\n        <activity\n            android:name=\"me.zhanghai.android.files.storage.EditSmbServerActivity\"\n            android:label=\"@string/storage_edit_smb_server_title_edit\"\n            android:theme=\"@style/Theme.MaterialFiles\" />\n\n        <activity\n            android:name=\"me.zhanghai.android.files.storage.EditWebDavServerActivity\"\n            android:label=\"@string/storage_edit_webdav_server_title_edit\"\n            android:theme=\"@style/Theme.MaterialFiles\" />\n\n        <activity\n            android:name=\"me.zhanghai.android.files.navigation.EditBookmarkDirectoryDialogActivity\"\n            android:label=\"@string/navigation_edit_bookmark_directory_title\"\n            android:theme=\"@style/Theme.MaterialFiles.Translucent\" />\n\n        <activity\n            android:name=\"me.zhanghai.android.files.ftpserver.FtpServerActivity\"\n            android:exported=\"true\"\n            android:label=\"@string/ftp_server_title\"\n            android:launchMode=\"singleTop\"\n            android:theme=\"@style/Theme.MaterialFiles\">\n            <intent-filter>\n                <action android:name=\"me.zhanghai.android.files.intent.action.MANAGE_FTP_SERVER\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.service.quicksettings.action.QS_TILE_PREFERENCES\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n            </intent-filter>\n        </activity>\n\n        <activity\n            android:name=\"me.zhanghai.android.files.settings.SettingsActivity\"\n            android:exported=\"true\"\n            android:label=\"@string/settings_title\"\n            android:launchMode=\"singleTop\"\n            android:theme=\"@style/Theme.MaterialFiles\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.APPLICATION_PREFERENCES\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n            </intent-filter>\n        </activity>\n\n        <activity\n            android:name=\"me.zhanghai.android.files.storage.StorageListActivity\"\n            android:label=\"@string/storage_list_title\"\n            android:launchMode=\"singleTop\"\n            android:theme=\"@style/Theme.MaterialFiles\" />\n\n        <activity\n            android:name=\"me.zhanghai.android.files.settings.StandardDirectoryListActivity\"\n            android:label=\"@string/settings_standard_directory_list_title\"\n            android:launchMode=\"singleTop\"\n            android:theme=\"@style/Theme.MaterialFiles\" />\n\n        <activity\n            android:name=\"me.zhanghai.android.files.settings.BookmarkDirectoryListActivity\"\n            android:label=\"@string/settings_bookmark_directory_list_title\"\n            android:launchMode=\"singleTop\"\n            android:theme=\"@style/Theme.MaterialFiles\" />\n\n        <activity\n            android:name=\"me.zhanghai.android.files.about.AboutActivity\"\n            android:label=\"@string/about_title\"\n            android:launchMode=\"singleTop\"\n            android:theme=\"@style/Theme.MaterialFiles\" />\n\n        <activity\n            android:name=\"me.zhanghai.android.files.fileaction.ArchivePasswordDialogActivity\"\n            android:autoRemoveFromRecents=\"true\"\n            android:theme=\"@style/Theme.MaterialFiles.Translucent\" />\n\n        <activity\n            android:name=\"me.zhanghai.android.files.filejob.FileJobErrorDialogActivity\"\n            android:autoRemoveFromRecents=\"true\"\n            android:theme=\"@style/Theme.MaterialFiles.Translucent\" />\n\n        <activity\n            android:name=\"me.zhanghai.android.files.filejob.FileJobConflictDialogActivity\"\n            android:autoRemoveFromRecents=\"true\"\n            android:theme=\"@style/Theme.MaterialFiles.Translucent\" />\n\n        <activity\n            android:name=\"me.zhanghai.android.files.viewer.saveas.SaveAsActivity\"\n            android:autoRemoveFromRecents=\"true\"\n            android:exported=\"true\"\n            android:label=\"@string/save_as_title\"\n            android:theme=\"@style/Theme.MaterialFiles.Translucent\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <data\n                    android:mimeType=\"*/*\"\n                    android:scheme=\"content\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.intent.action.SEND\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <data android:mimeType=\"*/*\" />\n            </intent-filter>\n        </activity>\n\n        <activity\n            android:name=\"me.zhanghai.android.files.viewer.text.TextEditorActivity\"\n            android:exported=\"true\"\n            android:label=\"@string/text_editor_title\"\n            android:theme=\"@style/Theme.MaterialFiles\">\n            <intent-filter tools:ignore=\"AppLinkUrlError\">\n                <action android:name=\"android.intent.action.VIEW\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <data android:mimeType=\"application/ecmascript\" />\n                <data android:mimeType=\"application/javascript\" />\n                <data android:mimeType=\"application/json\" />\n                <data android:mimeType=\"application/typescript\" />\n                <data android:mimeType=\"application/x-sh\" />\n                <data android:mimeType=\"application/x-shellscript\" />\n                <data android:mimeType=\"application/xml\" />\n                <data android:mimeType=\"application/yaml\" />\n                <data android:mimeType=\"text/*\" />\n            </intent-filter>\n        </activity>\n\n        <activity\n            android:name=\"me.zhanghai.android.files.viewer.image.ImageViewerActivity\"\n            android:exported=\"true\"\n            android:label=\"@string/image_viewer_title\"\n            android:theme=\"@style/Theme.MaterialFiles.Immersive\">\n            <intent-filter tools:ignore=\"AppLinkUrlError\">\n                <action android:name=\"android.intent.action.VIEW\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <data android:mimeType=\"image/*\" />\n            </intent-filter>\n        </activity>\n\n        <service\n            android:name=\"me.zhanghai.android.files.filejob.FileJobService\"\n            android:foregroundServiceType=\"dataSync\" />\n\n        <service android:name=\"me.zhanghai.android.files.ftpserver.FtpServerService\"\n            android:foregroundServiceType=\"dataSync\" />\n\n        <service\n            android:name=\"me.zhanghai.android.files.ftpserver.FtpServerTileService\"\n            android:exported=\"true\"\n            android:icon=\"@drawable/shared_directory_icon_white_24dp\"\n            android:label=\"@string/ftp_server_title\"\n            android:permission=\"android.permission.BIND_QUICK_SETTINGS_TILE\"\n            tools:ignore=\"NewApi\">\n            <intent-filter>\n                <action android:name=\"android.service.quicksettings.action.QS_TILE\"/>\n            </intent-filter>\n            <meta-data\n                android:name=\"android.service.quicksettings.TOGGLEABLE_TILE\"\n                android:value=\"true\" />\n        </service>\n\n        <service\n            android:name=\"androidx.appcompat.app.AppLocalesMetadataHolderService\"\n            android:enabled=\"false\"\n            android:exported=\"false\">\n            <meta-data\n                android:name=\"autoStoreLocales\"\n                android:value=\"true\" />\n        </service>\n\n        <provider\n            android:name=\"me.zhanghai.android.files.app.AppProvider\"\n            android:authorities=\"@string/app_provider_authority\"\n            android:exported=\"false\" />\n\n        <provider\n            android:name=\"me.zhanghai.android.files.file.FileProvider\"\n            android:authorities=\"@string/file_provider_authority\"\n            android:exported=\"false\"\n            android:grantUriPermissions=\"true\" />\n\n        <receiver android:name=\"me.zhanghai.android.files.filejob.FileJobReceiver\" />\n\n        <receiver android:name=\"me.zhanghai.android.files.ftpserver.FtpServerReceiver\" />\n\n        <meta-data\n            android:name=\"firebase_crashlytics_collection_enabled\"\n            android:value=\"false\" />\n    </application>\n</manifest>\n"
  },
  {
    "path": "app/src/main/aidl/me/zhanghai/android/files/provider/common/ParcelableFileTime.aidl",
    "content": "package me.zhanghai.android.files.provider.common;\n\nparcelable ParcelableFileTime;\n"
  },
  {
    "path": "app/src/main/aidl/me/zhanghai/android/files/provider/common/ParcelablePosixFileMode.aidl",
    "content": "package me.zhanghai.android.files.provider.common;\n\nparcelable ParcelablePosixFileMode;\n"
  },
  {
    "path": "app/src/main/aidl/me/zhanghai/android/files/provider/common/PosixGroup.aidl",
    "content": "package me.zhanghai.android.files.provider.common;\n\nparcelable PosixGroup;\n"
  },
  {
    "path": "app/src/main/aidl/me/zhanghai/android/files/provider/common/PosixUser.aidl",
    "content": "package me.zhanghai.android.files.provider.common;\n\nparcelable PosixUser;\n"
  },
  {
    "path": "app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemoteFileService.aidl",
    "content": "package me.zhanghai.android.files.provider.remote;\n\nimport me.zhanghai.android.files.provider.remote.IRemoteFileSystem;\nimport me.zhanghai.android.files.provider.remote.IRemoteFileSystemProvider;\nimport me.zhanghai.android.files.provider.remote.IRemotePosixFileAttributeView;\nimport me.zhanghai.android.files.provider.remote.IRemotePosixFileStore;\nimport me.zhanghai.android.files.provider.remote.ParcelableObject;\n\ninterface IRemoteFileService {\n    IRemoteFileSystemProvider getRemoteFileSystemProviderInterface(String scheme);\n\n    IRemoteFileSystem getRemoteFileSystemInterface(in ParcelableObject fileSystem);\n\n    IRemotePosixFileStore getRemotePosixFileStoreInterface(in ParcelableObject fileStore);\n\n    IRemotePosixFileAttributeView getRemotePosixFileAttributeViewInterface(\n        in ParcelableObject attributeView\n    );\n}\n"
  },
  {
    "path": "app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemoteFileSystem.aidl",
    "content": "package me.zhanghai.android.files.provider.remote;\n\nimport me.zhanghai.android.files.provider.remote.ParcelableException;\n\ninterface IRemoteFileSystem {\n    void close(out ParcelableException exception);\n}\n"
  },
  {
    "path": "app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemoteFileSystemProvider.aidl",
    "content": "package me.zhanghai.android.files.provider.remote;\n\nimport me.zhanghai.android.files.provider.remote.ParcelableCopyOptions;\nimport me.zhanghai.android.files.provider.remote.ParcelableDirectoryStream;\nimport me.zhanghai.android.files.provider.remote.ParcelableException;\nimport me.zhanghai.android.files.provider.remote.ParcelableFileAttributes;\nimport me.zhanghai.android.files.provider.remote.ParcelableObject;\nimport me.zhanghai.android.files.provider.remote.ParcelablePathListConsumer;\nimport me.zhanghai.android.files.provider.remote.ParcelableSerializable;\nimport me.zhanghai.android.files.provider.remote.RemotePathObservable;\nimport me.zhanghai.android.files.provider.remote.RemoteInputStream;\nimport me.zhanghai.android.files.provider.remote.RemoteSeekableByteChannel;\nimport me.zhanghai.android.files.util.RemoteCallback;\n\ninterface IRemoteFileSystemProvider {\n    RemoteInputStream newInputStream(\n        in ParcelableObject file,\n        in ParcelableSerializable options,\n        out ParcelableException exception\n    );\n\n    RemoteSeekableByteChannel newByteChannel(\n        in ParcelableObject file,\n        in ParcelableSerializable options,\n        in ParcelableFileAttributes attributes,\n        out ParcelableException exception\n    );\n\n    ParcelableDirectoryStream newDirectoryStream(\n        in ParcelableObject directory,\n        in ParcelableObject filter,\n        out ParcelableException exception\n    );\n\n    void createDirectory(\n        in ParcelableObject directory,\n        in ParcelableFileAttributes attributes,\n        out ParcelableException exception\n    );\n\n    void createSymbolicLink(\n        in ParcelableObject link,\n        in ParcelableObject target,\n        in ParcelableFileAttributes attributes,\n        out ParcelableException exception\n    );\n\n    void createLink(\n        in ParcelableObject link,\n        in ParcelableObject existing,\n        out ParcelableException exception\n    );\n\n    void delete(in ParcelableObject path, out ParcelableException exception);\n\n    ParcelableObject readSymbolicLink(in ParcelableObject link, out ParcelableException exception);\n\n    RemoteCallback copy(\n        in ParcelableObject source,\n        in ParcelableObject target,\n        in ParcelableCopyOptions options,\n        in RemoteCallback callback\n    );\n\n    RemoteCallback move(\n        in ParcelableObject source,\n        in ParcelableObject target,\n        in ParcelableCopyOptions options,\n        in RemoteCallback callback\n    );\n\n    boolean isSameFile(\n        in ParcelableObject path,\n        in ParcelableObject path2,\n        out ParcelableException exception\n    );\n\n    boolean isHidden(in ParcelableObject path, out ParcelableException exception);\n\n    ParcelableObject getFileStore(in ParcelableObject path, out ParcelableException exception);\n\n    void checkAccess(\n        in ParcelableObject path,\n        in ParcelableSerializable modes,\n        out ParcelableException exception\n    );\n\n    ParcelableObject readAttributes(\n        in ParcelableObject path,\n        in ParcelableSerializable type,\n        in ParcelableSerializable options,\n        out ParcelableException exception\n    );\n\n    RemotePathObservable observe(\n        in ParcelableObject path,\n        long intervalMillis,\n        out ParcelableException exception\n    );\n\n    RemoteCallback search(\n        in ParcelableObject directory,\n        in String query,\n        long intervalMillis,\n        in ParcelablePathListConsumer listener,\n        in RemoteCallback callback\n    );\n}\n"
  },
  {
    "path": "app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemoteInputStream.aidl",
    "content": "package me.zhanghai.android.files.provider.remote;\n\nimport me.zhanghai.android.files.provider.remote.ParcelableException;\n\ninterface IRemoteInputStream {\n    int read(out ParcelableException exception);\n\n    int read2(out byte[] buffer, out ParcelableException exception);\n\n    long skip(long size, out ParcelableException exception);\n\n    int available(out ParcelableException exception);\n\n    void close(out ParcelableException exception);\n}\n"
  },
  {
    "path": "app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemotePathObservable.aidl",
    "content": "package me.zhanghai.android.files.provider.remote;\n\nimport me.zhanghai.android.files.provider.remote.ParcelableException;\nimport me.zhanghai.android.files.util.RemoteCallback;\n\ninterface IRemotePathObservable {\n    void addObserver(in RemoteCallback observer);\n\n    void close(out ParcelableException exception);\n}\n"
  },
  {
    "path": "app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemotePosixFileAttributeView.aidl",
    "content": "package me.zhanghai.android.files.provider.remote;\n\nimport me.zhanghai.android.files.provider.common.ParcelableFileTime;\nimport me.zhanghai.android.files.provider.common.ParcelablePosixFileMode;\nimport me.zhanghai.android.files.provider.common.PosixGroup;\nimport me.zhanghai.android.files.provider.common.PosixUser;\nimport me.zhanghai.android.files.provider.remote.ParcelableException;\nimport me.zhanghai.android.files.provider.remote.ParcelableObject;\n\ninterface IRemotePosixFileAttributeView {\n    ParcelableObject readAttributes(out ParcelableException exception);\n\n    void setTimes(\n        in ParcelableFileTime lastModifiedTime,\n        in ParcelableFileTime lastAccessTime,\n        in ParcelableFileTime createTime,\n        out ParcelableException exception\n    );\n\n    void setOwner(in PosixUser owner, out ParcelableException exception);\n\n    void setGroup(in PosixGroup group, out ParcelableException exception);\n\n    void setMode(in ParcelablePosixFileMode mode, out ParcelableException exception);\n\n    void setSeLinuxContext(in ParcelableObject context, out ParcelableException exception);\n\n    void restoreSeLinuxContext(out ParcelableException exception);\n}\n"
  },
  {
    "path": "app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemotePosixFileStore.aidl",
    "content": "package me.zhanghai.android.files.provider.remote;\n\nimport me.zhanghai.android.files.provider.remote.ParcelableException;\n\ninterface IRemotePosixFileStore {\n    void setReadOnly(boolean readOnly, out ParcelableException exception);\n\n    long getTotalSpace(out ParcelableException exception);\n\n    long getUsableSpace(out ParcelableException exception);\n\n    long getUnallocatedSpace(out ParcelableException exception);\n}\n"
  },
  {
    "path": "app/src/main/aidl/me/zhanghai/android/files/provider/remote/IRemoteSeekableByteChannel.aidl",
    "content": "package me.zhanghai.android.files.provider.remote;\n\nimport me.zhanghai.android.files.provider.remote.ParcelableException;\n\ninterface IRemoteSeekableByteChannel {\n    int read(out byte[] destination, out ParcelableException exception);\n\n    int write(in byte[] source, out ParcelableException exception);\n\n    long position(out ParcelableException exception);\n\n    void position2(long newPosition, out ParcelableException exception);\n\n    long size(out ParcelableException exception);\n\n    void truncate(long size, out ParcelableException exception);\n\n    void force(boolean metaData, out ParcelableException exception);\n\n    void close(out ParcelableException exception);\n}\n"
  },
  {
    "path": "app/src/main/aidl/me/zhanghai/android/files/provider/remote/ParcelableCopyOptions.aidl",
    "content": "package me.zhanghai.android.files.provider.remote;\n\nparcelable ParcelableCopyOptions;\n"
  },
  {
    "path": "app/src/main/aidl/me/zhanghai/android/files/provider/remote/ParcelableDirectoryStream.aidl",
    "content": "package me.zhanghai.android.files.provider.remote;\n\nparcelable ParcelableDirectoryStream;\n"
  },
  {
    "path": "app/src/main/aidl/me/zhanghai/android/files/provider/remote/ParcelableException.aidl",
    "content": "package me.zhanghai.android.files.provider.remote;\n\nparcelable ParcelableException;\n"
  },
  {
    "path": "app/src/main/aidl/me/zhanghai/android/files/provider/remote/ParcelableFileAttributes.aidl",
    "content": "package me.zhanghai.android.files.provider.remote;\n\nparcelable ParcelableFileAttributes;\n"
  },
  {
    "path": "app/src/main/aidl/me/zhanghai/android/files/provider/remote/ParcelableObject.aidl",
    "content": "package me.zhanghai.android.files.provider.remote;\n\nparcelable ParcelableObject;\n"
  },
  {
    "path": "app/src/main/aidl/me/zhanghai/android/files/provider/remote/ParcelablePathListConsumer.aidl",
    "content": "package me.zhanghai.android.files.provider.remote;\n\nparcelable ParcelablePathListConsumer;\n"
  },
  {
    "path": "app/src/main/aidl/me/zhanghai/android/files/provider/remote/ParcelableSerializable.aidl",
    "content": "package me.zhanghai.android.files.provider.remote;\n\nparcelable ParcelableSerializable;\n"
  },
  {
    "path": "app/src/main/aidl/me/zhanghai/android/files/provider/remote/RemoteInputStream.aidl",
    "content": "package me.zhanghai.android.files.provider.remote;\n\nparcelable RemoteInputStream;\n"
  },
  {
    "path": "app/src/main/aidl/me/zhanghai/android/files/provider/remote/RemotePathObservable.aidl",
    "content": "package me.zhanghai.android.files.provider.remote;\n\nparcelable RemotePathObservable;\n"
  },
  {
    "path": "app/src/main/aidl/me/zhanghai/android/files/provider/remote/RemoteSeekableByteChannel.aidl",
    "content": "package me.zhanghai.android.files.provider.remote;\n\nparcelable RemoteSeekableByteChannel;\n"
  },
  {
    "path": "app/src/main/aidl/me/zhanghai/android/files/util/IRemoteCallback.aidl",
    "content": "package me.zhanghai.android.files.util;\n\nimport android.os.Bundle;\n\ninterface IRemoteCallback {\n    void sendResult(in Bundle result);\n}\n"
  },
  {
    "path": "app/src/main/aidl/me/zhanghai/android/files/util/ParcelSlicedList.aidl",
    "content": "package me.zhanghai.android.files.util;\n\nparcelable ParcelSlicedList;\n"
  },
  {
    "path": "app/src/main/aidl/me/zhanghai/android/files/util/RemoteCallback.aidl",
    "content": "package me.zhanghai.android.files.util;\n\nparcelable RemoteCallback;\n"
  },
  {
    "path": "app/src/main/java/androidx/appcompat/app/AppCompatDelegateCompat.java",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage androidx.appcompat.app;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\n\nimport androidx.annotation.NonNull;\n\npublic class AppCompatDelegateCompat {\n    private AppCompatDelegateCompat() {}\n\n    @SuppressLint(\"RestrictedApi\")\n    public static int mapNightMode(@NonNull AppCompatDelegate delegate, @NonNull Context context,\n                                   int mode) {\n        return ((AppCompatDelegateImpl) delegate).mapNightMode(context, mode);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/androidx/appcompat/widget/FixPaddingListPopupWindow.java",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage androidx.appcompat.widget;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\n\nimport androidx.annotation.AttrRes;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.annotation.StyleRes;\n\npublic class FixPaddingListPopupWindow extends ListPopupWindow {\n    public FixPaddingListPopupWindow(@NonNull Context context) {\n        super(context);\n    }\n\n    public FixPaddingListPopupWindow(@NonNull Context context, @Nullable AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public FixPaddingListPopupWindow(@NonNull Context context, @Nullable AttributeSet attrs,\n                                     @AttrRes int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    public FixPaddingListPopupWindow(@NonNull Context context, @Nullable AttributeSet attrs,\n                                     @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {\n        super(context, attrs, defStyleAttr, defStyleRes);\n    }\n\n    @NonNull\n    @Override\n    DropDownListView createDropDownListView(@NonNull Context context, boolean hijackFocus) {\n        return new FixPaddingDropDownListView(context, hijackFocus);\n    }\n\n    private static class FixPaddingDropDownListView extends DropDownListView {\n        public FixPaddingDropDownListView(Context context, boolean hijackFocus) {\n            super(context, hijackFocus);\n        }\n\n        // DropDownListView.measureHeightOfChildrenCompat() uses list padding instead of regular\n        // padding, which isn't initialized before onMeasure() so returns no padding for the first\n        // time. And ListPopupWindow.buildDropDown() adds the regular padding back every time, which\n        // will double the padding after the first show.\n        @Override\n        public int measureHeightOfChildrenCompat(int widthMeasureSpec, int startPosition,\n                                                 int endPosition, int maxHeight,\n                                                 int disallowPartialChildPosition) {\n            int height = super.measureHeightOfChildrenCompat(widthMeasureSpec, startPosition,\n                    endPosition, maxHeight, disallowPartialChildPosition);\n            height -= getListPaddingTop() + getListPaddingBottom();\n            return height;\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/androidx/swiperefreshlayout/widget/ThemedSwipeRefreshLayout.java",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage androidx.swiperefreshlayout.widget;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.graphics.drawable.ShapeDrawable;\nimport android.util.AttributeSet;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.graphics.ColorUtils;\nimport me.zhanghai.android.files.R;\nimport me.zhanghai.android.files.compat.ContextCompatKt;\nimport me.zhanghai.android.files.util.ContextExtensionsKt;\n\npublic class ThemedSwipeRefreshLayout extends SwipeRefreshLayout {\n    public ThemedSwipeRefreshLayout(@NonNull Context context) {\n        super(context);\n\n        init();\n    }\n\n    public ThemedSwipeRefreshLayout(@NonNull Context context, @Nullable AttributeSet attrs) {\n        super(context, attrs);\n\n        init();\n    }\n\n    private void init() {\n        Context context = getContext();\n        boolean isMaterial3Theme = ContextExtensionsKt.isMaterial3Theme(context);\n        int backgroundColor;\n        if (isMaterial3Theme) {\n            int surfaceColor = ContextExtensionsKt.getColorByAttr(context,\n                    com.google.android.material.R.attr.colorSurface);\n            @SuppressLint(\"PrivateResource\")\n            int overlayColor = ContextCompatKt.getColorCompat(context,\n                    com.google.android.material.R.color.m3_popupmenu_overlay_color);\n            backgroundColor = ColorUtils.compositeColors(overlayColor, surfaceColor);\n        } else {\n            backgroundColor = ContextExtensionsKt.getColorByAttr(context,\n                    androidx.appcompat.R.attr.colorBackgroundFloating);\n        }\n        ((ShapeDrawable) mCircleView.getBackground()).getPaint().setColor(backgroundColor);\n        setColorSchemeColors(ContextExtensionsKt.getColorByAttr(context,\n                androidx.appcompat.R.attr.colorAccent));\n    }\n\n    @Override\n    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec);\n\n        View child = getChildView();\n        if (child != null) {\n            measureChild(child, widthMeasureSpec, heightMeasureSpec);\n            setMeasuredDimension(child.getMeasuredWidth() + getPaddingLeft() + getPaddingRight(),\n                    child.getMeasuredHeight() + getPaddingTop() + getPaddingBottom());\n        }\n    }\n\n    @Nullable\n    private View getChildView() {\n        for (int i = 0; i < getChildCount(); ++i) {\n            View child = getChildAt(i);\n            if (!child.equals(mCircleView)) {\n                return child;\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/at/bitfire/dav4jvm/DavResourceAccessor.java",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage at.bitfire.dav4jvm;\n\nimport java.io.IOException;\n\nimport androidx.annotation.NonNull;\nimport at.bitfire.dav4jvm.exception.DavException;\nimport at.bitfire.dav4jvm.exception.HttpException;\nimport kotlin.jvm.functions.Function0;\nimport okhttp3.Response;\n\npublic class DavResourceAccessor {\n    private DavResourceAccessor() {}\n\n    public static void checkStatus(@NonNull DavResource davResource, @NonNull Response response)\n            throws HttpException {\n        davResource.checkStatus(response);\n    }\n\n    public static Response followRedirects(@NonNull DavResource davResource,\n            @NonNull Function0<Response> sendRequest) throws DavException, IOException {\n        return davResource.followRedirects$build(sendRequest);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/google/android/material/appbar/OnWindowInsetChangedAppBarLayout.java",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage com.google.android.material.appbar;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\n\nimport androidx.annotation.AttrRes;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.view.WindowInsetsCompat;\n\npublic class OnWindowInsetChangedAppBarLayout extends AppBarLayout {\n    public OnWindowInsetChangedAppBarLayout(@NonNull Context context) {\n        super(context);\n    }\n\n    public OnWindowInsetChangedAppBarLayout(@NonNull Context context,\n                                            @Nullable AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public OnWindowInsetChangedAppBarLayout(@NonNull Context context, @Nullable AttributeSet attrs,\n                                            @AttrRes int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    @Override\n    public WindowInsetsCompat onWindowInsetChanged(@NonNull WindowInsetsCompat insets) {\n        return super.onWindowInsetChanged(insets);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/google/android/material/shape/MaterialShapeDrawableAccessor.java",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage com.google.android.material.shape;\n\nimport android.annotation.SuppressLint;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport com.google.android.material.elevation.ElevationOverlayProvider;\n\npublic class MaterialShapeDrawableAccessor {\n    private MaterialShapeDrawableAccessor() {}\n\n    @SuppressLint(\"RestrictedApi\")\n    public static ElevationOverlayProvider getElevationOverlayProvider(\n            @NonNull MaterialShapeDrawable drawable) {\n        MaterialShapeDrawable.MaterialShapeDrawableState drawableState =\n                (MaterialShapeDrawable.MaterialShapeDrawableState) drawable.getConstantState();\n        return drawableState.elevationOverlayProvider;\n    }\n\n    @SuppressLint(\"RestrictedApi\")\n    public static void setElevationOverlayProvider(\n            @NonNull MaterialShapeDrawable drawable,\n            @Nullable ElevationOverlayProvider elevationOverlayProvider) {\n        MaterialShapeDrawable.MaterialShapeDrawableState drawableState =\n                (MaterialShapeDrawable.MaterialShapeDrawableState) drawable.getConstantState();\n        drawableState.elevationOverlayProvider = elevationOverlayProvider;\n    }\n\n    public static void updateZ(@NonNull MaterialShapeDrawable drawable) {\n        final float parentAbsoluteElevation = drawable.getParentAbsoluteElevation();\n        drawable.setParentAbsoluteElevation(parentAbsoluteElevation + 1);\n        drawable.setParentAbsoluteElevation(parentAbsoluteElevation);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/hierynomus/smbj/share/FileAccessor.java",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage com.hierynomus.smbj.share;\n\nimport com.hierynomus.mssmb2.messages.SMB2ReadResponse;\nimport com.hierynomus.smbj.common.SMBRuntimeException;\n\nimport java.util.concurrent.Future;\n\nimport androidx.annotation.NonNull;\n\npublic class FileAccessor {\n    private FileAccessor() {}\n\n    /**\n     * @see File#readAsync(long, int)\n     */\n    @NonNull\n    public static Future<SMB2ReadResponse> readAsync(@NonNull File file, long offset, int length)\n            throws SMBRuntimeException {\n        return file.readAsync(offset, length);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/hierynomus/smbj/share/ShareAccessor.java",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage com.hierynomus.smbj.share;\n\nimport androidx.annotation.NonNull;\n\nimport com.hierynomus.mssmb2.SMB2FileId;\nimport com.hierynomus.mssmb2.messages.SMB2IoctlResponse;\nimport com.hierynomus.smbj.io.ArrayByteChunkProvider;\nimport com.hierynomus.smbj.io.ByteChunkProvider;\n\nimport java.util.concurrent.Future;\n\npublic class ShareAccessor {\n    private ShareAccessor() {}\n\n    /**\n     * This ioctl() variant allows passing in the {@param statusHandler}.\n     *\n     * @see Share#ioctl(com.hierynomus.mssmb2.SMB2FileId, long, boolean, ByteChunkProvider, int)\n     */\n    @NonNull\n    public static SMB2IoctlResponse ioctl(@NonNull Share share, @NonNull SMB2FileId fileId,\n                                          long ctlCode, boolean isFsCtl, @NonNull byte[] inData,\n                                          int inOffset, int inLength,\n                                          @NonNull StatusHandler statusHandler, long timeout) {\n        final ByteChunkProvider inputData = new ArrayByteChunkProvider(inData, inOffset, inLength,\n                0);\n        final Future<SMB2IoctlResponse> future = share.ioctlAsync(fileId, ctlCode, isFsCtl,\n                inputData, -1);\n        return share.receive(future, \"IOCTL\", fileId, statusHandler, timeout);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/about/AboutActivity.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.about\n\nimport android.os.Bundle\nimport android.view.View\nimport androidx.fragment.app.add\nimport androidx.fragment.app.commit\nimport me.zhanghai.android.files.app.AppActivity\n\nclass AboutActivity : AppActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        // Calls ensureSubDecor().\n        findViewById<View>(android.R.id.content)\n        if (savedInstanceState == null) {\n            supportFragmentManager.commit { add<AboutFragment>(android.R.id.content) }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/about/AboutFragment.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.about\n\nimport android.net.Uri\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.core.view.isVisible\nimport androidx.fragment.app.Fragment\nimport me.zhanghai.android.files.databinding.AboutFragmentBinding\nimport me.zhanghai.android.files.ui.LicensesDialogFragment\nimport me.zhanghai.android.files.util.createViewIntent\nimport me.zhanghai.android.files.util.startActivitySafe\n\nclass AboutFragment : Fragment() {\n    private lateinit var binding: AboutFragmentBinding\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View =\n        AboutFragmentBinding.inflate(inflater, container, false)\n            .also { binding = it }\n            .root\n\n    override fun onActivityCreated(savedInstanceState: Bundle?) {\n        super.onActivityCreated(savedInstanceState)\n\n        val activity = requireActivity() as AppCompatActivity\n        activity.setSupportActionBar(binding.toolbar)\n        activity.supportActionBar!!.setDisplayHomeAsUpEnabled(true)\n        binding.gitHubLayout.setOnClickListener { startActivitySafe(GITHUB_URI.createViewIntent()) }\n        binding.licensesLayout.setOnClickListener { LicensesDialogFragment.show(this) }\n//#ifdef NONFREE\n        binding.privacyPolicyLayout.isVisible = true\n        binding.privacyPolicyLayout.setOnClickListener {\n            startActivitySafe(PRIVACY_POLICY_URI.createViewIntent())\n        }\n//#endif\n        binding.authorNameLayout.setOnClickListener {\n            startActivitySafe(AUTHOR_RESUME_URI.createViewIntent())\n        }\n        binding.authorGitHubLayout.setOnClickListener {\n            startActivitySafe(AUTHOR_GITHUB_URI.createViewIntent())\n        }\n        binding.authorTwitterLayout.setOnClickListener {\n            startActivitySafe(AUTHOR_TWITTER_URI.createViewIntent())\n        }\n    }\n\n    companion object {\n        private val GITHUB_URI = Uri.parse(\"https://github.com/zhanghai/MaterialFiles\")\n        private val PRIVACY_POLICY_URI =\n            Uri.parse(\"https://github.com/zhanghai/MaterialFiles/blob/master/PRIVACY.md\")\n        private val AUTHOR_RESUME_URI = Uri.parse(\"https://resume.zhanghai.me/\")\n        private val AUTHOR_GITHUB_URI = Uri.parse(\"https://github.com/zhanghai\")\n        private val AUTHOR_TWITTER_URI = Uri.parse(\"https://twitter.com/zhanghai95\")\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/app/AppActivity.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.app\n\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.appcompat.app.AppCompatDelegate\nimport me.zhanghai.android.files.theme.custom.CustomThemeHelper\nimport me.zhanghai.android.files.theme.night.NightModeHelper\n\nabstract class AppActivity : AppCompatActivity() {\n    private var isDelegateCreated = false\n\n    override fun getDelegate(): AppCompatDelegate {\n        val delegate = super.getDelegate()\n\n        if (!isDelegateCreated) {\n            isDelegateCreated = true\n            NightModeHelper.apply(this)\n        }\n        return delegate\n    }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        CustomThemeHelper.apply(this)\n\n        super.onCreate(savedInstanceState)\n    }\n\n    override fun onSupportNavigateUp(): Boolean {\n        if (!super.onSupportNavigateUp()) {\n            finish()\n        }\n        return true\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/app/AppInitializers.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.app\n\nimport android.os.AsyncTask\nimport android.os.Build\nimport android.webkit.WebView\nimport jcifs.context.SingletonContext\nimport me.zhanghai.android.files.BuildConfig\nimport me.zhanghai.android.files.coil.initializeCoil\nimport me.zhanghai.android.files.filejob.fileJobNotificationTemplate\nimport me.zhanghai.android.files.ftpserver.ftpServerServiceNotificationTemplate\nimport me.zhanghai.android.files.hiddenapi.HiddenApi\nimport me.zhanghai.android.files.provider.FileSystemProviders\nimport me.zhanghai.android.files.settings.Settings\nimport me.zhanghai.android.files.storage.FtpServerAuthenticator\nimport me.zhanghai.android.files.storage.SftpServerAuthenticator\nimport me.zhanghai.android.files.storage.SmbServerAuthenticator\nimport me.zhanghai.android.files.storage.StorageVolumeListLiveData\nimport me.zhanghai.android.files.storage.WebDavServerAuthenticator\nimport me.zhanghai.android.files.theme.custom.CustomThemeHelper\nimport me.zhanghai.android.files.theme.night.NightModeHelper\nimport java.util.Properties\nimport me.zhanghai.android.files.provider.ftp.client.Client as FtpClient\nimport me.zhanghai.android.files.provider.sftp.client.Client as SftpClient\nimport me.zhanghai.android.files.provider.smb.client.Client as SmbClient\nimport me.zhanghai.android.files.provider.webdav.client.Client as WebDavClient\n\nval appInitializers = listOf(\n    ::initializeCrashlytics,\n    ::disableHiddenApiChecks,\n    ::initializeWebViewDebugging,\n    ::initializeCoil,\n    ::initializeFileSystemProviders,\n    ::upgradeApp,\n    ::initializeLiveDataObjects,\n    ::initializeCustomTheme,\n    ::initializeNightMode,\n    ::createNotificationChannels\n)\n\nprivate fun initializeCrashlytics() {\n//#ifdef NONFREE\n    me.zhanghai.android.files.nonfree.CrashlyticsInitializer.initialize()\n//#endif\n}\n\nprivate fun disableHiddenApiChecks() {\n    HiddenApi.disableHiddenApiChecks()\n}\n\nprivate fun initializeWebViewDebugging() {\n    if (BuildConfig.DEBUG) {\n        WebView.setWebContentsDebuggingEnabled(true)\n    }\n}\n\nprivate fun initializeFileSystemProviders() {\n    FileSystemProviders.install()\n    FileSystemProviders.overflowWatchEvents = true\n    // SingletonContext.init() calls NameServiceClientImpl.initCache() which connects to network.\n    AsyncTask.THREAD_POOL_EXECUTOR.execute {\n        SingletonContext.init(\n            Properties().apply {\n                setProperty(\"jcifs.netbios.cachePolicy\", \"0\")\n                setProperty(\"jcifs.smb.client.maxVersion\", \"SMB1\")\n            }\n        )\n    }\n    FtpClient.authenticator = FtpServerAuthenticator\n    SftpClient.authenticator = SftpServerAuthenticator\n    SmbClient.authenticator = SmbServerAuthenticator\n    WebDavClient.authenticator = WebDavServerAuthenticator\n}\n\nprivate fun initializeLiveDataObjects() {\n    // Force initialization of LiveData objects so that it won't happen on a background thread.\n    StorageVolumeListLiveData.value\n    Settings.FILE_LIST_DEFAULT_DIRECTORY.value\n}\n\nprivate fun initializeCustomTheme() {\n    CustomThemeHelper.initialize(application)\n}\n\nprivate fun initializeNightMode() {\n    NightModeHelper.initialize(application)\n}\n\nprivate fun createNotificationChannels() {\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n        notificationManager.createNotificationChannels(\n            listOf(\n                backgroundActivityStartNotificationTemplate.channelTemplate,\n                fileJobNotificationTemplate.channelTemplate,\n                ftpServerServiceNotificationTemplate.channelTemplate\n            ).map { it.create(application) }\n        )\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/app/AppProvider.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.app\n\nimport android.app.Application\nimport android.content.ContentProvider\nimport android.content.ContentValues\nimport android.database.Cursor\nimport android.net.Uri\n\nlateinit var application: Application private set\n\nclass AppProvider : ContentProvider() {\n    override fun onCreate(): Boolean {\n        application = context as Application\n        appInitializers.forEach { it() }\n        return true\n    }\n\n    override fun query(\n        uri: Uri,\n        projection: Array<String?>?,\n        selection: String?,\n        selectionArgs: Array<String?>?,\n        sortOrder: String?\n    ): Cursor? {\n        throw UnsupportedOperationException()\n    }\n\n    override fun getType(uri: Uri): String? {\n        throw UnsupportedOperationException()\n    }\n\n    override fun insert(uri: Uri, values: ContentValues?): Uri? {\n        throw UnsupportedOperationException()\n    }\n\n    override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String?>?): Int {\n        throw UnsupportedOperationException()\n    }\n\n    override fun update(\n        uri: Uri,\n        values: ContentValues?,\n        selection: String?,\n        selectionArgs: Array<String?>?\n    ): Int {\n        throw UnsupportedOperationException()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/app/AppUpgrader.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.app\n\nimport androidx.core.content.edit\nimport me.zhanghai.android.files.BuildConfig\n\nprivate const val KEY_VERSION_CODE = \"key_version_code\"\n\nprivate const val VERSION_CODE_BELOW_1_1_0 = 17\nprivate const val VERSION_CODE_1_1_0 = 18\nprivate const val VERSION_CODE_1_2_0 = 22\nprivate const val VERSION_CODE_1_3_0 = 24\nprivate const val VERSION_CODE_1_4_0 = 26\nprivate const val VERSION_CODE_1_5_0 = 29\nprivate const val VERSION_CODE_1_6_0 = 32\nprivate const val VERSION_CODE_1_7_2 = 37\nprivate const val VERSION_CODE_LATEST = BuildConfig.VERSION_CODE\n\nprivate var lastVersionCode: Int\n    get() {\n        if (defaultSharedPreferences.all.isEmpty()) {\n            // This is a new install.\n            lastVersionCode = VERSION_CODE_LATEST\n            return VERSION_CODE_LATEST\n        }\n        return defaultSharedPreferences.getInt(KEY_VERSION_CODE, VERSION_CODE_BELOW_1_1_0)\n    }\n    set(value) {\n        defaultSharedPreferences.edit { putInt(KEY_VERSION_CODE, value) }\n    }\n\nfun upgradeApp() {\n    upgradeAppFrom(lastVersionCode)\n    lastVersionCode = VERSION_CODE_LATEST\n}\n\nprivate fun upgradeAppFrom(lastVersionCode: Int) {\n    if (lastVersionCode < VERSION_CODE_1_1_0) {\n        upgradeAppTo1_1_0()\n    }\n    if (lastVersionCode < VERSION_CODE_1_2_0) {\n        upgradeAppTo1_2_0()\n    }\n    if (lastVersionCode < VERSION_CODE_1_3_0) {\n        upgradeAppTo1_3_0()\n    }\n    if (lastVersionCode < VERSION_CODE_1_4_0) {\n        upgradeAppTo1_4_0()\n    }\n    if (lastVersionCode < VERSION_CODE_1_5_0) {\n        upgradeAppTo1_5_0()\n    }\n    if (lastVersionCode < VERSION_CODE_1_6_0) {\n        upgradeAppTo1_6_0()\n    }\n    if (lastVersionCode < VERSION_CODE_1_7_2) {\n        upgradeAppTo1_7_2()\n    }\n    // Continue with new `if`s on lastVersionCode instead of `else if`.\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/app/AppUpgraders.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.app\n\nimport android.content.SharedPreferences\nimport android.net.Uri\nimport android.os.Build\nimport android.os.Parcel\nimport android.os.Parcelable\nimport androidx.annotation.StringRes\nimport androidx.core.content.edit\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.compat.PreferenceManagerCompat\nimport me.zhanghai.android.files.compat.getDescriptionCompat\nimport me.zhanghai.android.files.compat.readBooleanCompat\nimport me.zhanghai.android.files.compat.writeBooleanCompat\nimport me.zhanghai.android.files.compat.writeParcelableListCompat\nimport me.zhanghai.android.files.file.DocumentTreeUri\nimport me.zhanghai.android.files.file.asExternalStorageUriOrNull\nimport me.zhanghai.android.files.file.displayName\nimport me.zhanghai.android.files.file.storageVolume\nimport me.zhanghai.android.files.filelist.FileSortOptions\nimport me.zhanghai.android.files.navigation.BookmarkDirectory\nimport me.zhanghai.android.files.navigation.StandardDirectorySettings\nimport me.zhanghai.android.files.provider.archive.ArchiveFileSystem\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.moveToByteString\nimport me.zhanghai.android.files.provider.content.ContentFileSystem\nimport me.zhanghai.android.files.provider.document.DocumentFileSystem\nimport me.zhanghai.android.files.provider.document.resolver.ExternalStorageProviderHacks\nimport me.zhanghai.android.files.provider.linux.LinuxFileSystem\nimport me.zhanghai.android.files.provider.root.RootStrategy\nimport me.zhanghai.android.files.provider.sftp.SftpFileSystem\nimport me.zhanghai.android.files.provider.smb.SmbFileSystem\nimport me.zhanghai.android.files.storage.DocumentTree\nimport me.zhanghai.android.files.storage.FileSystemRoot\nimport me.zhanghai.android.files.storage.PrimaryStorageVolume\nimport me.zhanghai.android.files.util.StableUriParceler\nimport me.zhanghai.android.files.util.asBase64\nimport me.zhanghai.android.files.util.readParcelable\nimport me.zhanghai.android.files.util.readParcelableListCompat\nimport me.zhanghai.android.files.util.toBase64\nimport me.zhanghai.android.files.util.toByteArray\nimport me.zhanghai.android.files.util.use\n\ninternal fun upgradeAppTo1_1_0() {\n    // Migrate settings.\n    migratePathSetting1_1_0(R.string.pref_key_file_list_default_directory)\n    migrateFileSortOptionsSetting1_1_0()\n    migrateCreateArchiveTypeSetting1_1_0()\n    migrateStandardDirectorySettingsSetting1_1_0()\n    migrateBookmarkDirectoriesSetting1_1_0()\n    migratePathSetting1_1_0(R.string.pref_key_ftp_server_home_directory)\n    for (key in pathSharedPreferences.all.keys) {\n        migrateFileSortOptionsSetting1_1_0(pathSharedPreferences, key)\n    }\n}\n\nprivate const val PARCEL_VAL_PARCELABLE = 4\nprivate const val PARCEL_VAL_LIST = 11\n\nprivate fun migratePathSetting1_1_0(@StringRes keyRes: Int) {\n    val key = application.getString(keyRes)\n    val oldBytes = defaultSharedPreferences.getString(key, null)?.asBase64()?.toByteArray()\n        ?: return\n    val newBytes = try {\n        Parcel.obtain().use { newParcel ->\n            newParcel.writeInt(PARCEL_VAL_PARCELABLE)\n            Parcel.obtain().use { oldParcel ->\n                oldParcel.unmarshall(oldBytes, 0, oldBytes.size)\n                oldParcel.setDataPosition(0)\n                migratePath1_1_0(oldParcel, newParcel)\n            }\n            newParcel.marshall()\n        }\n    } catch (e: Exception) {\n        e.printStackTrace()\n        null\n    }\n    defaultSharedPreferences.edit { putString(key, newBytes?.toBase64()?.value) }\n}\n\nprivate fun migrateFileSortOptionsSetting1_1_0() {\n    migrateFileSortOptionsSetting1_1_0(\n        defaultSharedPreferences, application.getString(R.string.pref_key_file_list_sort_options)\n    )\n}\n\nprivate fun migrateFileSortOptionsSetting1_1_0(sharedPreferences: SharedPreferences, key: String) {\n    val oldBytes = sharedPreferences.getString(key, null)?.asBase64()?.toByteArray() ?: return\n    val newBytes = try {\n        Parcel.obtain().use { newParcel ->\n            newParcel.writeInt(PARCEL_VAL_PARCELABLE)\n            Parcel.obtain().use { oldParcel ->\n                oldParcel.unmarshall(oldBytes, 0, oldBytes.size)\n                oldParcel.setDataPosition(0)\n                newParcel.writeString(oldParcel.readString())\n                newParcel.writeString(FileSortOptions.By.entries[oldParcel.readInt()].name)\n                newParcel.writeString(FileSortOptions.Order.entries[oldParcel.readInt()].name)\n                newParcel.writeInt(oldParcel.readByte().toInt())\n            }\n            newParcel.marshall()\n        }\n    } catch (e: Exception) {\n        e.printStackTrace()\n        null\n    }\n    sharedPreferences.edit { putString(key, newBytes?.toBase64()?.value) }\n}\n\nfun migrateCreateArchiveTypeSetting1_1_0() {\n    val key = application.getString(R.string.pref_key_create_archive_type)\n    val oldValue = defaultSharedPreferences.getString(key, null) ?: return\n    val newValue = oldValue.replace(Regex(\"type_.+$\")) {\n        when (it.value) {\n            \"type_zip\" -> \"zipRadio\"\n            \"type_tar_xz\" -> \"tarXzRadio\"\n            \"type_seven_z\" -> \"sevenZRadio\"\n            else -> \"zipRadio\"\n        }\n    }\n    defaultSharedPreferences.edit { putString(key, newValue) }\n}\n\nprivate fun migrateStandardDirectorySettingsSetting1_1_0() {\n    val key = application.getString(R.string.pref_key_standard_directory_settings)\n    val oldBytes = defaultSharedPreferences.getString(key, null)?.asBase64()?.toByteArray()\n        ?: return\n    val newBytes = try {\n        Parcel.obtain().use { newParcel ->\n            newParcel.writeInt(PARCEL_VAL_LIST)\n            Parcel.obtain().use { oldParcel ->\n                oldParcel.unmarshall(oldBytes, 0, oldBytes.size)\n                oldParcel.setDataPosition(0)\n                val size = oldParcel.readInt()\n                newParcel.writeInt(size)\n                repeat(size) {\n                    oldParcel.readInt()\n                    newParcel.writeInt(PARCEL_VAL_PARCELABLE)\n                    newParcel.writeString(StandardDirectorySettings::class.java.name)\n                    newParcel.writeString(oldParcel.readString())\n                    newParcel.writeString(oldParcel.readString())\n                    newParcel.writeInt(oldParcel.readByte().toInt())\n                }\n            }\n            newParcel.marshall()\n        }\n    } catch (e: Exception) {\n        e.printStackTrace()\n        null\n    }\n    defaultSharedPreferences.edit { putString(key, newBytes?.toBase64()?.value) }\n}\n\nprivate fun migrateBookmarkDirectoriesSetting1_1_0() {\n    val key = application.getString(R.string.pref_key_bookmark_directories)\n    val oldBytes = defaultSharedPreferences.getString(key, null)?.asBase64()?.toByteArray()\n        ?: return\n    val newBytes = try {\n        Parcel.obtain().use { newParcel ->\n            newParcel.writeInt(PARCEL_VAL_LIST)\n            Parcel.obtain().use { oldParcel ->\n                oldParcel.unmarshall(oldBytes, 0, oldBytes.size)\n                oldParcel.setDataPosition(0)\n                val size = oldParcel.readInt()\n                newParcel.writeInt(size)\n                repeat(size) {\n                    oldParcel.readInt()\n                    newParcel.writeInt(PARCEL_VAL_PARCELABLE)\n                    newParcel.writeString(BookmarkDirectory::class.java.name)\n                    newParcel.writeLong(oldParcel.readLong())\n                    newParcel.writeString(oldParcel.readString())\n                    migratePath1_1_0(oldParcel, newParcel)\n                }\n            }\n            newParcel.marshall()\n        }\n    } catch (e: Exception) {\n        e.printStackTrace()\n        null\n    }\n    defaultSharedPreferences.edit { putString(key, newBytes?.toBase64()?.value) }\n}\n\nprivate val oldByteStringCreator = object : Parcelable.Creator<ByteString> {\n    override fun createFromParcel(source: Parcel): ByteString =\n        source.createByteArray()!!.moveToByteString()\n\n    override fun newArray(size: Int): Array<ByteString?> = arrayOfNulls(size)\n}\n\nprivate fun migratePath1_1_0(oldParcel: Parcel, newParcel: Parcel) {\n    val className = oldParcel.readString()\n    newParcel.writeString(className)\n    newParcel.writeByte(oldParcel.readByte())\n    newParcel.writeBooleanCompat(oldParcel.readByte() != 0.toByte())\n    newParcel.writeParcelableListCompat(oldParcel.createTypedArrayList(oldByteStringCreator), 0)\n    when (className) {\n        \"me.zhanghai.android.files.provider.archive.ArchivePath\" -> {\n            oldParcel.readString()\n            newParcel.writeString(ArchiveFileSystem::class.java.name)\n            migratePath1_1_0(oldParcel, newParcel)\n        }\n        \"me.zhanghai.android.files.provider.content.ContentPath\" -> {\n            oldParcel.readString()\n            newParcel.writeString(ContentFileSystem::class.java.name)\n            newParcel.writeParcelable(oldParcel.readParcelable<Uri>(), 0)\n        }\n        \"me.zhanghai.android.files.provider.document.DocumentPath\" -> {\n            oldParcel.readString()\n            newParcel.writeString(DocumentFileSystem::class.java.name)\n            newParcel.writeParcelable(oldParcel.readParcelable<Uri>(), 0)\n        }\n        \"me.zhanghai.android.files.provider.linux.LinuxPath\" -> {\n            oldParcel.readString()\n            newParcel.writeString(LinuxFileSystem::class.java.name)\n            newParcel.writeBooleanCompat(oldParcel.readByte() != 0.toByte())\n        }\n        else -> throw IllegalStateException(className)\n    }\n}\n\nprivate val pathSharedPreferences: SharedPreferences\n    get() {\n        val name = \"${PreferenceManagerCompat.getDefaultSharedPreferencesName(application)}_path\"\n        val mode = PreferenceManagerCompat.defaultSharedPreferencesMode\n        return application.getSharedPreferences(name, mode)\n    }\n\ninternal fun upgradeAppTo1_2_0() {\n    migrateStoragesSetting1_2_0()\n}\n\nprivate fun migrateStoragesSetting1_2_0() {\n    val key = application.getString(R.string.pref_key_storages)\n    val storages = (listOf(FileSystemRoot(null, true), PrimaryStorageVolume(null, true))\n        + DocumentTreeUri.persistedUris.map {\n            DocumentTree(\n                null, it.storageVolume?.getDescriptionCompat(application) ?: it.displayName\n                    ?: it.value.toString(), it\n            )\n        })\n    val bytes = Parcel.obtain().use { parcel ->\n        parcel.writeValue(storages)\n        parcel.marshall()\n    }\n    defaultSharedPreferences.edit { putString(key, bytes.toBase64().value) }\n}\n\ninternal fun upgradeAppTo1_3_0() {\n    migrateSmbServersSetting1_3_0()\n}\n\nprivate fun migrateSmbServersSetting1_3_0() {\n    val key = application.getString(R.string.pref_key_storages)\n    val oldBytes = defaultSharedPreferences.getString(key, null)?.asBase64()?.toByteArray()\n        ?: return\n    val newBytes = try {\n        Parcel.obtain().use { newParcel ->\n            newParcel.writeInt(PARCEL_VAL_LIST)\n            Parcel.obtain().use { oldParcel ->\n                oldParcel.unmarshall(oldBytes, 0, oldBytes.size)\n                oldParcel.setDataPosition(0)\n                val size = oldParcel.readInt()\n                newParcel.writeInt(size)\n                repeat(size) {\n                    val oldPosition = oldParcel.dataPosition()\n                    oldParcel.readInt()\n                    val className = oldParcel.readString()\n                    if (className == \"me.zhanghai.android.files.storage.SmbServer\") {\n                        newParcel.writeInt(PARCEL_VAL_PARCELABLE)\n                        newParcel.writeString(\"me.zhanghai.android.files.storage.SmbServer\")\n                        val id = oldParcel.readLong()\n                        newParcel.writeLong(id)\n                        val customName = oldParcel.readString()\n                        newParcel.writeString(customName)\n                        oldParcel.readString()\n                        newParcel.writeString(\n                            \"me.zhanghai.android.files.provider.smb.client.Authority\"\n                        )\n                        val authorityHost = oldParcel.readString()\n                        newParcel.writeString(authorityHost)\n                        val authorityPort = oldParcel.readInt()\n                        newParcel.writeInt(authorityPort)\n                        oldParcel.readString()\n                        newParcel.writeString(\n                            \"me.zhanghai.android.files.provider.smb.client.Authentication\"\n                        )\n                        val authenticationUsername = oldParcel.readString()\n                        newParcel.writeString(authenticationUsername)\n                        val authenticationDomain = oldParcel.readString()\n                        newParcel.writeString(authenticationDomain)\n                        val authenticationPassword = oldParcel.readString()\n                        newParcel.writeString(authenticationPassword)\n                        val relativePath = \"\"\n                        newParcel.writeString(relativePath)\n                    } else {\n                        oldParcel.setDataPosition(oldPosition)\n                        val storage = oldParcel.readValue(appClassLoader)\n                        newParcel.writeValue(storage)\n                    }\n                }\n            }\n            newParcel.marshall()\n        }\n    } catch (e: Exception) {\n        e.printStackTrace()\n        null\n    }\n    defaultSharedPreferences.edit { putString(key, newBytes?.toBase64()?.value) }\n}\n\ninternal fun upgradeAppTo1_4_0() {\n    migratePathSetting1_4_0(R.string.pref_key_file_list_default_directory)\n    migrateSftpServersSetting1_4_0()\n    migrateBookmarkDirectoriesSetting1_4_0()\n    migrateRootStrategySetting1_4_0()\n    migratePathSetting1_4_0(R.string.pref_key_ftp_server_home_directory)\n}\n\nprivate fun migratePathSetting1_4_0(@StringRes keyRes: Int) {\n    val key = application.getString(keyRes)\n    val oldBytes = defaultSharedPreferences.getString(key, null)?.asBase64()?.toByteArray()\n        ?: return\n    val newBytes = try {\n        Parcel.obtain().use { newParcel ->\n            Parcel.obtain().use { oldParcel ->\n                oldParcel.unmarshall(oldBytes, 0, oldBytes.size)\n                oldParcel.setDataPosition(0)\n                newParcel.writeInt(oldParcel.readInt())\n                migratePath1_4_0(oldParcel, newParcel)\n            }\n            newParcel.marshall()\n        }\n    } catch (e: Exception) {\n        e.printStackTrace()\n        null\n    }\n    defaultSharedPreferences.edit { putString(key, newBytes?.toBase64()?.value) }\n}\n\nprivate fun migrateBookmarkDirectoriesSetting1_4_0() {\n    val key = application.getString(R.string.pref_key_bookmark_directories)\n    val oldBytes = defaultSharedPreferences.getString(key, null)?.asBase64()?.toByteArray()\n        ?: return\n    val newBytes = try {\n        Parcel.obtain().use { newParcel ->\n            Parcel.obtain().use { oldParcel ->\n                oldParcel.unmarshall(oldBytes, 0, oldBytes.size)\n                oldParcel.setDataPosition(0)\n                newParcel.writeInt(oldParcel.readInt())\n                val size = oldParcel.readInt()\n                newParcel.writeInt(size)\n                repeat(size) {\n                    newParcel.writeInt(oldParcel.readInt())\n                    newParcel.writeString(oldParcel.readString())\n                    newParcel.writeLong(oldParcel.readLong())\n                    newParcel.writeString(oldParcel.readString())\n                    migratePath1_4_0(oldParcel, newParcel)\n                }\n            }\n            newParcel.marshall()\n        }\n    } catch (e: Exception) {\n        e.printStackTrace()\n        null\n    }\n    defaultSharedPreferences.edit { putString(key, newBytes?.toBase64()?.value) }\n}\n\nprivate fun migratePath1_4_0(oldParcel: Parcel, newParcel: Parcel) {\n    val className = oldParcel.readString()\n    newParcel.writeString(className)\n    newParcel.writeByte(oldParcel.readByte())\n    newParcel.writeBooleanCompat(oldParcel.readBooleanCompat())\n    newParcel.writeParcelableListCompat(oldParcel.readParcelableListCompat<ByteString>(), 0)\n    when (className) {\n        \"me.zhanghai.android.files.provider.archive.ArchivePath\" -> {\n            newParcel.writeString(oldParcel.readString())\n            migratePath1_4_0(oldParcel, newParcel)\n        }\n        \"me.zhanghai.android.files.provider.content.ContentPath\" -> {\n            newParcel.writeParcelable(oldParcel.readParcelable<ContentFileSystem>(), 0)\n            newParcel.writeParcelable(oldParcel.readParcelable<Uri>(), 0)\n        }\n        \"me.zhanghai.android.files.provider.document.DocumentPath\" ->\n            newParcel.writeParcelable(oldParcel.readParcelable<DocumentFileSystem>(), 0)\n        \"me.zhanghai.android.files.provider.linux.LinuxPath\" -> {\n            newParcel.writeParcelable(oldParcel.readParcelable<LinuxFileSystem>(), 0)\n            oldParcel.readBooleanCompat()\n        }\n        \"me.zhanghai.android.files.provider.sftp.SftpPath\" ->\n            newParcel.writeParcelable(oldParcel.readParcelable<SftpFileSystem>(), 0)\n        \"me.zhanghai.android.files.provider.smb.SmbPath\" ->\n            newParcel.writeParcelable(oldParcel.readParcelable<SmbFileSystem>(), 0)\n        else -> throw IllegalStateException(className)\n    }\n}\n\nprivate fun migrateSftpServersSetting1_4_0() {\n    val key = application.getString(R.string.pref_key_storages)\n    val oldBytes = defaultSharedPreferences.getString(key, null)?.asBase64()?.toByteArray()\n        ?: return\n    val newBytes = try {\n        Parcel.obtain().use { newParcel ->\n            Parcel.obtain().use { oldParcel ->\n                oldParcel.unmarshall(oldBytes, 0, oldBytes.size)\n                oldParcel.setDataPosition(0)\n                newParcel.writeInt(oldParcel.readInt())\n                val size = oldParcel.readInt()\n                newParcel.writeInt(size)\n                repeat(size) {\n                    val oldPosition = oldParcel.dataPosition()\n                    oldParcel.readInt()\n                    when (oldParcel.readString()) {\n                        \"me.zhanghai.android.files.storage.SftpServer\" -> {\n                            newParcel.writeInt(PARCEL_VAL_PARCELABLE)\n                            newParcel.writeString(\"me.zhanghai.android.files.storage.SftpServer\")\n                            val id = oldParcel.readLong()\n                            newParcel.writeLong(id)\n                            val customName = oldParcel.readString()\n                            newParcel.writeString(customName)\n                            val authorityHost = oldParcel.readString()\n                            newParcel.writeString(authorityHost)\n                            val authorityPort = oldParcel.readInt()\n                            newParcel.writeInt(authorityPort)\n                            val authenticationClassName = oldParcel.readString()\n                            val authorityUsername = oldParcel.readString()\n                            newParcel.writeString(authorityUsername)\n                            newParcel.writeString(authenticationClassName)\n                            val authenticationPasswordOrPrivateKey = oldParcel.readString()\n                            newParcel.writeString(authenticationPasswordOrPrivateKey)\n                            val relativePath = oldParcel.readString()\n                            newParcel.writeString(relativePath)\n                        }\n                        else -> {\n                            oldParcel.setDataPosition(oldPosition)\n                            val storage = oldParcel.readValue(appClassLoader)\n                            newParcel.writeValue(storage)\n                        }\n                    }\n                }\n            }\n            newParcel.marshall()\n        }\n    } catch (e: Exception) {\n        e.printStackTrace()\n        null\n    }\n    defaultSharedPreferences.edit { putString(key, newBytes?.toBase64()?.value) }\n}\n\nprivate fun migrateRootStrategySetting1_4_0() {\n    val key = application.getString(R.string.pref_key_root_strategy)\n    val oldValue = defaultSharedPreferences.getString(key, null)?.toInt() ?: return\n    val newValue = when (oldValue) {\n        0 -> RootStrategy.NEVER\n        3 -> RootStrategy.ALWAYS\n        else -> RootStrategy.AUTOMATIC\n    }.ordinal.toString()\n    defaultSharedPreferences.edit { putString(key, newValue) }\n}\n\ninternal fun upgradeAppTo1_5_0() {\n    migrateSftpServersSetting1_5_0()\n}\n\nprivate fun migrateSftpServersSetting1_5_0() {\n    val key = application.getString(R.string.pref_key_storages)\n    val oldBytes = defaultSharedPreferences.getString(key, null)?.asBase64()?.toByteArray()\n        ?: return\n    val newBytes = try {\n        Parcel.obtain().use { newParcel ->\n            Parcel.obtain().use { oldParcel ->\n                oldParcel.unmarshall(oldBytes, 0, oldBytes.size)\n                oldParcel.setDataPosition(0)\n                newParcel.writeInt(oldParcel.readInt())\n                val size = oldParcel.readInt()\n                newParcel.writeInt(size)\n                repeat(size) {\n                    val oldPosition = oldParcel.dataPosition()\n                    oldParcel.readInt()\n                    when (oldParcel.readString()) {\n                        \"me.zhanghai.android.files.storage.SftpServer\" -> {\n                            newParcel.writeInt(PARCEL_VAL_PARCELABLE)\n                            newParcel.writeString(\"me.zhanghai.android.files.storage.SftpServer\")\n                            val id = oldParcel.readLong()\n                            newParcel.writeLong(id)\n                            val customName = oldParcel.readString()\n                            newParcel.writeString(customName)\n                            val authorityHost = oldParcel.readString()\n                            newParcel.writeString(authorityHost)\n                            val authorityPort = oldParcel.readInt()\n                            newParcel.writeInt(authorityPort)\n                            val authorityUsername = oldParcel.readString()\n                            newParcel.writeString(authorityUsername)\n                            val authenticationClassName = oldParcel.readString()\n                            newParcel.writeString(authenticationClassName)\n                            val authenticationPasswordOrPrivateKey = oldParcel.readString()\n                            newParcel.writeString(authenticationPasswordOrPrivateKey)\n                            if (authenticationClassName == \"me.zhanghai.android.files.provider.sftp\"\n                                + \".client.PublicKeyAuthentication\") {\n                                newParcel.writeString(null)\n                            }\n                            val relativePath = oldParcel.readString()\n                            newParcel.writeString(relativePath)\n                        }\n                        else -> {\n                            oldParcel.setDataPosition(oldPosition)\n                            val storage = oldParcel.readValue(appClassLoader)\n                            newParcel.writeValue(storage)\n                        }\n                    }\n                }\n            }\n            newParcel.marshall()\n        }\n    } catch (e: Exception) {\n        e.printStackTrace()\n        null\n    }\n    defaultSharedPreferences.edit { putString(key, newBytes?.toBase64()?.value) }\n}\n\ninternal fun upgradeAppTo1_6_0() {\n    addViewTypePathSetting1_6_0()\n}\n\nprivate fun addViewTypePathSetting1_6_0() {\n    val keys = pathSharedPreferences.all.keys.toSet()\n    val sortOptionsKey = application.getString(R.string.pref_key_file_list_sort_options)\n    val viewTypeKey = application.getString(R.string.pref_key_file_list_view_type)\n    val defaultViewType = application.getString(R.string.pref_default_value_file_list_view_type)\n    for (key in keys) {\n        if (!key.startsWith(sortOptionsKey)) {\n            continue\n        }\n        val newKey = key.replaceFirst(sortOptionsKey, viewTypeKey)\n        if (newKey in keys) {\n            continue\n        }\n        pathSharedPreferences.edit { putString(newKey, defaultViewType) }\n    }\n}\n\ninternal fun upgradeAppTo1_7_2() {\n    migrateDocumentManagerShortcutSetting1_7_2()\n}\n\nprivate fun migrateDocumentManagerShortcutSetting1_7_2() {\n    val key = application.getString(R.string.pref_key_storages)\n    val oldBytes =\n        defaultSharedPreferences.getString(key, null)?.asBase64()?.toByteArray() ?: return\n    val newBytes =\n        try {\n            Parcel.obtain().use { newParcel ->\n                Parcel.obtain().use { oldParcel ->\n                    oldParcel.unmarshall(oldBytes, 0, oldBytes.size)\n                    oldParcel.setDataPosition(0)\n                    newParcel.writeInt(oldParcel.readInt())\n                    readWriteLengthPrefixedValue(oldParcel, newParcel) {\n                        val size = oldParcel.readInt()\n                        newParcel.writeInt(size)\n                        repeat(size) {\n                            val oldPosition = oldParcel.dataPosition()\n                            oldParcel.readInt()\n                            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {\n                                // Skip prefix length.\n                                oldParcel.readInt()\n                            }\n                            val className = oldParcel.readString()\n                            oldParcel.setDataPosition(oldPosition)\n                            when (className) {\n                                \"me.zhanghai.android.files.storage.DocumentManagerShortcut\" -> {\n                                    newParcel.writeInt(oldParcel.readInt())\n                                    readWriteLengthPrefixedValue(oldParcel, newParcel) {\n                                        oldParcel.readString()\n                                        newParcel.writeString(\n                                            \"me.zhanghai.android.files.storage\" +\n                                                \".ExternalStorageShortcut\"\n                                        )\n                                        val id = oldParcel.readLong()\n                                        newParcel.writeLong(id)\n                                        val customName = oldParcel.readString()\n                                        newParcel.writeString(customName)\n                                        var uri = StableUriParceler.create(oldParcel)!!\n                                        if (uri.asExternalStorageUriOrNull() == null) {\n                                            // Reset to a valid external storage URI.\n                                            uri =\n                                                ExternalStorageProviderHacks\n                                                    .DOCUMENT_URI_ANDROID_DATA\n                                        }\n                                        with(StableUriParceler) { uri.write(newParcel, 0) }\n                                    }\n                                }\n                                else -> {\n                                    val storage = oldParcel.readValue(appClassLoader)\n                                    newParcel.writeValue(storage)\n                                }\n                            }\n                        }\n                    }\n                }\n                newParcel.marshall()\n            }\n        } catch (e: Exception) {\n            e.printStackTrace()\n            null\n        }\n    defaultSharedPreferences.edit { putString(key, newBytes?.toBase64()?.value) }\n}\n\nprivate fun readWriteLengthPrefixedValue(oldParcel: Parcel, newParcel: Parcel, block: () -> Unit) {\n    var lengthPosition = 0\n    var startPosition = 0\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {\n        oldParcel.readInt()\n        lengthPosition = newParcel.dataPosition()\n        newParcel.writeInt(-1)\n        startPosition = newParcel.dataPosition()\n    }\n    block()\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {\n        val endPosition = newParcel.dataPosition()\n        newParcel.setDataPosition(lengthPosition)\n        newParcel.writeInt(endPosition - startPosition)\n        newParcel.setDataPosition(endPosition)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/app/BackgroundActivityStarter.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.app\n\nimport android.annotation.SuppressLint\nimport android.app.PendingIntent\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Build\nimport androidx.core.app.NotificationCompat\nimport androidx.core.app.NotificationManagerCompat\nimport androidx.lifecycle.Lifecycle\nimport androidx.lifecycle.ProcessLifecycleOwner\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.util.NotificationChannelTemplate\nimport me.zhanghai.android.files.util.NotificationTemplate\nimport me.zhanghai.android.files.util.startActivitySafe\n\nval backgroundActivityStartNotificationTemplate =\n    NotificationTemplate(\n        NotificationChannelTemplate(\n            \"background_activity_start\",\n            R.string.notification_channel_background_activity_start_name,\n            NotificationManagerCompat.IMPORTANCE_HIGH,\n            descriptionRes = R.string.notification_channel_background_activity_start_description,\n            showBadge = false\n        ),\n        colorRes = R.color.color_primary,\n        smallIcon = R.drawable.notification_icon,\n        ongoing = true,\n        autoCancel = true,\n        category = NotificationCompat.CATEGORY_ERROR,\n        priority = NotificationCompat.PRIORITY_HIGH\n    )\n\nobject BackgroundActivityStarter {\n    fun startActivity(intent: Intent, title: CharSequence, text: CharSequence?, context: Context) {\n        // TODO: Only use new task when in background?\n        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)\n        if (isInForeground) {\n            context.startActivitySafe(intent)\n        } else {\n            notifyStartActivity(intent, title, text, context)\n        }\n    }\n\n    private val isInForeground: Boolean\n        get() = ProcessLifecycleOwner.get().lifecycle.currentState.isAtLeast(\n            Lifecycle.State.STARTED\n        )\n\n    // We'll just throw exception for the file job if we don't have the permission to post\n    // notifications.\n    @SuppressLint(\"MissingPermission\")\n    private fun notifyStartActivity(\n        intent: Intent,\n        title: CharSequence,\n        text: CharSequence?,\n        context: Context\n    ) {\n        var pendingIntentFlags = PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_CANCEL_CURRENT\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n            pendingIntentFlags = pendingIntentFlags or PendingIntent.FLAG_IMMUTABLE\n        }\n        val pendingIntent = PendingIntent.getActivity(\n            context, intent.hashCode(), intent, pendingIntentFlags\n        )\n        val notification = backgroundActivityStartNotificationTemplate.createBuilder(context)\n            .setContentTitle(title)\n            .setContentText(text)\n            .setContentIntent(pendingIntent)\n            .build()\n        notificationManager.notify(intent.hashCode(), notification)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/app/NotificationIds.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.app\n\nobject NotificationIds {\n    const val FTP_SERVER = 1\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/app/SystemServices.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.app\n\nimport android.content.ClipboardManager\nimport android.content.ContentResolver\nimport android.content.SharedPreferences\nimport android.content.pm.PackageManager\nimport android.net.wifi.WifiManager\nimport android.os.PowerManager\nimport android.os.storage.StorageManager\nimport android.view.inputmethod.InputMethodManager\nimport androidx.core.app.NotificationManagerCompat\nimport androidx.preference.PreferenceManager\nimport me.zhanghai.android.files.compat.getSystemServiceCompat\nimport me.zhanghai.android.files.compat.mainExecutorCompat\nimport okhttp3.OkHttpClient\nimport java.util.concurrent.Executor\n\nval appClassLoader = AppProvider::class.java.classLoader\n\nval clipboardManager: ClipboardManager by lazy {\n    application.getSystemServiceCompat(ClipboardManager::class.java)\n}\n\nval contentResolver: ContentResolver by lazy { application.contentResolver }\n\nval defaultSharedPreferences: SharedPreferences by lazy {\n    PreferenceManager.getDefaultSharedPreferences(application)\n}\n\nval okHttpClient: OkHttpClient by lazy { OkHttpClient() }\n\nval inputMethodManager: InputMethodManager by lazy {\n    application.getSystemServiceCompat(InputMethodManager::class.java)\n}\n\nval mainExecutor: Executor by lazy { application.mainExecutorCompat }\n\nval notificationManager: NotificationManagerCompat by lazy {\n    NotificationManagerCompat.from(application)\n}\n\nval packageManager: PackageManager by lazy { application.packageManager }\n\nval powerManager: PowerManager by lazy {\n    application.getSystemServiceCompat(PowerManager::class.java)\n}\n\nval storageManager: StorageManager by lazy {\n    application.getSystemServiceCompat(StorageManager::class.java)\n}\n\nval wifiManager: WifiManager by lazy {\n    application.getSystemServiceCompat(WifiManager::class.java)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/coil/AppIconApplicationInfoFetcherFactory.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.coil\n\nimport android.content.Context\nimport android.content.pm.ApplicationInfo\nimport coil.key.Keyer\nimport coil.request.Options\nimport me.zhanghai.android.appiconloader.AppIconLoader\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.compat.longVersionCodeCompat\nimport me.zhanghai.android.files.util.getDimensionPixelSize\nimport java.io.Closeable\n\nclass AppIconApplicationInfoKeyer : Keyer<ApplicationInfo> {\n    override fun key(data: ApplicationInfo, options: Options): String =\n        AppIconLoader.getIconKey(data, data.longVersionCodeCompat, options.context)\n}\n\nclass AppIconApplicationInfoFetcherFactory(\n    context: Context\n) : AppIconFetcher.Factory<ApplicationInfo>(\n    // This is used by PrincipalListAdapter.\n    context.getDimensionPixelSize(R.dimen.icon_size), context\n) {\n    override fun getApplicationInfo(data: ApplicationInfo): Pair<ApplicationInfo, Closeable?> =\n        data to null\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/coil/AppIconFetcher.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.coil\n\nimport android.content.Context\nimport android.content.pm.ApplicationInfo\nimport androidx.core.graphics.drawable.toDrawable\nimport coil.ImageLoader\nimport coil.decode.DataSource\nimport coil.fetch.DrawableResult\nimport coil.fetch.FetchResult\nimport coil.fetch.Fetcher\nimport coil.request.Options\nimport me.zhanghai.android.appiconloader.AppIconLoader\nimport java.io.Closeable\n\nclass AppIconFetcher(\n    private val options: Options,\n    private val appIconLoader: AppIconLoader,\n    private val getApplicationInfo: () -> Pair<ApplicationInfo, Closeable?>\n) : Fetcher {\n    override suspend fun fetch(): FetchResult {\n        val (applicationInfo, closeable) = getApplicationInfo()\n        val icon = closeable.use { appIconLoader.loadIcon(applicationInfo) }\n        // Not sampled because we only load with one fixed size.\n        return DrawableResult(icon.toDrawable(options.context.resources), false, DataSource.DISK)\n    }\n\n    abstract class Factory<T : Any>(\n        iconSize: Int,\n        context: Context,\n        shrinkNonAdaptiveIcons: Boolean = false\n    ) : Fetcher.Factory<T> {\n        private val appIconLoader =\n            AppIconLoader(iconSize, shrinkNonAdaptiveIcons, context)\n\n        override fun create(data: T, options: Options, imageLoader: ImageLoader): Fetcher =\n            AppIconFetcher(options, appIconLoader) { getApplicationInfo(data) }\n\n        abstract fun getApplicationInfo(data: T): Pair<ApplicationInfo, Closeable?>\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/coil/AppIconPackageNameFetcherFactory.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.coil\n\nimport android.content.Context\nimport android.content.pm.ApplicationInfo\nimport coil.key.Keyer\nimport coil.request.Options\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.compat.PackageManagerCompat\nimport me.zhanghai.android.files.util.getDimensionPixelSize\nimport java.io.Closeable\n\ndata class AppIconPackageName(val packageName: String)\n\nclass AppIconPackageNameKeyer : Keyer<AppIconPackageName> {\n    override fun key(data: AppIconPackageName, options: Options): String = data.packageName\n}\n\nclass AppIconPackageNameFetcherFactory(\n    private val context: Context\n) : AppIconFetcher.Factory<AppIconPackageName>(\n    // This is used by FileListAdapter, and shrinking non-adaptive icons makes it look better as a\n    // badge.\n    context.getDimensionPixelSize(R.dimen.badge_size_plus_1dp), context, true\n) {\n    override fun getApplicationInfo(data: AppIconPackageName): Pair<ApplicationInfo, Closeable?> {\n        // PackageManager.MATCH_UNINSTALLED_PACKAGES allows using PackageManager.MATCH_ANY_USER\n        // without the INTERACT_ACROSS_USERS permission when we are in the system user and it has a\n        // managed profile. It may also help corner cases like when the package is hidden.\n        return context.packageManager.getApplicationInfo(\n            data.packageName, PackageManagerCompat.MATCH_UNINSTALLED_PACKAGES\n        ) to null\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/coil/CoilExtensions.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.coil\n\nimport coil.request.ImageRequest\nimport coil.transition.CrossfadeTransition\n\nfun ImageRequest.Builder.fadeIn(durationMillis: Int): ImageRequest.Builder =\n    apply {\n        placeholder(android.R.color.transparent)\n        transitionFactory(CrossfadeTransition.Factory(durationMillis, true))\n    }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/coil/CoilInitializer.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.coil\n\nimport android.os.Build\nimport coil.Coil\nimport coil.ImageLoader\nimport coil.decode.GifDecoder\nimport coil.decode.ImageDecoderDecoder\nimport coil.decode.SvgDecoder\nimport me.zhanghai.android.files.app.application\n\nfun initializeCoil() {\n    Coil.setImageLoader(\n        ImageLoader.Builder(application)\n            .components {\n                add(AppIconApplicationInfoKeyer())\n                add(AppIconApplicationInfoFetcherFactory(application))\n                add(AppIconPackageNameKeyer())\n                add(AppIconPackageNameFetcherFactory(application))\n                add(PathAttributesKeyer())\n                add(PathAttributesFetcher.Factory(application))\n                add(\n                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {\n                        ImageDecoderDecoder.Factory()\n                    } else {\n                        GifDecoder.Factory()\n                    }\n                )\n                add(SvgDecoder.Factory(false))\n            }\n            .build()\n    )\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/coil/CoilUtils.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.coil\n\nimport android.graphics.Bitmap\nimport android.os.Build\nimport coil.decode.DataSource\nimport coil.size.Dimension\nimport coil.size.Scale\nimport coil.size.Size\nimport coil.size.isOriginal\nimport coil.size.pxOrElse\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.filelist.isRemotePath\n\nval Bitmap.Config.isHardware: Boolean\n    get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && this == Bitmap.Config.HARDWARE\n\nfun Bitmap.Config.toSoftware(): Bitmap.Config = if (isHardware) Bitmap.Config.ARGB_8888 else this\n\nval Path.dataSource: DataSource\n    get() = if (isRemotePath) DataSource.NETWORK else DataSource.DISK\n\ninline fun Size.widthPx(scale: Scale, original: () -> Int): Int =\n    if (isOriginal) original() else width.toPx(scale)\n\ninline fun Size.heightPx(scale: Scale, original: () -> Int): Int =\n    if (isOriginal) original() else height.toPx(scale)\n\nfun Dimension.toPx(scale: Scale) =\n    pxOrElse {\n        when (scale) {\n            Scale.FILL -> Int.MIN_VALUE\n            Scale.FIT -> Int.MAX_VALUE\n        }\n    }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/coil/LoadRequestBuilderExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.coil\n\nimport android.graphics.drawable.Drawable\nimport coil.request.ImageRequest\nimport coil.target.ImageViewTarget\n\n// Setting the placeholder drawable as error drawable again causes animation glitches, so we just\n// ignore the onError() callback.\nfun ImageRequest.Builder.ignoreError() {\n    val view = (build().target as ImageViewTarget).view\n    target(object : ImageViewTarget(view) {\n        override fun onError(error: Drawable?) {}\n    })\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/coil/PathAttributesFetcher.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.coil\n\nimport android.content.Context\nimport android.content.pm.ApplicationInfo\nimport android.media.MediaMetadataRetriever\nimport android.os.ParcelFileDescriptor\nimport androidx.core.graphics.drawable.toDrawable\nimport coil.ImageLoader\nimport coil.decode.ImageSource\nimport coil.fetch.DrawableResult\nimport coil.fetch.FetchResult\nimport coil.fetch.Fetcher\nimport coil.fetch.SourceResult\nimport coil.key.Keyer\nimport coil.request.Options\nimport coil.size.Dimension\nimport java8.nio.file.Path\nimport java8.nio.file.attribute.BasicFileAttributes\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.compat.use\nimport me.zhanghai.android.files.file.MimeType\nimport me.zhanghai.android.files.file.asMimeType\nimport me.zhanghai.android.files.file.isApk\nimport me.zhanghai.android.files.file.isImage\nimport me.zhanghai.android.files.file.isMedia\nimport me.zhanghai.android.files.file.isPdf\nimport me.zhanghai.android.files.file.isVideo\nimport me.zhanghai.android.files.file.lastModifiedInstant\nimport me.zhanghai.android.files.filelist.isRemotePath\nimport me.zhanghai.android.files.provider.common.AndroidFileTypeDetector\nimport me.zhanghai.android.files.provider.common.newInputStream\nimport me.zhanghai.android.files.provider.content.resolver.ResolverException\nimport me.zhanghai.android.files.provider.document.documentSupportsThumbnail\nimport me.zhanghai.android.files.provider.document.isDocumentPath\nimport me.zhanghai.android.files.provider.document.resolver.DocumentResolver\nimport me.zhanghai.android.files.provider.ftp.isFtpPath\nimport me.zhanghai.android.files.provider.linux.isLinuxPath\nimport me.zhanghai.android.files.settings.Settings\nimport me.zhanghai.android.files.util.getDimensionPixelSize\nimport me.zhanghai.android.files.util.getPackageArchiveInfoCompat\nimport me.zhanghai.android.files.util.isGetPackageArchiveInfoCompatible\nimport me.zhanghai.android.files.util.isMediaMetadataRetrieverCompatible\nimport me.zhanghai.android.files.util.runWithCancellationSignal\nimport me.zhanghai.android.files.util.setDataSource\nimport me.zhanghai.android.files.util.valueCompat\nimport okio.buffer\nimport okio.source\nimport java.io.Closeable\nimport java.io.IOException\nimport me.zhanghai.android.files.util.setDataSource as appSetDataSource\n\nclass PathAttributesKeyer : Keyer<Pair<Path, BasicFileAttributes>> {\n    override fun key(data: Pair<Path, BasicFileAttributes>, options: Options): String {\n        val (path, attributes) = data\n        return \"$path:${attributes.lastModifiedInstant.toEpochMilli()}\"\n    }\n}\n\nclass PathAttributesFetcher(\n    private val data: Pair<Path, BasicFileAttributes>,\n    private val options: Options,\n    private val imageLoader: ImageLoader,\n    private val appIconFetcherFactory: AppIconFetcher.Factory<Path>,\n    private val videoFrameFetcherFactory: VideoFrameFetcher.Factory<Path>,\n    private val pdfPageFetcherFactory: PdfPageFetcher.Factory<Path>\n) : Fetcher {\n    override suspend fun fetch(): FetchResult? {\n        val (path, attributes) = data\n        val (width, height) = options.size\n        // @see android.provider.MediaStore.ThumbnailConstants.MINI_SIZE\n        val isThumbnail = width is Dimension.Pixels && width.px <= 512\n            && height is Dimension.Pixels && height.px <= 384\n        if (isThumbnail) {\n            width as Dimension.Pixels\n            height as Dimension.Pixels\n            if (path.isDocumentPath && attributes.documentSupportsThumbnail) {\n                val thumbnail = runWithCancellationSignal { signal ->\n                    try {\n                        DocumentResolver.getThumbnail(\n                            path as DocumentResolver.Path, width.px, height.px, signal\n                        )\n                    } catch (e: ResolverException) {\n                        e.printStackTrace()\n                        null\n                    }\n                }\n                if (thumbnail != null) {\n                    return DrawableResult(\n                        thumbnail.toDrawable(options.context.resources), true, path.dataSource\n                    )\n                }\n            }\n            if (path.isRemotePath) {\n                // FTP doesn't support random access and requires one connection per parallel read.\n                val shouldReadRemotePath = !path.isFtpPath\n                    && Settings.READ_REMOTE_FILES_FOR_THUMBNAIL.valueCompat\n                if (!shouldReadRemotePath) {\n                    error(\"Cannot read $path for thumbnail\")\n                }\n            }\n        }\n        val mimeType = AndroidFileTypeDetector.getMimeType(data.first, data.second).asMimeType()\n        when {\n            mimeType.isApk && path.isGetPackageArchiveInfoCompatible -> {\n                try {\n                    return appIconFetcherFactory.create(path, options, imageLoader).fetch()\n                } catch (e: Exception) {\n                    e.printStackTrace()\n                }\n            }\n            mimeType.isImage || mimeType == MimeType.GENERIC -> {\n                val inputStream = path.newInputStream()\n                return SourceResult(\n                    ImageSource(inputStream.source().buffer(), options.context),\n                    if (mimeType != MimeType.GENERIC) mimeType.value else null, path.dataSource\n                )\n            }\n            mimeType.isMedia && path.isMediaMetadataRetrieverCompatible -> {\n                val embeddedPicture = try {\n                    MediaMetadataRetriever().use { retriever ->\n                        retriever.setDataSource(path)\n                        retriever.embeddedPicture\n                    }\n                } catch (e: Exception) {\n                    e.printStackTrace()\n                    null\n                }\n                if (embeddedPicture != null) {\n                    return SourceResult(\n                        ImageSource(\n                            embeddedPicture.inputStream().source().buffer(), options.context\n                        ), null, path.dataSource\n                    )\n                }\n                if (mimeType.isVideo) {\n                    try {\n                        return videoFrameFetcherFactory.create(path, options, imageLoader).fetch()\n                    } catch (e: Exception) {\n                        e.printStackTrace()\n                    }\n                }\n            }\n            mimeType.isPdf && (path.isLinuxPath || path.isDocumentPath) -> {\n                try {\n                    return pdfPageFetcherFactory.create(path, options, imageLoader).fetch()\n                } catch (e: Exception) {\n                    e.printStackTrace()\n                }\n            }\n        }\n        return null\n    }\n\n    class Factory(private val context: Context) : Fetcher.Factory<Pair<Path, BasicFileAttributes>> {\n        private val appIconFetcherFactory = object : AppIconFetcher.Factory<Path>(\n            // This is used by FileListAdapter.\n            context.getDimensionPixelSize(R.dimen.large_icon_size), context\n        ) {\n            override fun getApplicationInfo(data: Path): Pair<ApplicationInfo, Closeable?> {\n                val (packageInfo, closeable) =\n                    context.packageManager.getPackageArchiveInfoCompat(data, 0)\n                val applicationInfo = packageInfo?.applicationInfo\n                if (applicationInfo == null) {\n                    closeable?.close()\n                    throw IOException(\"ApplicationInfo is null\")\n                }\n                return applicationInfo to closeable\n            }\n        }\n\n        private val videoFrameFetcherFactory = object : VideoFrameFetcher.Factory<Path>() {\n            override fun MediaMetadataRetriever.setDataSource(data: Path) {\n                appSetDataSource(data)\n            }\n        }\n\n        private val pdfPageFetcherFactory = object : PdfPageFetcher.Factory<Path>() {\n            override fun openParcelFileDescriptor(data: Path): ParcelFileDescriptor =\n                when {\n                    data.isLinuxPath ->\n                        ParcelFileDescriptor.open(data.toFile(), ParcelFileDescriptor.MODE_READ_ONLY)\n                    data.isDocumentPath ->\n                        DocumentResolver.openParcelFileDescriptor(data as DocumentResolver.Path, \"r\")\n                    else -> throw IllegalArgumentException(data.toString())\n                }\n        }\n\n        override fun create(\n            data: Pair<Path, BasicFileAttributes>,\n            options: Options,\n            imageLoader: ImageLoader\n        ): Fetcher =\n            PathAttributesFetcher(\n                data, options, imageLoader, appIconFetcherFactory, videoFrameFetcherFactory,\n                pdfPageFetcherFactory\n            )\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/coil/PdfPageFetcher.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.coil\n\nimport android.graphics.Color\nimport android.graphics.pdf.PdfRenderer\nimport android.os.ParcelFileDescriptor\nimport androidx.annotation.ColorInt\nimport androidx.annotation.IntRange\nimport androidx.core.graphics.createBitmap\nimport androidx.core.graphics.drawable.toDrawable\nimport coil.ImageLoader\nimport coil.decode.DataSource\nimport coil.decode.DecodeUtils\nimport coil.fetch.DrawableResult\nimport coil.fetch.FetchResult\nimport coil.fetch.Fetcher\nimport coil.request.ImageRequest\nimport coil.request.Options\nimport coil.request.Parameters\nimport kotlin.math.roundToInt\n\nfun ImageRequest.Builder.pdfBackgroundColor(@ColorInt backgroundColor: Int): ImageRequest.Builder =\n    setParameter(PdfPageFetcher.PDF_BACKGROUND_COLOR_KEY, backgroundColor)\n\nfun ImageRequest.Builder.pdfPageIndex(@IntRange(from = 0) pageIndex: Int): ImageRequest.Builder {\n    require(pageIndex >= 0) { \"pageIndex must be >= 0.\" }\n    return setParameter(PdfPageFetcher.PDF_PAGE_INDEX_KEY, pageIndex)\n}\n\n@ColorInt\nfun Parameters.pdfBackgroundColor(): Int? = value(PdfPageFetcher.PDF_BACKGROUND_COLOR_KEY) as Int?\n\n@IntRange(from = 0)\nfun Parameters.pdfPageIndex(): Int? = value(PdfPageFetcher.PDF_PAGE_INDEX_KEY) as Int?\n\nclass PdfPageFetcher(\n    private val options: Options,\n    private val openParcelFileDescriptor: () -> ParcelFileDescriptor\n) : Fetcher {\n    override suspend fun fetch(): FetchResult =\n        openParcelFileDescriptor().use { pfd ->\n            PdfRenderer(pfd).use { renderer ->\n                val pageIndex = options.parameters.pdfPageIndex() ?: 0\n                renderer.openPage(pageIndex).use { page ->\n                    val srcWidth = page.width\n                    check(srcWidth > 0) {\n                        \"PDF page $pageIndex width $srcWidth isn't greater than 0\"\n                    }\n                    val srcHeight = page.height\n                    check(srcWidth > 0) {\n                        \"PDF page $pageIndex height $srcHeight isn't greater than 0\"\n                    }\n                    val dstWidth = options.size.widthPx(options.scale) { srcWidth }\n                    val dstHeight = options.size.heightPx(options.scale) { srcHeight }\n                    val rawScale = DecodeUtils.computeSizeMultiplier(\n                        srcWidth = srcWidth,\n                        srcHeight = srcHeight,\n                        dstWidth = dstWidth,\n                        dstHeight = dstHeight,\n                        scale = options.scale\n                    )\n                    val scale = if (options.allowInexactSize) {\n                        rawScale.coerceAtMost(1.0)\n                    } else {\n                        rawScale\n                    }\n                    val width = (scale * srcWidth).roundToInt()\n                    val height = (scale * srcHeight).roundToInt()\n                    val config = options.config.toSoftware()\n                    val bitmap = createBitmap(width, height, config)\n                    val backgroundColor = options.parameters.pdfBackgroundColor() ?: Color.WHITE\n                    bitmap.eraseColor(backgroundColor)\n                    page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY)\n                    DrawableResult(\n                        drawable = bitmap.toDrawable(options.context.resources),\n                        isSampled = scale < 1.0,\n                        dataSource = DataSource.DISK\n                    )\n                }\n            }\n        }\n\n    companion object {\n        const val PDF_BACKGROUND_COLOR_KEY = \"coil#pdf_background_color\"\n        const val PDF_PAGE_INDEX_KEY = \"coil#pdf_page_index\"\n    }\n\n    abstract class Factory<T : Any> : Fetcher.Factory<T> {\n        override fun create(data: T, options: Options, imageLoader: ImageLoader): Fetcher =\n            PdfPageFetcher(options) { openParcelFileDescriptor(data) }\n\n        protected abstract fun openParcelFileDescriptor(data: T): ParcelFileDescriptor\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/coil/VideoFrameFetcher.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.coil\n\nimport android.graphics.Paint\nimport android.media.MediaMetadataRetriever\nimport android.os.Build\nimport androidx.core.graphics.applyCanvas\nimport androidx.core.graphics.createBitmap\nimport androidx.core.graphics.drawable.toDrawable\nimport coil.ImageLoader\nimport coil.decode.DataSource\nimport coil.decode.DecodeUtils\nimport coil.fetch.DrawableResult\nimport coil.fetch.FetchResult\nimport coil.fetch.Fetcher\nimport coil.request.Options\nimport coil.request.videoFrameOption\nimport coil.request.videoFramePercent\nimport me.zhanghai.android.files.compat.getFrameAtTimeCompat\nimport me.zhanghai.android.files.compat.getScaledFrameAtTimeCompat\nimport me.zhanghai.android.files.compat.use\nimport java.util.concurrent.TimeUnit\nimport kotlin.math.roundToInt\nimport kotlin.math.roundToLong\n\nclass VideoFrameFetcher(\n    private val options: Options,\n    private val setDataSource: MediaMetadataRetriever.() -> Unit\n) : Fetcher {\n    override suspend fun fetch(): FetchResult =\n        MediaMetadataRetriever().use { retriever ->\n            retriever.setDataSource()\n            val rotation =\n                retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION)\n                    ?.toIntOrNull() ?: 0\n            var srcWidth: Int\n            var srcHeight: Int\n            when (rotation) {\n                90, 270 -> {\n                    srcWidth =\n                        retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)\n                            ?.toIntOrNull() ?: 0\n                    srcHeight =\n                        retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)\n                            ?.toIntOrNull() ?: 0\n                }\n                else -> {\n                    srcWidth =\n                        retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)\n                            ?.toIntOrNull() ?: 0\n                    srcHeight =\n                        retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)\n                            ?.toIntOrNull() ?: 0\n                }\n            }\n            val durationMillis =\n                retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)\n                    ?.toLongOrNull() ?: 0L\n            // 1/3 is the first percentage tried by totem-video-thumbnailer.\n            // @see https://gitlab.gnome.org/GNOME/totem/-/blob/master/src/totem-video-thumbnailer.c#L543\n            val framePercent = options.parameters.videoFramePercent() ?: (1.0 / 3.0)\n            val frameMicros = TimeUnit.MICROSECONDS.convert(\n                (framePercent * durationMillis).roundToLong(), TimeUnit.MILLISECONDS\n            )\n            val frameOption = options.parameters.videoFrameOption()\n                ?: MediaMetadataRetriever.OPTION_CLOSEST_SYNC\n            val bitmapParams = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {\n                MediaMetadataRetriever.BitmapParams().apply { preferredConfig = options.config }\n            } else {\n                null\n            }\n            val outBitmap = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1\n                && srcWidth > 0 && srcHeight > 0) {\n                val dstWidth = options.size.widthPx(options.scale) { srcWidth }\n                val dstHeight = options.size.heightPx(options.scale) { srcHeight }\n                val rawScale = DecodeUtils.computeSizeMultiplier(\n                    srcWidth = srcWidth,\n                    srcHeight = srcHeight,\n                    dstWidth = dstWidth,\n                    dstHeight = dstHeight,\n                    scale = options.scale\n                )\n                val scale = if (options.allowInexactSize) {\n                    rawScale.coerceAtMost(1.0)\n                } else {\n                    rawScale\n                }\n                val width = (scale * srcWidth).roundToInt()\n                val height = (scale * srcHeight).roundToInt()\n                retriever.getScaledFrameAtTimeCompat(\n                    frameMicros, frameOption, width, height, bitmapParams\n                )\n            } else {\n                retriever.getFrameAtTimeCompat(frameMicros, frameOption, bitmapParams)?.also {\n                    srcWidth = it.width\n                    srcHeight = it.height\n                }\n            }\n            val dstWidth = options.size.widthPx(options.scale) { srcWidth }\n            val dstHeight = options.size.heightPx(options.scale) { srcHeight }\n            val rawScale = DecodeUtils.computeSizeMultiplier(\n                srcWidth = srcWidth,\n                srcHeight = srcHeight,\n                dstWidth = dstWidth,\n                dstHeight = dstHeight,\n                scale = options.scale\n            )\n            checkNotNull(outBitmap) { \"Failed to decode frame at $frameMicros microseconds\" }\n            val scale = if (options.allowInexactSize) {\n                rawScale.coerceAtMost(1.0)\n            } else {\n                rawScale\n            }\n            val width = (scale * srcWidth).roundToInt()\n            val height = (scale * srcHeight).roundToInt()\n            val isValidSize = if (options.allowInexactSize) {\n                outBitmap.width <= width && outBitmap.height <= height\n            } else {\n                outBitmap.width == width && outBitmap.height == height\n            }\n            val isValidConfig = outBitmap.config?.isHardware != true || options.config.isHardware\n            val bitmap = if (isValidSize && isValidConfig) {\n                outBitmap\n            } else {\n                val config = options.config.toSoftware()\n                createBitmap(width, height, config).applyCanvas {\n                    scale(scale.toFloat(), scale.toFloat())\n                    val paint = Paint(Paint.ANTI_ALIAS_FLAG or Paint.FILTER_BITMAP_FLAG)\n                    drawBitmap(outBitmap, 0f, 0f, paint)\n                    outBitmap.recycle()\n                }\n            }\n            DrawableResult(\n                drawable = bitmap.toDrawable(options.context.resources),\n                isSampled = scale < 1.0,\n                dataSource = DataSource.DISK\n            )\n        }\n\n    abstract class Factory<T : Any> : Fetcher.Factory<T> {\n        override fun create(data: T, options: Options, imageLoader: ImageLoader): Fetcher =\n            VideoFrameFetcher(options) { setDataSource(data) }\n\n        protected abstract fun MediaMetadataRetriever.setDataSource(data: T)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/colorpicker/BaseColorPreference.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.colorpicker\n\nimport android.content.Context\nimport android.graphics.drawable.GradientDrawable\nimport android.util.AttributeSet\nimport androidx.annotation.AttrRes\nimport androidx.annotation.ColorInt\nimport androidx.annotation.StyleRes\nimport androidx.preference.DialogPreference\nimport androidx.preference.PreferenceViewHolder\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.util.getFloatByAttr\nimport kotlin.math.roundToInt\n\nabstract class BaseColorPreference : DialogPreference {\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    constructor(\n        context: Context,\n        attrs: AttributeSet?,\n        @AttrRes defStyleAttr: Int,\n        @StyleRes defStyleRes: Int\n    ) : super(context, attrs, defStyleAttr, defStyleRes)\n\n    init {\n        widgetLayoutResource = R.layout.color_preference_widget\n        dialogLayoutResource = R.layout.color_picker_dialog\n        setPositiveButtonText(android.R.string.ok)\n        setNegativeButtonText(android.R.string.cancel)\n    }\n\n    override fun onBindViewHolder(holder: PreferenceViewHolder) {\n        super.onBindViewHolder(holder)\n\n        val swatchView = holder.findViewById(R.id.swatch)\n        if (swatchView != null) {\n            val swatchDrawable = swatchView.background as GradientDrawable\n            swatchDrawable.setColor(value)\n            var alpha = 0xFF\n            if (!isEnabled) {\n                val disabledAlpha = context.getFloatByAttr(android.R.attr.disabledAlpha)\n                alpha = (disabledAlpha * alpha).roundToInt()\n            }\n            swatchDrawable.alpha = alpha\n        }\n    }\n\n    @get:ColorInt\n    abstract var value: Int\n\n    @get:ColorInt\n    abstract val defaultValue: Int\n\n    abstract val entryValues: IntArray\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/colorpicker/ColorPaletteAdapter.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.colorpicker\n\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.AbsListView\nimport android.widget.BaseAdapter\n\nclass ColorPaletteAdapter(private val colors: IntArray) : BaseAdapter() {\n    override fun hasStableIds(): Boolean = true\n\n    override fun getCount(): Int = colors.size\n\n    override fun getItem(position: Int): Int = colors[position]\n\n    override fun getItemId(position: Int): Long = getItem(position).toLong()\n\n    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {\n        val swatchView = convertView as ColorSwatchView?\n            ?: ColorSwatchView(parent.context).apply {\n                layoutParams = AbsListView.LayoutParams(\n                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT\n                )\n            }\n        return swatchView.apply {\n            setColor(getItem(position))\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/colorpicker/ColorPreferenceDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.colorpicker\n\nimport android.app.Dialog\nimport android.content.Context\nimport android.os.Bundle\nimport android.view.View\nimport android.widget.GridView\nimport androidx.appcompat.app.AlertDialog\nimport androidx.core.view.ViewCompat\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.ui.MaterialPreferenceDialogFragmentCompat\nimport me.zhanghai.android.files.util.ParcelableState\nimport me.zhanghai.android.files.util.getState\nimport me.zhanghai.android.files.util.putState\nimport me.zhanghai.android.files.util.withTheme\n\nclass ColorPreferenceDialogFragment : MaterialPreferenceDialogFragmentCompat() {\n    override val preference: BaseColorPreference\n        get() = super.preference as BaseColorPreference\n\n    private lateinit var colors: IntArray\n    private var checkedColor = 0\n    private var defaultColor = 0\n\n    private lateinit var paletteGrid: GridView\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        if (savedInstanceState == null) {\n            val preference = preference\n            colors = preference.entryValues\n            checkedColor = preference.value\n            defaultColor = preference.defaultValue\n        } else {\n            val state = savedInstanceState.getState<State>()\n            colors = state.colors\n            checkedColor = state.checkedColor\n            defaultColor = state.defaultColor\n        }\n    }\n\n    override fun onSaveInstanceState(outState: Bundle) {\n        super.onSaveInstanceState(outState)\n\n        val checkedPosition = paletteGrid.checkedItemPosition\n        val checkedColor = if (checkedPosition != -1) colors[checkedPosition] else checkedColor\n        outState.putState(State(colors, checkedColor, defaultColor))\n    }\n\n    override fun onCreateDialogView(context: Context): View? =\n        super.onCreateDialogView(context.withTheme(theme))\n\n    override fun onBindDialogView(view: View) {\n        super.onBindDialogView(view)\n\n        paletteGrid = ViewCompat.requireViewById(view, R.id.palette)\n        paletteGrid.adapter = ColorPaletteAdapter(colors)\n        val checkedPosition = colors.indexOf(checkedColor)\n        if (checkedPosition != -1) {\n            paletteGrid.setItemChecked(checkedPosition, true)\n        }\n    }\n\n    override fun onPrepareDialogBuilder(builder: AlertDialog.Builder) {\n        super.onPrepareDialogBuilder(builder)\n\n        if (defaultColor in colors) {\n            builder.setNeutralButton(R.string.default_, null)\n        }\n    }\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =\n        (super.onCreateDialog(savedInstanceState) as AlertDialog).apply {\n            if (defaultColor in colors) {\n                // Override the listener here so that we won't close the dialog.\n                setOnShowListener {\n                    getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener {\n                        paletteGrid.setItemChecked(colors.indexOf(defaultColor), true)\n                    }\n                }\n            }\n        }\n\n    override fun onDialogClosed(positiveResult: Boolean) {\n        if (!positiveResult) {\n            return\n        }\n        val checkedPosition = paletteGrid.checkedItemPosition\n        if (checkedPosition == -1) {\n            return\n        }\n        val checkedColor = colors[checkedPosition]\n        preference.value = checkedColor\n    }\n\n    @Parcelize\n    private class State(\n        val colors: IntArray,\n        val checkedColor: Int,\n        val defaultColor: Int\n    ) : ParcelableState\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/colorpicker/ColorSwatchView.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.colorpicker\n\nimport android.content.Context\nimport android.graphics.drawable.GradientDrawable\nimport android.graphics.drawable.LayerDrawable\nimport android.util.AttributeSet\nimport androidx.annotation.AttrRes\nimport androidx.annotation.ColorInt\nimport androidx.annotation.StyleRes\nimport androidx.appcompat.content.res.AppCompatResources\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.ui.CheckableView\n\nclass ColorSwatchView : CheckableView {\n    private val gradientDrawable: GradientDrawable\n\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    constructor(\n        context: Context,\n        attrs: AttributeSet?,\n        @AttrRes defStyleAttr: Int,\n        @StyleRes defStyleRes: Int\n    ) : super(context, attrs, defStyleAttr, defStyleRes)\n\n    init {\n        val background = AppCompatResources.getDrawable(\n            context, R.drawable.color_swatch_view_background\n        ) as LayerDrawable\n        gradientDrawable = background.getDrawable(0) as GradientDrawable\n        setBackground(background)\n    }\n\n    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {\n        setMeasuredDimension(\n            resolveSize(suggestedMinimumWidth, widthMeasureSpec),\n            resolveSize(suggestedMinimumHeight, heightMeasureSpec)\n        )\n    }\n\n    fun setColor(@ColorInt color: Int) {\n        gradientDrawable.apply {\n            mutate()\n            setColor(color)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/ActivityCompat.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.app.Activity\nimport android.app.ActivityManager.TaskDescription\nimport android.graphics.Color\nimport android.os.Build\nimport androidx.annotation.StyleRes\nimport androidx.core.app.ActivityCompat\nimport me.zhanghai.android.files.util.getColorByAttr\n\nfun Activity.recreateCompat() {\n    ActivityCompat.recreate(this)\n}\n\nfun Activity.setThemeCompat(@StyleRes resid: Int) {\n    setTheme(resid)\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n        val surfaceColor = getColorByAttr(com.google.android.material.R.attr.colorSurface)\n        if (surfaceColor != 0 && Color.alpha(surfaceColor) == 0xFF) {\n            @Suppress(\"DEPRECATION\")\n            setTaskDescription(TaskDescription(null, null, surfaceColor))\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/ApplicationInfoCompat.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.content.pm.ApplicationInfo\nimport android.os.Build\nimport me.zhanghai.android.files.hiddenapi.RestrictedHiddenApi\nimport me.zhanghai.android.files.util.lazyReflectedField\n\n@RestrictedHiddenApi\nprivate val versionCodeField by lazyReflectedField(ApplicationInfo::class.java, \"versionCode\")\n\n@RestrictedHiddenApi\nprivate val longVersionCodeField by lazyReflectedField(\n    ApplicationInfo::class.java, \"longVersionCode\"\n)\n\nval ApplicationInfo.longVersionCodeCompat: Long\n    get() =\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {\n            longVersionCodeField.getLong(this)\n        } else {\n            versionCodeField.getInt(this).toLong()\n        }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/ContextCompat.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.annotation.SuppressLint\nimport android.content.BroadcastReceiver\nimport android.content.Context\nimport android.content.Intent\nimport android.content.IntentFilter\nimport android.content.res.ColorStateList\nimport android.graphics.drawable.Drawable\nimport android.util.AttributeSet\nimport androidx.annotation.AttrRes\nimport androidx.annotation.ColorInt\nimport androidx.annotation.ColorRes\nimport androidx.annotation.DrawableRes\nimport androidx.annotation.StyleRes\nimport androidx.annotation.StyleableRes\nimport androidx.appcompat.content.res.AppCompatResources\nimport androidx.appcompat.widget.TintTypedArray\nimport androidx.core.content.ContextCompat\nimport me.zhanghai.android.files.hiddenapi.RestrictedHiddenApi\nimport me.zhanghai.android.files.util.lazyReflectedMethod\nimport java.util.concurrent.Executor\nimport kotlin.contracts.ExperimentalContracts\nimport kotlin.contracts.InvocationKind\nimport kotlin.contracts.contract\n\nfun Context.checkSelfPermissionCompat(permission: String): Int =\n    ContextCompat.checkSelfPermission(this, permission)\n\n@ColorInt\nfun Context.getColorCompat(@ColorRes id: Int): Int = getColorStateListCompat(id).defaultColor\n\nfun Context.getColorStateListCompat(@ColorRes id: Int): ColorStateList =\n    AppCompatResources.getColorStateList(this, id)!!\n\nfun Context.getDrawableCompat(@DrawableRes id: Int): Drawable =\n    AppCompatResources.getDrawable(this, id)!!\n\nfun <T> Context.getSystemServiceCompat(serviceClass: Class<T>): T =\n    ContextCompat.getSystemService(this, serviceClass)!!\n\nval Context.mainExecutorCompat: Executor\n    get() = ContextCompat.getMainExecutor(this)\n\n@SuppressLint(\"RestrictedApi\")\nfun Context.obtainStyledAttributesCompat(\n    set: AttributeSet? = null,\n    @StyleableRes attrs: IntArray,\n    @AttrRes defStyleAttr: Int = 0,\n    @StyleRes defStyleRes: Int = 0\n): TintTypedArray =\n    TintTypedArray.obtainStyledAttributes(this, set, attrs, defStyleAttr, defStyleRes)\n\n@OptIn(ExperimentalContracts::class)\n@SuppressLint(\"RestrictedApi\")\ninline fun <R> TintTypedArray.use(block: (TintTypedArray) -> R): R {\n    contract {\n        callsInPlace(block, InvocationKind.EXACTLY_ONCE)\n    }\n    return try {\n        block(this)\n    } finally {\n        recycle()\n    }\n}\n\nfun Context.registerReceiverCompat(\n    receiver: BroadcastReceiver?,\n    filter: IntentFilter,\n    flags: Int\n): Intent? = ContextCompat.registerReceiver(this, receiver, filter, flags)\n\n@RestrictedHiddenApi\nprivate val getThemeResIdMethod by lazyReflectedMethod(Context::class.java, \"getThemeResId\")\n\nval Context.themeResIdCompat: Int\n    @StyleRes\n    get() = getThemeResIdMethod.invoke(this) as Int\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/DialogCompat.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.app.Dialog\nimport android.view.View\nimport androidx.annotation.IdRes\nimport androidx.core.app.DialogCompat\n\n@Suppress(\"UNCHECKED_CAST\")\nfun <T : View> Dialog.requireViewByIdCompat(@IdRes id: Int): T =\n    DialogCompat.requireViewById(this, id) as T\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/DigitsKeyListenerCompat.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.os.Build\nimport android.text.method.DigitsKeyListener\nimport java.util.Locale\n\nobject DigitsKeyListenerCompat {\n    fun getInstance(locale: Locale?, sign: Boolean, decimal: Boolean): DigitsKeyListener =\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n            DigitsKeyListener.getInstance(locale, sign, decimal)\n        } else {\n            @Suppress(\"DEPRECATION\")\n            DigitsKeyListener.getInstance(sign, decimal)\n        }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/DocumentsContractCompat.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.content.ContentResolver\nimport android.net.Uri\nimport android.os.Build\nimport android.provider.DocumentsContract\nimport me.zhanghai.android.files.app.packageManager\n\nobject DocumentsContractCompat {\n    const val EXTRA_INITIAL_URI = \"android.provider.extra.INITIAL_URI\"\n    const val EXTRA_SHOW_ADVANCED = \"android.provider.extra.SHOW_ADVANCED\"\n\n    const val EXTERNAL_STORAGE_PROVIDER_AUTHORITY = \"com.android.externalstorage.documents\"\n    const val EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID = \"primary\"\n\n    private const val PATH_DOCUMENT = \"document\"\n    private const val PATH_CHILDREN = \"children\"\n    private const val PATH_TREE = \"tree\"\n\n    /** @see DocumentsContract.PACKAGE_DOCUMENTS_UI */\n    fun getDocumentsUiPackage(): String? {\n        // See android.permission.cts.ProviderPermissionTest.testManageDocuments()\n        val packageInfos = packageManager.getPackagesHoldingPermissions(\n            arrayOf(android.Manifest.permission.MANAGE_DOCUMENTS), 0\n        )\n        val packageInfo = packageInfos.firstOrNull { it.packageName.endsWith(\".documentsui\") }\n            ?: packageInfos.firstOrNull()\n        return packageInfo?.packageName\n    }\n\n    /** @see DocumentsContract.isDocumentUri */\n    fun isDocumentUri(uri: Uri): Boolean {\n        if (uri.scheme != ContentResolver.SCHEME_CONTENT) {\n            return false\n        }\n        val pathSegments = uri.pathSegments\n        return when (pathSegments.size) {\n            2 -> pathSegments[0] == PATH_DOCUMENT\n            4 -> pathSegments[0] == PATH_TREE && pathSegments[2] == PATH_DOCUMENT\n            else -> false\n        }\n    }\n\n    fun isTreeUri(uri: Uri): Boolean =\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n            DocumentsContract.isTreeUri(uri)\n        } else {\n            uri.pathSegments.let { it.size >= 2 && it[0] == PATH_TREE }\n        }\n\n    fun isChildDocumentsUri(uri: Uri): Boolean {\n        val pathSegments = uri.pathSegments\n        return when (pathSegments.size) {\n            3 -> pathSegments[0] == PATH_DOCUMENT && pathSegments[2] == PATH_CHILDREN\n            5 ->\n                pathSegments[0] == PATH_TREE && pathSegments[2] == PATH_DOCUMENT\n                    && pathSegments[4] == PATH_CHILDREN\n            else -> false\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/DrawableCompat.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.graphics.drawable.Drawable\nimport androidx.annotation.ColorInt\nimport androidx.core.graphics.drawable.DrawableCompat\n\nvar Drawable.layoutDirectionCompat: Int\n    get() = DrawableCompat.getLayoutDirection(this)\n    set(value) {\n        DrawableCompat.setLayoutDirection(this, value)\n    }\n\nfun Drawable.setTintCompat(@ColorInt tint: Int) {\n    DrawableCompat.setTint(this, tint)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/EnvironmentCompat2.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nobject EnvironmentCompat2 {\n    /**\n     * @see android.os.Environment.DIRECTORY_SCREENSHOTS\n     */\n    const val DIRECTORY_SCREENSHOTS = \"Screenshots\"\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/ErrnoExceptionCompat.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.system.ErrnoException\nimport me.zhanghai.android.files.hiddenapi.RestrictedHiddenApi\nimport me.zhanghai.android.files.util.lazyReflectedField\n\n@RestrictedHiddenApi\nprivate val functionNameField by lazyReflectedField(ErrnoException::class.java, \"functionName\")\n\nval ErrnoException.functionNameCompat: String\n    get() = functionNameField.get(this) as String\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/FrameLayoutCompat.kt",
    "content": "/*\n * Copyright (c) 2025 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.annotation.SuppressLint\nimport android.graphics.drawable.Drawable\nimport android.widget.FrameLayout\n\nvar FrameLayout.foregroundCompat: Drawable?\n    // The get/setForeground() methods were on FrameLayout and are now on View, so this is fine\n    // because both are classes and invoke-virtual works for both.\n    @SuppressLint(\"NewApi\")\n    get() = foreground\n    @SuppressLint(\"NewApi\")\n    set(value) {\n        foreground = value\n    }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/InputStreamCompat.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport java.io.IOException\nimport java.io.InputStream\nimport kotlin.reflect.KClass\n\nfun KClass<InputStream>.nullInputStream(): InputStream =\n    object : InputStream() {\n        private var closed = false\n\n        override fun read(): Int {\n            ensureOpen()\n            return -1\n        }\n\n        override fun read(bytes: ByteArray, offset: Int, length: Int): Int {\n            if (!(offset >= 0 && length >= 0 && length <= bytes.size - offset)) {\n                throw IndexOutOfBoundsException()\n            }\n            ensureOpen()\n            return if (length == 0) 0 else -1\n        }\n\n        override fun skip(length: Long): Long {\n            ensureOpen()\n            return 0\n        }\n\n        override fun available(): Int {\n            ensureOpen()\n            return 0\n        }\n\n        override fun close() {\n            closed = true\n        }\n\n        private fun ensureOpen() {\n            if (closed) {\n                throw IOException(\"Stream closed\")\n            }\n        }\n    }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/IntentCompat.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.content.Intent\nimport me.zhanghai.android.files.util.andInv\n\nfun Intent.removeFlagsCompat(flags: Int) {\n    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {\n        removeFlags(flags)\n    } else {\n        setFlags(this.flags andInv flags)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/KotlinCompat.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport kotlin.comparisons.reversed as kotlinReversed\nimport kotlin.collections.removeFirst as kotlinRemoveFirst\nimport kotlin.collections.removeLast as kotlinRemoveLast\n\nfun <T> Comparator<T>.reversedCompat(): Comparator<T> = kotlinReversed()\n\nfun <T> MutableList<T>.removeFirstCompat(): T = kotlinRemoveFirst()\n\nfun <T> MutableList<T>.removeLastCompat(): T = kotlinRemoveLast()\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/ListFormatterCompat.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.icu.text.ListFormatter\nimport android.os.Build\n\nobject ListFormatterCompat {\n    fun format(vararg items: Any?): String =\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n            ListFormatter.getInstance().format(*items)\n        } else {\n            formatCompat(items.asList())\n        }\n\n    fun format(items: Collection<*>): String =\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n            ListFormatter.getInstance().format(items)\n        } else {\n            formatCompat(items)\n        }\n\n    private fun formatCompat(items: Collection<*>): String = items.joinToString(\", \")\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/LocaleConfigCompat.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.app.LocaleConfig\nimport android.content.Context\nimport android.content.res.XmlResourceParser\nimport android.os.Build\nimport android.util.Log\nimport androidx.annotation.RequiresApi\nimport androidx.annotation.XmlRes\nimport androidx.core.content.res.ResourcesCompat\nimport androidx.core.os.LocaleListCompat\nimport org.xmlpull.v1.XmlPullParser\nimport java.io.FileNotFoundException\n\n/**\n * @see android.app.LocaleConfig\n */\nclass LocaleConfigCompat(context: Context) {\n    var status = 0\n        private set\n\n    var supportedLocales: LocaleListCompat? = null\n        private set\n\n    init {\n        val impl = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {\n            Api33Impl(context)\n        } else {\n            Api21Impl(context)\n        }\n        status = impl.status\n        supportedLocales = impl.supportedLocales\n    }\n\n    companion object {\n        /**\n         * Succeeded reading the LocaleConfig structure stored in an XML file.\n         */\n        const val STATUS_SUCCESS = 0\n\n        /**\n         * No android:localeConfig tag on <application>.\n         */\n        const val STATUS_NOT_SPECIFIED = 1\n\n        /**\n         * Malformed input in the XML file where the LocaleConfig was stored.\n         */\n        const val STATUS_PARSING_FAILED = 2\n    }\n\n    private abstract class Impl {\n        abstract val status: Int\n        abstract val supportedLocales: LocaleListCompat?\n    }\n\n    private class Api21Impl(context: Context) : Impl() {\n        override var status = 0\n            private set\n\n        override var supportedLocales: LocaleListCompat? = null\n            private set\n\n        init {\n            val resourceId = try {\n                getLocaleConfigResourceId(context)\n            } catch (e: Exception) {\n                Log.w(TAG, \"The resource file pointed to by the given resource ID isn't found.\", e)\n            }\n            if (resourceId == ResourcesCompat.ID_NULL) {\n                status = STATUS_NOT_SPECIFIED\n            } else {\n                val resources = context.resources\n                try {\n                    supportedLocales = resources.getXml(resourceId).use { parseLocaleConfig(it) }\n                    status = STATUS_SUCCESS\n                } catch (e: Exception) {\n                    val resourceEntryName = resources.getResourceEntryName(resourceId)\n                    Log.w(TAG, \"Failed to parse XML configuration from $resourceEntryName\", e)\n                    status = STATUS_PARSING_FAILED\n                }\n            }\n        }\n\n        // @see com.android.server.pm.pkg.parsing.ParsingPackageUtils\n        @XmlRes\n        private fun getLocaleConfigResourceId(context: Context): Int {\n            // Java cookies starts at 1, while passing 0 (invalid cookie for Java) makes\n            // AssetManager pick the last asset containing such a file name.\n            // We should go over all the assets containing AndroidManifest.xml, however there's no\n            // API to do that, so the best we can do is to start from the first asset and iterate\n            // until we can't find the next asset containing AndroidManifest.xml.\n            var cookie = 1\n            var isAndroidManifestFound = false\n            while (true) {\n                val parser = try {\n                    context.assets.openXmlResourceParser(cookie, FILE_NAME_ANDROID_MANIFEST)\n                } catch (e: FileNotFoundException) {\n                    if (!isAndroidManifestFound) {\n                        ++cookie\n                        continue\n                    } else {\n                        break\n                    }\n                }\n                isAndroidManifestFound = true\n                parser.use {\n                    do {\n                        if (parser.eventType != XmlPullParser.START_TAG) {\n                            continue\n                        }\n                        if (parser.name != TAG_MANIFEST) {\n                            parser.skipCurrentTag()\n                            continue\n                        }\n                        if (parser.getAttributeValue(null, ATTR_PACKAGE) != context.packageName) {\n                            break\n                        }\n                        while (parser.next() != XmlPullParser.END_TAG) {\n                            if (parser.eventType != XmlPullParser.START_TAG) {\n                                continue\n                            }\n                            if (parser.name != TAG_APPLICATION) {\n                                parser.skipCurrentTag()\n                                continue\n                            }\n                            return parser.getAttributeResourceValue(\n                                NAMESPACE_ANDROID, ATTR_LOCALE_CONFIG, ResourcesCompat.ID_NULL\n                            )\n                        }\n                    } while (parser.next() != XmlPullParser.END_DOCUMENT)\n                }\n                ++cookie\n            }\n            return ResourcesCompat.ID_NULL\n        }\n\n        private fun parseLocaleConfig(parser: XmlResourceParser): LocaleListCompat {\n            val localeNames = mutableSetOf<String>()\n            do {\n                if (parser.eventType != XmlPullParser.START_TAG) {\n                    continue\n                }\n                if (parser.name != TAG_LOCALE_CONFIG) {\n                    parser.skipCurrentTag()\n                    continue\n                }\n                while (parser.next() != XmlPullParser.END_TAG) {\n                    if (parser.eventType != XmlPullParser.START_TAG) {\n                        continue\n                    }\n                    if (parser.name != TAG_LOCALE) {\n                        parser.skipCurrentTag()\n                        continue\n                    }\n                    localeNames += parser.getAttributeValue(NAMESPACE_ANDROID, ATTR_NAME)\n                    parser.skipCurrentTag()\n                }\n            } while (parser.next() != XmlPullParser.END_DOCUMENT)\n            return LocaleListCompat.forLanguageTags(localeNames.joinToString(\",\"))\n        }\n\n        private fun XmlPullParser.skipCurrentTag() {\n            val outerDepth = depth\n            var type: Int\n            do {\n                type = next()\n            } while (type != XmlPullParser.END_DOCUMENT &&\n                (type != XmlPullParser.END_TAG || depth > outerDepth))\n        }\n\n        companion object {\n            private const val TAG = \"LocaleConfigCompat\"\n\n            private const val FILE_NAME_ANDROID_MANIFEST = \"AndroidManifest.xml\"\n\n            private const val TAG_APPLICATION = \"application\"\n            private const val TAG_LOCALE_CONFIG = \"locale-config\"\n            private const val TAG_LOCALE = \"locale\"\n            private const val TAG_MANIFEST = \"manifest\"\n\n            private const val NAMESPACE_ANDROID = \"http://schemas.android.com/apk/res/android\"\n\n            private const val ATTR_LOCALE_CONFIG = \"localeConfig\"\n            private const val ATTR_NAME = \"name\"\n            private const val ATTR_PACKAGE = \"package\"\n        }\n    }\n\n    @RequiresApi(Build.VERSION_CODES.TIRAMISU)\n    private class Api33Impl(context: Context) : Impl() {\n        override var status: Int = 0\n            private set\n\n        override var supportedLocales: LocaleListCompat? = null\n            private set\n\n        init {\n            val platformLocaleConfig = LocaleConfig(context)\n            status = platformLocaleConfig.status\n            supportedLocales = platformLocaleConfig.supportedLocales\n                ?.let { LocaleListCompat.wrap(it) }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/MediaMetadataRetrieverCompat.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.graphics.Bitmap\nimport android.media.MediaMetadataRetriever\nimport android.os.Build\nimport androidx.annotation.RequiresApi\nimport kotlin.contracts.ExperimentalContracts\nimport kotlin.contracts.InvocationKind\nimport kotlin.contracts.contract\nimport kotlin.reflect.KClass\n\nval KClass<MediaMetadataRetriever>.METADATA_KEY_SAMPLERATE: Int\n    @RequiresApi(Build.VERSION_CODES.Q)\n    get() = 38\n\nfun MediaMetadataRetriever.getFrameAtTimeCompat(\n    timeUs: Long,\n    option: Int,\n    params: MediaMetadataRetriever.BitmapParams?\n): Bitmap? =\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && params != null) {\n        getFrameAtTime(timeUs, option, params)\n    } else {\n        getFrameAtTime(timeUs, option)\n    }\n\n@RequiresApi(Build.VERSION_CODES.O_MR1)\nfun MediaMetadataRetriever.getScaledFrameAtTimeCompat(\n    timeUs: Long,\n    option: Int,\n    dstWidth: Int,\n    dstHeight: Int,\n    params: MediaMetadataRetriever.BitmapParams?\n): Bitmap? =\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && params != null) {\n        getScaledFrameAtTime(timeUs, option, dstWidth, dstHeight, params)\n    } else {\n        getScaledFrameAtTime(timeUs, option, dstWidth, dstHeight)\n    }\n\n@OptIn(ExperimentalContracts::class)\ninline fun <R> MediaMetadataRetriever.use(block: (MediaMetadataRetriever) -> R): R {\n    contract {\n        callsInPlace(block, InvocationKind.EXACTLY_ONCE)\n    }\n    val autoCloseable: AutoCloseable = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n        this\n    } else {\n        AutoCloseable { release() }\n    }\n    return autoCloseable.use { block(this) }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/MenuCompat.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.view.Menu\nimport androidx.core.view.MenuCompat\n\nfun Menu.setGroupDividerEnabledCompat(groupDividerEnabled: Boolean) {\n    MenuCompat.setGroupDividerEnabled(this, groupDividerEnabled)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/MimeTypeMapCompat.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.webkit.MimeTypeMap\n\n// Generated by mime/generate-extensions.sh and mime/generate-code.sh .\nprivate val extensionToMimeTypeMap = mapOf(\n    \"%\" to \"application/x-trash\",\n    \"123\" to \"application/vnd.lotus-1-2-3\",\n    \"1905.1\" to \"application/vnd.ieee.1905\",\n    \"1clr\" to \"application/clr\",\n    \"1km\" to \"application/vnd.1000minds.decision-model+xml\",\n    \"210\" to \"application/p21\",\n    \"3dm\" to \"text/vnd.in3d.3dml\",\n    \"3dml\" to \"text/vnd.in3d.3dml\",\n    \"3g2\" to \"video/3gpp2\",\n    \"3ga\" to \"audio/3gpp\",\n    \"3gp\" to \"video/3gpp\",\n    \"3gp2\" to \"video/3gpp2\",\n    \"3gpp\" to \"video/3gpp\",\n    \"3gpp2\" to \"video/3gpp2\",\n    \"3mf\" to \"application/vnd.ms-3mfdocument\",\n    \"3tz\" to \"application/vnd.maxar.archive.3tz+zip\",\n    \"726\" to \"audio/32kadpcm\",\n    \"7z\" to \"application/x-7z-compressed\",\n    \"AMR\" to \"audio/AMR\",\n    \"AWB\" to \"audio/AMR-WB\",\n    \"CQL\" to \"text/cql\",\n    \"ELN\" to \"application/vnd.eln+zip\",\n    \"J2C\" to \"image/j2c\",\n    \"J2K\" to \"image/j2c\",\n    \"PGB\" to \"image/vnd.globalgraphics.pgb\",\n    \"QCP\" to \"audio/EVRC-QCP\",\n    \"SAR\" to \"application/vnd.sar\",\n    \"VES\" to \"application/vnd.ves.encrypted\",\n    \"VFK\" to \"text/vnd.exchangeable\",\n    \"a\" to \"text/vnd.a\",\n    \"a2l\" to \"application/A2L\",\n    \"a52\" to \"audio/ac3\",\n    \"aa3\" to \"audio/ATRAC3\",\n    \"aac\" to \"audio/aac\",\n    \"aal\" to \"audio/ATRAC-ADVANCED-LOSSLESS\",\n    \"abc\" to \"text/vnd.abc\",\n    \"abw\" to \"application/x-abiword\",\n    \"ac\" to \"application/pkix-attr-cert\",\n    \"ac2\" to \"application/vnd.banana-accounting\",\n    \"ac3\" to \"audio/ac3\",\n    \"acc\" to \"application/vnd.americandynamics.acc\",\n    \"acn\" to \"audio/asc\",\n    \"acu\" to \"application/vnd.acucobol\",\n    \"acutc\" to \"application/vnd.acucorp\",\n    \"adt\" to \"audio/aac\",\n    \"adts\" to \"audio/aac\",\n    \"aep\" to \"application/vnd.audiograph\",\n    \"afp\" to \"application/vnd.afpc.modca\",\n    \"age\" to \"application/vnd.age\",\n    \"ahead\" to \"application/vnd.ahead.space\",\n    \"ahv\" to \"application/vnd.android.haptics.vibration+xml\",\n    \"ai\" to \"application/postscript\",\n    \"aif\" to \"audio/x-aiff\",\n    \"aifc\" to \"audio/x-aiff\",\n    \"aiff\" to \"audio/x-aiff\",\n    \"aion\" to \"application/vnd.veritone.aion+json\",\n    \"ait\" to \"application/vnd.dvb.ait\",\n    \"alc\" to \"chemical/x-alchemy\",\n    \"ami\" to \"application/vnd.amiga.ami\",\n    \"aml\" to \"application/AML\",\n    \"amlx\" to \"application/automationml-amlx+zip\",\n    \"amr\" to \"audio/amr\",\n    \"anx\" to \"application/annodex\",\n    \"apex\" to \"application/vnd.apexlang\",\n    \"apexlang\" to \"application/vnd.apexlang\",\n    \"apk\" to \"application/vnd.android.package-archive\",\n    \"apkg\" to \"application/vnd.anki\",\n    \"apng\" to \"image/apng\",\n    \"appcache\" to \"text/cache-manifest\",\n    \"apr\" to \"application/vnd.lotus-approach\",\n    \"apxml\" to \"application/auth-policy+xml\",\n    \"arrow\" to \"application/vnd.apache.arrow.file\",\n    \"arrows\" to \"application/vnd.apache.arrow.stream\",\n    \"art\" to \"image/x-jg\",\n    \"artisan\" to \"application/vnd.artisan+json\",\n    \"arw\" to \"image/x-sony-arw\",\n    \"asc\" to \"application/pgp-keys\",\n    \"ascii\" to \"text/vnd.ascii-art\",\n    \"asf\" to \"application/vnd.ms-asf\",\n    \"asice\" to \"application/vnd.etsi.asic-e+zip\",\n    \"asics\" to \"application/vnd.etsi.asic-s+zip\",\n    \"asn\" to \"chemical/x-ncbi-asn1-spec\",\n    \"aso\" to \"chemical/x-ncbi-asn1-binary\",\n    \"ass\" to \"audio/aac\",\n    \"at3\" to \"audio/ATRAC3\",\n    \"atc\" to \"application/vnd.acucorp\",\n    \"atf\" to \"application/ATF\",\n    \"atfx\" to \"application/ATFX\",\n    \"atom\" to \"application/atom+xml\",\n    \"atomcat\" to \"application/atomcat+xml\",\n    \"atomdeleted\" to \"application/atomdeleted+xml\",\n    \"atomsrv\" to \"application/atomserv+xml\",\n    \"atomsvc\" to \"application/atomsvc+xml\",\n    \"atx\" to \"audio/ATRAC-X\",\n    \"atxml\" to \"application/ATXML\",\n    \"au\" to \"audio/basic\",\n    \"auc\" to \"application/tamp-apex-update-confirm\",\n    \"avci\" to \"image/avci\",\n    \"avcs\" to \"image/avcs\",\n    \"avi\" to \"video/avi\",\n    \"avif\" to \"image/avif\",\n    \"awb\" to \"audio/AMR-WB\",\n    \"axa\" to \"audio/annodex\",\n    \"axv\" to \"video/annodex\",\n    \"azf\" to \"application/vnd.airzip.filesecure.azf\",\n    \"azs\" to \"application/vnd.airzip.filesecure.azs\",\n    \"azv\" to \"image/vnd.airzip.accelerator.azv\",\n    \"azw3\" to \"application/vnd.amazon.mobi8-ebook\",\n    \"b\" to \"chemical/x-molconn-Z\",\n    \"b16\" to \"image/vnd.pco.b16\",\n    \"bak\" to \"application/x-trash\",\n    \"bar\" to \"application/vnd.qualcomm.brew-app-res\",\n    \"bary\" to \"model/vnd.bary\",\n    \"bat\" to \"application/x-msdos-program\",\n    \"bcpio\" to \"application/x-bcpio\",\n    \"bdm\" to \"application/vnd.syncml.dm+wbxml\",\n    \"bed\" to \"application/vnd.realvnc.bed\",\n    \"bh2\" to \"application/vnd.fujitsu.oasysprs\",\n    \"bib\" to \"text/x-bibtex\",\n    \"bik\" to \"video/vnd.radgamettools.bink\",\n    \"bin\" to \"application/octet-stream\",\n    \"bk2\" to \"video/vnd.radgamettools.bink\",\n    \"bkm\" to \"application/vnd.nervana\",\n    \"bmed\" to \"multipart/vnd.bint.med-plus\",\n    \"bmi\" to \"application/vnd.bmi\",\n    \"bmml\" to \"application/vnd.balsamiq.bmml+xml\",\n    \"bmp\" to \"image/x-ms-bmp\",\n    \"bmpr\" to \"application/vnd.balsamiq.bmpr\",\n    \"boo\" to \"text/x-boo\",\n    \"book\" to \"application/x-maker\",\n    \"box\" to \"application/vnd.previewsystems.box\",\n    \"bpd\" to \"application/vnd.hbci\",\n    \"brf\" to \"text/plain\",\n    \"bsd\" to \"chemical/x-crossfire\",\n    \"bsp\" to \"model/vnd.valve.source.compiled-map\",\n    \"btf\" to \"image/prs.btif\",\n    \"btif\" to \"image/prs.btif\",\n    \"c\" to \"text/x-csrc\",\n    \"c++\" to \"text/x-c++src\",\n    \"c11amc\" to \"application/vnd.cluetrust.cartomobile-config\",\n    \"c11amz\" to \"application/vnd.cluetrust.cartomobile-config-pkg\",\n    \"c3d\" to \"chemical/x-chem3d\",\n    \"c3ex\" to \"application/cccex\",\n    \"c4d\" to \"application/vnd.clonk.c4group\",\n    \"c4f\" to \"application/vnd.clonk.c4group\",\n    \"c4g\" to \"application/vnd.clonk.c4group\",\n    \"c4p\" to \"application/vnd.clonk.c4group\",\n    \"c4u\" to \"application/vnd.clonk.c4group\",\n    \"c9r\" to \"application/vnd.cryptomator.encrypted\",\n    \"c9s\" to \"application/vnd.cryptomator.encrypted\",\n    \"cab\" to \"application/vnd.ms-cab-compressed\",\n    \"cac\" to \"chemical/x-cache\",\n    \"cache\" to \"chemical/x-cache\",\n    \"cap\" to \"application/vnd.tcpdump.pcap\",\n    \"car\" to \"application/vnd.ipld.car\",\n    \"carjson\" to \"application/vnd.eu.kasparian.car+json\",\n    \"cascii\" to \"chemical/x-cactvs-binary\",\n    \"cat\" to \"application/vnd.ms-pki.seccat\",\n    \"cbin\" to \"chemical/x-cactvs-binary\",\n    \"cbor\" to \"application/cbor\",\n    \"cbr\" to \"application/vnd.comicbook-rar\",\n    \"cbz\" to \"application/vnd.comicbook+zip\",\n    \"cc\" to \"text/x-c++src\",\n    \"ccc\" to \"text/vnd.net2phone.commcenter.command\",\n    \"ccmp\" to \"application/ccmp+xml\",\n    \"ccxml\" to \"application/ccxml+xml\",\n    \"cda\" to \"application/x-cdf\",\n    \"cdbcmsg\" to \"application/vnd.contact.cmsg\",\n    \"cdf\" to \"application/x-cdf\",\n    \"cdfx\" to \"application/CDFX+XML\",\n    \"cdkey\" to \"application/vnd.mediastation.cdkey\",\n    \"cdmia\" to \"application/cdmi-capability\",\n    \"cdmic\" to \"application/cdmi-container\",\n    \"cdmid\" to \"application/cdmi-domain\",\n    \"cdmio\" to \"application/cdmi-object\",\n    \"cdmiq\" to \"application/cdmi-queue\",\n    \"cdr\" to \"image/x-coreldraw\",\n    \"cdt\" to \"image/x-coreldrawtemplate\",\n    \"cdx\" to \"chemical/x-cdx\",\n    \"cdxml\" to \"application/vnd.chemdraw+xml\",\n    \"cdy\" to \"application/vnd.cinderella\",\n    \"cea\" to \"application/CEA\",\n    \"cef\" to \"chemical/x-cxf\",\n    \"cellml\" to \"application/cellml+xml\",\n    \"cer\" to \"application/pkix-cert\",\n    \"cgm\" to \"image/cgm\",\n    \"chm\" to \"chemical/x-chemdraw\",\n    \"chrt\" to \"application/vnd.kde.kchart\",\n    \"cif\" to \"chemical/x-cif\",\n    \"cii\" to \"application/vnd.anser-web-certificate-issue-initiation\",\n    \"cil\" to \"application/vnd.ms-artgalry\",\n    \"cl\" to \"application/simple-filter+xml\",\n    \"cla\" to \"application/vnd.claymore\",\n    \"class\" to \"application/java-vm\",\n    \"cld\" to \"model/vnd.cld\",\n    \"clkk\" to \"application/vnd.crick.clicker.keyboard\",\n    \"clkp\" to \"application/vnd.crick.clicker.palette\",\n    \"clkt\" to \"application/vnd.crick.clicker.template\",\n    \"clkw\" to \"application/vnd.crick.clicker.wordbank\",\n    \"clkx\" to \"application/vnd.crick.clicker\",\n    \"cls\" to \"text/x-tex\",\n    \"clue\" to \"application/clue_info+xml\",\n    \"cmc\" to \"application/vnd.cosmocaller\",\n    \"cmdf\" to \"chemical/x-cmdf\",\n    \"cml\" to \"chemical/x-cml\",\n    \"cmp\" to \"application/vnd.yellowriver-custom-menu\",\n    \"cmsc\" to \"application/cms\",\n    \"cnd\" to \"text/jcr-cnd\",\n    \"cod\" to \"application/vnd.rim.cod\",\n    \"coffee\" to \"application/vnd.coffeescript\",\n    \"com\" to \"application/x-msdos-program\",\n    \"copyright\" to \"text/vnd.debian.copyright\",\n    \"coswid\" to \"application/swid+cbor\",\n    \"cpa\" to \"chemical/x-compass\",\n    \"cpio\" to \"application/x-cpio\",\n    \"cpkg\" to \"application/vnd.xmpie.cpkg\",\n    \"cpl\" to \"application/cpl+xml\",\n    \"cpp\" to \"text/x-c++src\",\n    \"cpt\" to \"image/x-corelphotopaint\",\n    \"cr2\" to \"image/x-canon-cr2\",\n    \"crl\" to \"application/pkix-crl\",\n    \"crt\" to \"application/x-x509-ca-cert\",\n    \"crtr\" to \"application/vnd.multiad.creator\",\n    \"crw\" to \"image/x-canon-crw\",\n    \"cryptomator\" to \"application/vnd.cryptomator.vault\",\n    \"cryptonote\" to \"application/vnd.rig.cryptonote\",\n    \"csd\" to \"audio/csound\",\n    \"csf\" to \"chemical/x-cache-csf\",\n    \"csh\" to \"text/x-csh\",\n    \"csl\" to \"application/vnd.citationstyles.style+xml\",\n    \"csm\" to \"chemical/x-csml\",\n    \"csml\" to \"chemical/x-csml\",\n    \"csp\" to \"application/vnd.commonspace\",\n    \"csrattrs\" to \"application/csrattrs\",\n    \"css\" to \"text/css\",\n    \"cst\" to \"application/vnd.commonspace\",\n    \"csv\" to \"text/comma-separated-values\",\n    \"csvs\" to \"text/csv-schema\",\n    \"ctab\" to \"chemical/x-cactvs-binary\",\n    \"ctx\" to \"chemical/x-ctx\",\n    \"cu\" to \"application/cu-seeme\",\n    \"cub\" to \"chemical/x-gaussian-cube\",\n    \"cuc\" to \"application/tamp-community-update-confirm\",\n    \"cur\" to \"image/ico\",\n    \"curl\" to \"text/vnd.curl\",\n    \"cw\" to \"application/prs.cww\",\n    \"cwl\" to \"application/cwl\",\n    \"cwl.json\" to \"application/cwl+json\",\n    \"cww\" to \"application/prs.cww\",\n    \"cxf\" to \"chemical/x-cxf\",\n    \"cxx\" to \"text/x-c++src\",\n    \"d\" to \"text/x-dsrc\",\n    \"dae\" to \"model/vnd.collada+xml\",\n    \"daf\" to \"application/vnd.Mobius.DAF\",\n    \"dart\" to \"application/vnd.dart\",\n    \"dataless\" to \"application/vnd.fdsn.seed\",\n    \"davmount\" to \"application/davmount+xml\",\n    \"dbf\" to \"application/vnd.dbf\",\n    \"dcd\" to \"application/DCD\",\n    \"dcm\" to \"application/dicom\",\n    \"dcr\" to \"application/x-director\",\n    \"dd2\" to \"application/vnd.oma.dd2+xml\",\n    \"ddd\" to \"application/vnd.fujixerox.ddd\",\n    \"ddeb\" to \"application/vnd.debian.binary-package\",\n    \"ddf\" to \"application/vnd.syncml.dmddf+xml\",\n    \"deb\" to \"application/vnd.debian.binary-package\",\n    \"deploy\" to \"application/octet-stream\",\n    \"der\" to \"application/x-x509-ca-cert\",\n    \"dfac\" to \"application/vnd.dreamfactory\",\n    \"dfxp\" to \"application/ttml+xml\",\n    \"dif\" to \"video/dv\",\n    \"diff\" to \"text/plain\",\n    \"dii\" to \"application/DII\",\n    \"dim\" to \"application/vnd.fastcopy-disk-image\",\n    \"dir\" to \"application/x-director\",\n    \"dis\" to \"application/vnd.Mobius.DIS\",\n    \"dist\" to \"application/vnd.apple.installer+xml\",\n    \"distz\" to \"application/vnd.apple.installer+xml\",\n    \"dit\" to \"application/DIT\",\n    \"dive\" to \"application/vnd.patentdive\",\n    \"djv\" to \"image/vnd.djvu\",\n    \"djvu\" to \"image/vnd.djvu\",\n    \"dl\" to \"application/vnd.datalog\",\n    \"dll\" to \"application/x-msdos-program\",\n    \"dls\" to \"audio/dls\",\n    \"dmg\" to \"application/x-apple-diskimage\",\n    \"dmp\" to \"application/vnd.tcpdump.pcap\",\n    \"dms\" to \"text/vnd.DMClientScript\",\n    \"dna\" to \"application/vnd.dna\",\n    \"dng\" to \"image/x-adobe-dng\",\n    \"doc\" to \"application/msword\",\n    \"docjson\" to \"application/vnd.document+json\",\n    \"docm\" to \"application/vnd.ms-word.document.macroEnabled.12\",\n    \"docx\" to \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n    \"dor\" to \"model/vnd.gdl\",\n    \"dot\" to \"text/vnd.graphviz\",\n    \"dotm\" to \"application/vnd.ms-word.template.macroEnabled.12\",\n    \"dotx\" to \"application/vnd.openxmlformats-officedocument.wordprocessingml.template\",\n    \"dp\" to \"application/vnd.osgi.dp\",\n    \"dpg\" to \"application/vnd.dpgraph\",\n    \"dpgraph\" to \"application/vnd.dpgraph\",\n    \"dpkg\" to \"application/vnd.xmpie.dpkg\",\n    \"dpx\" to \"image/dpx\",\n    \"drle\" to \"image/dicom-rle\",\n    \"dsc\" to \"text/prs.lines.tag\",\n    \"dsm\" to \"application/vnd.desmume.movie\",\n    \"dssc\" to \"application/dssc+der\",\n    \"dtd\" to \"application/xml-dtd\",\n    \"dts\" to \"audio/vnd.dts\",\n    \"dtshd\" to \"audio/vnd.dts.hd\",\n    \"dv\" to \"video/dv\",\n    \"dvb\" to \"video/vnd.dvb.file\",\n    \"dvc\" to \"application/dvcs\",\n    \"dvi\" to \"application/x-dvi\",\n    \"dwd\" to \"application/atsc-dwd+xml\",\n    \"dwf\" to \"model/vnd.dwf\",\n    \"dwg\" to \"image/vnd.dwg\",\n    \"dx\" to \"chemical/x-jcamp-dx\",\n    \"dxf\" to \"image/vnd.dxf\",\n    \"dxp\" to \"application/vnd.spotfire.dxp\",\n    \"dxr\" to \"application/x-director\",\n    \"dzr\" to \"application/vnd.dzr\",\n    \"ebuild\" to \"application/vnd.gentoo.ebuild\",\n    \"ecelp4800\" to \"audio/vnd.nuera.ecelp4800\",\n    \"ecelp7470\" to \"audio/vnd.nuera.ecelp7470\",\n    \"ecelp9600\" to \"audio/vnd.nuera.ecelp9600\",\n    \"ecig\" to \"application/vnd.evolv.ecig.settings\",\n    \"ecigprofile\" to \"application/vnd.evolv.ecig.profile\",\n    \"ecigtheme\" to \"application/vnd.evolv.ecig.theme\",\n    \"eclass\" to \"application/vnd.gentoo.eclass\",\n    \"edm\" to \"application/vnd.novadigm.EDM\",\n    \"edx\" to \"application/vnd.novadigm.EDX\",\n    \"efi\" to \"application/efi\",\n    \"efif\" to \"application/vnd.picsel\",\n    \"ei6\" to \"application/vnd.pg.osasli\",\n    \"emb\" to \"chemical/x-embl-dl-nucleotide\",\n    \"embl\" to \"chemical/x-embl-dl-nucleotide\",\n    \"emf\" to \"image/emf\",\n    \"eml\" to \"message/rfc822\",\n    \"emm\" to \"application/vnd.ibm.electronic-media\",\n    \"emma\" to \"application/emma+xml\",\n    \"emotionml\" to \"application/emotionml+xml\",\n    \"ent\" to \"application/xml-external-parsed-entity\",\n    \"entity\" to \"application/vnd.nervana\",\n    \"enw\" to \"audio/EVRCNW\",\n    \"eol\" to \"audio/vnd.digital-winds\",\n    \"eot\" to \"application/vnd.ms-fontobject\",\n    \"ep\" to \"application/vnd.bluetooth.ep.oob\",\n    \"eps\" to \"application/postscript\",\n    \"eps2\" to \"application/postscript\",\n    \"eps3\" to \"application/postscript\",\n    \"epsf\" to \"application/postscript\",\n    \"epsi\" to \"application/postscript\",\n    \"epub\" to \"application/epub+zip\",\n    \"erf\" to \"image/x-epson-erf\",\n    \"es\" to \"text/javascript\",\n    \"es3\" to \"application/vnd.eszigno3+xml\",\n    \"esa\" to \"application/vnd.osgi.subsystem\",\n    \"esf\" to \"application/vnd.epson.esf\",\n    \"espass\" to \"application/vnd.espass-espass+zip\",\n    \"et3\" to \"application/vnd.eszigno3+xml\",\n    \"etx\" to \"text/x-setext\",\n    \"evb\" to \"audio/EVRCB\",\n    \"evc\" to \"audio/EVRC\",\n    \"evw\" to \"audio/EVRCWB\",\n    \"exe\" to \"application/x-msdos-program\",\n    \"exi\" to \"application/exi\",\n    \"exp\" to \"application/express\",\n    \"exr\" to \"image/aces\",\n    \"ext\" to \"application/vnd.novadigm.EXT\",\n    \"ez\" to \"application/andrew-inset\",\n    \"ez2\" to \"application/vnd.ezpix-album\",\n    \"ez3\" to \"application/vnd.ezpix-package\",\n    \"f4a\" to \"audio/mp4\",\n    \"f4b\" to \"audio/mp4\",\n    \"f4p\" to \"audio/mp4\",\n    \"f4v\" to \"video/mp4\",\n    \"fb\" to \"application/x-maker\",\n    \"fbdoc\" to \"application/x-maker\",\n    \"fbs\" to \"image/vnd.fastbidsheet\",\n    \"fcdt\" to \"application/vnd.adobe.formscentral.fcdt\",\n    \"fch\" to \"chemical/x-gaussian-checkpoint\",\n    \"fchk\" to \"chemical/x-gaussian-checkpoint\",\n    \"fcs\" to \"application/vnd.isac.fcs\",\n    \"fdf\" to \"application/fdf\",\n    \"fdt\" to \"application/fdt+xml\",\n    \"fe_launch\" to \"application/vnd.denovo.fcselayout-link\",\n    \"fg5\" to \"application/vnd.fujitsu.oasysgp\",\n    \"fig\" to \"application/x-xfig\",\n    \"finf\" to \"application/fastinfoset\",\n    \"fit\" to \"image/fits\",\n    \"fits\" to \"image/fits\",\n    \"fl\" to \"application/x-android-drm-fl\",\n    \"fla\" to \"application/vnd.dtg.local.flash\",\n    \"flac\" to \"audio/flac\",\n    \"flb\" to \"application/vnd.ficlab.flb+zip\",\n    \"fli\" to \"video/fli\",\n    \"flo\" to \"application/vnd.micrografx.flo\",\n    \"flt\" to \"text/vnd.ficlab.flt\",\n    \"flv\" to \"video/x-flv\",\n    \"flw\" to \"application/vnd.kde.kivio\",\n    \"flx\" to \"text/vnd.fmi.flexstor\",\n    \"fly\" to \"text/vnd.fly\",\n    \"fm\" to \"application/x-maker\",\n    \"fo\" to \"application/vnd.software602.filler.form+xml\",\n    \"fpx\" to \"image/vnd.fpx\",\n    \"frame\" to \"application/x-maker\",\n    \"frm\" to \"application/x-maker\",\n    \"fsc\" to \"application/vnd.fsc.weblaunch\",\n    \"fst\" to \"image/vnd.fst\",\n    \"ftc\" to \"application/vnd.fluxtime.clip\",\n    \"fti\" to \"application/vnd.anser-web-funds-transfer-initiation\",\n    \"fts\" to \"image/fits\",\n    \"fvt\" to \"video/vnd.fvt\",\n    \"fxp\" to \"application/vnd.adobe.fxp\",\n    \"fxpl\" to \"application/vnd.adobe.fxp\",\n    \"fzs\" to \"application/vnd.fuzzysheet\",\n    \"g2w\" to \"application/vnd.geoplan\",\n    \"g3w\" to \"application/vnd.geospace\",\n    \"gac\" to \"application/vnd.groove-account\",\n    \"gal\" to \"chemical/x-gaussian-log\",\n    \"gam\" to \"chemical/x-gamess-input\",\n    \"gamin\" to \"chemical/x-gamess-input\",\n    \"gan\" to \"application/x-ganttproject\",\n    \"gau\" to \"chemical/x-gaussian-input\",\n    \"gbr\" to \"application/rpki-ghostbusters\",\n    \"gcd\" to \"text/x-pcs-gcd\",\n    \"gcf\" to \"application/x-graphing-calculator\",\n    \"gcg\" to \"chemical/x-gcg8-sequence\",\n    \"gdl\" to \"model/vnd.gdl\",\n    \"gdz\" to \"application/vnd.familysearch.gedcom+zip\",\n    \"ged\" to \"text/vnd.familysearch.gedcom\",\n    \"gen\" to \"chemical/x-genbank\",\n    \"genozip\" to \"application/vnd.genozip\",\n    \"geo\" to \"application/vnd.dynageo\",\n    \"geojson\" to \"application/geo+json\",\n    \"gex\" to \"application/vnd.geometry-explorer\",\n    \"gf\" to \"application/x-tex-gf\",\n    \"gff3\" to \"text/gff3\",\n    \"ggb\" to \"application/vnd.geogebra.file\",\n    \"ggs\" to \"application/vnd.geogebra.slides\",\n    \"ggt\" to \"application/vnd.geogebra.tool\",\n    \"ghf\" to \"application/vnd.groove-help\",\n    \"gif\" to \"image/gif\",\n    \"gim\" to \"application/vnd.groove-identity-message\",\n    \"gjc\" to \"chemical/x-gaussian-input\",\n    \"gjf\" to \"chemical/x-gaussian-input\",\n    \"gl\" to \"video/gl\",\n    \"glb\" to \"model/gltf-binary\",\n    \"glbin\" to \"application/gltf-buffer\",\n    \"glbuf\" to \"application/gltf-buffer\",\n    \"gltf\" to \"model/gltf+json\",\n    \"gml\" to \"application/gml+xml\",\n    \"gnumeric\" to \"application/x-gnumeric\",\n    \"gph\" to \"application/vnd.FloGraphIt\",\n    \"gpkg\" to \"application/geopackage+sqlite3\",\n    \"gpkg.tar\" to \"application/vnd.gentoo.gpkg\",\n    \"gpt\" to \"chemical/x-mopac-graph\",\n    \"gqf\" to \"application/vnd.grafeq\",\n    \"gqs\" to \"application/vnd.grafeq\",\n    \"gram\" to \"application/srgs\",\n    \"grd\" to \"application/vnd.gentics.grd+json\",\n    \"gre\" to \"application/vnd.geometry-explorer\",\n    \"grv\" to \"application/vnd.groove-injector\",\n    \"grxml\" to \"application/srgs+xml\",\n    \"gsf\" to \"application/x-font\",\n    \"gsheet\" to \"application/urc-grpsheet+xml\",\n    \"gsm\" to \"audio/x-gsm\",\n    \"gtar\" to \"application/x-gtar\",\n    \"gtm\" to \"application/vnd.groove-tool-message\",\n    \"gtw\" to \"model/vnd.gtw\",\n    \"gv\" to \"text/vnd.graphviz\",\n    \"gxt\" to \"application/vnd.geonext\",\n    \"gz\" to \"application/gzip\",\n    \"h\" to \"text/x-chdr\",\n    \"h++\" to \"text/x-c++hdr\",\n    \"hal\" to \"application/vnd.hal+xml\",\n    \"hans\" to \"text/vnd.hans\",\n    \"hbc\" to \"application/vnd.hbci\",\n    \"hbci\" to \"application/vnd.hbci\",\n    \"hdf\" to \"application/x-hdf\",\n    \"hdr\" to \"image/vnd.radiance\",\n    \"hdt\" to \"application/vnd.hdt\",\n    \"heic\" to \"image/heic\",\n    \"heics\" to \"image/heic-sequence\",\n    \"heif\" to \"image/heif\",\n    \"heifs\" to \"image/heif-sequence\",\n    \"hej2\" to \"image/hej2k\",\n    \"held\" to \"application/atsc-held+xml\",\n    \"hgl\" to \"text/vnd.hgl\",\n    \"hh\" to \"text/x-c++hdr\",\n    \"hif\" to \"image/heif\",\n    \"hin\" to \"chemical/x-hin\",\n    \"hpgl\" to \"application/vnd.hp-HPGL\",\n    \"hpi\" to \"application/vnd.hp-hpid\",\n    \"hpid\" to \"application/vnd.hp-hpid\",\n    \"hpp\" to \"text/x-c++hdr\",\n    \"hps\" to \"application/vnd.hp-hps\",\n    \"hpub\" to \"application/prs.hpub+zip\",\n    \"hqx\" to \"application/mac-binhex40\",\n    \"hs\" to \"text/x-haskell\",\n    \"hsj2\" to \"image/hsj2\",\n    \"hsl\" to \"application/vnd.hsl\",\n    \"hta\" to \"application/hta\",\n    \"htc\" to \"text/x-component\",\n    \"htke\" to \"application/vnd.kenameaapp\",\n    \"htm\" to \"text/html\",\n    \"html\" to \"text/html\",\n    \"hvd\" to \"application/vnd.yamaha.hv-dic\",\n    \"hvp\" to \"application/vnd.yamaha.hv-voice\",\n    \"hvs\" to \"application/vnd.yamaha.hv-script\",\n    \"hwp\" to \"application/x-hwp\",\n    \"hxx\" to \"text/x-c++hdr\",\n    \"i2g\" to \"application/vnd.intergeo\",\n    \"ic0\" to \"application/vnd.commerce-battelle\",\n    \"ic1\" to \"application/vnd.commerce-battelle\",\n    \"ic2\" to \"application/vnd.commerce-battelle\",\n    \"ic3\" to \"application/vnd.commerce-battelle\",\n    \"ic4\" to \"application/vnd.commerce-battelle\",\n    \"ic5\" to \"application/vnd.commerce-battelle\",\n    \"ic6\" to \"application/vnd.commerce-battelle\",\n    \"ic7\" to \"application/vnd.commerce-battelle\",\n    \"ic8\" to \"application/vnd.commerce-battelle\",\n    \"ica\" to \"application/x-ica\",\n    \"icc\" to \"application/vnd.iccprofile\",\n    \"icd\" to \"application/vnd.commerce-battelle\",\n    \"icf\" to \"application/vnd.commerce-battelle\",\n    \"icm\" to \"application/vnd.iccprofile\",\n    \"ico\" to \"image/x-icon\",\n    \"ics\" to \"text/calendar\",\n    \"ief\" to \"image/ief\",\n    \"ifb\" to \"text/calendar\",\n    \"ifc\" to \"application/p21\",\n    \"ifm\" to \"application/vnd.shana.informed.formdata\",\n    \"iges\" to \"model/iges\",\n    \"igl\" to \"application/vnd.igloader\",\n    \"igm\" to \"application/vnd.insors.igm\",\n    \"ign\" to \"application/vnd.coreos.ignition+json\",\n    \"ignition\" to \"application/vnd.coreos.ignition+json\",\n    \"igs\" to \"model/iges\",\n    \"igx\" to \"application/vnd.micrografx.igx\",\n    \"iif\" to \"application/vnd.shana.informed.interchange\",\n    \"iii\" to \"application/x-iphone\",\n    \"imf\" to \"application/vnd.imagemeter.folder+zip\",\n    \"imgcal\" to \"application/vnd.3lightssoftware.imagescal\",\n    \"imi\" to \"application/vnd.imagemeter.image+zip\",\n    \"imp\" to \"application/vnd.accpac.simply.imp\",\n    \"ims\" to \"application/vnd.ms-ims\",\n    \"imscc\" to \"application/vnd.ims.imsccv1p1\",\n    \"imy\" to \"audio/imelody\",\n    \"info\" to \"application/x-info\",\n    \"ink\" to \"application/inkml+xml\",\n    \"inkml\" to \"application/inkml+xml\",\n    \"inp\" to \"chemical/x-gamess-input\",\n    \"ins\" to \"application/x-internet-signup\",\n    \"iota\" to \"application/vnd.astraea-software.iota\",\n    \"ipfix\" to \"application/ipfix\",\n    \"ipk\" to \"application/vnd.shana.informed.package\",\n    \"ipns-record\" to \"application/vnd.ipfs.ipns-record\",\n    \"irm\" to \"application/vnd.ibm.rights-management\",\n    \"irp\" to \"application/vnd.irepository.package+xml\",\n    \"ism\" to \"model/vnd.gdl\",\n    \"iso\" to \"application/x-iso9660-image\",\n    \"isp\" to \"application/x-internet-signup\",\n    \"ist\" to \"chemical/x-isostar\",\n    \"istc\" to \"application/vnd.veryant.thin\",\n    \"istr\" to \"chemical/x-isostar\",\n    \"isws\" to \"application/vnd.veryant.thin\",\n    \"itp\" to \"application/vnd.shana.informed.formtemplate\",\n    \"its\" to \"application/its+xml\",\n    \"ivp\" to \"application/vnd.immervision-ivp\",\n    \"ivu\" to \"application/vnd.immervision-ivu\",\n    \"j2c\" to \"image/j2c\",\n    \"j2k\" to \"image/j2c\",\n    \"jad\" to \"text/vnd.sun.j2me.app-descriptor\",\n    \"jam\" to \"application/vnd.jam\",\n    \"jar\" to \"application/java-archive\",\n    \"java\" to \"text/x-java\",\n    \"jdx\" to \"chemical/x-jcamp-dx\",\n    \"jfif\" to \"image/jpeg\",\n    \"jhc\" to \"image/jphc\",\n    \"jisp\" to \"application/vnd.jisp\",\n    \"jls\" to \"image/jls\",\n    \"jlt\" to \"application/vnd.hp-jlyt\",\n    \"jmz\" to \"application/x-jmol\",\n    \"jng\" to \"image/x-jng\",\n    \"jnlp\" to \"application/x-java-jnlp-file\",\n    \"joda\" to \"application/vnd.joost.joda-archive\",\n    \"jp2\" to \"image/jp2\",\n    \"jpe\" to \"image/jpeg\",\n    \"jpeg\" to \"image/jpeg\",\n    \"jpf\" to \"image/jpx\",\n    \"jpg\" to \"image/jpeg\",\n    \"jpg2\" to \"image/jp2\",\n    \"jpgm\" to \"image/jpm\",\n    \"jph\" to \"image/jph\",\n    \"jphc\" to \"image/jphc\",\n    \"jpm\" to \"image/jpm\",\n    \"jpx\" to \"image/jpx\",\n    \"jrd\" to \"application/jrd+json\",\n    \"js\" to \"text/javascript\",\n    \"json\" to \"application/json\",\n    \"json-patch\" to \"application/json-patch+json\",\n    \"jsonld\" to \"application/ld+json\",\n    \"jsontd\" to \"application/td+json\",\n    \"jsontm\" to \"application/tm+json\",\n    \"jt\" to \"model/JT\",\n    \"jtd\" to \"text/vnd.esmertec.theme-descriptor\",\n    \"jxl\" to \"image/jxl\",\n    \"jxr\" to \"image/jxr\",\n    \"jxra\" to \"image/jxrA\",\n    \"jxrs\" to \"image/jxrS\",\n    \"jxs\" to \"image/jxs\",\n    \"jxsc\" to \"image/jxsc\",\n    \"jxsi\" to \"image/jxsi\",\n    \"jxss\" to \"image/jxss\",\n    \"karbon\" to \"application/vnd.kde.karbon\",\n    \"kcm\" to \"application/vnd.nervana\",\n    \"key\" to \"application/pgp-keys\",\n    \"keynote\" to \"application/vnd.apple.keynote\",\n    \"kfo\" to \"application/vnd.kde.kformula\",\n    \"kia\" to \"application/vnd.kidspiration\",\n    \"kil\" to \"application/x-killustrator\",\n    \"kin\" to \"chemical/x-kinemage\",\n    \"kml\" to \"application/vnd.google-earth.kml+xml\",\n    \"kmz\" to \"application/vnd.google-earth.kmz\",\n    \"kne\" to \"application/vnd.Kinar\",\n    \"knp\" to \"application/vnd.Kinar\",\n    \"kom\" to \"application/vnd.hbci\",\n    \"kon\" to \"application/vnd.kde.kontour\",\n    \"koz\" to \"audio/vnd.audiokoz\",\n    \"kpr\" to \"application/vnd.kde.kpresenter\",\n    \"kpt\" to \"application/vnd.kde.kpresenter\",\n    \"ksp\" to \"application/vnd.kde.kspread\",\n    \"ktr\" to \"application/vnd.kahootz\",\n    \"ktx\" to \"image/ktx\",\n    \"ktx2\" to \"image/ktx2\",\n    \"ktz\" to \"application/vnd.kahootz\",\n    \"kwd\" to \"application/vnd.kde.kword\",\n    \"kwt\" to \"application/vnd.kde.kword\",\n    \"l16\" to \"audio/L16\",\n    \"las\" to \"application/vnd.las\",\n    \"lasjson\" to \"application/vnd.las.las+json\",\n    \"lasxml\" to \"application/vnd.las.las+xml\",\n    \"latex\" to \"application/x-latex\",\n    \"lbc\" to \"audio/iLBC\",\n    \"lbd\" to \"application/vnd.llamagraphics.life-balance.desktop\",\n    \"lbe\" to \"application/vnd.llamagraphics.life-balance.exchange+xml\",\n    \"lca\" to \"application/vnd.logipipe.circuit+zip\",\n    \"lcs\" to \"application/vnd.logipipe.circuit+zip\",\n    \"le\" to \"application/vnd.bluetooth.le.oob\",\n    \"les\" to \"application/vnd.hhe.lesson-player\",\n    \"lgr\" to \"application/lgr+xml\",\n    \"lha\" to \"application/x-lha\",\n    \"lhs\" to \"text/x-literate-haskell\",\n    \"lhzd\" to \"application/vnd.belightsoft.lhzd+zip\",\n    \"lhzl\" to \"application/vnd.belightsoft.lhzl+zip\",\n    \"lin\" to \"application/bbolin\",\n    \"line\" to \"application/vnd.nebumind.line\",\n    \"link66\" to \"application/vnd.route66.link66+xml\",\n    \"list3820\" to \"application/vnd.afpc.modca\",\n    \"listafp\" to \"application/vnd.afpc.modca\",\n    \"lmp\" to \"model/vnd.gdl\",\n    \"loas\" to \"audio/usac\",\n    \"loom\" to \"application/vnd.loom\",\n    \"lostsyncxml\" to \"application/lostsync+xml\",\n    \"lostxml\" to \"application/lost+xml\",\n    \"lpf\" to \"application/lpf+zip\",\n    \"lrc\" to \"application/lrc\",\n    \"lrm\" to \"application/vnd.ms-lrm\",\n    \"lsf\" to \"video/x-la-asf\",\n    \"lsx\" to \"video/x-la-asf\",\n    \"ltx\" to \"text/x-tex\",\n    \"lvp\" to \"audio/vnd.lucent.voice\",\n    \"lwp\" to \"application/vnd.lotus-wordpro\",\n    \"lxf\" to \"application/LXF\",\n    \"ly\" to \"text/x-lilypond\",\n    \"lyx\" to \"application/x-lyx\",\n    \"lzh\" to \"application/x-lzh\",\n    \"lzx\" to \"application/x-lzx\",\n    \"m\" to \"application/vnd.wolfram.mathematica.package\",\n    \"m1v\" to \"video/mpeg\",\n    \"m21\" to \"application/mp21\",\n    \"m2t\" to \"video/mpeg\",\n    \"m2ts\" to \"video/mp2t\",\n    \"m2v\" to \"video/mpeg\",\n    \"m3g\" to \"application/m3g\",\n    \"m3u\" to \"audio/x-mpegurl\",\n    \"m3u8\" to \"audio/x-mpegurl\",\n    \"m4a\" to \"audio/mpeg\",\n    \"m4b\" to \"audio/mp4\",\n    \"m4p\" to \"audio/mp4\",\n    \"m4r\" to \"audio/mpeg\",\n    \"m4s\" to \"video/iso.segment\",\n    \"m4u\" to \"video/vnd.mpegurl\",\n    \"m4v\" to \"video/mp4\",\n    \"ma\" to \"application/mathematica\",\n    \"mads\" to \"application/mads+xml\",\n    \"maei\" to \"application/mmt-aei+xml\",\n    \"mag\" to \"application/vnd.ecowin.chart\",\n    \"mail\" to \"message/rfc822\",\n    \"maker\" to \"application/x-maker\",\n    \"man\" to \"application/x-troff-man\",\n    \"manifest\" to \"text/cache-manifest\",\n    \"markdown\" to \"text/markdown\",\n    \"mb\" to \"application/mathematica\",\n    \"mbk\" to \"application/vnd.Mobius.MBK\",\n    \"mbox\" to \"application/mbox\",\n    \"mbsdf\" to \"application/vnd.mdl-mbsdf\",\n    \"mc1\" to \"application/vnd.medcalcdata\",\n    \"mc2\" to \"text/vnd.senx.warpscript\",\n    \"mcd\" to \"application/vnd.mcd\",\n    \"mcif\" to \"chemical/x-mmcif\",\n    \"mcm\" to \"chemical/x-macmolecule\",\n    \"md\" to \"text/markdown\",\n    \"mdb\" to \"application/msaccess\",\n    \"mdc\" to \"application/vnd.marlin.drm.mdcf\",\n    \"mdi\" to \"image/vnd.ms-modi\",\n    \"mdl\" to \"application/vnd.mdl\",\n    \"me\" to \"application/x-troff-me\",\n    \"mesh\" to \"model/mesh\",\n    \"meta4\" to \"application/metalink4+xml\",\n    \"mets\" to \"application/mets+xml\",\n    \"mf4\" to \"application/MF4\",\n    \"mfm\" to \"application/vnd.mfmp\",\n    \"mft\" to \"application/rpki-manifest\",\n    \"mgp\" to \"application/vnd.osgeo.mapguide.package\",\n    \"mgz\" to \"application/vnd.proteus.magazine\",\n    \"mhas\" to \"audio/mhas\",\n    \"mid\" to \"audio/sp-midi\",\n    \"mif\" to \"application/vnd.mif\",\n    \"miz\" to \"text/mizar\",\n    \"mj2\" to \"video/mj2\",\n    \"mjp2\" to \"video/mj2\",\n    \"mjs\" to \"text/javascript\",\n    \"mka\" to \"audio/x-matroska\",\n    \"mkv\" to \"video/x-matroska\",\n    \"ml2\" to \"application/vnd.sybyl.mol2\",\n    \"mlp\" to \"audio/vnd.dolby.mlp\",\n    \"mm\" to \"application/x-freemind\",\n    \"mmd\" to \"application/vnd.chipnuts.karaoke-mmd\",\n    \"mmdb\" to \"application/vnd.maxmind.maxmind-db\",\n    \"mmf\" to \"application/vnd.smaf\",\n    \"mml\" to \"application/mathml+xml\",\n    \"mmod\" to \"chemical/x-macromodel-input\",\n    \"mmr\" to \"image/vnd.fujixerox.edmics-mmr\",\n    \"mng\" to \"video/x-mng\",\n    \"mobi\" to \"application/x-mobipocket-ebook\",\n    \"moc\" to \"text/x-moc\",\n    \"mod\" to \"application/xml-dtd\",\n    \"model-inter\" to \"application/vnd.vd-study\",\n    \"modl\" to \"application/vnd.modl\",\n    \"mods\" to \"application/mods+xml\",\n    \"mol\" to \"chemical/x-mdl-molfile\",\n    \"mol2\" to \"application/vnd.sybyl.mol2\",\n    \"moml\" to \"model/vnd.moml+xml\",\n    \"moo\" to \"chemical/x-mopac-out\",\n    \"mop\" to \"chemical/x-mopac-input\",\n    \"mopcrt\" to \"chemical/x-mopac-input\",\n    \"mov\" to \"video/quicktime\",\n    \"movie\" to \"video/x-sgi-movie\",\n    \"mp1\" to \"audio/mpeg\",\n    \"mp1v\" to \"video/mpeg\",\n    \"mp2\" to \"audio/mpeg\",\n    \"mp21\" to \"application/mp21\",\n    \"mp2v\" to \"video/mpeg\",\n    \"mp3\" to \"audio/mpeg\",\n    \"mp4\" to \"video/mp4\",\n    \"mp4v\" to \"video/mp4\",\n    \"mpa\" to \"audio/mpeg\",\n    \"mpc\" to \"chemical/x-mopac-input\",\n    \"mpd\" to \"application/dash+xml\",\n    \"mpdd\" to \"application/dashdelta\",\n    \"mpe\" to \"video/mpeg\",\n    \"mpeg\" to \"video/mpeg\",\n    \"mpeg1\" to \"video/mpeg\",\n    \"mpeg2\" to \"video/mpeg\",\n    \"mpeg4\" to \"video/mp4\",\n    \"mpega\" to \"audio/mpeg\",\n    \"mpf\" to \"text/vnd.ms-mediapackage\",\n    \"mpg\" to \"video/mpeg\",\n    \"mpg4\" to \"video/mp4\",\n    \"mpga\" to \"audio/mpeg\",\n    \"mph\" to \"application/x-comsol\",\n    \"mpkg\" to \"application/vnd.apple.installer+xml\",\n    \"mpm\" to \"application/vnd.blueice.multipass\",\n    \"mpn\" to \"application/vnd.mophun.application\",\n    \"mpp\" to \"application/vnd.ms-project\",\n    \"mpt\" to \"application/vnd.ms-project\",\n    \"mpv\" to \"video/x-matroska\",\n    \"mpv1\" to \"video/mpeg\",\n    \"mpv2\" to \"video/mpeg\",\n    \"mpw\" to \"application/vnd.exstream-empower+zip\",\n    \"mpy\" to \"application/vnd.ibm.MiniPay\",\n    \"mqy\" to \"application/vnd.Mobius.MQY\",\n    \"mrc\" to \"application/marc\",\n    \"mrcx\" to \"application/marcxml+xml\",\n    \"ms\" to \"application/x-troff-ms\",\n    \"msa\" to \"application/vnd.msa-disk-image\",\n    \"msd\" to \"application/vnd.fdsn.mseed\",\n    \"mseed\" to \"application/vnd.fdsn.mseed\",\n    \"mseq\" to \"application/vnd.mseq\",\n    \"msf\" to \"application/vnd.epson.msf\",\n    \"msh\" to \"model/mesh\",\n    \"msi\" to \"application/x-msi\",\n    \"msl\" to \"application/vnd.Mobius.MSL\",\n    \"msm\" to \"model/vnd.gdl\",\n    \"msp\" to \"application/octet-stream\",\n    \"msty\" to \"application/vnd.muvee.style\",\n    \"msu\" to \"application/octet-stream\",\n    \"mtl\" to \"model/mtl\",\n    \"mts\" to \"video/mp2t\",\n    \"multitrack\" to \"audio/vnd.presonus.multitrack\",\n    \"mus\" to \"application/vnd.musician\",\n    \"musd\" to \"application/mmt-usd+xml\",\n    \"mvb\" to \"chemical/x-mopac-vib\",\n    \"mvt\" to \"application/vnd.mapbox-vector-tile\",\n    \"mwc\" to \"application/vnd.dpgraph\",\n    \"mwf\" to \"application/vnd.MFER\",\n    \"mxf\" to \"application/mxf\",\n    \"mxi\" to \"application/vnd.vd-study\",\n    \"mxl\" to \"application/vnd.recordare.musicxml\",\n    \"mxmf\" to \"audio/mobile-xmf\",\n    \"mxml\" to \"application/xv+xml\",\n    \"mxs\" to \"application/vnd.triscape.mxs\",\n    \"mxu\" to \"video/vnd.mpegurl\",\n    \"n3\" to \"text/n3\",\n    \"nb\" to \"application/vnd.wolfram.mathematica\",\n    \"nbp\" to \"application/vnd.wolfram.player\",\n    \"nc\" to \"application/x-netcdf\",\n    \"ndc\" to \"application/vnd.osa.netdeploy\",\n    \"ndl\" to \"application/vnd.lotus-notes\",\n    \"nds\" to \"application/vnd.nintendo.nitro.rom\",\n    \"nebul\" to \"application/vnd.nebumind.line\",\n    \"nef\" to \"image/x-nikon-nef\",\n    \"ngdat\" to \"application/vnd.nokia.n-gage.data\",\n    \"nim\" to \"video/vnd.nokia.interleaved-multimedia\",\n    \"nimn\" to \"application/vnd.nimn\",\n    \"nitf\" to \"application/vnd.nitf\",\n    \"nlu\" to \"application/vnd.neurolanguage.nlu\",\n    \"nml\" to \"application/vnd.enliven\",\n    \"nnd\" to \"application/vnd.noblenet-directory\",\n    \"nns\" to \"application/vnd.noblenet-sealer\",\n    \"nnw\" to \"application/vnd.noblenet-web\",\n    \"notebook\" to \"application/vnd.smart.notebook\",\n    \"nq\" to \"application/n-quads\",\n    \"nrw\" to \"image/x-nikon-nrw\",\n    \"ns2\" to \"application/vnd.lotus-notes\",\n    \"ns3\" to \"application/vnd.lotus-notes\",\n    \"ns4\" to \"application/vnd.lotus-notes\",\n    \"nsf\" to \"application/vnd.lotus-notes\",\n    \"nsg\" to \"application/vnd.lotus-notes\",\n    \"nsh\" to \"application/vnd.lotus-notes\",\n    \"nt\" to \"application/n-triples\",\n    \"ntf\" to \"application/vnd.lotus-notes\",\n    \"numbers\" to \"application/vnd.apple.numbers\",\n    \"nwc\" to \"application/x-nwc\",\n    \"o\" to \"application/x-object\",\n    \"oa2\" to \"application/vnd.fujitsu.oasys2\",\n    \"oa3\" to \"application/vnd.fujitsu.oasys3\",\n    \"oas\" to \"application/vnd.fujitsu.oasys\",\n    \"ob\" to \"application/vnd.1ob\",\n    \"obg\" to \"application/vnd.openblox.game-binary\",\n    \"obgx\" to \"application/vnd.openblox.game+xml\",\n    \"obj\" to \"model/obj\",\n    \"oda\" to \"application/ODA\",\n    \"odb\" to \"application/vnd.oasis.opendocument.base\",\n    \"odc\" to \"application/vnd.oasis.opendocument.chart\",\n    \"odd\" to \"application/tei+xml\",\n    \"odf\" to \"application/vnd.oasis.opendocument.formula\",\n    \"odg\" to \"application/vnd.oasis.opendocument.graphics\",\n    \"odi\" to \"application/vnd.oasis.opendocument.image\",\n    \"odm\" to \"application/vnd.oasis.opendocument.text-master\",\n    \"odp\" to \"application/vnd.oasis.opendocument.presentation\",\n    \"ods\" to \"application/vnd.oasis.opendocument.spreadsheet\",\n    \"odt\" to \"application/vnd.oasis.opendocument.text\",\n    \"odx\" to \"application/ODX\",\n    \"oeb\" to \"application/vnd.openeye.oeb\",\n    \"oga\" to \"audio/ogg\",\n    \"ogex\" to \"model/vnd.opengex\",\n    \"ogg\" to \"audio/ogg\",\n    \"ogv\" to \"video/ogg\",\n    \"ogx\" to \"application/ogg\",\n    \"old\" to \"application/x-trash\",\n    \"omg\" to \"audio/ATRAC3\",\n    \"one\" to \"application/onenote\",\n    \"onepkg\" to \"application/onenote\",\n    \"onetmp\" to \"application/onenote\",\n    \"onetoc2\" to \"application/onenote\",\n    \"opf\" to \"application/oebps-package+xml\",\n    \"oprc\" to \"application/vnd.palm\",\n    \"opus\" to \"audio/ogg\",\n    \"or2\" to \"application/vnd.lotus-organizer\",\n    \"or3\" to \"application/vnd.lotus-organizer\",\n    \"orc\" to \"audio/csound\",\n    \"orf\" to \"image/x-olympus-orf\",\n    \"org\" to \"application/vnd.lotus-organizer\",\n    \"orq\" to \"application/ocsp-request\",\n    \"ors\" to \"application/ocsp-response\",\n    \"osf\" to \"application/vnd.yamaha.openscoreformat\",\n    \"osm\" to \"application/vnd.openstreetmap.data+xml\",\n    \"ota\" to \"application/vnd.android.ota\",\n    \"otc\" to \"application/vnd.oasis.opendocument.chart-template\",\n    \"otf\" to \"font/otf\",\n    \"otg\" to \"application/vnd.oasis.opendocument.graphics-template\",\n    \"oth\" to \"application/vnd.oasis.opendocument.text-web\",\n    \"oti\" to \"application/vnd.oasis.opendocument.image-template\",\n    \"otm\" to \"application/vnd.oasis.opendocument.text-master-template\",\n    \"otp\" to \"application/vnd.oasis.opendocument.presentation-template\",\n    \"ots\" to \"application/vnd.oasis.opendocument.spreadsheet-template\",\n    \"ott\" to \"application/vnd.oasis.opendocument.text-template\",\n    \"ovl\" to \"application/vnd.afpc.modca-overlay\",\n    \"oxlicg\" to \"application/vnd.oxli.countgraph\",\n    \"oxps\" to \"application/oxps\",\n    \"oxt\" to \"application/vnd.openofficeorg.extension\",\n    \"oza\" to \"application/x-oz-application\",\n    \"p\" to \"text/x-pascal\",\n    \"p10\" to \"application/pkcs10\",\n    \"p12\" to \"application/x-pkcs12\",\n    \"p21\" to \"application/p21\",\n    \"p2p\" to \"application/vnd.wfa.p2p\",\n    \"p7c\" to \"application/pkcs7-mime\",\n    \"p7m\" to \"application/pkcs7-mime\",\n    \"p7r\" to \"application/x-pkcs7-certreqresp\",\n    \"p7s\" to \"application/pkcs7-signature\",\n    \"p7z\" to \"application/pkcs7-mime\",\n    \"p8\" to \"application/pkcs8\",\n    \"p8e\" to \"application/pkcs8-encrypted\",\n    \"pac\" to \"application/x-ns-proxy-autoconfig\",\n    \"package\" to \"application/vnd.autopackage\",\n    \"pages\" to \"application/vnd.apple.pages\",\n    \"pas\" to \"text/x-pascal\",\n    \"pat\" to \"image/x-coreldrawpattern\",\n    \"patch\" to \"text/x-diff\",\n    \"paw\" to \"application/vnd.pawaafile\",\n    \"pbd\" to \"application/vnd.powerbuilder6\",\n    \"pbm\" to \"image/x-portable-bitmap\",\n    \"pcap\" to \"application/vnd.tcpdump.pcap\",\n    \"pcf\" to \"application/x-font\",\n    \"pcf.Z\" to \"application/x-font-pcf\",\n    \"pcl\" to \"application/vnd.hp-PCL\",\n    \"pcx\" to \"image/vnd.zbrush.pcx\",\n    \"pdb\" to \"chemical/x-pdb\",\n    \"pdf\" to \"application/pdf\",\n    \"pdx\" to \"application/PDX\",\n    \"pef\" to \"image/x-pentax-pef\",\n    \"pem\" to \"application/x-pem-file\",\n    \"pfa\" to \"application/x-font\",\n    \"pfb\" to \"application/x-font\",\n    \"pfr\" to \"application/font-tdpfr\",\n    \"pfx\" to \"application/x-pkcs12\",\n    \"pgb\" to \"image/vnd.globalgraphics.pgb\",\n    \"pgm\" to \"image/x-portable-graymap\",\n    \"pgn\" to \"application/vnd.chess-pgn\",\n    \"pgp\" to \"application/pgp-signature\",\n    \"phps\" to \"text/text\",\n    \"pil\" to \"application/vnd.piaccess.application-licence\",\n    \"pk\" to \"application/x-tex-pk\",\n    \"pkd\" to \"application/vnd.hbci\",\n    \"pkg\" to \"application/vnd.apple.installer+xml\",\n    \"pki\" to \"application/pkixcmp\",\n    \"pkipath\" to \"application/pkix-pkipath\",\n    \"pkpass\" to \"application/vnd.apple.pkpass\",\n    \"pkpasses\" to \"application/vnd.apple.pkpasses\",\n    \"pl\" to \"text/x-perl\",\n    \"plb\" to \"application/vnd.3gpp.pic-bw-large\",\n    \"plc\" to \"application/vnd.Mobius.PLC\",\n    \"plf\" to \"application/vnd.pocketlearn\",\n    \"plj\" to \"audio/vnd.everad.plj\",\n    \"plp\" to \"application/vnd.panoply\",\n    \"pls\" to \"audio/x-scpls\",\n    \"pm\" to \"text/x-perl\",\n    \"pml\" to \"application/vnd.ctc-posml\",\n    \"png\" to \"image/png\",\n    \"pnm\" to \"image/x-portable-anymap\",\n    \"po\" to \"text/plain\",\n    \"portpkg\" to \"application/vnd.macports.portpkg\",\n    \"pot\" to \"application/vnd.ms-powerpoint\",\n    \"potm\" to \"application/vnd.ms-powerpoint.template.macroEnabled.12\",\n    \"potx\" to \"application/vnd.openxmlformats-officedocument.presentationml.template\",\n    \"ppam\" to \"application/vnd.ms-powerpoint.addin.macroEnabled.12\",\n    \"ppd\" to \"application/vnd.cups-ppd\",\n    \"ppkg\" to \"application/vnd.xmpie.ppkg\",\n    \"ppm\" to \"image/x-portable-pixmap\",\n    \"pps\" to \"application/vnd.ms-powerpoint\",\n    \"ppsm\" to \"application/vnd.ms-powerpoint.slideshow.macroEnabled.12\",\n    \"ppsx\" to \"application/vnd.openxmlformats-officedocument.presentationml.slideshow\",\n    \"ppt\" to \"application/vnd.ms-powerpoint\",\n    \"pptm\" to \"application/vnd.ms-powerpoint.presentation.macroEnabled.12\",\n    \"ppttc\" to \"application/vnd.think-cell.ppttc+json\",\n    \"pptx\" to \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n    \"pqa\" to \"application/vnd.palm\",\n    \"prc\" to \"application/x-mobipocket-ebook\",\n    \"pre\" to \"application/vnd.lotus-freelance\",\n    \"preminet\" to \"application/vnd.preminet\",\n    \"prf\" to \"application/pics-rules\",\n    \"provn\" to \"text/provenance-notation\",\n    \"provx\" to \"application/provenance+xml\",\n    \"prt\" to \"chemical/x-ncbi-asn1-ascii\",\n    \"prz\" to \"application/vnd.lotus-freelance\",\n    \"ps\" to \"application/postscript\",\n    \"psb\" to \"application/vnd.3gpp.pic-bw-small\",\n    \"psd\" to \"image/x-photoshop\",\n    \"pseg3820\" to \"application/vnd.afpc.modca\",\n    \"psfs\" to \"application/vnd.psfs\",\n    \"psg\" to \"application/vnd.afpc.modca-pagesegment\",\n    \"psid\" to \"audio/prs.sid\",\n    \"pskcxml\" to \"application/pskc+xml\",\n    \"pt\" to \"application/vnd.snesdev-page-table\",\n    \"pti\" to \"image/prs.pti\",\n    \"ptid\" to \"application/vnd.pvi.ptid1\",\n    \"ptrom\" to \"application/vnd.snesdev-page-table\",\n    \"pub\" to \"application/vnd.exstream-package\",\n    \"pvb\" to \"application/vnd.3gpp.pic-bw-var\",\n    \"pwn\" to \"application/vnd.3M.Post-it-Notes\",\n    \"py\" to \"text/x-python\",\n    \"pya\" to \"audio/vnd.ms-playready.media.pya\",\n    \"pyc\" to \"application/x-python-code\",\n    \"pyo\" to \"application/x-python-code\",\n    \"pyox\" to \"model/vnd.pytha.pyox\",\n    \"pyv\" to \"video/vnd.ms-playready.media.pyv\",\n    \"qam\" to \"application/vnd.epson.quickanime\",\n    \"qbo\" to \"application/vnd.intu.qbo\",\n    \"qca\" to \"application/vnd.ericsson.quickcall\",\n    \"qcall\" to \"application/vnd.ericsson.quickcall\",\n    \"qcp\" to \"audio/EVRC-QCP\",\n    \"qfx\" to \"application/vnd.intu.qfx\",\n    \"qgs\" to \"application/x-qgis\",\n    \"qps\" to \"application/vnd.publishare-delta-tree\",\n    \"qt\" to \"video/quicktime\",\n    \"qtl\" to \"application/x-quicktimeplayer\",\n    \"quiz\" to \"application/vnd.quobject-quoxdocument\",\n    \"quox\" to \"application/vnd.quobject-quoxdocument\",\n    \"qvd\" to \"application/vnd.theqvd\",\n    \"qwd\" to \"application/vnd.Quark.QuarkXPress\",\n    \"qwt\" to \"application/vnd.Quark.QuarkXPress\",\n    \"qxb\" to \"application/vnd.Quark.QuarkXPress\",\n    \"qxd\" to \"application/vnd.Quark.QuarkXPress\",\n    \"qxl\" to \"application/vnd.Quark.QuarkXPress\",\n    \"qxt\" to \"application/vnd.Quark.QuarkXPress\",\n    \"ra\" to \"audio/x-pn-realaudio\",\n    \"raf\" to \"image/x-fuji-raf\",\n    \"ram\" to \"audio/x-pn-realaudio\",\n    \"rapd\" to \"application/route-apd+xml\",\n    \"rar\" to \"application/vnd.rar\",\n    \"ras\" to \"image/x-cmu-raster\",\n    \"rb\" to \"application/x-ruby\",\n    \"rcprofile\" to \"application/vnd.ipunplugged.rcprofile\",\n    \"rct\" to \"application/prs.nprend\",\n    \"rd\" to \"chemical/x-mdl-rdfile\",\n    \"rdf\" to \"application/rdf+xml\",\n    \"rdf-crypt\" to \"application/prs.rdf-xml-crypt\",\n    \"rdp\" to \"application/x-rdp\",\n    \"rdz\" to \"application/vnd.data-vision.rdz\",\n    \"relo\" to \"application/p2p-overlay+xml\",\n    \"reload\" to \"application/vnd.resilient.logic\",\n    \"rep\" to \"application/vnd.businessobjects\",\n    \"request\" to \"application/vnd.nervana\",\n    \"rfcxml\" to \"application/rfc+xml\",\n    \"rgb\" to \"image/x-rgb\",\n    \"rgbe\" to \"image/vnd.radiance\",\n    \"rif\" to \"application/reginfo+xml\",\n    \"rip\" to \"audio/vnd.rip\",\n    \"rl\" to \"application/resource-lists+xml\",\n    \"rlc\" to \"image/vnd.fujixerox.edmics-rlc\",\n    \"rld\" to \"application/resource-lists-diff+xml\",\n    \"rlm\" to \"application/vnd.resilient.logic\",\n    \"rm\" to \"audio/x-pn-realaudio\",\n    \"rms\" to \"application/vnd.jcp.javame.midlet-rms\",\n    \"rnc\" to \"application/relax-ng-compact-syntax\",\n    \"rnd\" to \"application/prs.nprend\",\n    \"roa\" to \"application/rpki-roa\",\n    \"roff\" to \"text/troff\",\n    \"ros\" to \"chemical/x-rosdal\",\n    \"rp9\" to \"application/vnd.cloanto.rp9\",\n    \"rpm\" to \"application/x-redhat-package-manager\",\n    \"rpss\" to \"application/vnd.nokia.radio-presets\",\n    \"rpst\" to \"application/vnd.nokia.radio-preset\",\n    \"rq\" to \"application/sparql-query\",\n    \"rs\" to \"application/rls-services+xml\",\n    \"rsat\" to \"application/atsc-rsat+xml\",\n    \"rsheet\" to \"application/urc-ressheet+xml\",\n    \"rsm\" to \"model/vnd.gdl\",\n    \"rss\" to \"application/rss+xml\",\n    \"rst\" to \"text/prs.fallenstein.rst\",\n    \"rtf\" to \"text/rtf\",\n    \"rtttl\" to \"audio/midi\",\n    \"rtx\" to \"audio/midi\",\n    \"rusd\" to \"application/route-usd+xml\",\n    \"rw2\" to \"image/x-panasonic-rw2\",\n    \"rxn\" to \"chemical/x-mdl-rxnfile\",\n    \"rxt\" to \"application/vnd.medicalholodeck.recordxr\",\n    \"s11\" to \"video/vnd.sealed.mpeg1\",\n    \"s14\" to \"video/vnd.sealed.mpeg4\",\n    \"s1a\" to \"application/vnd.sealedmedia.softseal.pdf\",\n    \"s1e\" to \"application/vnd.sealed.xls\",\n    \"s1g\" to \"image/vnd.sealedmedia.softseal.gif\",\n    \"s1h\" to \"application/vnd.sealedmedia.softseal.html\",\n    \"s1j\" to \"image/vnd.sealedmedia.softseal.jpg\",\n    \"s1m\" to \"audio/vnd.sealedmedia.softseal.mpeg\",\n    \"s1n\" to \"image/vnd.sealed.png\",\n    \"s1p\" to \"application/vnd.sealed.ppt\",\n    \"s1q\" to \"video/vnd.sealedmedia.softseal.mov\",\n    \"s1w\" to \"application/vnd.sealed.doc\",\n    \"s3df\" to \"application/vnd.sealed.3df\",\n    \"sac\" to \"application/tamp-sequence-adjust-confirm\",\n    \"saf\" to \"application/vnd.yamaha.smaf-audio\",\n    \"sam\" to \"application/vnd.lotus-wordpro\",\n    \"sarif\" to \"application/sarif+json\",\n    \"sarif-external-properties\" to \"application/sarif-external-properties+json\",\n    \"sarif-external-properties.json\" to \"application/sarif-external-properties+json\",\n    \"sarif.json\" to \"application/sarif+json\",\n    \"sc\" to \"application/vnd.ibm.secure-container\",\n    \"scala\" to \"text/x-scala\",\n    \"scd\" to \"application/vnd.scribus\",\n    \"sce\" to \"application/x-scilab\",\n    \"sci\" to \"application/x-scilab\",\n    \"scim\" to \"application/scim+json\",\n    \"scl\" to \"application/vnd.sycle+xml\",\n    \"scld\" to \"application/vnd.doremir.scorecloud-binary-document\",\n    \"scm\" to \"application/vnd.lotus-screencam\",\n    \"sco\" to \"audio/csound\",\n    \"scq\" to \"application/scvp-cv-request\",\n    \"scr\" to \"application/x-silverlight\",\n    \"scs\" to \"application/scvp-cv-response\",\n    \"scsf\" to \"application/vnd.sealed.csf\",\n    \"sd\" to \"chemical/x-mdl-sdfile\",\n    \"sd2\" to \"audio/x-sd2\",\n    \"sda\" to \"application/vnd.stardivision.draw\",\n    \"sdc\" to \"application/vnd.stardivision.calc\",\n    \"sdd\" to \"application/vnd.stardivision.impress\",\n    \"sdf\" to \"chemical/x-mdl-sdfile\",\n    \"sdkd\" to \"application/vnd.solent.sdkm+xml\",\n    \"sdkm\" to \"application/vnd.solent.sdkm+xml\",\n    \"sdo\" to \"application/vnd.sealed.doc\",\n    \"sdoc\" to \"application/vnd.sealed.doc\",\n    \"sdp\" to \"application/vnd.stardivision.impress\",\n    \"sds\" to \"application/vnd.stardivision.chart\",\n    \"sdw\" to \"application/vnd.stardivision.writer\",\n    \"see\" to \"application/vnd.seemail\",\n    \"seed\" to \"application/vnd.fdsn.seed\",\n    \"sem\" to \"application/vnd.sealed.eml\",\n    \"sema\" to \"application/vnd.sema\",\n    \"semd\" to \"application/vnd.semd\",\n    \"semf\" to \"application/vnd.semf\",\n    \"seml\" to \"application/vnd.sealed.eml\",\n    \"senml\" to \"application/senml+json\",\n    \"senml-etchc\" to \"application/senml-etch+cbor\",\n    \"senml-etchj\" to \"application/senml-etch+json\",\n    \"senmlc\" to \"application/senml+cbor\",\n    \"senmle\" to \"application/senml-exi\",\n    \"senmlx\" to \"application/senml+xml\",\n    \"sensml\" to \"application/sensml+json\",\n    \"sensmlc\" to \"application/sensml+cbor\",\n    \"sensmle\" to \"application/sensml-exi\",\n    \"sensmlx\" to \"application/sensml+xml\",\n    \"ser\" to \"application/java-serialized-object\",\n    \"sfc\" to \"application/vnd.nintendo.snes.rom\",\n    \"sfd\" to \"application/vnd.font-fontforge-sfd\",\n    \"sfd-hdstx\" to \"application/vnd.hydrostatix.sof-data\",\n    \"sfs\" to \"application/vnd.spotfire.sfs\",\n    \"sfv\" to \"text/x-sfv\",\n    \"sgf\" to \"application/x-go-sgf\",\n    \"sgi\" to \"image/vnd.sealedmedia.softseal.gif\",\n    \"sgif\" to \"image/vnd.sealedmedia.softseal.gif\",\n    \"sgl\" to \"application/vnd.stardivision.writer-global\",\n    \"sgm\" to \"text/SGML\",\n    \"sgml\" to \"text/SGML\",\n    \"sh\" to \"text/x-sh\",\n    \"shaclc\" to \"text/shaclc\",\n    \"shar\" to \"application/x-shar\",\n    \"shc\" to \"text/shaclc\",\n    \"shex\" to \"text/shex\",\n    \"shf\" to \"application/shf+xml\",\n    \"shp\" to \"application/x-qgis\",\n    \"shtml\" to \"text/html\",\n    \"shx\" to \"application/x-qgis\",\n    \"si\" to \"text/vnd.wap.si\",\n    \"sic\" to \"application/vnd.wap.sic\",\n    \"sid\" to \"audio/prs.sid\",\n    \"sieve\" to \"application/sieve\",\n    \"sig\" to \"application/pgp-signature\",\n    \"sik\" to \"application/x-trash\",\n    \"silo\" to \"model/mesh\",\n    \"sipa\" to \"application/vnd.smintio.portals.archive\",\n    \"sis\" to \"application/vnd.symbian.install\",\n    \"sit\" to \"application/x-stuffit\",\n    \"sitx\" to \"application/x-stuffit\",\n    \"siv\" to \"application/sieve\",\n    \"sjp\" to \"image/vnd.sealedmedia.softseal.jpg\",\n    \"sjpg\" to \"image/vnd.sealedmedia.softseal.jpg\",\n    \"skd\" to \"application/vnd.koan\",\n    \"skm\" to \"application/vnd.koan\",\n    \"skp\" to \"application/vnd.koan\",\n    \"skt\" to \"application/vnd.koan\",\n    \"sl\" to \"text/vnd.wap.sl\",\n    \"sla\" to \"application/vnd.scribus\",\n    \"slaz\" to \"application/vnd.scribus\",\n    \"slc\" to \"application/vnd.wap.slc\",\n    \"sldm\" to \"application/vnd.ms-powerpoint.slide.macroEnabled.12\",\n    \"sldx\" to \"application/vnd.openxmlformats-officedocument.presentationml.slide\",\n    \"sls\" to \"application/route-s-tsid+xml\",\n    \"slt\" to \"application/vnd.epson.salt\",\n    \"sm\" to \"application/vnd.stepmania.stepchart\",\n    \"smc\" to \"application/vnd.nintendo.snes.rom\",\n    \"smf\" to \"audio/sp-midi\",\n    \"smh\" to \"application/vnd.sealed.mht\",\n    \"smht\" to \"application/vnd.sealed.mht\",\n    \"smi\" to \"application/smil+xml\",\n    \"smil\" to \"application/smil+xml\",\n    \"smk\" to \"video/vnd.radgamettools.smacker\",\n    \"sml\" to \"application/smil+xml\",\n    \"smo\" to \"video/vnd.sealedmedia.softseal.mov\",\n    \"smov\" to \"video/vnd.sealedmedia.softseal.mov\",\n    \"smp\" to \"audio/vnd.sealedmedia.softseal.mpeg\",\n    \"smp3\" to \"audio/vnd.sealedmedia.softseal.mpeg\",\n    \"smpg\" to \"video/vnd.sealed.mpeg1\",\n    \"sms\" to \"application/vnd.3gpp2.sms\",\n    \"smv\" to \"audio/SMV\",\n    \"smzip\" to \"application/vnd.stepmania.package\",\n    \"snd\" to \"audio/basic\",\n    \"soa\" to \"text/dns\",\n    \"soc\" to \"application/sgml-open-catalog\",\n    \"sofa\" to \"audio/sofa\",\n    \"sos\" to \"text/vnd.sosi\",\n    \"spc\" to \"chemical/x-galactic-spc\",\n    \"spd\" to \"application/vnd.sealedmedia.softseal.pdf\",\n    \"spdf\" to \"application/vnd.sealedmedia.softseal.pdf\",\n    \"spdx\" to \"text/spdx\",\n    \"spdx.json\" to \"application/spdx+json\",\n    \"spf\" to \"application/vnd.yamaha.smaf-phrase\",\n    \"spl\" to \"application/futuresplash\",\n    \"spn\" to \"image/vnd.sealed.png\",\n    \"spng\" to \"image/vnd.sealed.png\",\n    \"spo\" to \"text/vnd.in3d.spot\",\n    \"spot\" to \"text/vnd.in3d.spot\",\n    \"spp\" to \"application/scvp-vp-response\",\n    \"sppt\" to \"application/vnd.sealed.ppt\",\n    \"spq\" to \"application/scvp-vp-request\",\n    \"spx\" to \"audio/ogg\",\n    \"sql\" to \"application/sql\",\n    \"sqlite\" to \"application/vnd.sqlite3\",\n    \"sqlite3\" to \"application/vnd.sqlite3\",\n    \"sr\" to \"application/vnd.sigrok.session\",\n    \"src\" to \"application/x-wais-source\",\n    \"srt\" to \"application/x-subrip\",\n    \"sru\" to \"application/sru+xml\",\n    \"srw\" to \"image/x-samsung-srw\",\n    \"srx\" to \"application/sparql-results+xml\",\n    \"sse\" to \"application/vnd.kodak-descriptor\",\n    \"ssf\" to \"application/vnd.epson.ssf\",\n    \"ssml\" to \"application/ssml+xml\",\n    \"ssv\" to \"application/vnd.shade-save-file\",\n    \"ssvc\" to \"application/vnd.crypto-shade-file\",\n    \"ssw\" to \"video/vnd.sealed.swf\",\n    \"sswf\" to \"video/vnd.sealed.swf\",\n    \"st\" to \"application/vnd.sailingtracker.track\",\n    \"stc\" to \"application/vnd.sun.xml.calc.template\",\n    \"std\" to \"application/vnd.sun.xml.draw.template\",\n    \"step\" to \"model/step\",\n    \"stf\" to \"application/vnd.wt.stf\",\n    \"sti\" to \"application/vnd.sun.xml.impress.template\",\n    \"stif\" to \"application/vnd.sealed.tiff\",\n    \"stix\" to \"application/stix+json\",\n    \"stk\" to \"application/hyperstudio\",\n    \"stl\" to \"application/vnd.ms-pki.stl\",\n    \"stml\" to \"application/vnd.sealedmedia.softseal.html\",\n    \"stp\" to \"model/step\",\n    \"stpnc\" to \"application/p21\",\n    \"stpx\" to \"model/step+xml\",\n    \"stpxz\" to \"model/step-xml+zip\",\n    \"stpz\" to \"model/step+zip\",\n    \"str\" to \"application/vnd.pg.format\",\n    \"study-inter\" to \"application/vnd.vd-study\",\n    \"stw\" to \"application/vnd.sun.xml.writer.template\",\n    \"sty\" to \"text/x-tex\",\n    \"sus\" to \"application/vnd.sus-calendar\",\n    \"susp\" to \"application/vnd.sus-calendar\",\n    \"sv4cpio\" to \"application/x-sv4cpio\",\n    \"sv4crc\" to \"application/x-sv4crc\",\n    \"svc\" to \"application/vnd.dvb.service\",\n    \"svg\" to \"image/svg+xml\",\n    \"svgz\" to \"image/svg+xml\",\n    \"sw\" to \"chemical/x-swissprot\",\n    \"swf\" to \"application/vnd.adobe.flash.movie\",\n    \"swi\" to \"application/vnd.aristanetworks.swi\",\n    \"swidtag\" to \"application/swid+xml\",\n    \"sxc\" to \"application/vnd.sun.xml.calc\",\n    \"sxd\" to \"application/vnd.sun.xml.draw\",\n    \"sxg\" to \"application/vnd.sun.xml.writer.global\",\n    \"sxi\" to \"application/vnd.sun.xml.impress\",\n    \"sxl\" to \"application/vnd.sealed.xls\",\n    \"sxls\" to \"application/vnd.sealed.xls\",\n    \"sxm\" to \"application/vnd.sun.xml.math\",\n    \"sxw\" to \"application/vnd.sun.xml.writer\",\n    \"sy2\" to \"application/vnd.sybyl.mol2\",\n    \"syft.json\" to \"application/vnd.syft+json\",\n    \"t\" to \"text/troff\",\n    \"tag\" to \"text/prs.lines.tag\",\n    \"taglet\" to \"application/vnd.mynfc\",\n    \"tam\" to \"application/vnd.onepager\",\n    \"tamp\" to \"application/vnd.onepagertamp\",\n    \"tamx\" to \"application/vnd.onepagertamx\",\n    \"tao\" to \"application/vnd.tao.intent-module-archive\",\n    \"tap\" to \"image/vnd.tencent.tap\",\n    \"tar\" to \"application/x-tar\",\n    \"tat\" to \"application/vnd.onepagertat\",\n    \"tatp\" to \"application/vnd.onepagertatp\",\n    \"tatx\" to \"application/vnd.onepagertatx\",\n    \"tau\" to \"application/tamp-apex-update\",\n    \"taz\" to \"application/x-gtar-compressed\",\n    \"tcap\" to \"application/vnd.3gpp2.tcap\",\n    \"tcl\" to \"text/x-tcl\",\n    \"tcu\" to \"application/tamp-community-update\",\n    \"td\" to \"application/urc-targetdesc+xml\",\n    \"teacher\" to \"application/vnd.smart.teacher\",\n    \"tei\" to \"application/tei+xml\",\n    \"teiCorpus\" to \"application/tei+xml\",\n    \"ter\" to \"application/tamp-error\",\n    \"tex\" to \"text/x-tex\",\n    \"texi\" to \"application/x-texinfo\",\n    \"texinfo\" to \"application/x-texinfo\",\n    \"text\" to \"text/plain\",\n    \"tfi\" to \"application/thraud+xml\",\n    \"tfx\" to \"image/tiff-fx\",\n    \"tgf\" to \"chemical/x-mdl-tgf\",\n    \"tgz\" to \"application/x-gtar-compressed\",\n    \"thmx\" to \"application/vnd.ms-officetheme\",\n    \"tif\" to \"image/tiff\",\n    \"tiff\" to \"image/tiff\",\n    \"tk\" to \"text/x-tcl\",\n    \"tlclient\" to \"application/vnd.cendio.thinlinc.clientconf\",\n    \"tm\" to \"text/texmacs\",\n    \"tm.json\" to \"application/tm+json\",\n    \"tm.jsonld\" to \"application/tm+json\",\n    \"tmo\" to \"application/vnd.tmobile-livetv\",\n    \"tnef\" to \"application/vnd.ms-tnef\",\n    \"tnf\" to \"application/vnd.ms-tnef\",\n    \"torrent\" to \"application/x-bittorrent\",\n    \"tpl\" to \"application/vnd.groove-tool-template\",\n    \"tpt\" to \"application/vnd.trid.tpt\",\n    \"tr\" to \"text/troff\",\n    \"tra\" to \"application/vnd.trueapp\",\n    \"tree\" to \"application/vnd.rainstor.data\",\n    \"trig\" to \"application/trig\",\n    \"ts\" to \"video/mp2ts\",\n    \"tsa\" to \"application/tamp-sequence-adjust\",\n    \"tsd\" to \"application/timestamped-data\",\n    \"tsp\" to \"application/dsptype\",\n    \"tsq\" to \"application/timestamp-query\",\n    \"tsr\" to \"application/timestamp-reply\",\n    \"tst\" to \"application/vnd.etsi.timestamp-token\",\n    \"tsv\" to \"text/tab-separated-values\",\n    \"ttc\" to \"font/collection\",\n    \"ttf\" to \"font/ttf\",\n    \"ttl\" to \"text/turtle\",\n    \"ttml\" to \"application/ttml+xml\",\n    \"tuc\" to \"application/tamp-update-confirm\",\n    \"tur\" to \"application/tamp-update\",\n    \"twd\" to \"application/vnd.SimTech-MindMapper\",\n    \"twds\" to \"application/vnd.SimTech-MindMapper\",\n    \"txd\" to \"application/vnd.genomatix.tuxedo\",\n    \"txf\" to \"application/vnd.Mobius.TXF\",\n    \"txt\" to \"text/plain\",\n    \"u3d\" to \"model/u3d\",\n    \"u8dsn\" to \"message/global-delivery-status\",\n    \"u8hdr\" to \"message/global-headers\",\n    \"u8mdn\" to \"message/global-disposition-notification\",\n    \"u8msg\" to \"message/global\",\n    \"udeb\" to \"application/vnd.debian.binary-package\",\n    \"ufd\" to \"application/vnd.ufdl\",\n    \"ufdl\" to \"application/vnd.ufdl\",\n    \"uis\" to \"application/urc-uisocketdesc+xml\",\n    \"umj\" to \"application/vnd.umajin\",\n    \"unityweb\" to \"application/vnd.unity\",\n    \"uo\" to \"application/vnd.uoml+xml\",\n    \"uoml\" to \"application/vnd.uoml+xml\",\n    \"upa\" to \"application/vnd.hbci\",\n    \"uri\" to \"text/uri-list\",\n    \"urim\" to \"application/vnd.uri-map\",\n    \"urimap\" to \"application/vnd.uri-map\",\n    \"uris\" to \"text/uri-list\",\n    \"usda\" to \"model/vnd.usda\",\n    \"usdz\" to \"model/vnd.usdz+zip\",\n    \"ustar\" to \"application/x-ustar\",\n    \"utz\" to \"application/vnd.uiq.theme\",\n    \"uva\" to \"audio/vnd.dece.audio\",\n    \"uvd\" to \"application/vnd.dece.data\",\n    \"uvf\" to \"application/vnd.dece.data\",\n    \"uvg\" to \"image/vnd.dece.graphic\",\n    \"uvh\" to \"video/vnd.dece.hd\",\n    \"uvi\" to \"image/vnd.dece.graphic\",\n    \"uvm\" to \"video/vnd.dece.mobile\",\n    \"uvp\" to \"video/vnd.dece.pd\",\n    \"uvs\" to \"video/vnd.dece.sd\",\n    \"uvt\" to \"application/vnd.dece.ttml+xml\",\n    \"uvu\" to \"video/vnd.dece.mp4\",\n    \"uvv\" to \"video/vnd.dece.video\",\n    \"uvva\" to \"audio/vnd.dece.audio\",\n    \"uvvd\" to \"application/vnd.dece.data\",\n    \"uvvf\" to \"application/vnd.dece.data\",\n    \"uvvg\" to \"image/vnd.dece.graphic\",\n    \"uvvh\" to \"video/vnd.dece.hd\",\n    \"uvvi\" to \"image/vnd.dece.graphic\",\n    \"uvvm\" to \"video/vnd.dece.mobile\",\n    \"uvvp\" to \"video/vnd.dece.pd\",\n    \"uvvs\" to \"video/vnd.dece.sd\",\n    \"uvvt\" to \"application/vnd.dece.ttml+xml\",\n    \"uvvu\" to \"video/vnd.dece.mp4\",\n    \"uvvv\" to \"video/vnd.dece.video\",\n    \"uvvx\" to \"application/vnd.dece.unspecified\",\n    \"uvvz\" to \"application/vnd.dece.zip\",\n    \"uvx\" to \"application/vnd.dece.unspecified\",\n    \"uvz\" to \"application/vnd.dece.zip\",\n    \"val\" to \"chemical/x-ncbi-asn1-binary\",\n    \"vbk\" to \"audio/vnd.nortel.vbk\",\n    \"vbox\" to \"application/vnd.previewsystems.box\",\n    \"vcard\" to \"text/vcard\",\n    \"vcd\" to \"application/x-cdlink\",\n    \"vcf\" to \"text/x-vcard\",\n    \"vcg\" to \"application/vnd.groove-vcard\",\n    \"vcj\" to \"application/voucher-cms+json\",\n    \"vcs\" to \"text/x-vcalendar\",\n    \"vcx\" to \"application/vnd.vcx\",\n    \"vds\" to \"model/vnd.sap.vds\",\n    \"vew\" to \"application/vnd.lotus-approach\",\n    \"vfr\" to \"application/vnd.tml\",\n    \"viaframe\" to \"application/vnd.tml\",\n    \"vis\" to \"application/vnd.visionary\",\n    \"viv\" to \"video/vnd.vivo\",\n    \"vmd\" to \"chemical/x-vmd\",\n    \"vms\" to \"chemical/x-vamas-iso14976\",\n    \"vmt\" to \"application/vnd.valve.source.material\",\n    \"vor\" to \"application/vnd.stardivision.writer\",\n    \"vpm\" to \"multipart/voice-message\",\n    \"vrm\" to \"model/vrml\",\n    \"vrml\" to \"model/vrml\",\n    \"vsc\" to \"application/vnd.vidsoft.vidconference\",\n    \"vsd\" to \"application/vnd.visio\",\n    \"vsf\" to \"application/vnd.vsf\",\n    \"vss\" to \"application/vnd.visio\",\n    \"vst\" to \"application/vnd.visio\",\n    \"vsw\" to \"application/vnd.visio\",\n    \"vtf\" to \"image/vnd.valve.source.texture\",\n    \"vtnstd\" to \"application/vnd.veritone.aion+json\",\n    \"vtt\" to \"text/vtt\",\n    \"vtu\" to \"model/vnd.vtu\",\n    \"vwx\" to \"application/vnd.vectorworks\",\n    \"vxml\" to \"application/voicexml+xml\",\n    \"wad\" to \"application/x-doom\",\n    \"wadl\" to \"application/vnd.sun.wadl+xml\",\n    \"wafl\" to \"application/vnd.wasmflow.wafl\",\n    \"wasm\" to \"application/wasm\",\n    \"wav\" to \"audio/x-wav\",\n    \"wax\" to \"audio/x-ms-wax\",\n    \"wbmp\" to \"image/vnd.wap.wbmp\",\n    \"wbs\" to \"application/vnd.criticaltools.wbs+xml\",\n    \"wbxml\" to \"application/vnd.wap.wbxml\",\n    \"wcm\" to \"application/vnd.ms-works\",\n    \"wdb\" to \"application/vnd.ms-works\",\n    \"webarchive\" to \"application/x-webarchive\",\n    \"webarchivexml\" to \"application/x-webarchive-xml\",\n    \"webm\" to \"video/webm\",\n    \"webmanifest\" to \"application/manifest+json\",\n    \"webp\" to \"image/webp\",\n    \"wg\" to \"application/vnd.pmi.widget\",\n    \"wgsl\" to \"text/wgsl\",\n    \"wgt\" to \"application/widget\",\n    \"wif\" to \"application/watcherinfo+xml\",\n    \"win\" to \"model/vnd.gdl\",\n    \"wk\" to \"application/x-123\",\n    \"wk1\" to \"application/vnd.lotus-1-2-3\",\n    \"wk3\" to \"application/vnd.lotus-1-2-3\",\n    \"wk4\" to \"application/vnd.lotus-1-2-3\",\n    \"wks\" to \"application/vnd.ms-works\",\n    \"wlnk\" to \"application/link-format\",\n    \"wm\" to \"video/x-ms-wm\",\n    \"wma\" to \"audio/x-ms-wma\",\n    \"wmc\" to \"application/vnd.wmc\",\n    \"wmd\" to \"application/x-ms-wmd\",\n    \"wmf\" to \"image/wmf\",\n    \"wml\" to \"text/vnd.wap.wml\",\n    \"wmlc\" to \"application/vnd.wap.wmlc\",\n    \"wmls\" to \"text/vnd.wap.wmlscript\",\n    \"wmlsc\" to \"application/vnd.wap.wmlscriptc\",\n    \"wmv\" to \"video/x-ms-wmv\",\n    \"wmx\" to \"video/x-ms-wmx\",\n    \"wmz\" to \"application/x-ms-wmz\",\n    \"woff\" to \"font/woff\",\n    \"woff2\" to \"font/woff2\",\n    \"wpd\" to \"application/vnd.wordperfect\",\n    \"wpl\" to \"application/vnd.ms-wpl\",\n    \"wps\" to \"application/vnd.ms-works\",\n    \"wqd\" to \"application/vnd.wqd\",\n    \"wrf\" to \"video/x-webex\",\n    \"wrl\" to \"model/vrml\",\n    \"wsc\" to \"application/vnd.wfa.wsc\",\n    \"wsdl\" to \"application/wsdl+xml\",\n    \"wspolicy\" to \"application/wspolicy+xml\",\n    \"wtb\" to \"application/vnd.webturbo\",\n    \"wv\" to \"application/vnd.wv.csp+wbxml\",\n    \"wvx\" to \"video/x-ms-wvx\",\n    \"wz\" to \"application/x-wingz\",\n    \"x3d\" to \"model/x3d+xml\",\n    \"x3db\" to \"model/x3d+fastinfoset\",\n    \"x3dv\" to \"model/x3d-vrml\",\n    \"x3dvz\" to \"model/x3d-vrml\",\n    \"x3dz\" to \"model/x3d+xml\",\n    \"x_b\" to \"model/vnd.parasolid.transmit.binary\",\n    \"x_t\" to \"model/vnd.parasolid.transmit.text\",\n    \"xar\" to \"application/vnd.xara\",\n    \"xav\" to \"application/xcap-att+xml\",\n    \"xbd\" to \"application/vnd.fujixerox.docuworks.binder\",\n    \"xbm\" to \"image/x-xbitmap\",\n    \"xca\" to \"application/xcap-caps+xml\",\n    \"xcf\" to \"image/x-xcf\",\n    \"xcos\" to \"application/x-scilab-xcos\",\n    \"xcs\" to \"application/calendar+xml\",\n    \"xct\" to \"application/vnd.fujixerox.docuworks.container\",\n    \"xdd\" to \"application/bacnet-xdd+zip\",\n    \"xdf\" to \"application/xcap-diff+xml\",\n    \"xdm\" to \"application/vnd.syncml.dm+xml\",\n    \"xdp\" to \"application/vnd.adobe.xdp+xml\",\n    \"xdssc\" to \"application/dssc+xml\",\n    \"xdw\" to \"application/vnd.fujixerox.docuworks\",\n    \"xel\" to \"application/xcap-el+xml\",\n    \"xer\" to \"application/xcap-error+xml\",\n    \"xfd\" to \"application/vnd.xfdl\",\n    \"xfdf\" to \"application/xfdf\",\n    \"xfdl\" to \"application/vnd.xfdl\",\n    \"xhe\" to \"audio/usac\",\n    \"xht\" to \"application/xhtml+xml\",\n    \"xhtm\" to \"application/xhtml+xml\",\n    \"xhtml\" to \"application/xhtml+xml\",\n    \"xhvml\" to \"application/xv+xml\",\n    \"xif\" to \"image/vnd.xiff\",\n    \"xla\" to \"application/vnd.ms-excel\",\n    \"xlam\" to \"application/vnd.ms-excel.addin.macroEnabled.12\",\n    \"xlc\" to \"application/vnd.ms-excel\",\n    \"xlf\" to \"application/xliff+xml\",\n    \"xlim\" to \"application/vnd.xmpie.xlim\",\n    \"xlm\" to \"application/vnd.ms-excel\",\n    \"xls\" to \"application/vnd.ms-excel\",\n    \"xlsb\" to \"application/vnd.ms-excel.sheet.binary.macroEnabled.12\",\n    \"xlsm\" to \"application/vnd.ms-excel.sheet.macroEnabled.12\",\n    \"xlsx\" to \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n    \"xlt\" to \"application/vnd.ms-excel\",\n    \"xltm\" to \"application/vnd.ms-excel.template.macroEnabled.12\",\n    \"xltx\" to \"application/vnd.openxmlformats-officedocument.spreadsheetml.template\",\n    \"xlw\" to \"application/vnd.ms-excel\",\n    \"xmf\" to \"audio/midi\",\n    \"xml\" to \"text/xml\",\n    \"xmls\" to \"application/dskpp+xml\",\n    \"xmt_bin\" to \"model/vnd.parasolid.transmit.binary\",\n    \"xmt_txt\" to \"model/vnd.parasolid.transmit.text\",\n    \"xns\" to \"application/xcap-ns+xml\",\n    \"xo\" to \"application/vnd.olpc-sugar\",\n    \"xodp\" to \"application/vnd.collabio.xodocuments.presentation\",\n    \"xods\" to \"application/vnd.collabio.xodocuments.spreadsheet\",\n    \"xodt\" to \"application/vnd.collabio.xodocuments.document\",\n    \"xop\" to \"application/xop+xml\",\n    \"xotp\" to \"application/vnd.collabio.xodocuments.presentation-template\",\n    \"xots\" to \"application/vnd.collabio.xodocuments.spreadsheet-template\",\n    \"xott\" to \"application/vnd.collabio.xodocuments.document-template\",\n    \"xpak\" to \"application/vnd.gentoo.xpak\",\n    \"xpi\" to \"application/x-xpinstall\",\n    \"xpm\" to \"image/x-xpixmap\",\n    \"xpr\" to \"application/vnd.is-xpr\",\n    \"xps\" to \"application/vnd.ms-xpsdocument\",\n    \"xpw\" to \"application/vnd.intercon.formnet\",\n    \"xpx\" to \"application/vnd.intercon.formnet\",\n    \"xsf\" to \"application/prs.xsf+xml\",\n    \"xsl\" to \"application/xslt+xml\",\n    \"xslt\" to \"application/xslt+xml\",\n    \"xsm\" to \"application/vnd.syncml+xml\",\n    \"xspf\" to \"application/xspf+xml\",\n    \"xtel\" to \"chemical/x-xtel\",\n    \"xul\" to \"application/vnd.mozilla.xul+xml\",\n    \"xvm\" to \"application/xv+xml\",\n    \"xvml\" to \"application/xv+xml\",\n    \"xwd\" to \"image/x-xwindowdump\",\n    \"xyz\" to \"chemical/x-xyz\",\n    \"xyze\" to \"image/vnd.radiance\",\n    \"xz\" to \"application/x-xz\",\n    \"yaml\" to \"application/yaml\",\n    \"yang\" to \"application/yang\",\n    \"yin\" to \"application/yin+xml\",\n    \"yme\" to \"application/vnd.yaoweme\",\n    \"yml\" to \"application/yaml\",\n    \"yt\" to \"video/vnd.youtube.yt\",\n    \"zaz\" to \"application/vnd.zzazz.deck+xml\",\n    \"zfc\" to \"application/vnd.filmit.zfc\",\n    \"zfo\" to \"application/vnd.software602.filler.form-xml-zip\",\n    \"zip\" to \"application/zip\",\n    \"zir\" to \"application/vnd.zul\",\n    \"zirz\" to \"application/vnd.zul\",\n    \"zmm\" to \"application/vnd.HandHeld-Entertainment+xml\",\n    \"zmt\" to \"chemical/x-mopac-input\",\n    \"zone\" to \"text/dns\",\n    \"zst\" to \"application/zstd\",\n    \"~\" to \"application/x-trash\"\n)\n\nfun MimeTypeMap.getMimeTypeFromExtensionCompat(extension: String): String? {\n    return extensionToMimeTypeMap[extension] ?: getMimeTypeFromExtension(extension)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/NioUtilsCompat.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.os.Build\nimport android.system.OsConstants\nimport me.zhanghai.android.files.hiddenapi.RestrictedHiddenApi\nimport me.zhanghai.android.files.util.lazyReflectedMethod\nimport java.io.Closeable\nimport java.io.FileDescriptor\nimport java.nio.channels.FileChannel\n\nobject NioUtilsCompat {\n    @RestrictedHiddenApi\n    private val newFileChannelMethod by lazyReflectedMethod(\n        \"java.nio.NioUtils\", \"newFileChannel\", Closeable::class.java, FileDescriptor::class.java,\n        Int::class.java\n    )\n    @RestrictedHiddenApi\n    private val fileChannelImplOpenMethod by lazyReflectedMethod(\n        \"sun.nio.ch.FileChannelImpl\", \"open\", FileDescriptor::class.java, String::class.java,\n        Boolean::class.java, Boolean::class.java, Boolean::class.java, Any::class.java\n    )\n\n    fun newFileChannel(ioObject: Closeable, fd: FileDescriptor, flags: Int): FileChannel =\n        if (Build.VERSION.SDK_INT in Build.VERSION_CODES.N..<Build.VERSION_CODES.R) {\n            // They broke O_RDONLY by assuming it's non-zero, but in fact it is zero.\n            // https://android.googlesource.com/platform/libcore/+/nougat-release/luni/src/main/java/java/nio/NioUtils.java#63\n            val readable = flags and OsConstants.O_ACCMODE != OsConstants.O_WRONLY\n            val writable = flags and OsConstants.O_ACCMODE != OsConstants.O_RDONLY\n            val append = flags and OsConstants.O_APPEND == OsConstants.O_APPEND\n            fileChannelImplOpenMethod.invoke(\n                null, fd, null, readable, writable, append, ioObject\n            ) as FileChannel\n        } else {\n            newFileChannelMethod.invoke(null, ioObject, fd, flags) as FileChannel\n        }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/PackageInfoCompat.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.content.pm.PackageInfo\nimport androidx.core.content.pm.PackageInfoCompat\n\nval PackageInfo.longVersionCodeCompat: Long\n    get() = PackageInfoCompat.getLongVersionCode(this)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/PackageManagerCompat.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.annotation.SuppressLint\nimport android.content.pm.PackageInfo\nimport android.content.pm.PackageManager\nimport android.content.pm.SigningInfo\nimport android.os.Build\nimport me.zhanghai.android.files.util.andInv\nimport me.zhanghai.android.files.util.hasBits\n\nobject PackageManagerCompat {\n    @SuppressLint(\"InlinedApi\")\n    const val MATCH_UNINSTALLED_PACKAGES = PackageManager.MATCH_UNINSTALLED_PACKAGES\n}\n\nfun PackageManager.getPackageArchiveInfoCompat(archiveFilePath: String, flags: Int): PackageInfo? {\n    var packageInfo = getPackageArchiveInfo(archiveFilePath, flags)\n    // getPackageArchiveInfo() returns null for unsigned APKs if signing info is requested.\n    if (packageInfo == null) {\n        val flagsWithoutGetSigningInfo = flags.andInv(\n            @Suppress(\"DEPRECATION\")\n            PackageManager.GET_SIGNATURES or if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {\n                PackageManager.GET_SIGNING_CERTIFICATES\n            } else {\n                0\n            }\n        )\n        if (flags != flagsWithoutGetSigningInfo) {\n            packageInfo = getPackageArchiveInfo(archiveFilePath, flagsWithoutGetSigningInfo)\n                ?.apply {\n                    @Suppress(\"DEPRECATION\")\n                    if (flags.hasBits(PackageManager.GET_SIGNATURES)) {\n                        signatures = emptyArray()\n                    }\n                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P\n                        && flags.hasBits(PackageManager.GET_SIGNING_CERTIFICATES)) {\n                        signingInfo = SigningInfo()\n                    }\n                }\n        }\n    }\n    return packageInfo\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/ParcelCompat.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.os.Build\nimport android.os.Parcel\nimport android.os.Parcelable\nimport androidx.core.os.ParcelCompat\n\nfun Parcel.readBooleanCompat(): Boolean = ParcelCompat.readBoolean(this)\n\nfun Parcel.writeBooleanCompat(value: Boolean) {\n    ParcelCompat.writeBoolean(this, value)\n}\n\nfun <E : Parcelable?, L : MutableList<E>> Parcel.readParcelableListCompat(\n    list: L,\n    classLoader: ClassLoader?\n): L {\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n        @Suppress(\"UNCHECKED_CAST\")\n        return readParcelableList(list, classLoader) as L\n    } else {\n        val size = readInt()\n        if (size == -1) {\n            list.clear()\n            return list\n        }\n        val listSize = list.size\n        for (index in 0..<size) {\n            @Suppress(\"UNCHECKED_CAST\")\n            val element = readParcelable<E>(classLoader) as E\n            if (index < listSize) {\n                list[index] = element\n            } else {\n                list += element\n            }\n        }\n        if (size < listSize) {\n            list.subList(size, listSize).clear()\n        }\n        return list\n    }\n}\n\nfun <T : Parcelable?> Parcel.writeParcelableListCompat(value: List<T>?, flags: Int) {\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n        writeParcelableList(value, flags)\n    } else {\n        if (value == null) {\n            writeInt(-1)\n            return\n        }\n        writeInt(value.size)\n        for (element in value) {\n            writeParcelable(element, flags)\n        }\n    }\n}\n\n@Suppress(\"UNCHECKED_CAST\")\nfun <T> Parcel.readSerializableCompat(): T? = readSerializable() as T?\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/PermissionInfoCompat.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.content.pm.PermissionInfo\nimport androidx.core.content.pm.PermissionInfoCompat\n\nval PermissionInfo.protectionCompat: Int\n    get() = PermissionInfoCompat.getProtection(this)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/PreferenceManagerCompat.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.content.Context\n\nobject PreferenceManagerCompat {\n    fun getDefaultSharedPreferencesName(context: Context): String =\n        \"${context.packageName}_preferences\"\n\n    val defaultSharedPreferencesMode: Int\n        get() = Context.MODE_PRIVATE\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/ProxyFileDescriptorCallbackCompat.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.os.Build\nimport android.os.ProxyFileDescriptorCallback\nimport android.system.ErrnoException\nimport android.system.OsConstants\nimport androidx.annotation.RequiresApi\n\nabstract class ProxyFileDescriptorCallbackCompat {\n    @Throws(ErrnoException::class)\n    open fun onGetSize(): Long {\n        throw ErrnoException(\"onGetSize\", OsConstants.EBADF)\n    }\n\n    @Throws(ErrnoException::class)\n    open fun onRead(offset: Long, size: Int, data: ByteArray): Int {\n        throw ErrnoException(\"onRead\", OsConstants.EBADF)\n    }\n\n    @Throws(ErrnoException::class)\n    open fun onWrite(offset: Long, size: Int, data: ByteArray): Int {\n        throw ErrnoException(\"onWrite\", OsConstants.EBADF)\n    }\n\n    @Throws(ErrnoException::class)\n    open fun onFsync() {\n        throw ErrnoException(\"onFsync\", OsConstants.EINVAL)\n    }\n\n    abstract fun onRelease()\n\n    @RequiresApi(Build.VERSION_CODES.O)\n    fun toProxyFileDescriptorCallback(): ProxyFileDescriptorCallback {\n        return object : ProxyFileDescriptorCallback() {\n            @Throws(ErrnoException::class)\n            override fun onGetSize(): Long = this@ProxyFileDescriptorCallbackCompat.onGetSize()\n\n            @Throws(ErrnoException::class)\n            override fun onRead(offset: Long, size: Int, data: ByteArray): Int =\n                this@ProxyFileDescriptorCallbackCompat.onRead(offset, size, data)\n\n            @Throws(ErrnoException::class)\n            override fun onWrite(offset: Long, size: Int, data: ByteArray): Int =\n                this@ProxyFileDescriptorCallbackCompat.onWrite(offset, size, data)\n\n            @Throws(ErrnoException::class)\n            override fun onFsync() = this@ProxyFileDescriptorCallbackCompat.onFsync()\n\n            override fun onRelease() = this@ProxyFileDescriptorCallbackCompat.onRelease()\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/ResourcesCompat.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.content.res.Resources\nimport androidx.annotation.DimenRes\nimport androidx.core.content.res.ResourcesCompat\n\nfun Resources.getFloatCompat(@DimenRes id: Int) = ResourcesCompat.getFloat(this, id)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/RotateDrawableCompat.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.annotation.SuppressLint\nimport android.graphics.drawable.Drawable\nimport android.graphics.drawable.RotateDrawable\nimport android.os.Build\nimport me.zhanghai.android.files.util.lazyReflectedField\nimport kotlin.reflect.KClass\n\nfun KClass<RotateDrawable>.createCompat(): RotateDrawable =\n    RotateDrawable().apply {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {\n            isPivotXRelativeCompat = true\n            pivotXCompat = 0.5f\n            isPivotYRelativeCompat = true\n            pivotYCompat = 0.5f\n        }\n    }\n\nprivate val rotateDrawableMStateField by lazyReflectedField(RotateDrawable::class.java, \"mState\")\nprivate val ROTATE_STATE_CLASS_NAME = \"${RotateDrawable::class.java.name}\\$RotateState\"\nprivate val rotateStateMPivotXRelField by lazyReflectedField(ROTATE_STATE_CLASS_NAME, \"mPivotXRel\")\n\nvar RotateDrawable.isPivotXRelativeCompat: Boolean\n    get() = isPivotXRelative\n    set(value) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {\n            isPivotXRelative = value\n        } else {\n            if (isPivotXRelative != value) {\n                rotateStateMPivotXRelField.setBoolean(rotateDrawableMStateField.get(this), value)\n                invalidateSelf()\n            }\n        }\n    }\n\nprivate val rotateStateMPivotXField by lazyReflectedField(ROTATE_STATE_CLASS_NAME, \"mPivotX\")\n\nvar RotateDrawable.pivotXCompat: Float\n    get() = pivotX\n    set(value) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {\n            pivotX = value\n        } else {\n            if (pivotX != value) {\n                rotateStateMPivotXField.setFloat(rotateDrawableMStateField.get(this), value)\n                invalidateSelf()\n            }\n        }\n    }\n\nprivate val rotateStateMPivotYRelField by lazyReflectedField(ROTATE_STATE_CLASS_NAME, \"mPivotYRel\")\n\nvar RotateDrawable.isPivotYRelativeCompat: Boolean\n    get() = isPivotYRelative\n    set(value) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {\n            isPivotYRelative = value\n        } else {\n            if (isPivotYRelative != value) {\n                rotateStateMPivotYRelField.setBoolean(rotateDrawableMStateField.get(this), value)\n                invalidateSelf()\n            }\n        }\n    }\n\nprivate val rotateStateMPivotYField by lazyReflectedField(ROTATE_STATE_CLASS_NAME, \"mPivotY\")\n\nvar RotateDrawable.pivotYCompat: Float\n    get() = pivotY\n    set(value) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {\n            pivotY = value\n        } else {\n            if (pivotY != value) {\n                rotateStateMPivotYField.setFloat(rotateDrawableMStateField.get(this), value)\n                invalidateSelf()\n            }\n        }\n    }\n\nvar RotateDrawable.drawableCompat: Drawable?\n    // The get/setDrawable() methods were on RotateDrawable and are now on DrawableWrapper, so this\n    // is fine because both are classes and invoke-virtual works for both.\n    @SuppressLint(\"NewApi\")\n    get() = drawable\n    @SuppressLint(\"NewApi\")\n    set(value) {\n        drawable = value\n    }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/SELinuxCompat.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.os.Build\nimport androidx.annotation.RequiresApi\nimport me.zhanghai.android.files.hiddenapi.RestrictedHiddenApi\nimport me.zhanghai.android.files.util.lazyReflectedClass\nimport me.zhanghai.android.files.util.lazyReflectedMethod\nimport java.io.File\nimport java.io.FileDescriptor\n\n/*\n * @see android.os.SELinux\n * @see <a href=\"https://android.googlesource.com/platform/frameworks/base/+/jb-mr1-release/core/java/android/os/SELinux.java\">\n *      jb-mr1-release/SELinux.java</a>\n * @see <a href=\"https://android.googlesource.com/platform/prebuilts/runtime/+/master/appcompat/hiddenapi-light-greylist.txt\">\n *      hiddenapi-light-greylist.txt</a>\n */\nobject SELinuxCompat {\n    private val seLinuxClass by lazyReflectedClass(\"android.os.SELinux\")\n    private val isSELinuxEnabledMethod by lazyReflectedMethod(seLinuxClass, \"isSELinuxEnabled\")\n    private val isSELinuxEnforcedMethod by lazyReflectedMethod(seLinuxClass, \"isSELinuxEnforced\")\n    @RestrictedHiddenApi\n    private val setFSCreateContextMethod by lazyReflectedMethod(\n        seLinuxClass, \"setFSCreateContext\", String::class.java\n    )\n    @RestrictedHiddenApi\n    private val setFileContextMethod by lazyReflectedMethod(\n        seLinuxClass, \"setFileContext\", String::class.java, String::class.java\n    )\n    private val getFileContextStringMethod by lazyReflectedMethod(\n        seLinuxClass, \"getFileContext\", String::class.java\n    )\n    @RestrictedHiddenApi\n    private val getPeerContextMethod by lazyReflectedMethod(\n        seLinuxClass, \"getPeerContext\", FileDescriptor::class.java\n    )\n    @get:RequiresApi(Build.VERSION_CODES.Q)\n    @RestrictedHiddenApi\n    private val getFileContextFileDescriptorMethod by lazyReflectedMethod(\n        seLinuxClass, \"getFileContext\", FileDescriptor::class.java\n    )\n    private val getContextMethod by lazyReflectedMethod(seLinuxClass, \"getContext\")\n    private val getPidContextMethod by lazyReflectedMethod(\n        seLinuxClass, \"getPidContext\", Int::class.java\n    )\n    private val checkSELinuxAccessMethod by lazyReflectedMethod(\n        seLinuxClass, \"checkSELinuxAccess\", String::class.java, String::class.java,\n        String::class.java, String::class.java\n    )\n    @RestrictedHiddenApi\n    private val nativeRestoreconMethod by lazyReflectedMethod(\n        seLinuxClass, \"native_restorecon\", String::class.java, Int::class.java\n    )\n    @RestrictedHiddenApi\n    private val restoreconStringMethod by lazyReflectedMethod(\n        seLinuxClass, \"restorecon\", String::class.java\n    )\n    @RestrictedHiddenApi\n    private val restoreconFileMethod by lazyReflectedMethod(\n        seLinuxClass, \"restorecon\", File::class.java\n    )\n    private val restoreconRecursiveMethod by lazyReflectedMethod(\n        seLinuxClass, \"restoreconRecursive\", File::class.java\n    )\n\n    val isSELinuxEnabled: Boolean\n        get() = isSELinuxEnabledMethod.invoke(null) as Boolean\n\n    val isSELinuxEnforced: Boolean\n        get() = isSELinuxEnforcedMethod.invoke(null) as Boolean\n\n    fun setFSCreateContext(context: String?): Boolean =\n        setFSCreateContextMethod.invoke(null, context) as Boolean\n\n    fun setFileContext(path: String, context: String): Boolean =\n        setFileContextMethod.invoke(null, path, context) as Boolean\n\n    fun getFileContext(path: String): String? =\n        getFileContextStringMethod.invoke(null, path) as String?\n\n    fun getPeerContext(fd: FileDescriptor): String? =\n        getPeerContextMethod.invoke(null, fd) as String?\n\n    @RequiresApi(Build.VERSION_CODES.Q)\n    fun getFileContext(fd: FileDescriptor): String? =\n        getFileContextFileDescriptorMethod.invoke(null, fd) as String?\n\n    val context: String?\n        get() = getContextMethod.invoke(null) as String?\n\n    fun getPidContext(pid: Int): String? = getPidContextMethod.invoke(null, pid) as String?\n\n    fun checkSELinuxAccess(scon: String, tcon: String, tclass: String, perm: String): Boolean =\n        checkSELinuxAccessMethod.invoke(null, scon, tcon, tclass, perm) as Boolean\n\n    fun native_restorecon(pathname: String?, flags: Int): Boolean =\n        nativeRestoreconMethod.invoke(null, pathname, flags) as Boolean\n\n    fun restorecon(pathname: String): Boolean =\n        restoreconStringMethod.invoke(null, pathname) as Boolean\n\n    fun restorecon(file: File): Boolean = restoreconFileMethod.invoke(null, file) as Boolean\n\n    fun restoreconRecursive(file: File): Boolean =\n        restoreconRecursiveMethod.invoke(null, file) as Boolean\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/SeekableByteChannelCompat.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.os.Build\nimport androidx.annotation.RequiresApi\nimport java8.nio.channels.SeekableByteChannel\nimport java.io.IOException\nimport java.nio.ByteBuffer\nimport java.nio.channels.SeekableByteChannel as JavaSeekableByteChannel\n\n@RequiresApi(Build.VERSION_CODES.N)\nfun SeekableByteChannel.toJavaSeekableByteChannel(): JavaSeekableByteChannel =\n    // java8.nio.channels.FileChannel extends from java.nio.channels.FileChannel, so in that case\n    // the current object may already be implementing java.nio.channels.SeekableByteChannel.\n    this as? JavaSeekableByteChannel ?: DelegateJavaSeekableByteChannel(this)\n\n@RequiresApi(Build.VERSION_CODES.N)\nprivate class DelegateJavaSeekableByteChannel(\n    private val channel: SeekableByteChannel\n) : JavaSeekableByteChannel {\n    @Throws(IOException::class)\n    override fun read(dst: ByteBuffer): Int = channel.read(dst)\n\n    @Throws(IOException::class)\n    override fun write(src: ByteBuffer): Int = channel.write(src)\n\n    @Throws(IOException::class)\n    override fun position(): Long = channel.position()\n\n    @Throws(IOException::class)\n    override fun position(newPosition: Long): DelegateJavaSeekableByteChannel {\n        channel.position(newPosition)\n        return this\n    }\n\n    @Throws(IOException::class)\n    override fun size(): Long = channel.size()\n\n    @Throws(IOException::class)\n    override fun truncate(size: Long): DelegateJavaSeekableByteChannel {\n        channel.truncate(size)\n        return this\n    }\n\n    override fun isOpen(): Boolean = channel.isOpen\n\n    @Throws(IOException::class)\n    override fun close() {\n        channel.close()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/ServiceCompat.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.app.Service\nimport androidx.core.app.ServiceCompat\n\nfun Service.stopForegroundCompat(flags: Int) {\n    ServiceCompat.stopForeground(this, flags)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/StorageManagerCompat.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.os.Build\nimport android.os.Handler\nimport android.os.ParcelFileDescriptor\nimport android.os.storage.StorageManager\nimport android.os.storage.StorageVolume\nimport kotlinx.coroutines.runBlocking\nimport me.zhanghai.android.files.util.lazyReflectedMethod\nimport java.io.IOException\nimport java.util.concurrent.atomic.AtomicInteger\nimport kotlin.coroutines.resume\nimport kotlin.coroutines.resumeWithException\nimport kotlin.coroutines.suspendCoroutine\n\nprivate val getVolumeListMethod by lazyReflectedMethod(StorageManager::class.java, \"getVolumeList\")\n\nval StorageManager.storageVolumesCompat: List<StorageVolume>\n    get() =\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n            storageVolumes\n        } else {\n            @Suppress(\"UNCHECKED_CAST\")\n            (getVolumeListMethod.invoke(this) as Array<StorageVolume>).toList()\n        }\n\n// Thanks to fython for https://gist.github.com/fython/924f8d9019bca75d22de116bb69a54a1\n@Throws(IOException::class)\nfun StorageManager.openProxyFileDescriptorCompat(\n    mode: Int,\n    callback: ProxyFileDescriptorCallbackCompat,\n    handler: Handler\n): ParcelFileDescriptor =\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n        openProxyFileDescriptor(mode, callback.toProxyFileDescriptorCallback(), handler)\n    } else {\n        // TODO: Support other modes?\n        if (mode != ParcelFileDescriptor.MODE_READ_ONLY) {\n            throw UnsupportedOperationException(\"mode $mode\")\n        }\n        val pfds = ParcelFileDescriptor.createReliablePipe()\n        PipeWriter(pfds[1], callback, handler).start()\n        pfds[0]\n    }\n\nprivate class PipeWriter(\n    private val pfd: ParcelFileDescriptor,\n    private val callback: ProxyFileDescriptorCallbackCompat,\n    private val handler: Handler\n) : Thread(\"StorageManagerCompat.PipeWriter-${id.getAndIncrement()}\") {\n    override fun run() {\n        try {\n            ParcelFileDescriptor.AutoCloseOutputStream(pfd).use { outputStream ->\n                var offset = 0L\n                val buffer = ByteArray(4 * 1024)\n                while (true) {\n                    val size = runBlocking {\n                        callback.awaitOnRead(offset, buffer.size, buffer, handler)\n                    }\n                    if (size == 0) {\n                        break\n                    }\n                    offset += size.toLong()\n                    outputStream.write(buffer, 0, size)\n                }\n                runBlocking { callback.awaitOnRelease(handler) }\n            }\n        } catch (e: Exception) {\n            e.printStackTrace()\n            try {\n                pfd.closeWithError(e.message)\n            } catch (e2: IOException) {\n                e2.printStackTrace()\n            }\n        }\n    }\n\n    companion object {\n        private val id = AtomicInteger()\n    }\n}\n\nprivate suspend fun ProxyFileDescriptorCallbackCompat.awaitOnRead(\n    offset: Long,\n    size: Int,\n    data: ByteArray,\n    handler: Handler\n): Int =\n    suspendCoroutine { continuation ->\n        handler.post {\n            val readSize = try {\n                onRead(offset, size, data)\n            } catch (t: Throwable) {\n                continuation.resumeWithException(t)\n                return@post\n            }\n            continuation.resume(readSize)\n        }\n    }\n\nprivate suspend fun ProxyFileDescriptorCallbackCompat.awaitOnRelease(handler: Handler) {\n    suspendCoroutine<Unit> { continuation ->\n        handler.post {\n            try {\n                onRelease()\n            } catch (t: Throwable) {\n                continuation.resumeWithException(t)\n                return@post\n            }\n            continuation.resume(Unit)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/StorageVolumeCompat.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Build\nimport android.os.Environment\nimport android.os.storage.StorageVolume\nimport android.provider.DocumentsContract\nimport me.zhanghai.android.files.util.lazyReflectedMethod\nimport java.io.File\n\n// Work around @SuppressLint not applicable to top level property with delegate.\n@SuppressLint(\"NewApi\")\nprivate val storageVolumeClass = StorageVolume::class.java\n\nprivate val getPathMethod by lazyReflectedMethod(storageVolumeClass, \"getPath\")\n\nval StorageVolume.pathCompat: String\n    get() = getPathMethod.invoke(this) as String\n\nprivate val getPathFileMethod by lazyReflectedMethod(storageVolumeClass, \"getPathFile\")\n\nval StorageVolume.pathFileCompat: File\n    get() = File(pathCompat)\n\nval StorageVolume.directoryCompat: File?\n    get() =\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {\n            directory\n        } else {\n            when (stateCompat) {\n                Environment.MEDIA_MOUNTED, Environment.MEDIA_MOUNTED_READ_ONLY -> pathFileCompat\n                else -> null\n            }\n        }\n\n@SuppressLint(\"NewApi\")\nfun StorageVolume.getDescriptionCompat(context: Context): String = getDescription(context)\n\nval StorageVolume.isPrimaryCompat: Boolean\n    @SuppressLint(\"NewApi\")\n    get() = isPrimary\n\nval StorageVolume.isRemovableCompat: Boolean\n    @SuppressLint(\"NewApi\")\n    get() = isRemovable\n\nval StorageVolume.isEmulatedCompat: Boolean\n    @SuppressLint(\"NewApi\")\n    get() = isEmulated\n\nval StorageVolume.uuidCompat: String?\n    @SuppressLint(\"NewApi\")\n    get() = uuid\n\nval StorageVolume.stateCompat: String\n    @SuppressLint(\"NewApi\")\n    get() = state\n\nfun StorageVolume.createOpenDocumentTreeIntentCompat(): Intent =\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n        createOpenDocumentTreeIntent()\n    } else {\n        Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {\n            val rootId = if (isEmulatedCompat) {\n                DocumentsContractCompat.EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID\n            } else {\n                uuidCompat\n            }\n            val rootUri = DocumentsContract.buildRootUri(\n                DocumentsContractCompat.EXTERNAL_STORAGE_PROVIDER_AUTHORITY, rootId\n            )\n            putExtra(DocumentsContractCompat.EXTRA_INITIAL_URI, rootUri)\n            putExtra(DocumentsContractCompat.EXTRA_SHOW_ADVANCED, true)\n        }\n    }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/TextViewCompat.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.os.Build\nimport android.widget.TextView\nimport androidx.annotation.StyleRes\nimport androidx.core.widget.TextViewCompat\nimport me.zhanghai.android.files.util.lazyReflectedMethod\n\nprivate val isSingleLineMethod by lazyReflectedMethod(TextView::class.java, \"isSingleLine\")\n\nval TextView.isSingleLineCompat: Boolean\n    get() =\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n            isSingleLine\n        } else {\n            isSingleLineMethod.invoke(this) as Boolean\n        }\n\nfun TextView.setTextAppearanceCompat(@StyleRes resId: Int) {\n    TextViewCompat.setTextAppearance(this, resId)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/ThreadLocalCompat.kt",
    "content": "package me.zhanghai.android.files.compat\n\nimport android.os.Build\nimport kotlin.reflect.KClass\n\nfun <T> KClass<ThreadLocal<*>>.withInitial(supplier: () -> T): ThreadLocal<T> =\n    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n        ThreadLocal.withInitial(supplier)\n    } else {\n        object : ThreadLocal<T>() {\n            override fun initialValue(): T = supplier.invoke()\n        }\n    }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/TileServiceCompat.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.graphics.PixelFormat\nimport android.os.Build\nimport android.os.IBinder\nimport android.service.quicksettings.TileService\nimport android.view.View\nimport android.view.WindowManager\nimport androidx.annotation.RequiresApi\nimport androidx.core.view.doOnPreDraw\nimport me.zhanghai.android.files.hiddenapi.RestrictedHiddenApi\nimport me.zhanghai.android.files.util.lazyReflectedField\n\n// Work around https://issuetracker.google.com/issues/299506164 on U which is fixed in V.\nfun TileService.doWithStartForegroundServiceAllowed(action: () -> Unit) {\n    if (Build.VERSION.SDK_INT != Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {\n        action()\n        return\n    }\n    val windowManager = getSystemService(WindowManager::class.java)\n    val view = View(this)\n    val layoutParams =\n        WindowManager.LayoutParams().apply {\n            type = WindowManager_LayoutParams_TYPE_QS_DIALOG\n            format = PixelFormat.TRANSLUCENT\n            token = this@doWithStartForegroundServiceAllowed.token\n        }\n    windowManager.addView(view, layoutParams)\n    // We need to wait for WindowState.onSurfaceShownChanged(), basically when the first draw has\n    // finished and the surface is about to be shown to the user. However there's no good callback\n    // for that, while waiting for the second pre-draw seems to work.\n    view.doOnPreDraw {\n        view.post {\n            view.invalidate()\n            view.doOnPreDraw {\n                try {\n                    action()\n                } finally {\n                    windowManager.removeView(view)\n                }\n            }\n        }\n    }\n}\n\nprivate const val WindowManager_LayoutParams_TYPE_QS_DIALOG =\n    WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW + 35\n\n@delegate:RequiresApi(Build.VERSION_CODES.N)\n@get:RequiresApi(Build.VERSION_CODES.N)\n@RestrictedHiddenApi\nprivate val tokenField by lazyReflectedField(TileService::class.qualifiedName!!, \"mToken\")\n\nprivate val TileService.token: IBinder?\n    @RequiresApi(Build.VERSION_CODES.N)\n    get() = tokenField.get(this) as IBinder?\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/TypedValueCompat.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.util.TypedValue\nimport androidx.core.util.TypedValueCompat\n\nval TypedValue.complexUnitCompat: Int\n    get() = TypedValueCompat.getUnitFromComplexDimension(data)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/ViewCompat.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.content.res.ColorStateList\nimport android.graphics.PorterDuff\nimport android.graphics.drawable.Drawable\nimport android.view.View\nimport androidx.annotation.IdRes\nimport androidx.core.view.ViewCompat\nimport me.zhanghai.android.foregroundcompat.ForegroundCompat\n\n@Suppress(\"UNCHECKED_CAST\")\nfun <T : View> View.requireViewByIdCompat(@IdRes id: Int): T =\n    ViewCompat.requireViewById(this, id) as T\n\nvar View.scrollIndicatorsCompat: Int\n    get() = ViewCompat.getScrollIndicators(this)\n    set(value) {\n        ViewCompat.setScrollIndicators(this, value)\n    }\n\nfun View.setScrollIndicatorsCompat(indicators: Int, mask: Int) {\n    ViewCompat.setScrollIndicators(this, indicators, mask)\n}\n\nvar View.foregroundCompat: Drawable?\n    get() = ForegroundCompat.getForeground(this)\n    set(value) {\n        ForegroundCompat.setForeground(this, value)\n    }\n\nvar View.foregroundGravityCompat: Int\n    get() = ForegroundCompat.getForegroundGravity(this)\n    set(value) {\n        ForegroundCompat.setForegroundGravity(this, value)\n    }\n\nvar View.foregroundTintListCompat: ColorStateList?\n    get() = ForegroundCompat.getForegroundTintList(this)\n    set(value) {\n        ForegroundCompat.setForegroundTintList(this, value)\n    }\n\nvar View.foregroundTintModeCompat: PorterDuff.Mode?\n    get() = ForegroundCompat.getForegroundTintMode(this)\n    set(value) {\n        ForegroundCompat.setForegroundTintMode(this, value)\n    }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/compat/ViewGroupCompat.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.compat\n\nimport android.graphics.PointF\nimport android.view.View\nimport android.view.ViewGroup\nimport me.zhanghai.android.files.hiddenapi.RestrictedHiddenApi\nimport me.zhanghai.android.files.util.lazyReflectedMethod\n\n@RestrictedHiddenApi\nprivate val isTransformedTouchPointInViewMethod by lazyReflectedMethod(\n    ViewGroup::class.java, \"isTransformedTouchPointInView\", Float::class.java, Float::class.java,\n    View::class.java, PointF::class.java\n)\n\nfun ViewGroup.isTransformedTouchPointInViewCompat(\n    x: Float,\n    y: Float,\n    child: View,\n    outLocalPoint: PointF?\n): Boolean =\n    isTransformedTouchPointInViewMethod.invoke(this, x, y, child, outLocalPoint) as Boolean\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/file/BasicFileAttributesExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.file\n\nimport java.time.Instant\nimport java8.nio.file.attribute.BasicFileAttributes\n\nval BasicFileAttributes.fileSize: FileSize\n    get() = size().asFileSize()\n\nval BasicFileAttributes.lastModifiedInstant: Instant\n    get() = lastModifiedTime().toInstant()\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/file/DocumentTreeUri.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.file\n\nimport android.content.Intent\nimport android.net.Uri\nimport android.os.Parcelable\nimport android.os.storage.StorageVolume\nimport android.provider.DocumentsContract\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.app.contentResolver\nimport me.zhanghai.android.files.compat.DocumentsContractCompat\nimport me.zhanghai.android.files.compat.createOpenDocumentTreeIntentCompat\nimport me.zhanghai.android.files.storage.StorageVolumeListLiveData\nimport me.zhanghai.android.files.util.StableUriParceler\nimport me.zhanghai.android.files.util.getParcelableExtraSafe\nimport me.zhanghai.android.files.util.releasePersistablePermission\nimport me.zhanghai.android.files.util.takePersistablePermission\nimport me.zhanghai.android.files.util.valueCompat\n\n@Parcelize\n@JvmInline\nvalue class DocumentTreeUri(val value: @WriteWith<StableUriParceler> Uri) : Parcelable {\n    val documentId: String\n        get() = DocumentsContract.getTreeDocumentId(value)\n\n    companion object {\n        val persistedUris: List<DocumentTreeUri>\n            get() =\n                contentResolver.persistedUriPermissions\n                    .filter { it.uri.isDocumentTreeUri }\n                    .sortedBy { it.persistedTime }\n                    .map { it.uri.asDocumentTreeUri() }\n    }\n}\n\nfun Uri.asDocumentTreeUriOrNull(): DocumentTreeUri? =\n    if (isDocumentTreeUri) DocumentTreeUri(this) else null\n\nfun Uri.asDocumentTreeUri(): DocumentTreeUri {\n    check(isDocumentTreeUri)\n    return DocumentTreeUri(this)\n}\n\nprivate val Uri.isDocumentTreeUri: Boolean\n    get() = DocumentsContractCompat.isTreeUri(this)\n\nfun DocumentTreeUri.buildDocumentUri(documentId: String): DocumentUri =\n    DocumentsContract.buildDocumentUriUsingTree(value, documentId).asDocumentUri()\n\nval DocumentTreeUri.displayName: String?\n    get() = buildDocumentUri(documentId).displayName\n\nfun DocumentTreeUri.takePersistablePermission(): Boolean =\n    value.takePersistablePermission(\n        Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION\n    ) || value.takePersistablePermission(Intent.FLAG_GRANT_READ_URI_PERMISSION)\n\nfun DocumentTreeUri.releasePersistablePermission(): Boolean =\n    value.releasePersistablePermission(\n        Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION\n    )\n\nval StorageVolume.documentTreeUri: DocumentTreeUri\n    get() {\n        val intent = createOpenDocumentTreeIntentCompat()\n        val rootUri = intent.getParcelableExtraSafe<Uri>(\n            DocumentsContractCompat.EXTRA_INITIAL_URI\n        )!!\n        // @see com.android.externalstorage.ExternalStorageProvider#getDocIdForFile(File)\n        // @see com.android.documentsui.picker.ConfirmFragment#onCreateDialog(Bundle)\n        return DocumentsContract.buildTreeDocumentUri(\n            rootUri.authority, \"${DocumentsContract.getRootId(rootUri)}:\"\n        ).asDocumentTreeUri()\n    }\n\nval DocumentTreeUri.storageVolume: StorageVolume?\n    get() = StorageVolumeListLiveData.valueCompat.find { it.documentTreeUri == this }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/file/DocumentUri.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.file\n\nimport android.net.Uri\nimport android.os.Parcelable\nimport android.provider.DocumentsContract\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.app.contentResolver\nimport me.zhanghai.android.files.compat.DocumentsContractCompat\nimport me.zhanghai.android.files.util.StableUriParceler\n\n@Parcelize\n@JvmInline\nvalue class DocumentUri(val value: @WriteWith<StableUriParceler> Uri) : Parcelable {\n    val treeDocumentId: String\n        get() = DocumentsContract.getTreeDocumentId(value)\n\n    val documentId: String\n        get() = DocumentsContract.getDocumentId(value)\n}\n\nfun Uri.asDocumentUriOrNull(): DocumentUri? =\n    if (isDocumentUri) DocumentUri(this) else null\n\nfun Uri.asDocumentUri(): DocumentUri {\n    require(isDocumentUri)\n    return DocumentUri(this)\n}\n\nprivate val Uri.isDocumentUri: Boolean\n    get() = DocumentsContractCompat.isDocumentUri(this)\n\nval DocumentUri.displayName: String?\n    get() {\n        try {\n            contentResolver.query(\n                value, arrayOf(DocumentsContract.Document.COLUMN_DISPLAY_NAME), null, null, null\n            ).use { cursor ->\n                if (cursor != null && cursor.moveToFirst()) {\n                    val displayNameIndex = cursor.getColumnIndex(\n                        DocumentsContract.Document.COLUMN_DISPLAY_NAME\n                    )\n                    if (displayNameIndex != -1) {\n                        val displayName = cursor.getString(displayNameIndex)\n                        if (!displayName.isNullOrEmpty()) {\n                            return displayName\n                        }\n                    }\n                }\n            }\n        } catch (e: Exception) {\n            e.printStackTrace()\n        }\n        return null\n    }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/file/DurationExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.file\n\nimport android.text.format.DateUtils\nimport java.time.Duration\n\nfun Duration.format(): String = DateUtils.formatElapsedTime(seconds)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/file/ExternalStorageUri.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.file\n\nimport android.net.Uri\nimport android.os.Parcelable\nimport android.provider.DocumentsContract\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.compat.DocumentsContractCompat\nimport me.zhanghai.android.files.util.StableUriParceler\nimport me.zhanghai.android.files.util.takeIfNotEmpty\n\n@Parcelize\n@JvmInline\nvalue class ExternalStorageUri(val value: @WriteWith<StableUriParceler> Uri) : Parcelable {\n    constructor(\n        rootId: String,\n        path: String\n    ) : this(\n        DocumentsContract.buildDocumentUriUsingTree(\n            DocumentsContract.buildTreeDocumentUri(\n                DocumentsContractCompat.EXTERNAL_STORAGE_PROVIDER_AUTHORITY,\n                rootId\n            ),\n            \"$rootId:$path\"\n        )\n    )\n\n    val rootId: String\n        get() = DocumentsContract.getTreeDocumentId(value)\n\n    val path: String\n        get() = DocumentsContract.getDocumentId(value).removePrefix(\"$rootId:\")\n}\n\nfun Uri.asExternalStorageUriOrNull(): ExternalStorageUri? =\n    if (isExternalStorageUri) ExternalStorageUri(this) else null\n\nfun Uri.asExternalStorageUri(): ExternalStorageUri {\n    require(isExternalStorageUri)\n    return ExternalStorageUri(this)\n}\n\n/** @see DocumentsContractCompat.isDocumentUri */\nprivate val Uri.isExternalStorageUri: Boolean\n    get() =\n        DocumentsContractCompat.isDocumentUri(this) &&\n            authority == DocumentsContractCompat.EXTERNAL_STORAGE_PROVIDER_AUTHORITY &&\n            pathSegments.size == 4\n\nval ExternalStorageUri.displayName: String\n    get() = path.takeLastWhile { it != '/' }.takeIfNotEmpty() ?: \"/\"\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/file/FileItem.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.file\n\nimport android.os.Parcelable\nimport androidx.annotation.WorkerThread\nimport java8.nio.file.LinkOption\nimport java8.nio.file.Path\nimport java8.nio.file.attribute.BasicFileAttributes\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.filelist.getCollationKeyForFileName\nimport me.zhanghai.android.files.filelist.name\nimport me.zhanghai.android.files.provider.common.AndroidFileTypeDetector\nimport me.zhanghai.android.files.provider.common.isHidden\nimport me.zhanghai.android.files.provider.common.readAttributes\nimport me.zhanghai.android.files.provider.common.readSymbolicLinkByteString\nimport me.zhanghai.android.files.util.ParcelableParceler\nimport java.io.IOException\nimport java.text.CollationKey\nimport java.text.Collator\n\n@Parcelize\ndata class FileItem(\n    val path: @WriteWith<ParcelableParceler> Path,\n    val nameCollationKey: @WriteWith<ParcelableParceler> CollationKey,\n    val attributesNoFollowLinks: @WriteWith<ParcelableParceler> BasicFileAttributes,\n    val symbolicLinkTarget: String?,\n    private val symbolicLinkTargetAttributes: @WriteWith<ParcelableParceler> BasicFileAttributes?,\n    val isHidden: Boolean,\n    val mimeType: MimeType\n) : Parcelable {\n    val attributes: BasicFileAttributes\n        get() = symbolicLinkTargetAttributes ?: attributesNoFollowLinks\n\n    val isSymbolicLinkBroken: Boolean\n        get() {\n            check(attributesNoFollowLinks.isSymbolicLink) { \"Not a symbolic link\" }\n            return symbolicLinkTargetAttributes == null\n        }\n}\n\n@WorkerThread\n@Throws(IOException::class)\nfun Path.loadFileItem(): FileItem {\n    val nameCollationKey = Collator.getInstance().getCollationKeyForFileName(name)\n    val attributes = readAttributes(BasicFileAttributes::class.java, LinkOption.NOFOLLOW_LINKS)\n    val isHidden = isHidden\n    if (!attributes.isSymbolicLink) {\n        val mimeType = AndroidFileTypeDetector.getMimeType(this, attributes).asMimeType()\n        return FileItem(this, nameCollationKey, attributes, null, null, isHidden, mimeType)\n    }\n    val symbolicLinkTarget = readSymbolicLinkByteString().toString()\n    val symbolicLinkTargetAttributes = try {\n        readAttributes(BasicFileAttributes::class.java)\n    } catch (e: IOException) {\n        e.printStackTrace()\n        null\n    }\n    val mimeType = AndroidFileTypeDetector.getMimeType(\n        this, symbolicLinkTargetAttributes ?: attributes\n    ).asMimeType()\n    return FileItem(\n        this, nameCollationKey, attributes, symbolicLinkTarget, symbolicLinkTargetAttributes,\n        isHidden, mimeType\n    )\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/file/FileProvider.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.file\n\nimport android.content.ContentProvider\nimport android.content.ContentResolver\nimport android.content.ContentValues\nimport android.content.Context\nimport android.content.pm.ProviderInfo\nimport android.database.Cursor\nimport android.database.MatrixCursor\nimport android.net.Uri\nimport android.os.Binder\nimport android.os.Build\nimport android.os.Handler\nimport android.os.HandlerThread\nimport android.os.ParcelFileDescriptor\nimport android.os.Process\nimport android.os.StrictMode\nimport android.provider.DocumentsContract\nimport android.provider.MediaStore\nimport android.provider.OpenableColumns\nimport android.system.ErrnoException\nimport android.system.OsConstants\nimport java8.nio.channels.SeekableByteChannel\nimport java8.nio.file.AccessDeniedException\nimport java8.nio.file.FileSystemException\nimport java8.nio.file.FileSystemLoopException\nimport java8.nio.file.NoSuchFileException\nimport java8.nio.file.OpenOption\nimport java8.nio.file.Path\nimport java8.nio.file.Paths\nimport java8.nio.file.StandardOpenOption\nimport me.zhanghai.android.files.BuildConfig\nimport me.zhanghai.android.files.app.storageManager\nimport me.zhanghai.android.files.compat.ProxyFileDescriptorCallbackCompat\nimport me.zhanghai.android.files.compat.openProxyFileDescriptorCompat\nimport me.zhanghai.android.files.provider.common.InvalidFileNameException\nimport me.zhanghai.android.files.provider.common.IsDirectoryException\nimport me.zhanghai.android.files.provider.common.force\nimport me.zhanghai.android.files.provider.common.getLastModifiedTime\nimport me.zhanghai.android.files.provider.common.isForceable\nimport me.zhanghai.android.files.provider.common.newByteChannel\nimport me.zhanghai.android.files.provider.common.size\nimport me.zhanghai.android.files.provider.document.documentUri\nimport me.zhanghai.android.files.provider.document.isDocumentPath\nimport me.zhanghai.android.files.provider.linux.isLinuxPath\nimport me.zhanghai.android.files.provider.linux.syscall.SyscallException\nimport me.zhanghai.android.files.util.hasBits\nimport me.zhanghai.android.files.util.withoutPenaltyDeathOnNetwork\nimport java.io.FileNotFoundException\nimport java.io.IOException\nimport java.io.InterruptedIOException\nimport java.net.URI\nimport java.nio.ByteBuffer\nimport java.nio.channels.ClosedByInterruptException\n\nclass FileProvider : ContentProvider() {\n    private lateinit var callbackThread: HandlerThread\n    private lateinit var callbackHandler: Handler\n\n    override fun onCreate(): Boolean {\n        callbackThread = HandlerThread(\"FileProvider.CallbackThread\")\n        callbackThread.start()\n        callbackHandler = Handler(callbackThread.looper)\n        return true\n    }\n\n    override fun shutdown() {\n        callbackThread.quitSafely()\n    }\n\n    override fun attachInfo(context: Context, info: ProviderInfo) {\n        super.attachInfo(context, info)\n\n        if (info.exported) {\n            throw SecurityException(\"Provider must not be exported\")\n        }\n        if (!info.grantUriPermissions) {\n            throw SecurityException(\"Provider must grant uri permissions\")\n        }\n    }\n\n    override fun query(\n        uri: Uri,\n        projection: Array<String?>?,\n        selection: String?,\n        selectionArgs: Array<String?>?,\n        sortOrder: String?\n    ): Cursor? {\n        // ContentProvider has already checked granted permissions\n        val projectionColumns = projection ?: getDefaultProjection()\n        val path = uri.fileProviderPath\n        val columns = mutableListOf<String>()\n        val values = mutableListOf<Any?>()\n        loop@ for (column in projectionColumns) {\n            @Suppress(\"DEPRECATION\")\n            when (column) {\n                OpenableColumns.DISPLAY_NAME -> {\n                    columns += column\n                    values += path.fileName.toString()\n                }\n                OpenableColumns.SIZE -> {\n                    val size = try {\n                        path.size()\n                    } catch (e: IOException) {\n                        e.printStackTrace()\n                        null\n                    }\n                    columns += column\n                    values += size\n                }\n                MediaStore.MediaColumns.DATA -> {\n                    val file = try {\n                        path.toFile()\n                    } catch (e: UnsupportedOperationException) {\n                        continue@loop\n                    }\n                    columns += column\n                    values += file.absolutePath\n                }\n                // TODO: We should actually implement a DocumentsProvider since we are handling\n                //  ACTION_OPEN_DOCUMENT.\n                DocumentsContract.Document.COLUMN_MIME_TYPE -> {\n                    columns += column\n                    values += MimeType.guessFromPath(path.toString()).value\n                }\n                DocumentsContract.Document.COLUMN_LAST_MODIFIED -> {\n                    val lastModified = try {\n                        path.getLastModifiedTime().toMillis()\n                    } catch (e: IOException) {\n                        e.printStackTrace()\n                        null\n                    }\n                    columns += column\n                    values += lastModified\n                }\n            }\n        }\n        return MatrixCursor(columns.toTypedArray(), 1).apply {\n            addRow(values)\n        }\n    }\n\n    private fun getDefaultProjection(): Array<String> =\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q\n            && Binder.getCallingUid() == Process.SYSTEM_UID) {\n            // com.android.internal.app.ChooserActivity.queryResolver() in Q queries with a null\n            // projection (meaning all columns) on main thread but only actually needs the display\n            // name (and document flags). However if we do return all the columns, we may perform\n            // network requests and crash it due to StrictMode. So just work around by only\n            // returning the display name in this case.\n            CHOOSER_ACTIVITY_DEFAULT_PROJECTION\n        } else {\n            DEFAULT_PROJECTION\n        }\n\n    override fun getType(uri: Uri): String? {\n        val path = uri.fileProviderPath\n        return MimeType.guessFromPath(path.toString()).value\n    }\n\n    override fun insert(uri: Uri, values: ContentValues?): Uri? {\n        throw UnsupportedOperationException(\"No external inserts\")\n    }\n\n    override fun update(\n        uri: Uri,\n        values: ContentValues?,\n        selection: String?,\n        selectionArgs: Array<String>?\n    ): Int {\n        throw UnsupportedOperationException(\"No external updates\")\n    }\n\n    override fun delete(\n        uri: Uri,\n        selection: String?,\n        selectionArgs: Array<String>?\n    ): Int {\n        throw UnsupportedOperationException(\"No external deletes\")\n    }\n\n    @Throws(FileNotFoundException::class)\n    override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor? {\n        // ContentProvider has already checked granted permissions\n        val path = uri.fileProviderPath\n        val modeBits = ParcelFileDescriptor.parseMode(mode)\n        if (path.canOpenDirectly(modeBits)) {\n            return ParcelFileDescriptor.open(path.toFile(), modeBits)\n        }\n        val options = modeBits.toOpenOptions()\n        val channel = try {\n            // Strict mode thread policy is passed through binder, but some apps (notably music\n            // players) like to open file on their main thread.\n            StrictMode::class.withoutPenaltyDeathOnNetwork {\n                path.newByteChannel(options)\n            }\n        } catch (e: IOException) {\n            throw e.toFileNotFoundException()\n        }\n        return try {\n            storageManager.openProxyFileDescriptorCompat(\n                modeBits, ChannelCallback(channel), callbackHandler\n            )\n        } catch (e: IOException) {\n            throw e.toFileNotFoundException()\n        }\n    }\n\n    private fun Path.canOpenDirectly(mode: Int): Boolean {\n        if (!isLinuxPath) {\n            return false\n        }\n        val file = toFile()\n        val readOnly = mode.hasBits(ParcelFileDescriptor.MODE_READ_ONLY)\n        val writeOnly = mode.hasBits(ParcelFileDescriptor.MODE_WRITE_ONLY)\n        val readWrite = mode.hasBits(ParcelFileDescriptor.MODE_READ_WRITE)\n        val needRead = readOnly || readWrite\n        val needWrite = writeOnly || readWrite\n        return !((needRead && !file.canRead()) || (needWrite && !file.canWrite()))\n    }\n\n    private fun Int.toOpenOptions(): Set<OpenOption> =\n        mutableSetOf<OpenOption>().apply {\n            // May be \"r\" for read-only access, \"rw\" for read and write access, or \"rwt\" for\n            // read and write access that truncates any existing file.\n            require(!hasBits(ParcelFileDescriptor.MODE_APPEND)) { \"mode ${this@toOpenOptions}\" }\n            if (hasBits(ParcelFileDescriptor.MODE_READ_ONLY)\n                || hasBits(ParcelFileDescriptor.MODE_READ_WRITE)) {\n                this += StandardOpenOption.READ\n            }\n            if (hasBits(ParcelFileDescriptor.MODE_WRITE_ONLY)\n                || hasBits(ParcelFileDescriptor.MODE_READ_WRITE)) {\n                this += StandardOpenOption.WRITE\n            }\n            if (hasBits(ParcelFileDescriptor.MODE_CREATE)) {\n                this += StandardOpenOption.CREATE\n            }\n            if (hasBits(ParcelFileDescriptor.MODE_TRUNCATE)) {\n                this += StandardOpenOption.TRUNCATE_EXISTING\n            }\n        }\n\n    private fun IOException.toFileNotFoundException(): FileNotFoundException =\n        if (this is FileNotFoundException) {\n            this\n        } else {\n            FileNotFoundException(message).apply { initCause(this@toFileNotFoundException) }\n        }\n\n    private class ChannelCallback(\n        private val channel: SeekableByteChannel\n    ) : ProxyFileDescriptorCallbackCompat() {\n        private var offset = 0L\n        private var released = false\n\n        @Throws(ErrnoException::class)\n        override fun onGetSize(): Long {\n            ensureNotReleased()\n            return try {\n                channel.size()\n            } catch (e: IOException) {\n                throw e.toErrnoException()\n            }\n        }\n\n        @Throws(ErrnoException::class)\n        override fun onRead(offset: Long, size: Int, data: ByteArray): Int {\n            ensureNotReleased()\n            if (this.offset != offset) {\n                try {\n                    channel.position(offset)\n                } catch (e: IOException) {\n                    throw e.toErrnoException()\n                }\n                this.offset = offset\n            }\n            val buffer = ByteBuffer.wrap(data, 0, size)\n            // Unlike ReadableByteChannel which may not fill the buffer and returns -1 upon\n            // end-of-stream, we need to read as much as we can unless end-of-stream is reached.\n            while (buffer.hasRemaining()) {\n                val channelSize = try {\n                    channel.read(buffer)\n                } catch (e: IOException) {\n                    throw e.toErrnoException()\n                }\n                if (channelSize == -1) {\n                    break\n                }\n                this.offset += channelSize\n            }\n            return (this.offset - offset).toInt()\n        }\n\n        @Throws(ErrnoException::class)\n        override fun onWrite(offset: Long, size: Int, data: ByteArray): Int {\n            ensureNotReleased()\n            if (this.offset != offset) {\n                try {\n                    channel.position(offset)\n                } catch (e: IOException) {\n                    throw e.toErrnoException()\n                }\n                this.offset = offset\n            }\n            val buffer = ByteBuffer.wrap(data, 0, size)\n            return try {\n                channel.write(buffer)\n            } catch (e: IOException) {\n                throw e.toErrnoException()\n            }.also { this.offset += it.toLong() }\n        }\n\n        @Throws(ErrnoException::class)\n        override fun onFsync() {\n            ensureNotReleased()\n            if (channel.isForceable) {\n                try {\n                    channel.force(true)\n                } catch (e: IOException) {\n                    throw e.toErrnoException()\n                }\n            }\n        }\n\n        @Throws(ErrnoException::class)\n        private fun ensureNotReleased() {\n            if (released) {\n                throw ErrnoException(null, OsConstants.EBADF)\n            }\n        }\n\n        override fun onRelease() {\n            if (released) {\n                return\n            }\n            try {\n                channel.close()\n            } catch (e: IOException) {\n                e.printStackTrace()\n            }\n            released = true\n        }\n\n        private fun IOException.toErrnoException(): ErrnoException {\n            val cause = cause\n            return if (this is FileSystemException && cause is SyscallException) {\n                ErrnoException(cause.functionName, cause.errno, this)\n            } else {\n                val errno = when (this) {\n                    is AccessDeniedException -> OsConstants.EPERM\n                    is FileSystemLoopException -> OsConstants.ELOOP\n                    is InvalidFileNameException -> OsConstants.EINVAL\n                    is IsDirectoryException -> OsConstants.EISDIR\n                    is NoSuchFileException -> OsConstants.ENOENT\n                    is ClosedByInterruptException, is InterruptedIOException -> OsConstants.EINTR\n                    else -> OsConstants.EIO\n                }\n                ErrnoException(message, errno, this)\n            }\n        }\n    }\n\n    companion object {\n        private val DEFAULT_PROJECTION = arrayOf(\n            OpenableColumns.DISPLAY_NAME,\n            OpenableColumns.SIZE,\n            MediaStore.MediaColumns.DATA,\n            DocumentsContract.Document.COLUMN_MIME_TYPE,\n            DocumentsContract.Document.COLUMN_LAST_MODIFIED\n        )\n\n        private val CHOOSER_ACTIVITY_DEFAULT_PROJECTION = arrayOf(\n            OpenableColumns.DISPLAY_NAME\n        )\n    }\n}\n\nval Path.fileProviderUri: Uri\n    get() {\n        // Try avoid going through FUSE two times, which is bad for media playback.\n        if (isDocumentPath) {\n            try {\n                return documentUri\n            } catch (e: IOException) {\n                e.printStackTrace()\n            }\n        }\n        val uriPath = Uri.encode(toUri().toString())\n        return Uri.Builder()\n            .scheme(ContentResolver.SCHEME_CONTENT)\n            .authority(BuildConfig.FILE_PROVIDIER_AUTHORITY)\n            .path(uriPath)\n            .build()\n    }\n\nprivate val Uri.fileProviderPath: Path\n    get() {\n        // Strip the prepended slash. A slash is always prepended because our Uri path starts with\n        // our URI scheme, which can never start with a slash; but our Uri has an authority so its\n        // path must start with a slash.\n        val uriPath = Uri.decode(path).substring(1)\n        return Paths.get(URI.create(uriPath))\n    }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/file/FileSize.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.file\n\nimport android.content.Context\nimport android.text.format.Formatter\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.util.getQuantityString\n\n@JvmInline\nvalue class FileSize(val value: Long) {\n\n    /* @see android.text.format.Formatter#formatBytes(Resources, long, int) */\n    val isHumanReadableInBytes: Boolean\n        get() = value <= 900\n\n    fun formatInBytes(context: Context): String =\n        context.getQuantityString(R.plurals.size_in_bytes_format, value.toInt(), value)\n\n    fun formatHumanReadable(context: Context): String =\n        Formatter.formatFileSize(context, value)\n}\n\nfun Long.asFileSize(): FileSize = FileSize(this)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/file/InstantExtensions.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.file\n\nimport android.content.Context\nimport android.text.format.DateUtils\nimport android.text.format.Time\nimport java.time.Instant\nimport java.time.ZoneId\nimport java.time.format.DateTimeFormatter\nimport java.time.format.FormatStyle\n\n/* @see com.android.documentsui.base.Shared#formatTime(Context, long) */\n@Suppress(\"DEPRECATION\")\nfun Instant.formatShort(context: Context): String {\n    val time = toEpochMilli()\n    val then = Time().apply { set(time) }\n    val now = Time().apply { setToNow() }\n    val flags = DateUtils.FORMAT_NO_NOON or DateUtils.FORMAT_NO_MIDNIGHT or\n        DateUtils.FORMAT_ABBREV_ALL or when {\n            then.year != now.year -> DateUtils.FORMAT_SHOW_YEAR or DateUtils.FORMAT_SHOW_DATE\n            then.yearDay != now.yearDay -> DateUtils.FORMAT_SHOW_DATE\n            else -> DateUtils.FORMAT_SHOW_TIME\n        }\n    return DateUtils.formatDateTime(context, time, flags)\n}\n\nfun Instant.formatLong(): String =\n    DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)\n        .withZone(ZoneId.systemDefault())\n        .format(this)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/file/JavaFile.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.file\n\nimport java.io.File\n\nobject JavaFile {\n    fun isDirectory(path: String): Boolean = File(path).isDirectory\n\n    fun getFreeSpace(path: String): Long = File(path).freeSpace\n\n    fun getTotalSpace(path: String): Long = File(path).totalSpace\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/file/MimeType.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.file\n\nimport android.os.Parcelable\nimport android.provider.DocumentsContract\nimport kotlinx.parcelize.Parcelize\n\n@Parcelize\n@JvmInline\nvalue class MimeType(val value: String) : Parcelable {\n    val type: String\n        get() = value.substring(0, value.indexOf('/'))\n\n    val subtype: String\n        get() {\n            val indexOfSlash = value.indexOf('/')\n            val indexOfSemicolon = value.indexOf(';')\n            return value.substring(\n                indexOfSlash + 1, if (indexOfSemicolon != -1) indexOfSemicolon else value.length\n            )\n        }\n\n    val suffix: String?\n        get() {\n            val indexOfPlus = value.indexOf('+')\n            if (indexOfPlus == -1) {\n                return null\n            }\n            val indexOfSemicolon = value.indexOf(';')\n            if (indexOfSemicolon != -1 && indexOfPlus > indexOfSemicolon) {\n                return null\n            }\n            return value.substring(\n                indexOfPlus + 1, if (indexOfSemicolon != -1) indexOfSemicolon else value.length\n            )\n        }\n\n    val parameters: String?\n        get() {\n            val indexOfSemicolon = value.indexOf(';')\n            return if (indexOfSemicolon != -1) value.substring(indexOfSemicolon + 1) else null\n        }\n\n    fun match(mimeType: MimeType): Boolean =\n        type.let { it == \"*\" || mimeType.type == it }\n            && subtype.let { it == \"*\" || mimeType.subtype == it }\n            && parameters.let { it == null || mimeType.parameters == it }\n\n    companion object {\n        val ANY = \"*/*\".asMimeType()\n        val APK = \"application/vnd.android.package-archive\".asMimeType()\n        val DIRECTORY = DocumentsContract.Document.MIME_TYPE_DIR.asMimeType()\n        val IMAGE_ANY = \"image/*\".asMimeType()\n        val IMAGE_GIF = \"image/gif\".asMimeType()\n        val IMAGE_SVG_XML = \"image/svg+xml\".asMimeType()\n        val PDF = \"application/pdf\".asMimeType()\n        val TEXT_PLAIN = \"text/plain\".asMimeType()\n        val GENERIC = \"application/octet-stream\".asMimeType()\n\n        fun of(type: String, subtype: String, parameters: String?): MimeType =\n            \"$type/$subtype${if (parameters != null) \";$parameters\" else \"\"}\".asMimeType()\n    }\n}\n\nfun String.asMimeTypeOrNull(): MimeType? = if (isValidMimeType) MimeType(this) else null\n\nfun String.asMimeType(): MimeType {\n    require(isValidMimeType)\n    return MimeType(this)\n}\n\nprivate val String.isValidMimeType: Boolean\n    get() {\n        val indexOfSlash = indexOf('/')\n        if (indexOfSlash == -1 || indexOfSlash !in 1..<length) {\n            return false\n        }\n        val indexOfSemicolon = indexOf(';')\n        if (indexOfSemicolon != -1) {\n            if (indexOfSemicolon !in indexOfSlash + 2..<length) {\n                return false\n            }\n        }\n        val indexOfPlus = indexOf('+')\n        if (indexOfPlus != -1 && !(indexOfSemicolon != -1 && indexOfPlus > indexOfSemicolon)) {\n            if (indexOfPlus !in indexOfSlash + 2..<\n                if (indexOfSemicolon != -1) indexOfSemicolon - 1 else length) {\n                return false\n            }\n        }\n        return true\n    }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/file/MimeTypeConversionExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.file\n\nimport android.webkit.MimeTypeMap\nimport me.zhanghai.android.files.compat.getMimeTypeFromExtensionCompat\nimport me.zhanghai.android.files.provider.common.PosixFileType\nimport me.zhanghai.android.files.util.asFileName\nimport me.zhanghai.android.files.util.asPathName\n\nfun MimeType.Companion.guessFromPath(path: String): MimeType {\n    val fileName = path.asPathName().fileName ?: return DIRECTORY\n    return guessFromExtension(fileName.asFileName().singleExtension)\n}\n\nfun MimeType.Companion.guessFromExtension(extension: String): MimeType {\n    val extension = extension.lowercase()\n    return extensionToMimeTypeOverrideMap[extension]\n        ?: MimeTypeMap.getSingleton().getMimeTypeFromExtensionCompat(extension)?.asMimeTypeOrNull()\n        ?: GENERIC\n}\n\n// @see https://android.googlesource.com/platform/external/mime-support/+/master/mime.types\n// @see https://android.googlesource.com/platform/frameworks/base/+/master/mime/java-res/android.mime.types\n// @see http://www.iana.org/assignments/media-types/media-types.xhtml\n// @see https://salsa.debian.org/debian/media-types/-/blob/master/mime.types\n// @see /usr/share/mime/packages/freedesktop.org.xml\nprivate val extensionToMimeTypeOverrideMap = mapOf(\n    // Fixes\n    \"csv\" to \"text/csv\", // Was \"text/comma-separated-values\"\n    \"sh\" to \"application/x-sh\", // Was \"text/x-sh\"\n    // Addition\n    \"bz\" to \"application/x-bzip\",\n    \"bz2\" to \"application/x-bzip2\",\n    \"z\" to \"application/x-compress\",\n    \"lzma\" to \"application/x-lzma\",\n    \"p7b\" to \"application/x-pkcs7-certificates\",\n    \"spc\" to \"application/x-pkcs7-certificates\", // Clashes with \"chemical/x-galactic-spc\"\n    \"ts\" to \"application/typescript\", // Clashes with \"video/mp2ts\"\n    \"py3\" to \"text/x-python\",\n    \"py3x\" to \"text/x-python\",\n    \"pyx\" to \"text/x-python\",\n    \"wsgi\" to \"text/x-python\",\n    \"yml\" to \"application/yaml\",\n    \"asm\" to \"text/x-asm\",\n    \"s\" to \"text/x-asm\",\n    \"cs\" to \"text/x-csharp\",\n    \"azw\" to \"application/vnd.amazon.ebook\",\n    \"ibooks\" to \"application/x-ibooks+zip\",\n    \"msg\" to \"application/vnd.ms-outlook\",\n    \"mkd\" to \"text/markdown\",\n    \"conf\" to \"text/plain\",\n    \"ini\" to \"text/plain\",\n    \"list\" to \"text/plain\",\n    \"log\" to \"text/plain\",\n    \"prop\" to \"text/plain\",\n    \"properties\" to \"text/plain\",\n    \"rc\" to \"text/plain\"\n).mapValues { it.value.asMimeType() }\n\nfun MimeType.Companion.forSpecialPosixFileType(type: PosixFileType): MimeType? =\n    specialPosixFileTypeToMimeTypeMap[type]\n\n// See also https://developer.gnome.org/shared-mime-info-spec/\n/** @see specialPosixFileTypeToNameResMap */\nprivate val specialPosixFileTypeToMimeTypeMap = mapOf(\n    PosixFileType.CHARACTER_DEVICE to \"inode/chardevice\",\n    PosixFileType.BLOCK_DEVICE to \"inode/blockdevice\",\n    PosixFileType.FIFO to \"inode/fifo\",\n    PosixFileType.SYMBOLIC_LINK to \"inode/symlink\",\n    PosixFileType.SOCKET to \"inode/socket\"\n).mapValues { it.value.asMimeType() }\n\nval MimeType.extension: String?\n    // TODO: Add compat implementation as well.\n    get() = MimeTypeMap.getSingleton().getExtensionFromMimeType(value)\n\nval MimeType.intentType: String\n    get() = intentMimeType.value\n\nprivate val MimeType.intentMimeType: MimeType\n    get() = mimeTypeToIntentMimeTypeMap[this] ?: this\n\nprivate val mimeTypeToIntentMimeTypeMap = listOf(\n    // Allows matching \"text/*\"\n    \"application/ecmascript\" to \"text/ecmascript\",\n    \"application/javascript\" to \"text/javascript\",\n    \"application/json\" to \"text/json\",\n    \"application/typescript\" to \"text/typescript\",\n    \"application/yaml\" to \"text/x-yaml\",\n    \"application/x-sh\" to \"text/x-shellscript\",\n    \"application/x-shellscript\" to \"text/x-shellscript\",\n    // Allows matching generic\n    MimeType.GENERIC.value to MimeType.ANY.value\n).associate { it.first.asMimeType() to it.second.asMimeType() }\n\nval Collection<MimeType>.intentType: String\n    get() {\n        if (isEmpty()) {\n            return MimeType.ANY.value\n        }\n        val intentMimeTypes = map { it.intentMimeType }\n        val firstIntentMimeType = intentMimeTypes.first()\n        if (intentMimeTypes.all { firstIntentMimeType.match(it) }) {\n            return firstIntentMimeType.value\n        }\n        val wildcardIntentMimeType = MimeType.of(firstIntentMimeType.type, \"*\", null)\n        if (intentMimeTypes.all { wildcardIntentMimeType.match(it) }) {\n            return wildcardIntentMimeType.value\n        }\n        return MimeType.ANY.value\n    }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/file/MimeTypeIcon.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.file\n\nimport me.zhanghai.android.files.R\n\nenum class MimeTypeIcon(val resourceId: Int) {\n    APK(R.drawable.file_apk_icon),\n    ARCHIVE(R.drawable.file_archive_icon),\n    AUDIO(R.drawable.file_audio_icon),\n    CALENDAR(R.drawable.file_calendar_icon),\n    CERTIFICATE(R.drawable.file_certificate_icon),\n    CODE(R.drawable.file_code_icon),\n    CONTACT(R.drawable.file_contact_icon),\n    DIRECTORY(R.drawable.file_directory_icon),\n    DOCUMENT(R.drawable.file_document_icon),\n    EBOOK(R.drawable.file_ebook_icon),\n    EMAIL(R.drawable.file_email_icon),\n    FONT(R.drawable.file_font_icon),\n    GENERIC(R.drawable.file_generic_icon),\n    IMAGE(R.drawable.file_image_icon),\n    PDF(R.drawable.file_pdf_icon),\n    PRESENTATION(R.drawable.file_presentation_icon),\n    SPREADSHEET(R.drawable.file_spreadsheet_icon),\n    TEXT(R.drawable.file_text_icon),\n    VIDEO(R.drawable.file_video_icon),\n    WORD(R.drawable.file_word_icon),\n    EXCEL(R.drawable.file_excel_icon),\n    POWERPOINT(R.drawable.file_powerpoint_icon)\n}\n\n// See also https://android.googlesource.com/platform/frameworks/base.git/+/master/core/java/com/android/internal/util/MimeIconUtils.java\n// See also https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Complete_list_of_MIME_types\n// See also http://www.iana.org/assignments/media-types/media-types.xhtml\n// See also /usr/share/mime/packages/freedesktop.org.xml\nval MimeType.icon: MimeTypeIcon\n    get() =\n        mimeTypeToIconMap[this] ?: typeToIconMap[type] ?: suffix?.let { suffixToIconMap[it] }\n        ?: MimeTypeIcon.GENERIC\n\n// See also https://mimesniff.spec.whatwg.org/#mime-type-groups\nprivate val mimeTypeToIconMap = mapOf(\n    \"application/vnd.android.package-archive\" to MimeTypeIcon.APK,\n    \"application/gzip\" to MimeTypeIcon.ARCHIVE,\n    // Not in IANA list, but Mozilla and Wikipedia say so.\n    \"application/java-archive\" to MimeTypeIcon.ARCHIVE,\n    \"application/mac-binhex40\" to MimeTypeIcon.ARCHIVE,\n    // Not in IANA list, but AOSP MimeUtils used to say so.\n    \"application/rar\" to MimeTypeIcon.ARCHIVE,\n    \"application/zip\" to MimeTypeIcon.ARCHIVE,\n    \"application/zstd\" to MimeTypeIcon.ARCHIVE,\n    \"application/vnd.debian.binary-package\" to MimeTypeIcon.ARCHIVE,\n    \"application/vnd.ms-cab-compressed\" to MimeTypeIcon.ARCHIVE,\n    \"application/vnd.rar\" to MimeTypeIcon.ARCHIVE,\n    \"application/x-7z-compressed\" to MimeTypeIcon.ARCHIVE,\n    \"application/x-apple-diskimage\" to MimeTypeIcon.ARCHIVE,\n    \"application/x-bzip\" to MimeTypeIcon.ARCHIVE,\n    \"application/x-bzip2\" to MimeTypeIcon.ARCHIVE,\n    \"application/x-compress\" to MimeTypeIcon.ARCHIVE,\n    \"application/x-cpio\" to MimeTypeIcon.ARCHIVE,\n    \"application/x-deb\" to MimeTypeIcon.ARCHIVE,\n    \"application/x-debian-package\" to MimeTypeIcon.ARCHIVE,\n    \"application/x-gtar\" to MimeTypeIcon.ARCHIVE,\n    \"application/x-gtar-compressed\" to MimeTypeIcon.ARCHIVE,\n    \"application/x-gzip\" to MimeTypeIcon.ARCHIVE,\n    \"application/x-iso9660-image\" to MimeTypeIcon.ARCHIVE,\n    \"application/x-java-archive\" to MimeTypeIcon.ARCHIVE,\n    \"application/x-lha\" to MimeTypeIcon.ARCHIVE,\n    \"application/x-lzh\" to MimeTypeIcon.ARCHIVE,\n    \"application/x-lzma\" to MimeTypeIcon.ARCHIVE,\n    \"application/x-lzx\" to MimeTypeIcon.ARCHIVE,\n    \"application/x-rar-compressed\" to MimeTypeIcon.ARCHIVE,\n    \"application/x-stuffit\" to MimeTypeIcon.ARCHIVE,\n    \"application/x-tar\" to MimeTypeIcon.ARCHIVE,\n    \"application/x-webarchive\" to MimeTypeIcon.ARCHIVE,\n    \"application/x-webarchive-xml\" to MimeTypeIcon.ARCHIVE,\n    \"application/x-xz\" to MimeTypeIcon.ARCHIVE,\n    \"application/ogg\" to MimeTypeIcon.AUDIO,\n    \"application/x-flac\" to MimeTypeIcon.AUDIO,\n    \"text/calendar\" to MimeTypeIcon.CALENDAR,\n    \"text/x-vcalendar\" to MimeTypeIcon.CALENDAR,\n    \"application/pgp-keys\" to MimeTypeIcon.CERTIFICATE,\n    \"application/pgp-signature\" to MimeTypeIcon.CERTIFICATE,\n    \"application/x-pkcs12\" to MimeTypeIcon.CERTIFICATE,\n    \"application/x-pkcs7-certificates\" to MimeTypeIcon.CERTIFICATE,\n    \"application/x-pkcs7-certreqresp\" to MimeTypeIcon.CERTIFICATE,\n    \"application/x-pkcs7-crl\" to MimeTypeIcon.CERTIFICATE,\n    \"application/x-pkcs7-mime\" to MimeTypeIcon.CERTIFICATE,\n    \"application/x-pkcs7-signature\" to MimeTypeIcon.CERTIFICATE,\n    \"application/x-x509-ca-cert\" to MimeTypeIcon.CERTIFICATE,\n    \"application/x-x509-server-cert\" to MimeTypeIcon.CERTIFICATE,\n    \"application/x-x509-user-cert\" to MimeTypeIcon.CERTIFICATE,\n    \"application/ecmascript\" to MimeTypeIcon.CODE,\n    \"application/javascript\" to MimeTypeIcon.CODE,\n    \"application/json\" to MimeTypeIcon.CODE,\n    \"application/typescript\" to MimeTypeIcon.CODE,\n    \"application/xml\" to MimeTypeIcon.CODE,\n    \"application/yaml\" to MimeTypeIcon.CODE,\n    \"application/x-csh\" to MimeTypeIcon.CODE,\n    \"application/x-ecmascript\" to MimeTypeIcon.CODE,\n    \"application/x-javascript\" to MimeTypeIcon.CODE,\n    \"application/x-latex\" to MimeTypeIcon.CODE,\n    \"application/x-perl\" to MimeTypeIcon.CODE,\n    \"application/x-python\" to MimeTypeIcon.CODE,\n    \"application/x-ruby\" to MimeTypeIcon.CODE,\n    \"application/x-sh\" to MimeTypeIcon.CODE,\n    \"application/x-shellscript\" to MimeTypeIcon.CODE,\n    \"application/x-texinfo\" to MimeTypeIcon.CODE,\n    \"application/x-yaml\" to MimeTypeIcon.CODE,\n    \"text/css\" to MimeTypeIcon.CODE,\n    \"text/html\" to MimeTypeIcon.CODE,\n    \"text/ecmascript\" to MimeTypeIcon.CODE,\n    \"text/javascript\" to MimeTypeIcon.CODE,\n    \"text/jscript\" to MimeTypeIcon.CODE,\n    \"text/livescript\" to MimeTypeIcon.CODE,\n    \"text/xml\" to MimeTypeIcon.CODE,\n    \"text/x-asm\" to MimeTypeIcon.CODE,\n    \"text/x-c++hdr\" to MimeTypeIcon.CODE,\n    \"text/x-c++src\" to MimeTypeIcon.CODE,\n    \"text/x-chdr\" to MimeTypeIcon.CODE,\n    \"text/x-csh\" to MimeTypeIcon.CODE,\n    \"text/x-csharp\" to MimeTypeIcon.CODE,\n    \"text/x-csrc\" to MimeTypeIcon.CODE,\n    \"text/x-dsrc\" to MimeTypeIcon.CODE,\n    \"text/x-ecmascript\" to MimeTypeIcon.CODE,\n    \"text/x-haskell\" to MimeTypeIcon.CODE,\n    \"text/x-java\" to MimeTypeIcon.CODE,\n    \"text/x-javascript\" to MimeTypeIcon.CODE,\n    \"text/x-literate-haskell\" to MimeTypeIcon.CODE,\n    \"text/x-pascal\" to MimeTypeIcon.CODE,\n    \"text/x-perl\" to MimeTypeIcon.CODE,\n    \"text/x-python\" to MimeTypeIcon.CODE,\n    \"text/x-ruby\" to MimeTypeIcon.CODE,\n    \"text/x-shellscript\" to MimeTypeIcon.CODE,\n    \"text/x-tcl\" to MimeTypeIcon.CODE,\n    \"text/x-tex\" to MimeTypeIcon.CODE,\n    \"text/x-yaml\" to MimeTypeIcon.CODE,\n    \"text/vcard\" to MimeTypeIcon.CONTACT,\n    \"text/x-vcard\" to MimeTypeIcon.CONTACT,\n    \"inode/directory\" to MimeTypeIcon.DIRECTORY,\n    MimeType.DIRECTORY.value to MimeTypeIcon.DIRECTORY,\n    \"application/rtf\" to MimeTypeIcon.DOCUMENT,\n    \"application/vnd.kde.kword\" to MimeTypeIcon.DOCUMENT,\n    \"application/vnd.oasis.opendocument.text\" to MimeTypeIcon.DOCUMENT,\n    \"application/vnd.oasis.opendocument.text-master\" to MimeTypeIcon.DOCUMENT,\n    \"application/vnd.oasis.opendocument.text-template\" to MimeTypeIcon.DOCUMENT,\n    \"application/vnd.oasis.opendocument.text-web\" to MimeTypeIcon.DOCUMENT,\n    \"application/vnd.stardivision.writer\" to MimeTypeIcon.DOCUMENT,\n    \"application/vnd.stardivision.writer-global\" to MimeTypeIcon.DOCUMENT,\n    \"application/vnd.sun.xml.writer\" to MimeTypeIcon.DOCUMENT,\n    \"application/vnd.sun.xml.writer.global\" to MimeTypeIcon.DOCUMENT,\n    \"application/vnd.sun.xml.writer.template\" to MimeTypeIcon.DOCUMENT,\n    \"application/x-abiword\" to MimeTypeIcon.DOCUMENT,\n    \"application/x-kword\" to MimeTypeIcon.DOCUMENT,\n    \"text/rtf\" to MimeTypeIcon.DOCUMENT,\n    \"application/epub+zip\" to MimeTypeIcon.EBOOK,\n    \"application/vnd.amazon.ebook\" to MimeTypeIcon.EBOOK,\n    \"application/vnd.amazon.mobi8-ebook\" to MimeTypeIcon.EBOOK,\n    \"application/vnd.comicbook-rar\" to MimeTypeIcon.EBOOK,\n    \"application/vnd.comicbook+zip\" to MimeTypeIcon.EBOOK,\n    \"application/x-cbr\" to MimeTypeIcon.EBOOK,\n    \"application/x-cbz\" to MimeTypeIcon.EBOOK,\n    \"application/x-ibooks+zip\" to MimeTypeIcon.EBOOK,\n    \"application/x-mobipocket-ebook\" to MimeTypeIcon.EBOOK,\n    \"application/vnd.ms-outlook\" to MimeTypeIcon.EMAIL,\n    \"message/rfc822\" to MimeTypeIcon.EMAIL,\n    \"application/font-cff\" to MimeTypeIcon.FONT,\n    \"application/font-off\" to MimeTypeIcon.FONT,\n    \"application/font-sfnt\" to MimeTypeIcon.FONT,\n    \"application/font-ttf\" to MimeTypeIcon.FONT,\n    \"application/font-woff\" to MimeTypeIcon.FONT,\n    \"application/vnd.ms-fontobject\" to MimeTypeIcon.FONT,\n    \"application/vnd.ms-opentype\" to MimeTypeIcon.FONT,\n    \"application/x-font\" to MimeTypeIcon.FONT,\n    \"application/x-font-ttf\" to MimeTypeIcon.FONT,\n    \"application/x-font-woff\" to MimeTypeIcon.FONT,\n    \"application/vnd.oasis.opendocument.graphics\" to MimeTypeIcon.IMAGE,\n    \"application/vnd.oasis.opendocument.graphics-template\" to MimeTypeIcon.IMAGE,\n    \"application/vnd.oasis.opendocument.image\" to MimeTypeIcon.IMAGE,\n    \"application/vnd.stardivision.draw\" to MimeTypeIcon.IMAGE,\n    \"application/vnd.sun.xml.draw\" to MimeTypeIcon.IMAGE,\n    \"application/vnd.sun.xml.draw.template\" to MimeTypeIcon.IMAGE,\n    \"application/vnd.visio\" to MimeTypeIcon.IMAGE,\n    \"application/pdf\" to MimeTypeIcon.PDF,\n    \"application/vnd.kde.kpresenter\" to MimeTypeIcon.PRESENTATION,\n    \"application/vnd.oasis.opendocument.presentation\" to MimeTypeIcon.PRESENTATION,\n    \"application/vnd.oasis.opendocument.presentation-template\" to MimeTypeIcon.PRESENTATION,\n    \"application/vnd.stardivision.impress\" to MimeTypeIcon.PRESENTATION,\n    \"application/vnd.sun.xml.impress\" to MimeTypeIcon.PRESENTATION,\n    \"application/vnd.sun.xml.impress.template\" to MimeTypeIcon.PRESENTATION,\n    \"application/x-kpresenter\" to MimeTypeIcon.PRESENTATION,\n    \"application/vnd.kde.kspread\" to MimeTypeIcon.SPREADSHEET,\n    \"application/vnd.oasis.opendocument.spreadsheet\" to MimeTypeIcon.SPREADSHEET,\n    \"application/vnd.oasis.opendocument.spreadsheet-template\" to MimeTypeIcon.SPREADSHEET,\n    \"application/vnd.stardivision.calc\" to MimeTypeIcon.SPREADSHEET,\n    \"application/vnd.sun.xml.calc\" to MimeTypeIcon.SPREADSHEET,\n    \"application/vnd.sun.xml.calc.template\" to MimeTypeIcon.SPREADSHEET,\n    \"application/x-kspread\" to MimeTypeIcon.SPREADSHEET,\n    \"application/vnd.adobe.flash.movie\" to MimeTypeIcon.VIDEO,\n    \"application/x-quicktimeplayer\" to MimeTypeIcon.VIDEO,\n    \"application/x-shockwave-flash\" to MimeTypeIcon.VIDEO,\n    \"application/msword\" to MimeTypeIcon.WORD,\n    \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\" to MimeTypeIcon.WORD,\n    \"application/vnd.openxmlformats-officedocument.wordprocessingml.template\" to MimeTypeIcon.WORD,\n    \"application/vnd.ms-excel\" to MimeTypeIcon.EXCEL,\n    \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\" to MimeTypeIcon.EXCEL,\n    \"application/vnd.openxmlformats-officedocument.spreadsheetml.template\" to MimeTypeIcon.EXCEL,\n    \"application/vnd.ms-powerpoint\" to MimeTypeIcon.POWERPOINT,\n    \"application/vnd.openxmlformats-officedocument.presentationml.presentation\"\n        to MimeTypeIcon.POWERPOINT,\n    \"application/vnd.openxmlformats-officedocument.presentationml.slideshow\"\n        to MimeTypeIcon.POWERPOINT,\n    \"application/vnd.openxmlformats-officedocument.presentationml.template\"\n        to MimeTypeIcon.POWERPOINT\n).mapKeys { it.key.asMimeType() }\n\nprivate val typeToIconMap = mapOf(\n    \"audio\" to MimeTypeIcon.AUDIO,\n    \"font\" to MimeTypeIcon.FONT,\n    \"image\" to MimeTypeIcon.IMAGE,\n    \"text\" to MimeTypeIcon.TEXT,\n    \"video\" to MimeTypeIcon.VIDEO\n)\n\nprivate val suffixToIconMap = mapOf(\n    \"json\" to MimeTypeIcon.CODE,\n    \"xml\" to MimeTypeIcon.CODE,\n    \"zip\" to MimeTypeIcon.ARCHIVE\n)\n\nval MimeType.iconRes: Int\n    get() = icon.resourceId\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/file/MimeTypeNameExtensions.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.file\n\nimport android.content.Context\nimport me.zhanghai.android.files.R\n\nfun MimeType.getName(extension: String, context: Context): String {\n    val nameRes = specialPosixFileTypeToNameResMap[this]\n        ?: icon.getNameRes(this, extension.isNotEmpty())\n    return context.getString(nameRes, extension.uppercase())\n}\n\n/**\n *  @see specialPosixFileTypeToMimeTypeMap\n */\nprivate val specialPosixFileTypeToNameResMap = mapOf(\n    \"inode/chardevice\" to R.string.file_type_name_posix_character_device,\n    \"inode/blockdevice\" to R.string.file_type_name_posix_block_device,\n    \"inode/fifo\" to R.string.file_type_name_posix_fifo,\n    \"inode/symlink\" to R.string.file_type_name_posix_symbolic_link,\n    \"inode/socket\" to R.string.file_type_name_posix_socket\n).mapKeys { it.key.asMimeType() }\n\nprivate fun MimeTypeIcon.getNameRes(mimeType: MimeType, hasExtension: Boolean): Int =\n    when (this) {\n        MimeTypeIcon.APK -> R.string.file_type_name_apk\n        MimeTypeIcon.ARCHIVE -> R.string.file_type_name_archive\n        MimeTypeIcon.AUDIO -> R.string.file_type_name_audio\n        MimeTypeIcon.CALENDAR -> R.string.file_type_name_calendar\n        MimeTypeIcon.CERTIFICATE -> R.string.file_type_name_certificate\n        MimeTypeIcon.CODE -> R.string.file_type_name_code\n        MimeTypeIcon.CONTACT -> R.string.file_type_name_contact\n        MimeTypeIcon.DIRECTORY -> R.string.file_type_name_directory\n        MimeTypeIcon.DOCUMENT -> R.string.file_type_name_document\n        MimeTypeIcon.EBOOK -> R.string.file_type_name_ebook\n        MimeTypeIcon.EMAIL -> R.string.file_type_name_email\n        MimeTypeIcon.FONT -> R.string.file_type_name_font\n        MimeTypeIcon.GENERIC -> {\n            if (!hasExtension) R.string.file_type_name_unknown else R.string.file_type_name_generic\n        }\n        MimeTypeIcon.IMAGE -> R.string.file_type_name_image\n        MimeTypeIcon.PDF -> R.string.file_type_name_pdf\n        MimeTypeIcon.PRESENTATION -> R.string.file_type_name_presentation\n        MimeTypeIcon.SPREADSHEET -> R.string.file_type_name_spreadsheet\n        MimeTypeIcon.TEXT ->\n            if (mimeType == MimeType.TEXT_PLAIN) {\n                R.string.file_type_name_text_plain\n            } else {\n                R.string.file_type_name_text\n            }\n        MimeTypeIcon.VIDEO -> R.string.file_type_name_video\n        MimeTypeIcon.WORD -> R.string.file_type_name_word\n        MimeTypeIcon.EXCEL -> R.string.file_type_name_excel\n        MimeTypeIcon.POWERPOINT -> R.string.file_type_name_powerpoint\n    }\n\nfun MimeType.Companion.getBrokenSymbolicLinkName(context: Context): String =\n    context.getString(R.string.file_type_name_posix_symbolic_link_broken)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/file/MimeTypeTypeExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.file\n\nval MimeType.isApk: Boolean\n    get() = this == MimeType.APK\n\nval MimeType.isSupportedArchive: Boolean\n    get() = this in supportedArchiveMimeTypes\n\nprivate val supportedArchiveMimeTypes = mutableListOf(\n    \"application/gzip\",\n    \"application/java-archive\",\n    \"application/rar\",\n    \"application/zip\",\n    \"application/zstd\",\n    \"application/vnd.android.package-archive\",\n    \"application/vnd.debian.binary-package\",\n    \"application/vnd.ms-cab-compressed\",\n    \"application/vnd.rar\",\n    \"application/x-7z-compressed\",\n    \"application/x-bzip2\",\n    \"application/x-cab\",\n    \"application/x-compress\",\n    \"application/x-cpio\",\n    \"application/x-deb\",\n    \"application/x-debian-package\",\n    \"application/x-gtar\",\n    \"application/x-gtar-compressed\",\n    \"application/x-iso9660-image\",\n    \"application/x-java-archive\",\n    \"application/x-lha\",\n    \"application/x-lzma\",\n    \"application/x-redhat-package-manager\",\n    \"application/x-tar\",\n    \"application/x-ustar\",\n    \"application/x-xz\"\n).map { it.asMimeType() }.toSet()\n\nval MimeType.isImage: Boolean\n    get() = icon == MimeTypeIcon.IMAGE\n\nval MimeType.isAudio: Boolean\n    get() = icon == MimeTypeIcon.AUDIO\n\nval MimeType.isVideo: Boolean\n    get() = icon == MimeTypeIcon.VIDEO\n\nval MimeType.isMedia: Boolean\n    get() = isAudio || isVideo\n\nval MimeType.isPdf: Boolean\n    get() = this == MimeType.PDF\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileaction/ArchivePasswordDialogActivity.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileaction\n\nimport android.os.Bundle\nimport android.view.View\nimport androidx.fragment.app.commit\nimport me.zhanghai.android.files.app.AppActivity\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.putArgs\n\nclass ArchivePasswordDialogActivity : AppActivity() {\n    private val args by args<ArchivePasswordDialogFragment.Args>()\n\n    private lateinit var fragment: ArchivePasswordDialogFragment\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        // Calls ensureSubDecor().\n        findViewById<View>(android.R.id.content)\n        if (savedInstanceState == null) {\n            fragment = ArchivePasswordDialogFragment().putArgs(args)\n            supportFragmentManager.commit {\n                add(fragment, ArchivePasswordDialogFragment::class.java.name)\n            }\n        } else {\n            fragment = supportFragmentManager.findFragmentByTag(\n                ArchivePasswordDialogFragment::class.java.name\n            ) as ArchivePasswordDialogFragment\n        }\n    }\n\n    override fun onDestroy() {\n        super.onDestroy()\n\n        if (isFinishing) {\n            fragment.onFinish()\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileaction/ArchivePasswordDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileaction\n\nimport android.app.Dialog\nimport android.content.Context\nimport android.content.DialogInterface\nimport android.os.Bundle\nimport android.os.Parcel\nimport android.os.Parcelable\nimport android.util.SparseArray\nimport android.view.WindowManager\nimport android.widget.LinearLayout\nimport androidx.appcompat.app.AlertDialog\nimport androidx.appcompat.app.AppCompatDialogFragment\nimport androidx.core.widget.NestedScrollView\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport java8.nio.file.Path\nimport kotlinx.parcelize.Parceler\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.compat.requireViewByIdCompat\nimport me.zhanghai.android.files.databinding.ArchivePasswordDialogBinding\nimport me.zhanghai.android.files.provider.archive.archiveAddPassword\nimport me.zhanghai.android.files.provider.archive.archiveFile\nimport me.zhanghai.android.files.ui.AllowSoftInputHackAlertDialogCustomView\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.ParcelableParceler\nimport me.zhanghai.android.files.util.ParcelableState\nimport me.zhanghai.android.files.util.RemoteCallback\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.finish\nimport me.zhanghai.android.files.util.getArgs\nimport me.zhanghai.android.files.util.getState\nimport me.zhanghai.android.files.util.hideTextInputLayoutErrorOnTextChange\nimport me.zhanghai.android.files.util.layoutInflater\nimport me.zhanghai.android.files.util.putArgs\nimport me.zhanghai.android.files.util.putState\nimport me.zhanghai.android.files.util.readParcelable\nimport me.zhanghai.android.files.util.setOnEditorConfirmActionListener\n\nclass ArchivePasswordDialogFragment : AppCompatDialogFragment() {\n    private val args by args<Args>()\n\n    private lateinit var binding: ArchivePasswordDialogBinding\n\n    private var isListenerNotified = false\n\n    override fun onSaveInstanceState(outState: Bundle) {\n        super.onSaveInstanceState(outState)\n\n        val hierarchyState = SparseArray<Parcelable>()\n            .apply { binding.root.saveHierarchyState(this) }\n        outState.putState(State(hierarchyState))\n    }\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {\n        val context = requireContext()\n        return MaterialAlertDialogBuilder(requireContext(), theme)\n            .setTitle(getTitle(context))\n            .setMessage(getMessage(args.path.archiveFile.fileName, context))\n            .apply {\n                binding = ArchivePasswordDialogBinding.inflate(context.layoutInflater)\n                binding.passwordEdit.hideTextInputLayoutErrorOnTextChange(binding.passwordLayout)\n                binding.passwordEdit.setOnEditorConfirmActionListener { onOk() }\n                if (savedInstanceState != null) {\n                    val state = savedInstanceState.getState<State>()\n                    binding.root.restoreHierarchyState(state.hierarchyState)\n                }\n                setView(AllowSoftInputHackAlertDialogCustomView(context))\n            }\n            .setPositiveButton(android.R.string.ok, null)\n            .setNegativeButton(android.R.string.cancel) { _, _ -> finish() }\n            .create()\n            .apply {\n                setCanceledOnTouchOutside(false)\n                // Override the listener here so that we have control over when to close the dialog.\n                setOnShowListener {\n                    getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { onOk() }\n                }\n                window!!.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)\n            }\n    }\n\n    override fun onStart() {\n        super.onStart()\n\n        val dialog = requireDialog() as AlertDialog\n        if (binding.root.parent == null) {\n            val scrollView = dialog.requireViewByIdCompat<NestedScrollView>(R.id.scrollView)\n            val linearLayout = scrollView.getChildAt(0) as LinearLayout\n            linearLayout.addView(binding.root)\n            binding.passwordEdit.requestFocus()\n        }\n    }\n\n    private fun onOk() {\n        val password = binding.passwordEdit.text!!.toString()\n        if (password.isEmpty()) {\n            binding.passwordLayout.error =\n                getString(R.string.file_action_archive_password_error_empty)\n            return\n        }\n        args.path.archiveAddPassword(password)\n        notifyListenerOnce(true)\n        finish()\n    }\n\n    override fun onCancel(dialog: DialogInterface) {\n        super.onCancel(dialog)\n\n        notifyListenerOnce(false)\n        finish()\n    }\n\n    fun onFinish() {\n        notifyListenerOnce(false)\n    }\n\n    private fun notifyListenerOnce(successful: Boolean) {\n        if (isListenerNotified) {\n            return\n        }\n        args.listener(successful)\n        isListenerNotified = true\n    }\n\n    companion object {\n        fun getTitle(context: Context): String =\n            context.getString(R.string.file_action_archive_password_title)\n\n        fun getMessage(archiveFile: Path, context: Context): String =\n            context.getString(\n                R.string.file_action_archive_password_message_format, archiveFile.fileName\n            )\n    }\n\n    @Parcelize\n    class Args(\n        val path: @WriteWith<ParcelableParceler> Path,\n        val listener: @WriteWith<ListenerParceler>()\n        (Boolean) -> Unit\n    ) : ParcelableArgs {\n        object ListenerParceler : Parceler<(Boolean) -> Unit> {\n            override fun create(parcel: Parcel): (Boolean) -> Unit =\n                parcel.readParcelable<RemoteCallback>()!!.let {\n                    { successful ->\n                        it.sendResult(Bundle().putArgs(ListenerArgs(successful)))\n                    }\n                }\n\n            override fun ((Boolean) -> Unit).write(parcel: Parcel, flags: Int) {\n                parcel.writeParcelable(\n                    RemoteCallback {\n                        val args = it.getArgs<ListenerArgs>()\n                        this(args.successful)\n                    }, flags\n                )\n            }\n\n            @Parcelize\n            private class ListenerArgs(\n                val successful: Boolean\n            ) : ParcelableArgs\n        }\n    }\n\n    @Parcelize\n    private class State(\n        val hierarchyState: SparseArray<Parcelable>\n    ) : ParcelableState\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filejob/FileJob.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filejob\n\nimport me.zhanghai.android.files.util.showToast\nimport java.io.IOException\nimport java.io.InterruptedIOException\nimport java.util.Random\n\nabstract class FileJob {\n    val id = Random().nextInt()\n\n    internal lateinit var service: FileJobService\n        private set\n\n    fun runOn(service: FileJobService) {\n        this.service = service\n        try {\n            run()\n            // TODO: Toast\n        } catch (e: InterruptedIOException) {\n            // TODO\n            e.printStackTrace()\n        } catch (e: Exception) {\n            e.printStackTrace()\n            service.showToast(e.toString())\n        } finally {\n            service.notificationManager.cancel(id)\n        }\n    }\n\n    @Throws(IOException::class)\n    protected abstract fun run()\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filejob/FileJobConflictAction.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filejob\n\nenum class FileJobConflictAction {\n    MERGE_OR_REPLACE,\n    RENAME,\n    SKIP,\n    CANCEL,\n    CANCELED\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filejob/FileJobConflictDialogActivity.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filejob\n\nimport android.os.Bundle\nimport android.view.View\nimport androidx.fragment.app.commit\nimport me.zhanghai.android.files.app.AppActivity\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.putArgs\n\nclass FileJobConflictDialogActivity : AppActivity() {\n    private val args by args<FileJobConflictDialogFragment.Args>()\n\n    private lateinit var fragment: FileJobConflictDialogFragment\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        // Calls ensureSubDecor().\n        findViewById<View>(android.R.id.content)\n        if (savedInstanceState == null) {\n            fragment = FileJobConflictDialogFragment().putArgs(args)\n            supportFragmentManager.commit {\n                add(fragment, FileJobConflictDialogFragment::class.java.name)\n            }\n        } else {\n            fragment = supportFragmentManager.findFragmentByTag(\n                FileJobConflictDialogFragment::class.java.name\n            ) as FileJobConflictDialogFragment\n        }\n    }\n\n    override fun onDestroy() {\n        super.onDestroy()\n\n        if (isFinishing) {\n            fragment.onFinish()\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filejob/FileJobConflictDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filejob\n\nimport android.app.Dialog\nimport android.content.Context\nimport android.content.DialogInterface\nimport android.os.Bundle\nimport android.os.Parcel\nimport android.view.WindowManager\nimport android.widget.Button\nimport android.widget.ImageView\nimport android.widget.LinearLayout\nimport android.widget.TextView\nimport androidx.appcompat.app.AlertDialog\nimport androidx.appcompat.app.AppCompatDialogFragment\nimport androidx.core.view.isVisible\nimport androidx.core.widget.NestedScrollView\nimport androidx.core.widget.doAfterTextChanged\nimport androidx.interpolator.view.animation.FastOutSlowInInterpolator\nimport coil.dispose\nimport coil.load\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport kotlinx.parcelize.Parceler\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.coil.AppIconPackageName\nimport me.zhanghai.android.files.compat.requireViewByIdCompat\nimport me.zhanghai.android.files.databinding.FileJobConflictDialogViewBinding\nimport me.zhanghai.android.files.file.FileItem\nimport me.zhanghai.android.files.file.fileSize\nimport me.zhanghai.android.files.file.formatShort\nimport me.zhanghai.android.files.file.iconRes\nimport me.zhanghai.android.files.file.lastModifiedInstant\nimport me.zhanghai.android.files.filelist.appDirectoryPackageName\nimport me.zhanghai.android.files.filelist.supportsThumbnail\nimport me.zhanghai.android.files.provider.common.isEncrypted\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.ParcelableState\nimport me.zhanghai.android.files.util.RemoteCallback\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.finish\nimport me.zhanghai.android.files.util.getArgs\nimport me.zhanghai.android.files.util.getState\nimport me.zhanghai.android.files.util.layoutInflater\nimport me.zhanghai.android.files.util.putArgs\nimport me.zhanghai.android.files.util.putState\nimport me.zhanghai.android.files.util.readParcelable\nimport me.zhanghai.android.files.util.setTextWithSelection\nimport me.zhanghai.android.files.util.shortAnimTime\nimport me.zhanghai.android.files.util.showSoftInput\n\nclass FileJobConflictDialogFragment : AppCompatDialogFragment() {\n    private val args by args<Args>()\n\n    private lateinit var binding: FileJobConflictDialogViewBinding\n\n    private var isListenerNotified = false\n\n    override fun onSaveInstanceState(outState: Bundle) {\n        super.onSaveInstanceState(outState)\n\n        outState.putState(State(binding.allCheck.isChecked))\n    }\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {\n        val sourceFile = args.sourceFile\n        val targetFile = args.targetFile\n        val title = getTitle(sourceFile, targetFile, requireContext())\n        val message = getMessage(sourceFile, targetFile, args.type, requireContext())\n        val isMerge = isMerge(sourceFile, targetFile)\n        val positiveButtonRes = if (isMerge) R.string.merge else R.string.replace\n        return MaterialAlertDialogBuilder(requireContext(), theme)\n            .setTitle(title)\n            .setMessage(message)\n            .apply {\n                binding = FileJobConflictDialogViewBinding.inflate(context.layoutInflater)\n                binding.targetNameText.setText(\n                    if (isMerge) {\n                        R.string.file_job_merge_target_name\n                    } else {\n                        R.string.file_job_replace_target_name\n                    }\n                )\n                bindFileItem(\n                    targetFile, binding.targetIconImage, binding.targetThumbnailImage,\n                    binding.targetAppIconBadgeImage, binding.targetBadgeImage,\n                    binding.targetDescriptionText\n                )\n                binding.sourceNameText.setText(\n                    if (isMerge) {\n                        R.string.file_job_merge_source_name\n                    } else {\n                        R.string.file_job_replace_source_name\n                    }\n                )\n                bindFileItem(\n                    sourceFile, binding.sourceIconImage, binding.sourceThumbnailImage,\n                    binding.sourceAppIconBadgeImage, binding.sourceBadgeImage,\n                    binding.sourceDescriptionText\n                )\n                binding.showNameLayout.setOnClickListener {\n                    val visible = !binding.nameLayout.isVisible\n                    binding.showNameArrowImage.animate()\n                        .rotation(if (visible) 90f else 0f)\n                        .setDuration(shortAnimTime.toLong())\n                        .setInterpolator(FastOutSlowInInterpolator())\n                        .start()\n                    binding.nameLayout.isVisible = visible\n                    if (visible) {\n                        binding.nameEdit.requestFocus()\n                        binding.nameEdit.showSoftInput()\n                    }\n                }\n                val targetFileName = targetFile.path.fileName.toString()\n                binding.nameEdit.setTextWithSelection(targetFileName)\n                binding.nameEdit.doAfterTextChanged {\n                    val hasNewName = hasNewName()\n                    binding.allCheck.isEnabled = !hasNewName\n                    if (hasNewName) {\n                        binding.allCheck.isChecked = false\n                    }\n                    val positiveButton = requireDialog()\n                        .requireViewByIdCompat<Button>(android.R.id.button1)\n                    positiveButton.setText(if (hasNewName) R.string.rename else positiveButtonRes)\n                }\n                binding.nameLayout.setEndIconOnClickListener {\n                    binding.nameEdit.setTextWithSelection(targetFileName)\n                }\n                if (savedInstanceState != null) {\n                    binding.allCheck.isChecked = savedInstanceState.getState<State>().isAllChecked\n                }\n            }\n            .setPositiveButton(positiveButtonRes, ::onDialogButtonClick)\n            .setNegativeButton(R.string.skip, ::onDialogButtonClick)\n            .setNeutralButton(android.R.string.cancel, ::onDialogButtonClick)\n            .create()\n            .apply {\n                setCanceledOnTouchOutside(false)\n                window!!.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)\n            }\n    }\n\n    /** @see me.zhanghai.android.files.filelist.FileListAdapter.onBindViewHolder */\n    private fun bindFileItem(\n        file: FileItem,\n        iconImage: ImageView,\n        thumbnailImage: ImageView,\n        appIconBadgeImage: ImageView,\n        badgeImage: ImageView,\n        descriptionText: TextView\n    ) {\n        val path = file.path\n        iconImage.apply {\n            isVisible = true\n            setImageResource(file.mimeType.iconRes)\n        }\n        val attributes = file.attributes\n        thumbnailImage.apply {\n            dispose()\n            setImageDrawable(null)\n            val supportsThumbnail = file.supportsThumbnail\n            isVisible = supportsThumbnail\n            if (supportsThumbnail) {\n                load(path to attributes) {\n                    listener { _, _ -> iconImage.isVisible = false }\n                }\n            }\n        }\n        appIconBadgeImage.apply {\n            dispose()\n            setImageDrawable(null)\n            val appDirectoryPackageName = file.appDirectoryPackageName\n            val hasAppIconBadge = appDirectoryPackageName != null\n            isVisible = hasAppIconBadge\n            if (hasAppIconBadge) {\n                load(AppIconPackageName(appDirectoryPackageName!!))\n            }\n        }\n        badgeImage.apply {\n            val badgeIconRes = if (file.attributesNoFollowLinks.isSymbolicLink) {\n                if (file.isSymbolicLinkBroken) {\n                    R.drawable.error_badge_icon_18dp\n                } else {\n                    R.drawable.symbolic_link_badge_icon_18dp\n                }\n            } else if (file.attributesNoFollowLinks.isEncrypted()) {\n                R.drawable.encrypted_badge_icon_18dp\n            } else {\n                null\n            }\n            val hasBadge = badgeIconRes != null\n            isVisible = hasBadge\n            if (hasBadge) {\n                setImageResource(badgeIconRes!!)\n            } else {\n                setImageDrawable(null)\n            }\n        }\n        val lastModificationTime = attributes.lastModifiedInstant\n            .formatShort(descriptionText.context)\n        val size = attributes.fileSize.formatHumanReadable(descriptionText.context)\n        val descriptionSeparator = getString(R.string.file_item_description_separator)\n        descriptionText.text = listOf(lastModificationTime, size).joinToString(descriptionSeparator)\n    }\n\n    private fun onDialogButtonClick(dialog: DialogInterface, which: Int) {\n        val action: FileJobConflictAction\n        val name: String?\n        val all: Boolean\n        when (which) {\n            DialogInterface.BUTTON_POSITIVE ->\n                if (hasNewName()) {\n                    action = FileJobConflictAction.RENAME\n                    name = binding.nameEdit.text.toString()\n                    all = false\n                } else {\n                    action = FileJobConflictAction.MERGE_OR_REPLACE\n                    name = null\n                    all = binding.allCheck.isChecked\n                }\n            DialogInterface.BUTTON_NEGATIVE -> {\n                action = FileJobConflictAction.SKIP\n                name = null\n                all = binding.allCheck.isChecked\n            }\n            DialogInterface.BUTTON_NEUTRAL -> {\n                action = FileJobConflictAction.CANCEL\n                name = null\n                all = false\n            }\n            else -> throw AssertionError(which)\n        }\n        notifyListenerOnce(action, name, all)\n        finish()\n    }\n\n    private fun hasNewName(): Boolean {\n        val name = binding.nameEdit.text.toString()\n        if (name.isEmpty()) {\n            return false\n        }\n        val fileName = args.targetFile.path.fileName.toString()\n        return name != fileName\n    }\n\n    override fun onStart() {\n        super.onStart()\n\n        if (binding.root.parent == null) {\n            val dialog = requireDialog() as AlertDialog\n            dialog.window!!.clearFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM)\n            val scrollView = dialog.requireViewByIdCompat<NestedScrollView>(R.id.scrollView)\n            val linearLayout = scrollView.getChildAt(0) as LinearLayout\n            linearLayout.addView(binding.root)\n        }\n    }\n\n    override fun onCancel(dialog: DialogInterface) {\n        super.onCancel(dialog)\n\n        notifyListenerOnce(FileJobConflictAction.CANCELED, null, false)\n        finish()\n    }\n\n    fun onFinish() {\n        notifyListenerOnce(FileJobConflictAction.CANCELED, null, false)\n    }\n\n    private fun notifyListenerOnce(action: FileJobConflictAction, name: String?, isAll: Boolean) {\n        if (isListenerNotified) {\n            return\n        }\n        args.listener(action, name, isAll)\n        isListenerNotified = true\n    }\n\n    companion object {\n        fun getTitle(sourceFile: FileItem, targetFile: FileItem, context: Context): String {\n            val titleRes = if (isMerge(sourceFile, targetFile)) {\n                R.string.file_job_merge_title_format\n            } else {\n                R.string.file_job_replace_title_format\n            }\n            return context.getString(titleRes, targetFile.path.fileName)\n        }\n\n        fun getMessage(\n            sourceFile: FileItem,\n            targetFile: FileItem,\n            type: CopyMoveType,\n            context: Context\n        ): String {\n            val messageRes = if (isMerge(sourceFile, targetFile)) {\n                type.getResourceId(\n                    R.string.file_job_merge_copy_message_format,\n                    R.string.file_job_merge_extract_message_format,\n                    R.string.file_job_merge_move_message_format\n                )\n            } else {\n                R.string.file_job_replace_message_format\n            }\n            return context.getString(messageRes, targetFile.path.parent.fileName)\n        }\n\n        private fun isMerge(sourceFile: FileItem, targetFile: FileItem): Boolean {\n            val sourceIsDirectory = sourceFile.attributesNoFollowLinks.isDirectory\n            val targetIsDirectory = targetFile.attributesNoFollowLinks.isDirectory\n            return sourceIsDirectory && targetIsDirectory\n        }\n    }\n\n    @Parcelize\n    class Args(\n        val sourceFile: FileItem,\n        val targetFile: FileItem,\n        val type: CopyMoveType,\n        val listener: @WriteWith<ListenerParceler>()\n        (FileJobConflictAction, String?, Boolean) -> Unit\n    ) : ParcelableArgs {\n        object ListenerParceler : Parceler<(FileJobConflictAction, String?, Boolean) -> Unit> {\n            override fun create(parcel: Parcel): (FileJobConflictAction, String?, Boolean) -> Unit =\n                parcel.readParcelable<RemoteCallback>()!!.let {\n                    { action, name, isAll ->\n                        it.sendResult(Bundle().putArgs(ListenerArgs(action, name, isAll)))\n                    }\n                }\n\n            override fun ((FileJobConflictAction, String?, Boolean) -> Unit).write(\n                parcel: Parcel,\n                flags: Int\n            ) {\n                parcel.writeParcelable(\n                    RemoteCallback {\n                        val args = it.getArgs<ListenerArgs>()\n                        this(args.action, args.name, args.isAll)\n                    }, flags\n                )\n            }\n\n            @Parcelize\n            private class ListenerArgs(\n                val action: FileJobConflictAction,\n                val name: String?,\n                val isAll: Boolean\n            ) : ParcelableArgs\n        }\n    }\n\n    @Parcelize\n    private class State(\n        val isAllChecked: Boolean\n    ) : ParcelableState\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filejob/FileJobErrorAction.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filejob\n\nenum class FileJobErrorAction {\n    POSITIVE,\n    NEGATIVE,\n    NEUTRAL,\n    CANCELED\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filejob/FileJobErrorDialogActivity.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filejob\n\nimport android.os.Bundle\nimport android.view.View\nimport androidx.fragment.app.commit\nimport me.zhanghai.android.files.app.AppActivity\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.putArgs\n\nclass FileJobErrorDialogActivity : AppActivity() {\n    private val args by args<FileJobErrorDialogFragment.Args>()\n\n    private lateinit var fragment: FileJobErrorDialogFragment\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        // Calls ensureSubDecor().\n        findViewById<View>(android.R.id.content)\n        if (savedInstanceState == null) {\n            fragment = FileJobErrorDialogFragment().putArgs(args)\n            supportFragmentManager.commit {\n                add(fragment, FileJobErrorDialogFragment::class.java.name)\n            }\n        } else {\n            fragment = supportFragmentManager.findFragmentByTag(\n                FileJobErrorDialogFragment::class.java.name\n            ) as FileJobErrorDialogFragment\n        }\n    }\n\n    override fun onDestroy() {\n        super.onDestroy()\n\n        if (isFinishing) {\n            fragment.onFinish()\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filejob/FileJobErrorDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filejob\n\nimport android.app.Dialog\nimport android.content.DialogInterface\nimport android.os.Bundle\nimport android.os.Parcel\nimport android.widget.LinearLayout\nimport androidx.appcompat.app.AlertDialog\nimport androidx.appcompat.app.AppCompatDialogFragment\nimport androidx.core.view.isVisible\nimport androidx.core.widget.NestedScrollView\nimport androidx.lifecycle.lifecycleScope\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport kotlinx.coroutines.launch\nimport kotlinx.parcelize.Parceler\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.compat.requireViewByIdCompat\nimport me.zhanghai.android.files.databinding.FileJobErrorDialogViewBinding\nimport me.zhanghai.android.files.provider.common.PosixFileStore\nimport me.zhanghai.android.files.util.ActionState\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.ParcelableParceler\nimport me.zhanghai.android.files.util.ParcelableState\nimport me.zhanghai.android.files.util.RemoteCallback\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.finish\nimport me.zhanghai.android.files.util.getArgs\nimport me.zhanghai.android.files.util.getState\nimport me.zhanghai.android.files.util.isReady\nimport me.zhanghai.android.files.util.isRunning\nimport me.zhanghai.android.files.util.layoutInflater\nimport me.zhanghai.android.files.util.putArgs\nimport me.zhanghai.android.files.util.putState\nimport me.zhanghai.android.files.util.readParcelable\nimport me.zhanghai.android.files.util.showToast\nimport me.zhanghai.android.files.util.viewModels\n\nclass FileJobErrorDialogFragment : AppCompatDialogFragment() {\n    private val args by args<Args>()\n\n    private val viewModel by viewModels { { FileJobErrorViewModel() } }\n\n    private lateinit var binding: FileJobErrorDialogViewBinding\n\n    private var isListenerNotified = false\n\n    override fun onSaveInstanceState(outState: Bundle) {\n        super.onSaveInstanceState(outState)\n\n        outState.putState(State(binding.allCheck.isChecked))\n    }\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =\n        MaterialAlertDialogBuilder(requireContext(), theme)\n            .setTitle(args.title)\n            .setMessage(args.message)\n            .apply {\n                binding = FileJobErrorDialogViewBinding.inflate(context.layoutInflater)\n                val hasReadOnlyFileStore = args.readOnlyFileStore != null\n                binding.remountButton.isVisible = hasReadOnlyFileStore\n                if (hasReadOnlyFileStore) {\n                    updateRemountButton()\n                    binding.remountButton.setOnClickListener { remount() }\n                }\n                binding.allSpace.isVisible = !hasReadOnlyFileStore && args.showAll\n                binding.allCheck.isVisible = args.showAll\n                if (savedInstanceState != null) {\n                    binding.allCheck.isChecked = savedInstanceState.getState<State>().isAllChecked\n                }\n\n                if (hasReadOnlyFileStore) {\n                    lifecycleScope.launchWhenStarted {\n                        launch { viewModel.remountState.collect { onRemountStateChanged(it) } }\n                    }\n                }\n            }\n            .setPositiveButton(args.positiveButtonText, ::onDialogButtonClick)\n            .setNegativeButton(args.negativeButtonText, ::onDialogButtonClick)\n            .setNeutralButton(args.neutralButtonText, ::onDialogButtonClick)\n            .create()\n            .apply { setCanceledOnTouchOutside(false) }\n\n    private fun remount() {\n        if (!viewModel.remountState.value.isReady || !args.readOnlyFileStore!!.isReadOnly) {\n            return\n        }\n        viewModel.remount(args.readOnlyFileStore!!)\n    }\n\n    private fun onRemountStateChanged(state: ActionState<PosixFileStore, Unit>) {\n        when (state) {\n            is ActionState.Ready, is ActionState.Running -> updateRemountButton()\n            is ActionState.Success -> viewModel.finishRemounting()\n            is ActionState.Error -> {\n                val throwable = state.throwable\n                throwable.printStackTrace()\n                showToast(throwable.toString())\n                viewModel.finishRemounting()\n            }\n        }\n    }\n\n    private fun updateRemountButton() {\n        val textRes = when {\n            viewModel.remountState.value.isRunning -> R.string.file_job_remount_loading_format\n            args.readOnlyFileStore!!.isReadOnly -> R.string.file_job_remount_format\n            else -> R.string.file_job_remount_success_format\n        }\n        binding.remountButton.text = getString(textRes, args.readOnlyFileStore!!.name())\n    }\n\n    private fun onDialogButtonClick(dialog: DialogInterface, which: Int) {\n        val action = when (which) {\n            DialogInterface.BUTTON_POSITIVE -> FileJobErrorAction.POSITIVE\n            DialogInterface.BUTTON_NEGATIVE -> FileJobErrorAction.NEGATIVE\n            DialogInterface.BUTTON_NEUTRAL -> FileJobErrorAction.NEUTRAL\n            else -> throw AssertionError(which)\n        }\n        notifyListenerOnce(action, args.showAll && binding.allCheck.isChecked)\n        finish()\n    }\n\n    override fun onStart() {\n        super.onStart()\n\n        if (binding.root.parent == null) {\n            val dialog = requireDialog() as AlertDialog\n            val scrollView = dialog.requireViewByIdCompat<NestedScrollView>(R.id.scrollView)\n            val linearLayout = scrollView.getChildAt(0) as LinearLayout\n            linearLayout.addView(binding.root)\n        }\n    }\n\n    override fun onCancel(dialog: DialogInterface) {\n        super.onCancel(dialog)\n\n        notifyListenerOnce(FileJobErrorAction.CANCELED, false)\n        finish()\n    }\n\n    fun onFinish() {\n        notifyListenerOnce(FileJobErrorAction.CANCELED, false)\n    }\n\n    private fun notifyListenerOnce(action: FileJobErrorAction, isAll: Boolean) {\n        if (isListenerNotified) {\n            return\n        }\n        args.listener(action, isAll)\n        isListenerNotified = true\n    }\n\n    @Parcelize\n    class Args(\n        val title: CharSequence,\n        val message: CharSequence,\n        val readOnlyFileStore: @WriteWith<ParcelableParceler> PosixFileStore?,\n        val showAll: Boolean,\n        val positiveButtonText: CharSequence?,\n        val negativeButtonText: CharSequence?,\n        val neutralButtonText: CharSequence?,\n        val listener: @WriteWith<ListenerParceler>() (FileJobErrorAction, Boolean) -> Unit\n    ) : ParcelableArgs {\n        object ListenerParceler : Parceler<(FileJobErrorAction, Boolean) -> Unit> {\n            override fun create(parcel: Parcel): (FileJobErrorAction, Boolean) -> Unit =\n                parcel.readParcelable<RemoteCallback>()!!.let {\n                    { action, isAll ->\n                        it.sendResult(Bundle().putArgs(ListenerArgs(action, isAll)))\n                    }\n                }\n\n            override fun ((FileJobErrorAction, Boolean) -> Unit).write(parcel: Parcel, flags: Int) {\n                parcel.writeParcelable(RemoteCallback {\n                    val args = it.getArgs<ListenerArgs>()\n                    this(args.action, args.isAll)\n                }, flags)\n            }\n\n            @Parcelize\n            private class ListenerArgs(\n                val action: FileJobErrorAction,\n                val isAll: Boolean\n            ) : ParcelableArgs\n        }\n    }\n\n    @Parcelize\n    private class State(\n        val isAllChecked: Boolean\n    ) : ParcelableState\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filejob/FileJobErrorViewModel.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filejob\n\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.asStateFlow\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.runInterruptible\nimport me.zhanghai.android.files.provider.common.PosixFileStore\nimport me.zhanghai.android.files.util.ActionState\nimport me.zhanghai.android.files.util.isFinished\nimport me.zhanghai.android.files.util.isReady\n\nclass FileJobErrorViewModel : ViewModel() {\n    private val _remountState =\n        MutableStateFlow<ActionState<PosixFileStore, Unit>>(ActionState.Ready())\n    val remountState = _remountState.asStateFlow()\n\n    fun remount(fileStore: PosixFileStore) {\n        viewModelScope.launch {\n            check(_remountState.value.isReady)\n            _remountState.value = ActionState.Running(fileStore)\n            _remountState.value = try {\n                runInterruptible(Dispatchers.IO) {\n                    fileStore.isReadOnly = false\n                }\n                ActionState.Success(fileStore, Unit)\n            } catch (e: Exception) {\n                ActionState.Error(fileStore, e)\n            }\n        }\n    }\n\n    fun finishRemounting() {\n        viewModelScope.launch {\n            check(_remountState.value.isFinished)\n            _remountState.value = ActionState.Ready()\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filejob/FileJobNotificationTemplate.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filejob\n\nimport androidx.core.app.NotificationCompat\nimport androidx.core.app.NotificationManagerCompat\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.util.NotificationChannelTemplate\nimport me.zhanghai.android.files.util.NotificationTemplate\n\nval fileJobNotificationTemplate =\n    NotificationTemplate(\n        NotificationChannelTemplate(\n            \"file_job\",\n            R.string.notification_channel_file_job_name,\n            NotificationManagerCompat.IMPORTANCE_LOW,\n            descriptionRes = R.string.notification_channel_file_job_description,\n            showBadge = false\n        ),\n        colorRes = R.color.color_primary,\n        smallIcon = R.drawable.notification_icon,\n        ongoing = true,\n        onlyAlertOnce = true,\n        category = NotificationCompat.CATEGORY_PROGRESS,\n        priority = NotificationCompat.PRIORITY_LOW\n    )\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filejob/FileJobReceiver.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filejob\n\nimport android.content.BroadcastReceiver\nimport android.content.Context\nimport android.content.Intent\nimport me.zhanghai.android.files.app.application\n\nclass FileJobReceiver : BroadcastReceiver() {\n    override fun onReceive(context: Context, intent: Intent) {\n        when (val action = intent.action) {\n            ACTION_CANCEL -> {\n                val jobId = intent.getIntExtra(EXTRA_JOB_ID, 0)\n                FileJobService.cancelJob(jobId)\n            }\n            else -> throw IllegalArgumentException(action)\n        }\n    }\n\n    companion object {\n        private const val ACTION_CANCEL = \"cancel\"\n\n        private const val EXTRA_JOB_ID = \"jobId\"\n\n        fun createIntent(jobId: Int): Intent =\n            Intent(application, FileJobReceiver::class.java)\n                .setAction(ACTION_CANCEL)\n                .putExtra(EXTRA_JOB_ID, jobId)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filejob/FileJobService.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filejob\n\nimport android.app.Service\nimport android.content.Context\nimport android.content.Intent\nimport android.os.IBinder\nimport androidx.annotation.MainThread\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.file.MimeType\nimport me.zhanghai.android.files.provider.common.PosixFileModeBit\nimport me.zhanghai.android.files.provider.common.PosixGroup\nimport me.zhanghai.android.files.provider.common.PosixUser\nimport me.zhanghai.android.files.util.ForegroundNotificationManager\nimport me.zhanghai.android.files.util.WakeWifiLock\nimport me.zhanghai.android.files.util.removeFirst\nimport java.util.concurrent.Executors\nimport java.util.concurrent.Future\nimport me.zhanghai.android.files.compat.removeFirstCompat\n\nclass FileJobService : Service() {\n    private lateinit var wakeWifiLock: WakeWifiLock\n\n    internal lateinit var notificationManager: ForegroundNotificationManager\n        private set\n\n    private val executorService = Executors.newCachedThreadPool()\n\n    private val runningJobs = mutableMapOf<FileJob, Future<*>>()\n\n    override fun onCreate() {\n        super.onCreate()\n\n        wakeWifiLock = WakeWifiLock(FileJobService::class.java.simpleName)\n        notificationManager = ForegroundNotificationManager(this)\n        instance = this\n\n        while (pendingJobs.isNotEmpty()) {\n            startJob(pendingJobs.removeFirstCompat())\n        }\n    }\n\n    override fun onBind(intent: Intent): IBinder? = null\n\n    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int = START_STICKY\n\n    private val jobCount: Int\n        get() = synchronized(runningJobs) { runningJobs.size }\n\n    private fun startJob(job: FileJob) {\n        // Synchronize on runningJobs to prevent a job from removing itself before being added.\n        synchronized(runningJobs) {\n            val future = executorService.submit {\n                job.runOn(this)\n                synchronized(runningJobs) {\n                    runningJobs.remove(job)\n                    updateWakeWifiLockLocked()\n                }\n            }\n            runningJobs[job] = future\n            updateWakeWifiLockLocked()\n        }\n    }\n\n    private fun cancelJob(id: Int) {\n        synchronized(runningJobs) {\n            runningJobs.removeFirst { it.key.id == id }?.value?.cancel(true)\n            updateWakeWifiLockLocked()\n        }\n    }\n\n    override fun onDestroy() {\n        super.onDestroy()\n\n        instance = null\n\n        synchronized(runningJobs) {\n            while (runningJobs.isNotEmpty()) {\n                runningJobs.removeFirst().value.cancel(true)\n            }\n            updateWakeWifiLockLocked()\n        }\n    }\n\n    // Synchronize on runningJobs to avoid the potential race condition that the lock is\n    // acquired after all jobs are finished in a very short time.\n    private fun updateWakeWifiLockLocked() {\n        wakeWifiLock.isAcquired = jobCount > 0\n    }\n\n    companion object {\n        private var instance: FileJobService? = null\n\n        private val pendingJobs = mutableListOf<FileJob>()\n\n        val runningJobCount: Int\n            @MainThread\n            get() = instance?.jobCount ?: 0\n\n        @MainThread\n        private fun startJob(job: FileJob, context: Context) {\n            val instance = instance\n            if (instance != null) {\n                instance.startJob(job)\n            } else {\n                pendingJobs.add(job)\n                context.startService(Intent(context, FileJobService::class.java))\n            }\n        }\n\n        fun archive(\n            sources: List<Path>,\n            archiveFile: Path,\n            format: Int,\n            filter: Int,\n            password: String?,\n            context: Context\n        ) {\n            startJob(ArchiveFileJob(sources, archiveFile, format, filter, password), context)\n        }\n\n        fun copy(sources: List<Path>, targetDirectory: Path, context: Context) {\n            startJob(CopyFileJob(sources, targetDirectory), context)\n        }\n\n        fun create(path: Path, createDirectory: Boolean, context: Context) {\n            startJob(CreateFileJob(path, createDirectory), context)\n        }\n\n        fun delete(paths: List<Path>, context: Context) {\n            startJob(DeleteFileJob(paths), context)\n        }\n\n        fun move(sources: List<Path>, targetDirectory: Path, context: Context) {\n            startJob(MoveFileJob(sources, targetDirectory), context)\n        }\n\n        fun installApk(file: Path, context: Context) {\n            startJob(InstallApkJob(file), context)\n        }\n\n        fun open(file: Path, mimeType: MimeType, withChooser: Boolean, context: Context) {\n            startJob(OpenFileJob(file, mimeType, withChooser), context)\n        }\n\n        fun rename(path: Path, newName: String, context: Context) {\n            startJob(RenameFileJob(path, newName), context)\n        }\n\n        fun restoreSeLinuxContext(path: Path, recursive: Boolean, context: Context) {\n            startJob(RestoreFileSeLinuxContextJob(path, recursive), context)\n        }\n\n        fun save(source: Path, target: Path, context: Context) {\n            startJob(SaveFileJob(source, target), context)\n        }\n\n        fun setGroup(path: Path, group: PosixGroup, recursive: Boolean, context: Context) {\n            startJob(SetFileGroupJob(path, group, recursive), context)\n        }\n\n        fun setMode(\n            path: Path,\n            mode: Set<PosixFileModeBit>,\n            recursive: Boolean,\n            uppercaseX: Boolean,\n            context: Context\n        ) {\n            startJob(SetFileModeJob(path, mode, recursive, uppercaseX), context)\n        }\n\n        fun setOwner(path: Path, owner: PosixUser, recursive: Boolean, context: Context) {\n            startJob(SetFileOwnerJob(path, owner, recursive), context)\n        }\n\n        fun setSeLinuxContext(\n            path: Path,\n            seLinuxContext: String,\n            recursive: Boolean,\n            context: Context\n        ) {\n            startJob(SetFileSeLinuxContextJob(path, seLinuxContext, recursive), context)\n        }\n\n        fun write(\n            file: Path,\n            content: ByteArray,\n            context: Context,\n            listener: ((Boolean) -> Unit)?\n        ) {\n            startJob(WriteFileJob(file, content, listener), context)\n        }\n\n        @MainThread\n        fun cancelJob(id: Int) {\n            pendingJobs.removeFirst { it.id == id }\n            instance?.cancelJob(id)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filejob/FileJobs.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filejob\n\nimport android.app.PendingIntent\nimport android.content.Intent\nimport android.net.Uri\nimport android.os.Build\nimport android.os.Environment\nimport android.widget.Toast\nimport androidx.annotation.AnyRes\nimport androidx.annotation.PluralsRes\nimport androidx.annotation.StringRes\nimport java8.nio.file.CopyOption\nimport java8.nio.file.DirectoryIteratorException\nimport java8.nio.file.FileAlreadyExistsException\nimport java8.nio.file.FileVisitResult\nimport java8.nio.file.FileVisitor\nimport java8.nio.file.Files\nimport java8.nio.file.LinkOption\nimport java8.nio.file.Path\nimport java8.nio.file.Paths\nimport java8.nio.file.SimpleFileVisitor\nimport java8.nio.file.StandardCopyOption\nimport java8.nio.file.StandardOpenOption\nimport java8.nio.file.attribute.BasicFileAttributes\nimport kotlinx.coroutines.runBlocking\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.app.BackgroundActivityStarter\nimport me.zhanghai.android.files.app.mainExecutor\nimport me.zhanghai.android.files.compat.mainExecutorCompat\nimport me.zhanghai.android.files.file.FileItem\nimport me.zhanghai.android.files.file.MimeType\nimport me.zhanghai.android.files.file.asFileSize\nimport me.zhanghai.android.files.file.fileProviderUri\nimport me.zhanghai.android.files.file.loadFileItem\nimport me.zhanghai.android.files.filelist.OpenFileAsDialogActivity\nimport me.zhanghai.android.files.filelist.OpenFileAsDialogFragment\nimport me.zhanghai.android.files.provider.archive.archiveFile\nimport me.zhanghai.android.files.provider.archive.archiver.ArchiveWriter\nimport me.zhanghai.android.files.provider.archive.createArchiveRootPath\nimport me.zhanghai.android.files.provider.archive.isArchivePath\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.ByteStringBuilder\nimport me.zhanghai.android.files.provider.common.InvalidFileNameException\nimport me.zhanghai.android.files.provider.common.PosixFileModeBit\nimport me.zhanghai.android.files.provider.common.PosixFileStore\nimport me.zhanghai.android.files.provider.common.PosixGroup\nimport me.zhanghai.android.files.provider.common.PosixPrincipal\nimport me.zhanghai.android.files.provider.common.PosixUser\nimport me.zhanghai.android.files.provider.common.ProgressCopyOption\nimport me.zhanghai.android.files.provider.common.ReadOnlyFileSystemException\nimport me.zhanghai.android.files.provider.common.UserActionRequiredException\nimport me.zhanghai.android.files.provider.common.asByteStringListPath\nimport me.zhanghai.android.files.provider.common.copyTo\nimport me.zhanghai.android.files.provider.common.createDirectories\nimport me.zhanghai.android.files.provider.common.createDirectory\nimport me.zhanghai.android.files.provider.common.createFile\nimport me.zhanghai.android.files.provider.common.delete\nimport me.zhanghai.android.files.provider.common.deleteIfExists\nimport me.zhanghai.android.files.provider.common.exists\nimport me.zhanghai.android.files.provider.common.getFileStore\nimport me.zhanghai.android.files.provider.common.getMode\nimport me.zhanghai.android.files.provider.common.getPath\nimport me.zhanghai.android.files.provider.common.isDirectory\nimport me.zhanghai.android.files.provider.common.moveTo\nimport me.zhanghai.android.files.provider.common.newByteChannel\nimport me.zhanghai.android.files.provider.common.newDirectoryStream\nimport me.zhanghai.android.files.provider.common.newOutputStream\nimport me.zhanghai.android.files.provider.common.readAttributes\nimport me.zhanghai.android.files.provider.common.resolveForeign\nimport me.zhanghai.android.files.provider.common.restoreSeLinuxContext\nimport me.zhanghai.android.files.provider.common.setGroup\nimport me.zhanghai.android.files.provider.common.setMode\nimport me.zhanghai.android.files.provider.common.setOwner\nimport me.zhanghai.android.files.provider.common.setSeLinuxContext\nimport me.zhanghai.android.files.provider.common.toByteString\nimport me.zhanghai.android.files.provider.common.toModeString\nimport me.zhanghai.android.files.provider.linux.isLinuxPath\nimport me.zhanghai.android.files.util.asFileName\nimport me.zhanghai.android.files.util.createInstallPackageIntent\nimport me.zhanghai.android.files.util.createIntent\nimport me.zhanghai.android.files.util.createViewIntent\nimport me.zhanghai.android.files.util.extraPath\nimport me.zhanghai.android.files.util.getQuantityString\nimport me.zhanghai.android.files.util.putArgs\nimport me.zhanghai.android.files.util.showToast\nimport me.zhanghai.android.files.util.toEnumSet\nimport me.zhanghai.android.files.util.withChooser\nimport java.io.ByteArrayInputStream\nimport java.io.File\nimport java.io.IOException\nimport java.io.InterruptedIOException\nimport kotlin.coroutines.resume\nimport kotlin.coroutines.suspendCoroutine\n\nfun FileJob.getString(@StringRes stringRes: Int): String {\n    return service.getString(stringRes)\n}\n\nfun FileJob.getString(@StringRes stringRes: Int, vararg formatArguments: Any?): String {\n    return service.getString(stringRes, *formatArguments)\n}\n\nfun FileJob.getQuantityString(@PluralsRes pluralRes: Int, quantity: Int): String {\n    return service.getQuantityString(pluralRes, quantity)\n}\n\nfun FileJob.getQuantityString(\n    @PluralsRes pluralRes: Int,\n    quantity: Int,\n    vararg formatArguments: Any?\n): String {\n    return service.getQuantityString(pluralRes, quantity, *formatArguments)\n}\n\nprivate fun FileJob.postNotification(\n    title: CharSequence,\n    text: CharSequence?,\n    subText: CharSequence?,\n    info: CharSequence?,\n    max: Int,\n    progress: Int,\n    indeterminate: Boolean,\n    showCancel: Boolean\n) {\n    val notification = fileJobNotificationTemplate.createBuilder(service).apply {\n        setContentTitle(title)\n        setContentText(text)\n        setSubText(subText)\n        setContentInfo(info)\n        setProgress(max, progress, indeterminate)\n        // TODO\n        //setContentIntent()\n        if (showCancel) {\n            val intent = FileJobReceiver.createIntent(id)\n            var pendingIntentFlags = PendingIntent.FLAG_UPDATE_CURRENT\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n                pendingIntentFlags = pendingIntentFlags or PendingIntent.FLAG_IMMUTABLE\n            }\n            val pendingIntent = PendingIntent.getBroadcast(\n                service, id + 1, intent, pendingIntentFlags\n            )\n            addAction(\n                R.drawable.close_icon_white_24dp, getString(android.R.string.cancel), pendingIntent\n            )\n        }\n    }.build()\n    service.notificationManager.notify(id, notification)\n}\n\nprivate const val PROGRESS_INTERVAL_MILLIS = 200L\n\nprivate const val NOTIFICATION_INTERVAL_MILLIS = 500L\n\nprivate fun FileJob.showToast(textRes: Int, duration: Int = Toast.LENGTH_SHORT) {\n    service.mainExecutorCompat.execute {\n        service.showToast(textRes, duration)\n    }\n}\n\nprivate fun FileJob.showToast(text: CharSequence, duration: Int = Toast.LENGTH_SHORT) {\n    service.mainExecutorCompat.execute {\n        service.showToast(text, duration)\n    }\n}\n\nprivate fun FileJob.getFileName(path: Path): String =\n    if (path.isAbsolute && path.nameCount == 0) {\n        path.fileSystem.separator\n    } else {\n        path.fileName.toString()\n    }\n\nprivate fun FileJob.getTargetFileName(source: Path): Path {\n    if (source.isArchivePath) {\n        val archiveFile = source.archiveFile.asByteStringListPath()\n        val archiveRoot = archiveFile.createArchiveRootPath()\n        if (source == archiveRoot) {\n            return archiveFile.fileSystem.getPath(\n                archiveFile.fileNameByteString!!.asFileName().baseName\n            )\n        }\n    }\n    return source.fileName\n}\n\n// The attributes for start path prefers following links, but falls back to not following.\n// FileVisitResult returned from visitor may be ignored and always considered CONTINUE.\n@Throws(IOException::class)\nprivate fun FileJob.walkFileTreeForSettingAttributes(\n    start: Path,\n    recursive: Boolean,\n    visitor: FileVisitor<in Path>\n): Path {\n    val attributes = try {\n        start.readAttributes(BasicFileAttributes::class.java)\n    } catch (ignored: IOException) {\n        try {\n            start.readAttributes(BasicFileAttributes::class.java, LinkOption.NOFOLLOW_LINKS)\n        } catch (e: IOException) {\n            visitor.visitFileFailed(start, e)\n            return start\n        }\n    }\n    if (!recursive || !attributes.isDirectory) {\n        visitor.visitFile(start, attributes)\n        return start\n    }\n    val directoryStream = try {\n        start.newDirectoryStream()\n    } catch (e: IOException) {\n        visitor.visitFileFailed(start, e)\n        return start\n    }\n    directoryStream.use {\n        visitor.preVisitDirectory(start, attributes)\n        try {\n            directoryStream.forEach { Files.walkFileTree(it, visitor) }\n        } catch (e: DirectoryIteratorException) {\n            visitor.postVisitDirectory(start, e.cause)\n            return start\n        }\n    }\n    visitor.postVisitDirectory(start, null)\n    return start\n}\n\n@Throws(InterruptedIOException::class)\nprivate fun FileJob.throwIfInterrupted() {\n    if (Thread.interrupted()) {\n        throw InterruptedIOException()\n    }\n}\n\n@Throws(IOException::class)\nprivate fun FileJob.scan(sources: List<Path?>, @PluralsRes notificationTitleRes: Int): ScanInfo {\n    val scanInfo = ScanInfo()\n    for (source in sources) {\n        Files.walkFileTree(source, object : SimpleFileVisitor<Path>() {\n            @Throws(IOException::class)\n            override fun preVisitDirectory(\n                directory: Path,\n                attributes: BasicFileAttributes\n            ): FileVisitResult {\n                scanPath(attributes, scanInfo, notificationTitleRes)\n                throwIfInterrupted()\n                return FileVisitResult.CONTINUE\n            }\n\n            @Throws(IOException::class)\n            override fun visitFile(file: Path, attributes: BasicFileAttributes): FileVisitResult {\n                scanPath(attributes, scanInfo, notificationTitleRes)\n                throwIfInterrupted()\n                return FileVisitResult.CONTINUE\n            }\n\n            @Throws(IOException::class)\n            override fun visitFileFailed(file: Path, exception: IOException): FileVisitResult {\n                // TODO: Prompt retry, skip, skip-all or abort.\n                return super.visitFileFailed(file, exception)\n            }\n        })\n    }\n    postScanNotification(scanInfo, notificationTitleRes)\n    return scanInfo\n}\n\n@Throws(IOException::class)\nprivate fun FileJob.scan(source: Path, @PluralsRes notificationTitleRes: Int): ScanInfo {\n    return scan(listOf(source), notificationTitleRes)\n}\n\n@Throws(IOException::class)\nprivate fun FileJob.scan(\n    source: Path,\n    recursive: Boolean,\n    @PluralsRes notificationTitleRes: Int\n): ScanInfo {\n    if (recursive) {\n        return scan(source, notificationTitleRes)\n    }\n    val scanInfo = ScanInfo()\n    val attributes = source.readAttributes(\n        BasicFileAttributes::class.java, LinkOption.NOFOLLOW_LINKS\n    )\n    scanPath(attributes, scanInfo, notificationTitleRes)\n    throwIfInterrupted()\n    return scanInfo\n}\n\nprivate fun FileJob.scanPath(\n    attributes: BasicFileAttributes,\n    scanInfo: ScanInfo,\n    @PluralsRes notificationTitleRes: Int\n) {\n    scanInfo.incrementFileCount()\n    scanInfo.addToSize(attributes.size())\n    postScanNotification(scanInfo, notificationTitleRes)\n}\n\nprivate fun FileJob.postScanNotification(scanInfo: ScanInfo, @PluralsRes titleRes: Int) {\n    if (!scanInfo.shouldPostNotification()) {\n        return\n    }\n    val size = scanInfo.size.asFileSize().formatHumanReadable(service)\n    val fileCount: Int = scanInfo.fileCount\n    val title: String = getQuantityString(titleRes, fileCount, fileCount, size)\n    postNotification(title, null, null, null, 0, 0, true, true)\n}\n\nprivate class ScanInfo {\n    var fileCount = 0\n        private set\n    var size = 0L\n        private set\n\n    private var lastNotificationTimeMillis = 0L\n\n    fun incrementFileCount() {\n        ++fileCount\n    }\n\n    fun addToSize(size: Long) {\n        this.size += size\n    }\n\n    fun shouldPostNotification(): Boolean {\n        val currentTimeMillis = System.currentTimeMillis()\n        return if (fileCount % 100 == 0\n            || lastNotificationTimeMillis + NOTIFICATION_INTERVAL_MILLIS < currentTimeMillis) {\n            lastNotificationTimeMillis = currentTimeMillis\n            true\n        } else {\n            false\n        }\n    }\n}\n\nprivate fun FileJob.postTransferSizeNotification(\n    transferInfo: TransferInfo,\n    currentSource: Path,\n    @StringRes titleOneRes: Int,\n    @PluralsRes titleMultipleRes: Int\n) {\n    if (!transferInfo.shouldPostNotification()) {\n        return\n    }\n    val title: String\n    val text: String\n    val fileCount = transferInfo.fileCount\n    val target = transferInfo.target!!\n    val size = transferInfo.size\n    val transferredSize = transferInfo.transferredSize\n    if (fileCount == 1) {\n        title = getString(titleOneRes, getFileName(currentSource), getFileName(target))\n        val sizeString = size.asFileSize().formatHumanReadable(service)\n        val transferredSizeString = transferredSize.asFileSize().formatHumanReadable(service)\n        text = getString(\n            R.string.file_job_transfer_size_notification_text_one_format, transferredSizeString,\n            sizeString\n        )\n    } else {\n        title = getQuantityString(titleMultipleRes, fileCount, fileCount, getFileName(target))\n        val currentFileIndex = (transferInfo.transferredFileCount + 1)\n            .coerceAtMost(fileCount)\n        text = getString(\n            R.string.file_job_transfer_size_notification_text_multiple_format, currentFileIndex,\n            fileCount\n        )\n    }\n    val max: Int\n    val progress: Int\n    if (size <= Int.MAX_VALUE) {\n        max = size.toInt()\n        progress = transferredSize.toInt()\n    } else {\n        var maxLong = size\n        var progressLong = transferredSize\n        while (maxLong > Int.MAX_VALUE) {\n            maxLong /= 2\n            progressLong /= 2\n        }\n        max = maxLong.toInt()\n        progress = progressLong.toInt()\n    }\n    postNotification(title, text, null, null, max, progress, false, true)\n}\n\nprivate fun FileJob.postTransferCountNotification(\n    transferInfo: TransferInfo,\n    currentPath: Path,\n    @StringRes titleOneRes: Int,\n    @PluralsRes titleMultipleRes: Int\n) {\n    if (!transferInfo.shouldPostNotification()) {\n        return\n    }\n    val title: String\n    val text: String?\n    val max: Int\n    val progress: Int\n    val indeterminate: Boolean\n    val fileCount = transferInfo.fileCount\n    if (fileCount == 1) {\n        title = getString(titleOneRes, getFileName(currentPath))\n        text = null\n        max = 0\n        progress = 0\n        indeterminate = true\n    } else {\n        title = getQuantityString(titleMultipleRes, fileCount, fileCount)\n        val transferredFileCount = transferInfo.transferredFileCount\n        val currentFileIndex = (transferredFileCount + 1).coerceAtMost(fileCount)\n        text = getString(\n            R.string.file_job_transfer_count_notification_text_multiple_format, currentFileIndex,\n            fileCount\n        )\n        max = fileCount\n        progress = transferredFileCount\n        indeterminate = false\n    }\n    postNotification(title, text, null, null, max, progress, indeterminate, true)\n}\n\nprivate class TransferInfo(scanInfo: ScanInfo, val target: Path?) {\n    var fileCount: Int = scanInfo.fileCount\n        private set\n    var transferredFileCount = 0\n        private set\n    var size: Long = scanInfo.size\n        private set\n    var transferredSize = 0L\n        private set\n\n    private var lastNotificationTimeMillis = 0L\n\n    fun incrementTransferredFileCount() {\n        ++transferredFileCount\n    }\n\n    fun addTransferredFile(path: Path) {\n        ++transferredFileCount\n        try {\n            transferredSize += path.readAttributes(\n                BasicFileAttributes::class.java, LinkOption.NOFOLLOW_LINKS\n            ).size()\n        } catch (e: IOException) {\n            e.printStackTrace()\n        }\n    }\n\n    fun skipFile(path: Path) {\n        --fileCount\n        try {\n            size -= path.readAttributes(\n                BasicFileAttributes::class.java, LinkOption.NOFOLLOW_LINKS\n            ).size()\n        } catch (e: IOException) {\n            e.printStackTrace()\n        }\n    }\n\n    fun skipFileIgnoringSize() {\n        --fileCount\n    }\n\n    fun addToTransferredSize(size: Long) {\n        transferredSize += size\n    }\n\n    fun shouldPostNotification(): Boolean {\n        val currentTimeMillis = System.currentTimeMillis()\n        return if (lastNotificationTimeMillis + NOTIFICATION_INTERVAL_MILLIS < currentTimeMillis) {\n            lastNotificationTimeMillis = currentTimeMillis\n            true\n        } else {\n            false\n        }\n    }\n}\n\n// TODO: Make invalid file name, remount etc user actions as well.\n@Throws(InterruptedIOException::class)\nprivate fun FileJob.showUserAction(exception: UserActionRequiredException): Boolean =\n    try {\n        runBlocking {\n            suspendCoroutine { continuation ->\n                val userAction = exception.getUserAction(continuation, service)\n                BackgroundActivityStarter.startActivity(\n                    userAction.intent, userAction.title, userAction.message, service\n                )\n            }\n        }\n    } catch (e: InterruptedException) {\n        throw InterruptedIOException().apply { initCause(e) }\n    }\n\n@Throws(InterruptedIOException::class)\nprivate fun FileJob.showErrorDialog(\n    title: CharSequence,\n    message: CharSequence,\n    readOnlyFileStore: PosixFileStore?,\n    showAll: Boolean,\n    positiveButtonText: CharSequence?,\n    negativeButtonText: CharSequence?,\n    neutralButtonText: CharSequence?\n): ErrorResult =\n    try {\n        runBlocking {\n            suspendCoroutine { continuation ->\n                BackgroundActivityStarter.startActivity(\n                    FileJobErrorDialogActivity::class.createIntent().putArgs(\n                        FileJobErrorDialogFragment.Args(\n                            title, message, readOnlyFileStore, showAll, positiveButtonText,\n                            negativeButtonText, neutralButtonText\n                        ) { action, isAll ->\n                            continuation.resume(ErrorResult(action, isAll))\n                        }\n                    ), title, message, service\n                )\n            }\n        }\n    } catch (e: InterruptedException) {\n        throw InterruptedIOException().apply { initCause(e) }\n    }\n\nprivate fun FileJob.getReadOnlyFileStore(path: Path, exception: IOException): PosixFileStore? {\n    if (exception !is ReadOnlyFileSystemException || !path.isLinuxPath) {\n        return null\n    }\n    val fileStore = try {\n        path.getFileStore() as PosixFileStore\n    } catch (e: IOException) {\n        e.printStackTrace()\n        return null\n    }\n    return if (fileStore.isReadOnly) fileStore else null\n}\n\nprivate class ErrorResult(\n    val action: FileJobErrorAction,\n    val isAll: Boolean\n)\n\n@Throws(IOException::class)\nprivate fun FileJob.showConflictDialog(\n    sourceFile: FileItem,\n    targetFile: FileItem,\n    type: CopyMoveType\n): ConflictResult =\n    try {\n        runBlocking {\n            suspendCoroutine { continuation ->\n                BackgroundActivityStarter.startActivity(\n                    FileJobConflictDialogActivity::class.createIntent().putArgs(\n                        FileJobConflictDialogFragment.Args(\n                            sourceFile, targetFile, type\n                        ) { action, name, all ->\n                            continuation.resume(ConflictResult(action, name, all))\n                        }\n                    ), FileJobConflictDialogFragment.getTitle(sourceFile, targetFile, service),\n                    FileJobConflictDialogFragment.getMessage(sourceFile, targetFile, type, service),\n                    service\n                )\n            }\n        }\n    } catch (e: InterruptedException) {\n        throw InterruptedIOException().apply { initCause(e) }\n    }\n\nenum class CopyMoveType {\n    COPY,\n    EXTRACT,\n    MOVE\n}\n\nfun CopyMoveType.getResourceId(\n    @AnyRes copyRes: Int,\n    @AnyRes extractRes: Int,\n    @AnyRes moveRes: Int\n): Int =\n    when (this) {\n        CopyMoveType.COPY -> copyRes\n        CopyMoveType.EXTRACT -> extractRes\n        CopyMoveType.MOVE -> moveRes\n    }\n\nprivate class ConflictResult(\n    val action: FileJobConflictAction,\n    val name: String?,\n    val isAll: Boolean\n)\n\nprivate class ActionAllInfo(\n    var skipCopyMoveIntoItself: Boolean = false,\n    var skipCopyMoveOverItself: Boolean = false,\n    var merge: Boolean = false,\n    var replace: Boolean = false,\n    var skipMerge: Boolean = false,\n    var skipReplace: Boolean = false,\n    var skipCopyMoveError: Boolean = false,\n    var skipDeleteError: Boolean = false,\n    var skipRestoreSeLinuxContextError: Boolean = false,\n    var skipSetGroupError: Boolean = false,\n    var skipSetOwnerError: Boolean = false,\n    var skipSetModeError: Boolean = false,\n    var skipSetSeLinuxContextError: Boolean = false\n)\n\nclass ArchiveFileJob(\n    private val sources: List<Path>,\n    private val archiveFile: Path,\n    private val format: Int,\n    private val filter: Int,\n    private val password: String?\n) : FileJob() {\n    @Throws(IOException::class)\n    override fun run() {\n        val scanInfo = scan(sources, R.plurals.file_job_archive_scan_notification_title_format)\n        val channel = archiveFile.newByteChannel(\n            StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE\n        )\n        var successful = false\n        try {\n            channel.use {\n                ArchiveWriter(channel, format, filter, password).use { writer ->\n                    val transferInfo = TransferInfo(scanInfo, archiveFile)\n                    for (source in sources) {\n                        val target = getTargetFileName(source)\n                        archiveRecursively(source, writer, target, transferInfo)\n                        throwIfInterrupted()\n                    }\n                }\n            }\n            successful = true\n        } finally {\n            if (!successful) {\n                try {\n                    archiveFile.deleteIfExists()\n                } catch (e: IOException) {\n                    e.printStackTrace()\n                } catch (e: UnsupportedOperationException) {\n                    e.printStackTrace()\n                }\n            }\n        }\n    }\n\n    @Throws(IOException::class)\n    private fun archiveRecursively(\n        source: Path,\n        writer: ArchiveWriter,\n        target: Path,\n        transferInfo: TransferInfo\n    ) {\n        Files.walkFileTree(source, object : SimpleFileVisitor<Path>() {\n            @Throws(IOException::class)\n            override fun preVisitDirectory(\n                directory: Path,\n                attributes: BasicFileAttributes\n            ): FileVisitResult {\n                val directoryInTarget = target.resolveForeign(source.relativize(directory))\n                archive(directory, writer, directoryInTarget, archiveFile, transferInfo)\n                throwIfInterrupted()\n                return FileVisitResult.CONTINUE\n            }\n\n            @Throws(IOException::class)\n            override fun visitFile(file: Path, attributes: BasicFileAttributes): FileVisitResult {\n                val fileInTarget = target.resolveForeign(source.relativize(file))\n                archive(file, writer, fileInTarget, archiveFile, transferInfo)\n                throwIfInterrupted()\n                return FileVisitResult.CONTINUE\n            }\n\n            @Throws(IOException::class)\n            override fun visitFileFailed(file: Path, exception: IOException): FileVisitResult {\n                // TODO: Prompt retry, skip, skip-all or abort.\n                return super.visitFileFailed(file, exception)\n            }\n        })\n    }\n}\n\n@Throws(IOException::class)\nprivate fun FileJob.archive(\n    file: Path,\n    writer: ArchiveWriter,\n    entryName: Path,\n    archiveFile: Path,\n    transferInfo: TransferInfo\n) {\n    try {\n        postArchiveNotification(transferInfo, file)\n        writer.write(file, entryName, PROGRESS_INTERVAL_MILLIS) {\n            transferInfo.addToTransferredSize(it)\n            postArchiveNotification(transferInfo, file)\n        }\n        transferInfo.incrementTransferredFileCount()\n        postArchiveNotification(transferInfo, file)\n    } catch (e: InterruptedIOException) {\n        throw e\n    } catch (e: IOException) {\n        e.printStackTrace()\n        val result = showErrorDialog(\n            getString(R.string.file_job_archive_error_title_format, getFileName(file)),\n            getString(\n                R.string.file_job_archive_error_message_format, getFileName(archiveFile),\n                e.toString()\n            ),\n            getReadOnlyFileStore(archiveFile, e),\n            false,\n            null,\n            getString(android.R.string.cancel),\n            null\n        )\n        when (result.action) {\n            FileJobErrorAction.NEGATIVE, FileJobErrorAction.CANCELED ->\n                throw InterruptedIOException()\n            else -> throw AssertionError(result.action)\n        }\n    }\n}\n\nprivate fun FileJob.postArchiveNotification(transferInfo: TransferInfo, currentFile: Path) {\n    postTransferSizeNotification(\n        transferInfo, currentFile, R.string.file_job_archive_notification_title_one_format,\n        R.plurals.file_job_archive_notification_title_multiple_format\n    )\n}\n\nclass CopyFileJob(private val sources: List<Path>, private val targetDirectory: Path) : FileJob() {\n    @Throws(IOException::class)\n    override fun run() {\n        val isExtract = sources.all { it.isArchivePath }\n        val scanInfo = scan(\n            sources, if (isExtract) {\n                R.plurals.file_job_extract_scan_notification_title_format\n            } else {\n                R.plurals.file_job_copy_scan_notification_title_format\n            }\n        )\n        val transferInfo = TransferInfo(scanInfo, targetDirectory)\n        val actionAllInfo = ActionAllInfo()\n        for (source in sources) {\n            val target = if (source.parent == targetDirectory) {\n                getTargetPathForDuplicate(source)\n            } else {\n                targetDirectory.resolveForeign(getTargetFileName(source))\n            }\n            copyRecursively(source, target, isExtract, transferInfo, actionAllInfo)\n            throwIfInterrupted()\n        }\n    }\n\n    @Throws(IOException::class)\n    private fun copyRecursively(\n        source: Path,\n        target: Path,\n        isExtract: Boolean,\n        transferInfo: TransferInfo,\n        actionAllInfo: ActionAllInfo\n    ) {\n        Files.walkFileTree(source, object : SimpleFileVisitor<Path>() {\n            @Throws(IOException::class)\n            override fun preVisitDirectory(\n                directory: Path,\n                attributes: BasicFileAttributes\n            ): FileVisitResult {\n                val directoryInTarget = target.resolveForeign(source.relativize(directory))\n                val copied = copy(\n                    directory, directoryInTarget, isExtract, transferInfo, actionAllInfo\n                )\n                throwIfInterrupted()\n                return if (copied) FileVisitResult.CONTINUE else FileVisitResult.SKIP_SUBTREE\n            }\n\n            @Throws(IOException::class)\n            override fun visitFile(file: Path, attributes: BasicFileAttributes): FileVisitResult {\n                val fileInTarget = target.resolveForeign(source.relativize(file))\n                copy(file, fileInTarget, isExtract, transferInfo, actionAllInfo)\n                throwIfInterrupted()\n                return FileVisitResult.CONTINUE\n            }\n\n            @Throws(IOException::class)\n            override fun visitFileFailed(file: Path, exception: IOException): FileVisitResult {\n                // TODO: Prompt retry, skip, skip-all or abort.\n                return super.visitFileFailed(file, exception)\n            }\n        })\n    }\n\n    private fun getTargetPathForDuplicate(source: Path): Path {\n        source.asByteStringListPath()\n        val sourceFileName = source.fileNameByteString!!\n        // We do want to follow symbolic links here.\n        val countEndIndex = if (source.isDirectory()) {\n            sourceFileName.length\n        } else {\n            sourceFileName.asFileName().baseName.length\n        }\n        val countInfo = getDuplicateCountInfo(sourceFileName, countEndIndex)\n        var i = countInfo.count + 1\n        while (i > 0) {\n            val targetFileName = setDuplicateCount(sourceFileName, countInfo, i)\n            val target = source.resolveSibling(targetFileName)\n            if (!target.exists(LinkOption.NOFOLLOW_LINKS)) {\n                return target\n            }\n            ++i\n        }\n        // Just leave it to conflict handling logic.\n        return source\n    }\n\n    private fun getDuplicateCountInfo(fileName: ByteString, countEnd: Int): DuplicateCountInfo {\n        while (true) {\n            // /(?<=.) \\(\\d+\\)$/\n            var index = countEnd - 1\n            // \\)\n            if (index < 0 || fileName[index] != ')'.code.toByte()) {\n                break\n            }\n            --index\n            // \\d+\n            val digitsEndInclusive = index\n            while (index >= 0) {\n                val b = fileName[index]\n                if (b < '0'.code.toByte() || b > '9'.code.toByte()) {\n                    break\n                }\n                --index\n            }\n            if (index == digitsEndInclusive) {\n                break\n            }\n            val countString = fileName.substring(index + 1, digitsEndInclusive + 1).toString()\n            val count = try {\n                countString.toInt()\n            } catch (e: NumberFormatException) {\n                break\n            }\n            // \\(\n            if (index < 0 || fileName[index] != '('.code.toByte()) {\n                break\n            }\n            --index\n            //\n            if (index < 0 || fileName[index] != ' '.code.toByte()) {\n                break\n            }\n            // (?<=.)\n            if (index == 0) {\n                break\n            }\n            return DuplicateCountInfo(index, countEnd, count)\n        }\n        return DuplicateCountInfo(countEnd, countEnd, 0)\n    }\n\n    private fun setDuplicateCount(\n        fileName: ByteString,\n        countInfo: DuplicateCountInfo,\n        count: Int\n    ): ByteString {\n        return ByteStringBuilder(fileName.substring(0, countInfo.countStart))\n            .append(\" ($count)\".toByteString())\n            .append(fileName.substring(countInfo.countEnd))\n            .toByteString()\n    }\n\n    private class DuplicateCountInfo(val countStart: Int, val countEnd: Int, val count: Int)\n}\n\n@Throws(IOException::class)\nprivate fun FileJob.copy(\n    source: Path,\n    target: Path,\n    isExtract: Boolean,\n    transferInfo: TransferInfo,\n    actionAllInfo: ActionAllInfo\n): Boolean =\n    copyOrMove(\n        source, target, if (isExtract) CopyMoveType.EXTRACT else CopyMoveType.COPY, true, false,\n        transferInfo, actionAllInfo\n    )\n\nclass CreateFileJob(private val path: Path, private val createDirectory: Boolean) : FileJob() {\n    @Throws(IOException::class)\n    override fun run() {\n        create(path, createDirectory)\n    }\n}\n\n@Throws(IOException::class)\nprivate fun FileJob.create(path: Path, createDirectory: Boolean) {\n    var retry: Boolean\n    do {\n        retry = false\n        try {\n            if (createDirectory) {\n                path.createDirectory()\n            } else {\n                path.createFile()\n            }\n        } catch (e: InterruptedIOException) {\n            throw e\n        } catch (e: IOException) {\n            e.printStackTrace()\n            if (e is UserActionRequiredException) {\n                val result = showUserAction(e)\n                if (result) {\n                    retry = true\n                    continue\n                }\n            }\n            val result = showErrorDialog(\n                getString(R.string.file_job_create_error_title),\n                getString(\n                    R.string.file_job_create_error_message_format, getFileName(path), e.toString()\n                ),\n                getReadOnlyFileStore(path, e),\n                false,\n                getString(R.string.retry),\n                getString(android.R.string.cancel),\n                null\n            )\n            when (result.action) {\n                FileJobErrorAction.POSITIVE -> {\n                    retry = true\n                    continue\n                }\n                FileJobErrorAction.NEGATIVE, FileJobErrorAction.CANCELED ->\n                    throw InterruptedIOException()\n                else -> throw AssertionError(result.action)\n            }\n        }\n    } while (retry)\n}\n\nclass DeleteFileJob(private val paths: List<Path>) : FileJob() {\n    @Throws(IOException::class)\n    override fun run() {\n        val scanInfo = scan(paths, R.plurals.file_job_delete_scan_notification_title_format)\n        val transferInfo = TransferInfo(scanInfo, null)\n        val actionAllInfo = ActionAllInfo()\n        for (path in paths) {\n            deleteRecursively(path, transferInfo, actionAllInfo)\n            throwIfInterrupted()\n        }\n    }\n\n    @Throws(IOException::class)\n    private fun deleteRecursively(\n        path: Path,\n        transferInfo: TransferInfo,\n        actionAllInfo: ActionAllInfo\n    ) {\n        Files.walkFileTree(path, object : SimpleFileVisitor<Path>() {\n            @Throws(IOException::class)\n            override fun visitFile(file: Path, attributes: BasicFileAttributes): FileVisitResult {\n                delete(file, transferInfo, actionAllInfo)\n                throwIfInterrupted()\n                return FileVisitResult.CONTINUE\n            }\n\n            @Throws(IOException::class)\n            override fun visitFileFailed(file: Path, exception: IOException): FileVisitResult {\n                // TODO: Prompt retry, skip, skip-all or abort.\n                return super.visitFileFailed(file, exception)\n            }\n\n            @Throws(IOException::class)\n            override fun postVisitDirectory(\n                directory: Path,\n                exception: IOException?\n            ): FileVisitResult {\n                // TODO: Prompt retry, skip, skip-all or abort.\n                if (exception != null) {\n                    throw exception\n                }\n                delete(directory, transferInfo, actionAllInfo)\n                throwIfInterrupted()\n                return FileVisitResult.CONTINUE\n            }\n        })\n    }\n}\n\n@Throws(IOException::class)\nprivate fun FileJob.delete(path: Path, transferInfo: TransferInfo?, actionAllInfo: ActionAllInfo) {\n    var retry: Boolean\n    do {\n        retry = false\n        try {\n            path.delete()\n            if (transferInfo != null) {\n                transferInfo.incrementTransferredFileCount()\n                postDeleteNotification(transferInfo, path)\n            }\n        } catch (e: InterruptedIOException) {\n            throw e\n        } catch (e: IOException) {\n            e.printStackTrace()\n            if (actionAllInfo.skipDeleteError) {\n                if (transferInfo != null) {\n                    transferInfo.skipFileIgnoringSize()\n                    postDeleteNotification(transferInfo, path)\n                }\n                return\n            }\n            if (e is UserActionRequiredException) {\n                val result = showUserAction(e)\n                if (result) {\n                    retry = true\n                    continue\n                }\n            }\n            val result = showErrorDialog(\n                getString(R.string.file_job_delete_error_title),\n                getString(\n                    R.string.file_job_delete_error_message_format, getFileName(path), e.toString()\n                ),\n                getReadOnlyFileStore(path, e),\n                true,\n                getString(R.string.retry),\n                getString(R.string.skip),\n                getString(android.R.string.cancel)\n            )\n            when (result.action) {\n                FileJobErrorAction.POSITIVE -> {\n                    retry = true\n                    continue\n                }\n                FileJobErrorAction.NEGATIVE -> {\n                    if (result.isAll) {\n                        actionAllInfo.skipDeleteError = true\n                    }\n                    if (transferInfo != null) {\n                        transferInfo.skipFileIgnoringSize()\n                        postDeleteNotification(transferInfo, path)\n                    }\n                    return\n                }\n                FileJobErrorAction.CANCELED -> {\n                    if (transferInfo != null) {\n                        transferInfo.skipFileIgnoringSize()\n                        postDeleteNotification(transferInfo, path)\n                    }\n                    return\n                }\n                FileJobErrorAction.NEUTRAL -> throw InterruptedIOException()\n                else -> throw AssertionError(result.action)\n            }\n        }\n    } while (retry)\n}\n\nprivate fun FileJob.postDeleteNotification(transferInfo: TransferInfo, currentPath: Path) {\n    postTransferCountNotification(\n        transferInfo, currentPath, R.string.file_job_delete_notification_title_one_format,\n        R.plurals.file_job_delete_notification_title_multiple_format\n    )\n}\n\nclass MoveFileJob(private val sources: List<Path>, private val targetDirectory: Path) : FileJob() {\n    @Throws(IOException::class)\n    override fun run() {\n        val sourcesToMove = mutableListOf<Path>()\n        for (source in sources) {\n            val target = targetDirectory.resolveForeign(source.fileName)\n            try {\n                moveAtomically(source, target)\n            } catch (e: InterruptedIOException) {\n                throw e\n            } catch (e: IOException) {\n                sourcesToMove.add(source)\n            }\n            throwIfInterrupted()\n        }\n        val scanInfo = scan(sourcesToMove, R.plurals.file_job_move_scan_notification_title_format)\n        val transferInfo = TransferInfo(scanInfo, targetDirectory)\n        val actionAllInfo = ActionAllInfo()\n        for (source in sourcesToMove) {\n            val target = targetDirectory.resolveForeign(source.fileName)\n            moveRecursively(source, target, transferInfo, actionAllInfo)\n            throwIfInterrupted()\n        }\n    }\n\n    @Throws(IOException::class)\n    private fun moveRecursively(\n        source: Path,\n        target: Path,\n        transferInfo: TransferInfo,\n        actionAllInfo: ActionAllInfo\n    ) {\n        Files.walkFileTree(source, object : SimpleFileVisitor<Path>() {\n            @Throws(IOException::class)\n            override fun preVisitDirectory(\n                directory: Path,\n                attributes: BasicFileAttributes\n            ): FileVisitResult {\n                val directoryInTarget = target.resolveForeign(source.relativize(directory))\n                try {\n                    moveAtomically(directory, directoryInTarget)\n                    throwIfInterrupted()\n                    return FileVisitResult.SKIP_SUBTREE\n                } catch (e: InterruptedIOException) {\n                    throw e\n                } catch (e: IOException) {\n                    e.printStackTrace()\n                }\n                val copied = copyForMove(directory, directoryInTarget, transferInfo, actionAllInfo)\n                throwIfInterrupted()\n                return if (copied) FileVisitResult.CONTINUE else FileVisitResult.SKIP_SUBTREE\n            }\n\n            @Throws(IOException::class)\n            override fun visitFile(file: Path, attributes: BasicFileAttributes): FileVisitResult {\n                val fileInTarget = target.resolveForeign(source.relativize(file))\n                try {\n                    moveAtomically(file, fileInTarget)\n                    throwIfInterrupted()\n                    return FileVisitResult.CONTINUE\n                } catch (e: InterruptedIOException) {\n                    throw e\n                } catch (e: IOException) {\n                    e.printStackTrace()\n                }\n                moveByCopy(file, fileInTarget, transferInfo, actionAllInfo)\n                throwIfInterrupted()\n                return FileVisitResult.CONTINUE\n            }\n\n            @Throws(IOException::class)\n            override fun visitFileFailed(file: Path, exception: IOException): FileVisitResult {\n                // TODO: Prompt retry, skip, skip-all or abort.\n                return super.visitFileFailed(file, exception)\n            }\n\n            @Throws(IOException::class)\n            override fun postVisitDirectory(\n                directory: Path,\n                exception: IOException?\n            ): FileVisitResult? {\n                if (exception != null) {\n                    throw exception\n                }\n                delete(directory, null, actionAllInfo)\n                throwIfInterrupted()\n                return FileVisitResult.CONTINUE\n            }\n        })\n    }\n}\n\n@Throws(IOException::class)\nprivate fun FileJob.copyForMove(\n    source: Path,\n    target: Path,\n    transferInfo: TransferInfo,\n    actionAllInfo: ActionAllInfo\n): Boolean = copyOrMove(source, target, CopyMoveType.MOVE, true, true, transferInfo, actionAllInfo)\n\n@Throws(IOException::class)\nprivate fun FileJob.moveAtomically(source: Path, target: Path) {\n    source.moveTo(target, LinkOption.NOFOLLOW_LINKS, StandardCopyOption.ATOMIC_MOVE)\n}\n\n@Throws(IOException::class)\nprivate fun FileJob.moveByCopy(\n    source: Path,\n    target: Path,\n    transferInfo: TransferInfo,\n    actionAllInfo: ActionAllInfo\n): Boolean =\n    copyOrMove(source, target, CopyMoveType.MOVE, false, true, transferInfo, actionAllInfo)\n\n// @see https://github.com/GNOME/nautilus/blob/master/src/nautilus-file-operations.c copy_move_file\n@Throws(IOException::class)\nprivate fun FileJob.copyOrMove(\n    source: Path,\n    target: Path,\n    type: CopyMoveType,\n    useCopy: Boolean,\n    copyAttributes: Boolean,\n    transferInfo: TransferInfo,\n    actionAllInfo: ActionAllInfo\n): Boolean {\n    val targetParent = target.parent\n    if (targetParent.startsWith(source)) {\n        // Don't allow copy/move into the source itself.\n        if (actionAllInfo.skipCopyMoveIntoItself) {\n            transferInfo.skipFile(source)\n            postCopyMoveNotification(transferInfo, source, type)\n            return false\n        }\n        val result = showErrorDialog(\n            getString(\n                type.getResourceId(\n                    R.string.file_job_cannot_copy_into_itself_title,\n                    R.string.file_job_cannot_extract_into_itself_title,\n                    R.string.file_job_cannot_move_into_itself_title\n                )\n            ),\n            getString(R.string.file_job_cannot_copy_move_into_itself_message),\n            null,\n            true,\n            getString(R.string.skip),\n            getString(android.R.string.cancel),\n            null\n        )\n        return when (result.action) {\n            FileJobErrorAction.POSITIVE -> {\n                if (result.isAll) {\n                    actionAllInfo.skipCopyMoveIntoItself = true\n                }\n                transferInfo.skipFile(source)\n                postCopyMoveNotification(transferInfo, source, type)\n                false\n            }\n            FileJobErrorAction.CANCELED -> {\n                transferInfo.skipFile(source)\n                postCopyMoveNotification(transferInfo, source, type)\n                false\n            }\n            FileJobErrorAction.NEGATIVE -> throw InterruptedIOException()\n            else -> throw AssertionError(result.action)\n        }\n    }\n    if (source.startsWith(target)) {\n        // Don't allow copy/move over the source itself or its ancestors.\n        if (actionAllInfo.skipCopyMoveOverItself) {\n            transferInfo.skipFile(source)\n            postCopyMoveNotification(transferInfo, source, type)\n            return false\n        }\n        val result = showErrorDialog(\n            getString(\n                type.getResourceId(\n                    R.string.file_job_cannot_copy_over_itself_title,\n                    R.string.file_job_cannot_extract_over_itself_title,\n                    R.string.file_job_cannot_move_over_itself_title\n                )\n            ),\n            getString(R.string.file_job_cannot_copy_move_over_itself_message),\n            null,\n            true,\n            getString(R.string.skip),\n            getString(android.R.string.cancel),\n            null\n        )\n        return when (result.action) {\n            FileJobErrorAction.POSITIVE -> {\n                if (result.isAll) {\n                    actionAllInfo.skipCopyMoveOverItself = true\n                }\n                transferInfo.skipFile(source)\n                postCopyMoveNotification(transferInfo, source, type)\n                false\n            }\n            FileJobErrorAction.CANCELED -> {\n                transferInfo.skipFile(source)\n                postCopyMoveNotification(transferInfo, source, type)\n                false\n            }\n            FileJobErrorAction.NEGATIVE -> throw InterruptedIOException()\n            else -> throw AssertionError(result.action)\n        }\n    }\n    var target = target\n    var replaceExisting = false\n    var retry: Boolean\n    do {\n        retry = false\n        val options = mutableListOf<CopyOption>().apply {\n            this += LinkOption.NOFOLLOW_LINKS\n            if (copyAttributes) {\n                this += StandardCopyOption.COPY_ATTRIBUTES\n            }\n            if (replaceExisting) {\n                this += StandardCopyOption.REPLACE_EXISTING\n            }\n            this += ProgressCopyOption(PROGRESS_INTERVAL_MILLIS) {\n                transferInfo.addToTransferredSize(it)\n                postCopyMoveNotification(transferInfo, source, type)\n            }\n        }.toTypedArray()\n        try {\n            postCopyMoveNotification(transferInfo, source, type)\n            if (useCopy) {\n                source.copyTo(target, *options)\n            } else {\n                source.moveTo(target, *options)\n            }\n            transferInfo.incrementTransferredFileCount()\n            postCopyMoveNotification(transferInfo, source, type)\n        } catch (e: FileAlreadyExistsException) {\n            val sourceFile = source.loadFileItem()\n            val targetFile = target.loadFileItem()\n            val sourceIsDirectory = sourceFile.attributesNoFollowLinks.isDirectory\n            val targetIsDirectory = targetFile.attributesNoFollowLinks.isDirectory\n            if (!sourceIsDirectory && targetIsDirectory) {\n                // TODO: Don't allow replace directory with file.\n                throw e\n            }\n            val isMerge = sourceIsDirectory && targetIsDirectory\n            if (isMerge && actionAllInfo.merge) {\n                transferInfo.addTransferredFile(target)\n                postCopyMoveNotification(transferInfo, source, type)\n                return true\n            } else if (!isMerge && actionAllInfo.replace) {\n                replaceExisting = true\n                retry = true\n                continue\n            } else if ((isMerge && actionAllInfo.skipMerge)\n                || (!isMerge && actionAllInfo.skipReplace)) {\n                transferInfo.skipFile(source)\n                postCopyMoveNotification(transferInfo, source, type)\n                return false\n            }\n            val result = showConflictDialog(sourceFile, targetFile, type)\n            return when (result.action) {\n                FileJobConflictAction.MERGE_OR_REPLACE -> {\n                    if (result.isAll) {\n                        if (isMerge) {\n                            actionAllInfo.merge = true\n                        } else {\n                            actionAllInfo.replace = true\n                        }\n                    }\n                    if (isMerge) {\n                        transferInfo.addTransferredFile(target)\n                        postCopyMoveNotification(transferInfo, source, type)\n                        true\n                    } else {\n                        replaceExisting = true\n                        retry = true\n                        continue\n                    }\n                }\n                FileJobConflictAction.RENAME -> {\n                    target = target.resolveSibling(result.name)\n                    retry = true\n                    continue\n                }\n                FileJobConflictAction.SKIP -> {\n                    if (result.isAll) {\n                        if (isMerge) {\n                            actionAllInfo.skipMerge = true\n                        } else {\n                            actionAllInfo.skipReplace = true\n                        }\n                    }\n                    transferInfo.skipFile(source)\n                    postCopyMoveNotification(transferInfo, source, type)\n                    false\n                }\n                FileJobConflictAction.CANCELED -> {\n                    transferInfo.skipFile(source)\n                    postCopyMoveNotification(transferInfo, source, type)\n                    false\n                }\n                FileJobConflictAction.CANCEL -> throw InterruptedIOException()\n            }\n        } catch (e: InvalidFileNameException) {\n            // TODO: Prompt invalid name.\n            if (false) {\n                retry = true\n                continue\n            }\n            throw e\n        } catch (e: InterruptedIOException) {\n            throw e\n        } catch (e: IOException) {\n            e.printStackTrace()\n            if (actionAllInfo.skipCopyMoveError) {\n                transferInfo.skipFile(source)\n                postCopyMoveNotification(transferInfo, source, type)\n                return false\n            }\n            if (e is UserActionRequiredException) {\n                val result = showUserAction(e)\n                if (result) {\n                    retry = true\n                    continue\n                }\n            }\n            val result = showErrorDialog(\n                getString(\n                    type.getResourceId(\n                        R.string.file_job_copy_error_title_format,\n                        R.string.file_job_extract_error_title_format,\n                        R.string.file_job_move_error_title_format\n                    ), getFileName(source)\n                ),\n                getString(\n                    type.getResourceId(\n                        R.string.file_job_copy_error_message_format,\n                        R.string.file_job_extract_error_message_format,\n                        R.string.file_job_move_error_message_format\n                    ), getFileName(targetParent), e.toString()\n                ),\n                getReadOnlyFileStore(target, e),\n                true,\n                getString(R.string.retry),\n                getString(R.string.skip),\n                getString(android.R.string.cancel)\n            )\n            return when (result.action) {\n                FileJobErrorAction.POSITIVE -> {\n                    retry = true\n                    continue\n                }\n                FileJobErrorAction.NEGATIVE -> {\n                    if (result.isAll) {\n                        actionAllInfo.skipCopyMoveError = true\n                    }\n                    transferInfo.skipFile(source)\n                    postCopyMoveNotification(transferInfo, source, type)\n                    false\n                }\n                FileJobErrorAction.CANCELED -> {\n                    transferInfo.skipFile(source)\n                    postCopyMoveNotification(transferInfo, source, type)\n                    false\n                }\n                FileJobErrorAction.NEUTRAL -> throw InterruptedIOException()\n            }\n        }\n    } while (retry)\n    return true\n}\n\nprivate fun FileJob.postCopyMoveNotification(\n    transferInfo: TransferInfo,\n    currentSource: Path,\n    type: CopyMoveType\n) {\n    postTransferSizeNotification(\n        transferInfo, currentSource, type.getResourceId(\n            R.string.file_job_copy_notification_title_one_format,\n            R.string.file_job_extract_notification_title_one_format,\n            R.string.file_job_move_notification_title_one_format\n        ), type.getResourceId(\n            R.plurals.file_job_copy_notification_title_multiple_format,\n            R.plurals.file_job_extract_notification_title_multiple_format,\n            R.plurals.file_job_move_notification_title_multiple_format\n        )\n    )\n}\n\nclass InstallApkJob(private val file: Path) : FileJob() {\n    override fun run() {\n        open(\n            file, R.string.file_install_apk_from_background_title_format,\n            R.string.file_install_apk_from_background_text\n        ) { file ->\n            val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n                file.fileProviderUri\n            } else {\n                // PackageInstaller only supports file URI before N.\n                Uri.fromFile(file.toFile())\n            }\n            uri.createInstallPackageIntent()\n        }\n    }\n}\n\nclass OpenFileJob(\n    private val file: Path,\n    private val mimeType: MimeType,\n    private val withChooser: Boolean\n) : FileJob() {\n    override fun run() {\n        open(\n            file, R.string.file_open_from_background_title_format,\n            R.string.file_open_from_background_text\n        ) { file ->\n            file.fileProviderUri.createViewIntent(mimeType)\n                .addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)\n                .apply { extraPath = file }\n                .let {\n                    if (withChooser) {\n                        it.withChooser(\n                            OpenFileAsDialogActivity::class.createIntent()\n                                .putArgs(OpenFileAsDialogFragment.Args(file))\n                        )\n                    } else {\n                        it\n                    }\n                }\n        }\n    }\n}\n\nprivate val FileJob.cacheDirectory: File\n    get() =\n        service.externalCacheDir?.takeIf {\n            Environment.getExternalStorageState(it) == Environment.MEDIA_MOUNTED\n        } ?: service.cacheDir\n\n@Throws(IOException::class)\nprivate fun FileJob.open(\n    file: Path,\n    @StringRes notificationTitleFormatRes: Int,\n    @StringRes notificationTextRes: Int,\n    intentCreator: (Path) -> Intent\n) {\n    val isExtract = file.isArchivePath\n    val scanInfo = scan(\n        file, if (isExtract) {\n            R.plurals.file_job_extract_scan_notification_title_format\n        } else {\n            R.plurals.file_job_copy_scan_notification_title_format\n        }\n    )\n    val cacheDirectory = Paths.get(cacheDirectory.path, \"open_cache\")\n    cacheDirectory.createDirectories()\n    val targetFileName = getTargetFileName(file)\n    val targetFile = cacheDirectory.resolveForeign(targetFileName)\n    val transferInfo = TransferInfo(scanInfo, cacheDirectory)\n    val actionAllInfo = ActionAllInfo(replace = true)\n    val copied = copy(file, targetFile, isExtract, transferInfo, actionAllInfo)\n    if (!copied) {\n        return\n    }\n    BackgroundActivityStarter.startActivity(\n        intentCreator(targetFile), getString(notificationTitleFormatRes, targetFileName),\n        getString(notificationTextRes), service\n    )\n}\n\nclass RenameFileJob(private val path: Path, private val newName: String) : FileJob() {\n    @Throws(IOException::class)\n    override fun run() {\n        val newPath = path.resolveSibling(newName)\n        rename(path, newPath)\n    }\n}\n\n@Throws(IOException::class)\nprivate fun FileJob.rename(path: Path, newPath: Path) {\n    var retry: Boolean\n    do {\n        retry = false\n        try {\n            moveAtomically(path, newPath)\n        } catch (e: InterruptedIOException) {\n            throw e\n        } catch (e: IOException) {\n            e.printStackTrace()\n            if (e is UserActionRequiredException) {\n                val result = showUserAction(e)\n                if (result) {\n                    retry = true\n                    continue\n                }\n            }\n            val result = showErrorDialog(\n                getString(R.string.file_job_rename_error_title_format, getFileName(path)),\n                getString(\n                    R.string.file_job_rename_error_message_format, getFileName(newPath),\n                    e.toString()\n                ),\n                getReadOnlyFileStore(path, e),\n                false,\n                getString(R.string.retry),\n                getString(android.R.string.cancel),\n                null\n            )\n            when (result.action) {\n                FileJobErrorAction.POSITIVE -> {\n                    retry = true\n                    continue\n                }\n                FileJobErrorAction.NEGATIVE, FileJobErrorAction.CANCELED ->\n                    throw InterruptedIOException()\n                else -> throw AssertionError(result.action)\n            }\n        }\n    } while (retry)\n}\n\nclass RestoreFileSeLinuxContextJob(\n    private val path: Path,\n    private val recursive: Boolean\n) : FileJob() {\n    @Throws(IOException::class)\n    override fun run() {\n        val scanInfo = scan(\n            path, recursive,\n            R.plurals.file_job_restore_selinux_context_scan_notification_title_format\n        )\n        val transferInfo = TransferInfo(scanInfo, null)\n        val actionAllInfo = ActionAllInfo()\n        walkFileTreeForSettingAttributes(path, recursive, object : SimpleFileVisitor<Path>() {\n            @Throws(IOException::class)\n            override fun preVisitDirectory(\n                directory: Path,\n                attributes: BasicFileAttributes\n            ): FileVisitResult = visitFile(directory, attributes)\n\n            @Throws(IOException::class)\n            override fun visitFile(file: Path, attributes: BasicFileAttributes): FileVisitResult {\n                restoreSeLinuxContext(file, !attributes.isSymbolicLink, transferInfo, actionAllInfo)\n                throwIfInterrupted()\n                return FileVisitResult.CONTINUE\n            }\n\n            @Throws(IOException::class)\n            override fun visitFileFailed(file: Path, exception: IOException): FileVisitResult {\n                // TODO: Prompt retry, skip, skip-all or abort.\n                return super.visitFileFailed(file, exception)\n            }\n\n            @Throws(IOException::class)\n            override fun postVisitDirectory(\n                directory: Path,\n                exception: IOException?\n            ): FileVisitResult {\n                // TODO: Prompt retry, skip, skip-all or abort.\n                return super.postVisitDirectory(directory, exception)\n            }\n        })\n    }\n}\n\n@Throws(IOException::class)\nprivate fun FileJob.restoreSeLinuxContext(\n    path: Path,\n    followLinks: Boolean,\n    transferInfo: TransferInfo,\n    actionAllInfo: ActionAllInfo\n) {\n    var retry: Boolean\n    do {\n        retry = false\n        try {\n            val options = if (followLinks) arrayOf() else arrayOf(LinkOption.NOFOLLOW_LINKS)\n            path.restoreSeLinuxContext(*options)\n            transferInfo.incrementTransferredFileCount()\n            postRestoreSeLinuxContextNotification(transferInfo, path)\n        } catch (e: InterruptedIOException) {\n            throw e\n        } catch (e: IOException) {\n            e.printStackTrace()\n            if (actionAllInfo.skipRestoreSeLinuxContextError) {\n                transferInfo.skipFileIgnoringSize()\n                postRestoreSeLinuxContextNotification(transferInfo, path)\n                return\n            }\n            if (e is UserActionRequiredException) {\n                val result = showUserAction(e)\n                if (result) {\n                    retry = true\n                    continue\n                }\n            }\n            val result = showErrorDialog(\n                getString(R.string.file_job_restore_selinux_context_error_title),\n                getString(\n                    R.string.file_job_restore_selinux_context_error_message_format,\n                    getFileName(path), e.toString()\n                ),\n                getReadOnlyFileStore(path, e),\n                true,\n                getString(R.string.retry),\n                getString(R.string.skip),\n                getString(android.R.string.cancel)\n            )\n            when (result.action) {\n                FileJobErrorAction.POSITIVE -> {\n                    retry = true\n                    continue\n                }\n                FileJobErrorAction.NEGATIVE -> {\n                    if (result.isAll) {\n                        actionAllInfo.skipRestoreSeLinuxContextError = true\n                    }\n                    transferInfo.skipFileIgnoringSize()\n                    postRestoreSeLinuxContextNotification(transferInfo, path)\n                    return\n                }\n                FileJobErrorAction.CANCELED -> {\n                    transferInfo.skipFileIgnoringSize()\n                    postRestoreSeLinuxContextNotification(transferInfo, path)\n                    return\n                }\n                FileJobErrorAction.NEUTRAL -> throw InterruptedIOException()\n                else -> throw AssertionError(result.action)\n            }\n        }\n    } while (retry)\n}\n\nprivate fun FileJob.postRestoreSeLinuxContextNotification(\n    transferInfo: TransferInfo,\n    currentPath: Path\n) {\n    postTransferCountNotification(\n        transferInfo, currentPath,\n        R.string.file_job_restore_selinux_context_notification_title_one_format,\n        R.plurals.file_job_restore_selinux_context_notification_title_multiple_format\n    )\n}\n\nclass SaveFileJob(private val source: Path, private val target: Path) : FileJob() {\n    override fun run() {\n        save(source, target)\n    }\n}\n\n@Throws(IOException::class)\nprivate fun FileJob.save(source: Path, target: Path) {\n    val scanInfo = scan(source, R.plurals.file_job_copy_scan_notification_title_format)\n    val targetParent = target.parent\n    val transferInfo = TransferInfo(scanInfo, targetParent)\n    val actionAllInfo = ActionAllInfo(replace = true)\n    val copied = copy(source, target, false, transferInfo, actionAllInfo)\n    if (!copied) {\n        return\n    }\n    showToast(\n        getString(R.string.save_as_success_format, getFileName(target), getFileName(targetParent))\n    )\n}\n\nclass SetFileGroupJob(\n    private val path: Path,\n    private val group: PosixGroup,\n    private val recursive: Boolean\n) : FileJob() {\n    @Throws(IOException::class)\n    override fun run() {\n        val scanInfo = scan(\n            path, recursive, R.plurals.file_job_set_group_scan_notification_title_format\n        )\n        val transferInfo = TransferInfo(scanInfo, null)\n        val actionAllInfo = ActionAllInfo()\n        walkFileTreeForSettingAttributes(path, recursive, object : SimpleFileVisitor<Path>() {\n            @Throws(IOException::class)\n            override fun preVisitDirectory(\n                directory: Path,\n                attributes: BasicFileAttributes\n            ): FileVisitResult = visitFile(directory, attributes)\n\n            @Throws(IOException::class)\n            override fun visitFile(file: Path, attributes: BasicFileAttributes): FileVisitResult {\n                setGroup(file, group, !attributes.isSymbolicLink, transferInfo, actionAllInfo)\n                throwIfInterrupted()\n                return FileVisitResult.CONTINUE\n            }\n\n            @Throws(IOException::class)\n            override fun visitFileFailed(file: Path, exception: IOException): FileVisitResult {\n                // TODO: Prompt retry, skip, skip-all or abort.\n                return super.visitFileFailed(file, exception)\n            }\n\n            @Throws(IOException::class)\n            override fun postVisitDirectory(\n                directory: Path,\n                exception: IOException?\n            ): FileVisitResult {\n                // TODO: Prompt retry, skip, skip-all or abort.\n                return super.postVisitDirectory(directory, exception)\n            }\n        })\n    }\n}\n\n@Throws(IOException::class)\nprivate fun FileJob.setGroup(\n    path: Path,\n    group: PosixGroup,\n    followLinks: Boolean,\n    transferInfo: TransferInfo,\n    actionAllInfo: ActionAllInfo\n) {\n    var retry: Boolean\n    do {\n        retry = false\n        try {\n            val options = if (followLinks) arrayOf() else arrayOf(LinkOption.NOFOLLOW_LINKS)\n            path.setGroup(group, *options)\n            transferInfo.incrementTransferredFileCount()\n            postSetGroupNotification(transferInfo, path)\n        } catch (e: InterruptedIOException) {\n            throw e\n        } catch (e: IOException) {\n            e.printStackTrace()\n            if (actionAllInfo.skipSetGroupError) {\n                transferInfo.skipFileIgnoringSize()\n                postSetGroupNotification(transferInfo, path)\n                return\n            }\n            if (e is UserActionRequiredException) {\n                val result = showUserAction(e)\n                if (result) {\n                    retry = true\n                    continue\n                }\n            }\n            val result = showErrorDialog(\n                getString(R.string.file_job_set_group_error_title_format, getFileName(path)),\n                getString(\n                    R.string.file_job_set_group_error_message_format, getPrincipalName(group),\n                    e.toString()\n                ),\n                getReadOnlyFileStore(path, e),\n                true,\n                getString(R.string.retry),\n                getString(R.string.skip),\n                getString(android.R.string.cancel)\n            )\n            when (result.action) {\n                FileJobErrorAction.POSITIVE -> {\n                    retry = true\n                    continue\n                }\n                FileJobErrorAction.NEGATIVE -> {\n                    if (result.isAll) {\n                        actionAllInfo.skipSetGroupError = true\n                    }\n                    transferInfo.skipFileIgnoringSize()\n                    postSetGroupNotification(transferInfo, path)\n                    return\n                }\n                FileJobErrorAction.CANCELED -> {\n                    transferInfo.skipFileIgnoringSize()\n                    postSetGroupNotification(transferInfo, path)\n                    return\n                }\n                FileJobErrorAction.NEUTRAL -> throw InterruptedIOException()\n                else -> throw AssertionError(result.action)\n            }\n        }\n    } while (retry)\n}\n\nprivate fun FileJob.postSetGroupNotification(transferInfo: TransferInfo, currentPath: Path) {\n    postTransferCountNotification(\n        transferInfo, currentPath, R.string.file_job_set_group_notification_title_one_format,\n        R.plurals.file_job_set_group_notification_title_multiple_format\n    )\n}\n\nclass SetFileModeJob(\n    private val path: Path,\n    private val mode: Set<PosixFileModeBit>,\n    private val recursive: Boolean,\n    private val uppercaseX: Boolean\n) : FileJob() {\n    @Throws(IOException::class)\n    override fun run() {\n        val scanInfo = scan(\n            path, recursive, R.plurals.file_job_set_mode_scan_notification_title_format\n        )\n        val transferInfo = TransferInfo(scanInfo, null)\n        val actionAllInfo = ActionAllInfo()\n        walkFileTreeForSettingAttributes(path, recursive, object : SimpleFileVisitor<Path>() {\n            @Throws(IOException::class)\n            override fun preVisitDirectory(\n                directory: Path,\n                attributes: BasicFileAttributes\n            ): FileVisitResult = visitFile(directory, attributes)\n\n            @Throws(IOException::class)\n            override fun visitFile(file: Path, attributes: BasicFileAttributes): FileVisitResult {\n                if (attributes.isSymbolicLink) {\n                    // We cannot set mode on symbolic links.\n                    transferInfo.skipFileIgnoringSize()\n                    return FileVisitResult.CONTINUE\n                }\n                // The file may actually be a directory if we are not entering it.\n                val mode = if (!attributes.isDirectory) getFileMode(file) else mode\n                setMode(file, mode, transferInfo, actionAllInfo)\n                throwIfInterrupted()\n                return FileVisitResult.CONTINUE\n            }\n\n            @Throws(IOException::class)\n            override fun visitFileFailed(file: Path, exception: IOException): FileVisitResult {\n                // TODO: Prompt retry, skip, skip-all or abort.\n                return super.visitFileFailed(file, exception)\n            }\n\n            @Throws(IOException::class)\n            override fun postVisitDirectory(\n                directory: Path,\n                exception: IOException?\n            ): FileVisitResult {\n                // TODO: Prompt retry, skip, skip-all or abort.\n                return super.postVisitDirectory(directory, exception)\n            }\n        })\n    }\n\n    @Throws(IOException::class)\n    private fun getFileMode(file: Path): Set<PosixFileModeBit> {\n        if (file == path || !uppercaseX) {\n            return mode\n        }\n        val mode = mode.toEnumSet()\n        val currentMode = file.getMode(LinkOption.NOFOLLOW_LINKS)!!\n        if (PosixFileModeBit.OWNER_EXECUTE !in currentMode) {\n            mode -= PosixFileModeBit.OWNER_EXECUTE\n        }\n        if (PosixFileModeBit.GROUP_EXECUTE !in currentMode) {\n            mode -= PosixFileModeBit.GROUP_EXECUTE\n        }\n        if (PosixFileModeBit.OTHERS_EXECUTE !in currentMode) {\n            mode -= PosixFileModeBit.OTHERS_EXECUTE\n        }\n        return mode\n    }\n}\n\n@Throws(IOException::class)\nprivate fun FileJob.setMode(\n    path: Path,\n    mode: Set<PosixFileModeBit>,\n    transferInfo: TransferInfo,\n    actionAllInfo: ActionAllInfo\n) {\n    var retry: Boolean\n    do {\n        retry = false\n        try {\n            // This will always follow symbolic links.\n            path.setMode(mode)\n            transferInfo.incrementTransferredFileCount()\n            postSetModeNotification(transferInfo, path)\n        } catch (e: InterruptedIOException) {\n            throw e\n        } catch (e: IOException) {\n            e.printStackTrace()\n            if (actionAllInfo.skipSetModeError) {\n                transferInfo.skipFileIgnoringSize()\n                postSetModeNotification(transferInfo, path)\n                return\n            }\n            if (e is UserActionRequiredException) {\n                val result = showUserAction(e)\n                if (result) {\n                    retry = true\n                    continue\n                }\n            }\n            val result = showErrorDialog(\n                getString(R.string.file_job_set_mode_error_title_format, getFileName(path)),\n                getString(\n                    R.string.file_job_set_mode_error_message_format, mode.toModeString(),\n                    e.toString()\n                ),\n                getReadOnlyFileStore(path, e),\n                true,\n                getString(R.string.retry),\n                getString(R.string.skip),\n                getString(android.R.string.cancel)\n            )\n            when (result.action) {\n                FileJobErrorAction.POSITIVE -> {\n                    retry = true\n                    continue\n                }\n                FileJobErrorAction.NEGATIVE -> {\n                    if (result.isAll) {\n                        actionAllInfo.skipSetModeError = true\n                    }\n                    transferInfo.skipFileIgnoringSize()\n                    postSetModeNotification(transferInfo, path)\n                    return\n                }\n                FileJobErrorAction.CANCELED -> {\n                    transferInfo.skipFileIgnoringSize()\n                    postSetModeNotification(transferInfo, path)\n                    return\n                }\n                FileJobErrorAction.NEUTRAL -> throw InterruptedIOException()\n                else -> throw AssertionError(result.action)\n            }\n        }\n    } while (retry)\n}\n\nprivate fun FileJob.postSetModeNotification(transferInfo: TransferInfo, currentPath: Path) {\n    postTransferCountNotification(\n        transferInfo, currentPath, R.string.file_job_set_mode_notification_title_one_format,\n        R.plurals.file_job_set_mode_notification_title_multiple_format\n    )\n}\n\nclass SetFileOwnerJob(\n    private val path: Path,\n    private val owner: PosixUser,\n    private val recursive: Boolean\n) : FileJob() {\n    @Throws(IOException::class)\n    override fun run() {\n        val scanInfo = scan(\n            path, recursive, R.plurals.file_job_set_owner_scan_notification_title_format\n        )\n        val transferInfo = TransferInfo(scanInfo, null)\n        val actionAllInfo = ActionAllInfo()\n        walkFileTreeForSettingAttributes(path, recursive, object : SimpleFileVisitor<Path>() {\n            @Throws(IOException::class)\n            override fun preVisitDirectory(\n                directory: Path,\n                attributes: BasicFileAttributes\n            ): FileVisitResult = visitFile(directory, attributes)\n\n            @Throws(IOException::class)\n            override fun visitFile(file: Path, attributes: BasicFileAttributes): FileVisitResult {\n                setOwner(file, owner, !attributes.isSymbolicLink, transferInfo, actionAllInfo)\n                throwIfInterrupted()\n                return FileVisitResult.CONTINUE\n            }\n\n            @Throws(IOException::class)\n            override fun visitFileFailed(file: Path, exception: IOException): FileVisitResult {\n                // TODO: Prompt retry, skip, skip-all or abort.\n                return super.visitFileFailed(file, exception)\n            }\n\n            @Throws(IOException::class)\n            override fun postVisitDirectory(\n                directory: Path,\n                exception: IOException?\n            ): FileVisitResult {\n                // TODO: Prompt retry, skip, skip-all or abort.\n                return super.postVisitDirectory(directory, exception)\n            }\n        })\n    }\n}\n\n@Throws(IOException::class)\nprivate fun FileJob.setOwner(\n    path: Path,\n    owner: PosixUser,\n    followLinks: Boolean,\n    transferInfo: TransferInfo,\n    actionAllInfo: ActionAllInfo\n) {\n    var retry: Boolean\n    do {\n        retry = false\n        try {\n            val options = if (followLinks) arrayOf() else arrayOf(LinkOption.NOFOLLOW_LINKS)\n            path.setOwner(owner, *options)\n            transferInfo.incrementTransferredFileCount()\n            postSetOwnerNotification(transferInfo, path)\n        } catch (e: InterruptedIOException) {\n            throw e\n        } catch (e: IOException) {\n            e.printStackTrace()\n            if (actionAllInfo.skipSetOwnerError) {\n                transferInfo.skipFileIgnoringSize()\n                postSetOwnerNotification(transferInfo, path)\n                return\n            }\n            if (e is UserActionRequiredException) {\n                val result = showUserAction(e)\n                if (result) {\n                    retry = true\n                    continue\n                }\n            }\n            val result = showErrorDialog(\n                getString(R.string.file_job_set_owner_error_title_format, getFileName(path)),\n                getString(\n                    R.string.file_job_set_owner_error_message_format, getPrincipalName(owner),\n                    e.toString()\n                ),\n                getReadOnlyFileStore(path, e),\n                true,\n                getString(R.string.retry),\n                getString(R.string.skip),\n                getString(android.R.string.cancel)\n            )\n            when (result.action) {\n                FileJobErrorAction.POSITIVE -> {\n                    retry = true\n                    continue\n                }\n                FileJobErrorAction.NEGATIVE -> {\n                    if (result.isAll) {\n                        actionAllInfo.skipSetOwnerError = true\n                    }\n                    transferInfo.skipFileIgnoringSize()\n                    postSetOwnerNotification(transferInfo, path)\n                    return\n                }\n                FileJobErrorAction.CANCELED -> {\n                    transferInfo.skipFileIgnoringSize()\n                    postSetOwnerNotification(transferInfo, path)\n                    return\n                }\n                FileJobErrorAction.NEUTRAL -> throw InterruptedIOException()\n                else -> throw AssertionError(result.action)\n            }\n        }\n    } while (retry)\n}\n\nprivate fun FileJob.postSetOwnerNotification(transferInfo: TransferInfo, currentPath: Path) {\n    postTransferCountNotification(\n        transferInfo, currentPath, R.string.file_job_set_owner_notification_title_one_format,\n        R.plurals.file_job_set_owner_notification_title_multiple_format\n    )\n}\n\nprivate fun FileJob.getPrincipalName(principal: PosixPrincipal): String =\n    principal.name ?: principal.id.toString()\n\nclass SetFileSeLinuxContextJob(\n    private val path: Path,\n    private val seLinuxContext: String,\n    private val recursive: Boolean\n) : FileJob() {\n    @Throws(IOException::class)\n    override fun run() {\n        val scanInfo = scan(\n            path, recursive, R.plurals.file_job_set_selinux_context_scan_notification_title_format\n        )\n        val transferInfo = TransferInfo(scanInfo, null)\n        val actionAllInfo = ActionAllInfo()\n        walkFileTreeForSettingAttributes(path, recursive, object : SimpleFileVisitor<Path>() {\n            @Throws(IOException::class)\n            override fun preVisitDirectory(\n                directory: Path,\n                attributes: BasicFileAttributes\n            ): FileVisitResult = visitFile(directory, attributes)\n\n            @Throws(IOException::class)\n            override fun visitFile(\n                file: Path,\n                attributes: BasicFileAttributes\n            ): FileVisitResult {\n                setSeLinuxContext(\n                    file, seLinuxContext, !attributes.isSymbolicLink, transferInfo, actionAllInfo\n                )\n                throwIfInterrupted()\n                return FileVisitResult.CONTINUE\n            }\n\n            @Throws(IOException::class)\n            override fun visitFileFailed(file: Path, exception: IOException): FileVisitResult {\n                // TODO: Prompt retry, skip, skip-all or abort.\n                return super.visitFileFailed(file, exception)\n            }\n\n            @Throws(IOException::class)\n            override fun postVisitDirectory(\n                directory: Path,\n                exception: IOException?\n            ): FileVisitResult {\n                // TODO: Prompt retry, skip, skip-all or abort.\n                return super.postVisitDirectory(directory, exception)\n            }\n        })\n    }\n}\n\n@Throws(IOException::class)\nprivate fun FileJob.setSeLinuxContext(\n    path: Path,\n    seLinuxContext: String,\n    followLinks: Boolean,\n    transferInfo: TransferInfo,\n    actionAllInfo: ActionAllInfo\n) {\n    var retry: Boolean\n    do {\n        retry = false\n        try {\n            val options = if (followLinks) arrayOf() else arrayOf(LinkOption.NOFOLLOW_LINKS)\n            path.setSeLinuxContext(seLinuxContext.toByteString(), *options)\n            transferInfo.incrementTransferredFileCount()\n            postSetSeLinuxContextNotification(transferInfo, path)\n        } catch (e: InterruptedIOException) {\n            throw e\n        } catch (e: IOException) {\n            e.printStackTrace()\n            if (actionAllInfo.skipSetSeLinuxContextError) {\n                transferInfo.skipFileIgnoringSize()\n                postSetSeLinuxContextNotification(transferInfo, path)\n                return\n            }\n            if (e is UserActionRequiredException) {\n                val result = showUserAction(e)\n                if (result) {\n                    retry = true\n                    continue\n                }\n            }\n            val result = showErrorDialog(\n                getString(\n                    R.string.file_job_set_selinux_context_error_title_format, getFileName(path)\n                ),\n                getString(\n                    R.string.file_job_set_selinux_context_error_message_format, seLinuxContext,\n                    e.toString()\n                ),\n                getReadOnlyFileStore(path, e),\n                true,\n                getString(R.string.retry),\n                getString(R.string.skip),\n                getString(android.R.string.cancel)\n            )\n            when (result.action) {\n                FileJobErrorAction.POSITIVE -> {\n                    retry = true\n                    continue\n                }\n                FileJobErrorAction.NEGATIVE -> {\n                    if (result.isAll) {\n                        actionAllInfo.skipSetSeLinuxContextError = true\n                    }\n                    transferInfo.skipFileIgnoringSize()\n                    postSetSeLinuxContextNotification(transferInfo, path)\n                    return\n                }\n                FileJobErrorAction.CANCELED -> {\n                    transferInfo.skipFileIgnoringSize()\n                    postSetSeLinuxContextNotification(transferInfo, path)\n                    return\n                }\n                FileJobErrorAction.NEUTRAL -> throw InterruptedIOException()\n                else -> throw AssertionError(result.action)\n            }\n        }\n    } while (retry)\n}\n\nprivate fun FileJob.postSetSeLinuxContextNotification(\n    transferInfo: TransferInfo,\n    currentPath: Path\n) {\n    postTransferCountNotification(\n        transferInfo, currentPath,\n        R.string.file_job_set_selinux_context_notification_title_one_format,\n        R.plurals.file_job_set_selinux_context_notification_title_multiple_format\n    )\n}\n\nclass WriteFileJob(\n    private val file: Path,\n    private val content: ByteArray,\n    private val listener: ((Boolean) -> Unit)?\n) : FileJob() {\n    @Throws(IOException::class)\n    override fun run() {\n        val successful = write(file, content)\n        listener?.let { mainExecutor.execute { it(successful) } }\n    }\n}\n\n@Throws(IOException::class)\nprivate fun FileJob.write(file: Path, content: ByteArray): Boolean {\n    val scanInfo = ScanInfo().apply {\n        incrementFileCount()\n        addToSize(content.size.toLong())\n    }\n    var retry: Boolean\n    do {\n        retry = false\n        val transferInfo = TransferInfo(scanInfo, file)\n        try {\n            file.newOutputStream().use { outputStream ->\n                ByteArrayInputStream(content).copyTo(outputStream, PROGRESS_INTERVAL_MILLIS) {\n                    transferInfo.addToTransferredSize(it)\n                    postWriteNotification(transferInfo)\n                }\n                postWriteNotification(transferInfo)\n            }\n        } catch (e: InterruptedIOException) {\n            throw e\n        } catch (e: IOException) {\n            e.printStackTrace()\n            if (e is UserActionRequiredException) {\n                val result = showUserAction(e)\n                if (result) {\n                    retry = true\n                    continue\n                }\n            }\n            val result = showErrorDialog(\n                getString(R.string.file_job_write_error_title, getFileName(file)),\n                getString(\n                    R.string.file_job_write_error_message_format, getFileName(file), e.toString()\n                ),\n                getReadOnlyFileStore(file, e),\n                false,\n                getString(R.string.retry),\n                getString(android.R.string.cancel),\n                null\n            )\n            return when (result.action) {\n                FileJobErrorAction.POSITIVE -> {\n                    retry = true\n                    continue\n                }\n                FileJobErrorAction.NEGATIVE, FileJobErrorAction.CANCELED -> false\n                FileJobErrorAction.NEUTRAL -> throw InterruptedIOException()\n            }\n        }\n    } while (retry)\n    return true\n}\n\nprivate fun FileJob.postWriteNotification(transferInfo: TransferInfo) {\n    if (!transferInfo.shouldPostNotification()) {\n        return\n    }\n    val target = transferInfo.target!!\n    val title = getString(R.string.file_job_write_notification_title_format, getFileName(target))\n    val size = transferInfo.size\n    val sizeString = size.asFileSize().formatHumanReadable(service)\n    val transferredSize = transferInfo.transferredSize\n    val transferredSizeString = transferredSize.asFileSize().formatHumanReadable(service)\n    val text = getString(\n        R.string.file_job_transfer_size_notification_text_one_format, transferredSizeString,\n        sizeString\n    )\n    val max = size.toInt()\n    val progress = transferredSize.toInt()\n    postNotification(title, text, null, null, max, progress, false, true)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/BreadcrumbData.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.content.Context\nimport java8.nio.file.Path\n\ndata class BreadcrumbData(\n    val paths: List<Path>,\n    val nameProducers: List<(Context) -> String>,\n    val selectedIndex: Int\n)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/BreadcrumbLayout.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.content.Context\nimport android.content.res.ColorStateList\nimport android.util.AttributeSet\nimport android.view.View\nimport android.widget.HorizontalScrollView\nimport android.widget.LinearLayout\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport androidx.appcompat.widget.PopupMenu\nimport androidx.core.view.isVisible\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.databinding.BreadcrumbItemBinding\nimport me.zhanghai.android.files.util.getColorByAttr\nimport me.zhanghai.android.files.util.getDimensionPixelSize\nimport me.zhanghai.android.files.util.getResourceIdByAttr\nimport me.zhanghai.android.files.util.layoutInflater\nimport me.zhanghai.android.files.util.withTheme\n\nclass BreadcrumbLayout : HorizontalScrollView {\n    private val tabLayoutHeight = context.getDimensionPixelSize(R.dimen.tab_layout_height)\n    // Using a color state list resource somehow results in red color in dark mode on API 21.\n    // Run `git revert 5bb2fd1` once we no longer support API 21.\n    private val itemColor =\n        ColorStateList(\n            arrayOf(intArrayOf(android.R.attr.state_activated), intArrayOf()),\n            intArrayOf(\n                context.getColorByAttr(android.R.attr.textColorPrimary),\n                context.getColorByAttr(android.R.attr.textColorSecondary)\n            )\n        )\n    private val popupContext = context.withTheme(\n        context.getResourceIdByAttr(androidx.appcompat.R.attr.actionBarPopupTheme)\n    )\n\n    private val itemsLayout: LinearLayout\n\n    private lateinit var listener: Listener\n    private lateinit var data: BreadcrumbData\n\n    private var isLayoutDirty = true\n    private var isScrollToSelectedItemPending = false\n    private var isFirstScroll = true\n\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(\n        context, attrs\n    )\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    constructor(\n        context: Context,\n        attrs: AttributeSet?,\n        @AttrRes defStyleAttr: Int,\n        @StyleRes defStyleRes: Int\n    ) : super(context, attrs, defStyleAttr, defStyleRes)\n\n    init {\n        isHorizontalScrollBarEnabled = false\n        itemsLayout = LinearLayout(context).apply { orientation = LinearLayout.HORIZONTAL }\n        itemsLayout.setPaddingRelative(paddingStart, paddingTop, paddingEnd, paddingBottom)\n        setPaddingRelative(0, 0, 0, 0)\n        addView(itemsLayout, LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT))\n    }\n\n    override fun jumpDrawablesToCurrentState() {\n        // HACK: AppBarLayout.updateAppBarLayoutDrawableState() calls\n        // CoordinatorLayout.jumpDrawablesToCurrentState() to fix a pre-N visual bug according to a\n        // comment in AppBarLayout.BaseBehavior.onLayoutChild(), however that results in our ripple\n        // disappearing. One way to ignore that call path is to skip when we are in layout, so that\n        // we at least preserve the other call path upon being attached to window.\n        if (isInLayout) {\n            return\n        }\n        super.jumpDrawablesToCurrentState()\n    }\n\n    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {\n        val heightMode = MeasureSpec.getMode(heightMeasureSpec)\n        val newHeightMeasureSpec = if (heightMode != MeasureSpec.EXACTLY) {\n            val maximumHeight = if (heightMode == MeasureSpec.AT_MOST) {\n                MeasureSpec.getSize(heightMeasureSpec)\n            } else {\n                Int.MAX_VALUE\n            }\n            val height = tabLayoutHeight.coerceAtMost(maximumHeight)\n            MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)\n        } else {\n            heightMeasureSpec\n        }\n        super.onMeasure(widthMeasureSpec, newHeightMeasureSpec)\n    }\n\n    override fun requestLayout() {\n        isLayoutDirty = true\n\n        super.requestLayout()\n    }\n\n    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {\n        super.onLayout(changed, left, top, right, bottom)\n\n        isLayoutDirty = false\n        if (isScrollToSelectedItemPending) {\n            scrollToSelectedItem()\n            isScrollToSelectedItemPending = false\n        }\n    }\n\n    fun setListener(listener: Listener) {\n        this.listener = listener\n    }\n\n    fun setData(data: BreadcrumbData) {\n        if (this::data.isInitialized && this.data == data) {\n            return\n        }\n        this.data = data\n        inflateItemViews()\n        bindItemViews()\n        scrollToSelectedItem()\n    }\n\n    private fun scrollToSelectedItem() {\n        if (isLayoutDirty) {\n            isScrollToSelectedItemPending = true\n            return\n        }\n        val selectedItemView = itemsLayout.getChildAt(data.selectedIndex)\n        val scrollX = if (layoutDirection == View.LAYOUT_DIRECTION_LTR) {\n            selectedItemView.left - itemsLayout.paddingStart\n        } else {\n            selectedItemView.right - width + itemsLayout.paddingStart\n        }\n        if (!isFirstScroll && isShown) {\n            smoothScrollTo(scrollX, 0)\n        } else {\n            scrollTo(scrollX, 0)\n        }\n        isFirstScroll = false\n    }\n\n    private fun inflateItemViews() {\n        // HACK: Remove/add views at the front so that ripple remains correct, as we are potentially\n        // collapsing/expanding breadcrumbs at the front.\n        for (index in data.paths.size..<itemsLayout.childCount) {\n            itemsLayout.removeViewAt(0)\n        }\n        for (index in itemsLayout.childCount..<data.paths.size) {\n            val binding = BreadcrumbItemBinding.inflate(context.layoutInflater, itemsLayout, false)\n            val menu = PopupMenu(popupContext, binding.root)\n                .apply { inflate(R.menu.file_list_breadcrumb) }\n            binding.root.setOnLongClickListener {\n                menu.show()\n                true\n            }\n            binding.text.setTextColor(itemColor)\n            binding.arrowImage.imageTintList = itemColor\n            binding.root.tag = binding to menu\n            itemsLayout.addView(binding.root, 0)\n        }\n    }\n\n    private fun bindItemViews() {\n        for (index in data.paths.indices) {\n            @Suppress(\"UNCHECKED_CAST\")\n            val tag = itemsLayout.getChildAt(index).tag as Pair<BreadcrumbItemBinding, PopupMenu>\n            val (binding, menu) = tag\n            binding.text.text = data.nameProducers[index](binding.text.context)\n            binding.arrowImage.isVisible = index != data.paths.size - 1\n            binding.root.isActivated = index == data.selectedIndex\n            val path = data.paths[index]\n            binding.root.setOnClickListener {\n                if (data.selectedIndex == index) {\n                    scrollToSelectedItem()\n                } else {\n                    listener.navigateTo(path)\n                }\n            }\n            menu.setOnMenuItemClickListener {\n                when (it.itemId) {\n                    R.id.action_copy_path -> {\n                        listener.copyPath(path)\n                        true\n                    }\n                    R.id.action_open_in_new_task -> {\n                        listener.openInNewTask(path)\n                        true\n                    }\n                    else -> false\n                }\n            }\n        }\n    }\n\n    interface Listener {\n        fun navigateTo(path: Path)\n        fun copyPath(path: Path)\n        fun openInNewTask(path: Path)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/BreadcrumbLiveData.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.content.Context\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.MediatorLiveData\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.navigation.NavigationRootMapLiveData\nimport me.zhanghai.android.files.util.valueCompat\n\nclass BreadcrumbLiveData(\n    private val trailLiveData: LiveData<TrailData>\n) : MediatorLiveData<BreadcrumbData>() {\n    init {\n        addSource(trailLiveData) { loadValue() }\n        addSource(NavigationRootMapLiveData) { loadValue() }\n    }\n\n    private fun loadValue() {\n        val navigationRootMap = NavigationRootMapLiveData.valueCompat\n        val trailData = trailLiveData.valueCompat\n        val paths = mutableListOf<Path>()\n        val nameProducers = mutableListOf<(Context) -> String>()\n        var selectedIndex = trailData.currentIndex\n        for (path in trailData.trail) {\n            val navigationRoot = navigationRootMap[path]\n            val itemCount = nameProducers.size\n            if (navigationRoot != null && selectedIndex >= itemCount) {\n                selectedIndex -= itemCount\n                paths.clear()\n                paths.add(navigationRoot.path)\n                nameProducers.clear()\n                nameProducers.add { navigationRoot.getName(it) }\n            } else {\n                paths.add(path)\n                nameProducers.add { path.name }\n            }\n        }\n        value = BreadcrumbData(paths, nameProducers, selectedIndex)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/CollatorFileNameExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.os.Parcelable\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.provider.common.ByteStringBuilder\nimport me.zhanghai.android.files.provider.common.toByteString\nimport java.text.CollationKey\nimport java.text.Collator\nimport kotlin.math.min\n\nprivate val COLLATION_SENTINEL = byteArrayOf(1, 1, 1)\n\n// @see https://github.com/GNOME/glib/blob/mainline/glib/gunicollate.c\n//      g_utf8_collate_key_for_filename()\nfun Collator.getCollationKeyForFileName(source: String): CollationKey {\n    val result = ByteStringBuilder()\n    val suffix = ByteStringBuilder()\n    var previousIndex = 0\n    var index = 0\n    val endIndex = source.length\n    while (index < endIndex) {\n        when {\n            source[index] == '.' -> {\n                if (previousIndex != index) {\n                    val collationKey = getCollationKey(source.substring(previousIndex, index))\n                    result.append(collationKey.toByteArray())\n                }\n                result.append(COLLATION_SENTINEL).append(1)\n                previousIndex = index + 1\n            }\n            source[index].isAsciiDigit() -> {\n                if (previousIndex != index) {\n                    val collationKey = getCollationKey(source.substring(previousIndex, index))\n                    result.append(collationKey.toByteArray())\n                }\n                result.append(COLLATION_SENTINEL).append(2)\n                previousIndex = index\n                var leadingZeros: Int\n                var digits: Int\n                if (source[index] == '0') {\n                    leadingZeros = 1\n                    digits = 0\n                } else {\n                    leadingZeros = 0\n                    digits = 1\n                }\n                while (++index < endIndex) {\n                    if (source[index] == '0' && digits == 0) {\n                        ++leadingZeros\n                    } else if (source[index].isAsciiDigit()) {\n                        ++digits\n                    } else {\n                        if (digits == 0) {\n                            ++digits\n                            --leadingZeros\n                        }\n                        break\n                    }\n                }\n                while (digits > 1) {\n                    result.append(':'.code.toByte())\n                    --digits\n                }\n                if (leadingZeros > 0) {\n                    suffix.append(leadingZeros.toByte())\n                    previousIndex += leadingZeros\n                }\n                result.append(source.substring(previousIndex, index).toByteString())\n                previousIndex = index\n                --index\n            }\n            else -> {}\n        }\n        ++index\n    }\n    if (previousIndex != index) {\n        val collationKey = getCollationKey(source.substring(previousIndex, index))\n        result.append(collationKey.toByteArray())\n    }\n    result.append(suffix.toByteString())\n    return ByteArrayCollationKey(source, result.toByteString().borrowBytes())\n}\n\nprivate fun Char.isAsciiDigit(): Boolean = this in '0'..'9'\n\n@Parcelize\nprivate class ByteArrayCollationKey(\n    @Suppress(\"CanBeParameter\")\n    private val source: String,\n    private val bytes: ByteArray\n) : CollationKey(source), Parcelable {\n    override fun compareTo(other: CollationKey): Int {\n        other as ByteArrayCollationKey\n        return bytes.unsignedCompareTo(other.bytes)\n    }\n\n    override fun toByteArray(): ByteArray = bytes.copyOf()\n}\n\nprivate fun ByteArray.unsignedCompareTo(other: ByteArray): Int {\n    val size = size\n    val otherSize = other.size\n    for (index in 0..<min(size, otherSize)) {\n        val byte = this[index].toInt() and 0xFF\n        val otherByte = other[index].toInt() and 0xFF\n        if (byte < otherByte) {\n            return -1\n        } else if (byte > otherByte) {\n            return 1\n        }\n    }\n    return size - otherSize\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/ConfirmDeleteFilesDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.app.Dialog\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatDialogFragment\nimport androidx.fragment.app.Fragment\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.getQuantityString\nimport me.zhanghai.android.files.util.putArgs\nimport me.zhanghai.android.files.util.show\n\nclass ConfirmDeleteFilesDialogFragment : AppCompatDialogFragment() {\n    private val args by args<Args>()\n\n    private val listener: Listener\n        get() = requireParentFragment() as Listener\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {\n        val files = args.files\n        val message = if (files.size == 1) {\n            val file = files.single()\n            val messageRes = if (file.attributesNoFollowLinks.isDirectory) {\n                R.string.file_delete_message_directory_format\n            } else {\n                R.string.file_delete_message_file_format\n            }\n            getString(messageRes, file.name)\n        } else {\n            val allDirectories = files.all { it.attributesNoFollowLinks.isDirectory }\n            val allFiles = files.none { it.attributesNoFollowLinks.isDirectory }\n            val messageRes = when {\n                allDirectories -> R.plurals.file_delete_message_multiple_directories_format\n                allFiles -> R.plurals.file_delete_message_multiple_files_format\n                else -> R.plurals.file_delete_message_multiple_mixed_format\n            }\n            getQuantityString(messageRes, files.size, files.size)\n        }\n        return MaterialAlertDialogBuilder(requireContext(), theme)\n            .setMessage(message)\n            .setPositiveButton(android.R.string.ok) { _, _ -> listener.deleteFiles(files) }\n            .setNegativeButton(android.R.string.cancel, null)\n            .create()\n    }\n\n    companion object {\n        fun show(files: FileItemSet, fragment: Fragment) {\n            ConfirmDeleteFilesDialogFragment().putArgs(Args(files)).show(fragment)\n        }\n    }\n\n    @Parcelize\n    class Args(val files: FileItemSet) : ParcelableArgs\n\n    interface Listener {\n        fun deleteFiles(files: FileItemSet)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/ConfirmReplaceFileDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.app.Dialog\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatDialogFragment\nimport androidx.fragment.app.Fragment\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.file.FileItem\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.putArgs\nimport me.zhanghai.android.files.util.show\n\nclass ConfirmReplaceFileDialogFragment : AppCompatDialogFragment() {\n    private val args by args<Args>()\n\n    private val listener: Listener\n        get() = requireParentFragment() as Listener\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {\n        val file = args.file\n        return MaterialAlertDialogBuilder(requireContext(), theme)\n            .setMessage(getString(R.string.file_replace_message_format, file.name))\n            .setPositiveButton(android.R.string.ok) { _, _ -> listener.replaceFile(file) }\n            .setNegativeButton(android.R.string.cancel, null)\n            .create()\n    }\n\n    companion object {\n        fun show(file: FileItem, fragment: Fragment) {\n            ConfirmReplaceFileDialogFragment().putArgs(Args(file)).show(fragment)\n        }\n    }\n\n    @Parcelize\n    class Args(val file: FileItem) : ParcelableArgs\n\n    interface Listener {\n        fun replaceFile(file: FileItem)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/CreateArchiveDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.app.Dialog\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.widget.EditText\nimport android.widget.RadioGroup\nimport androidx.annotation.StringRes\nimport androidx.core.view.isGone\nimport androidx.fragment.app.Fragment\nimport com.google.android.material.textfield.TextInputEditText\nimport com.google.android.material.textfield.TextInputLayout\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.databinding.CreateArchiveDialogBinding\nimport me.zhanghai.android.files.databinding.NameDialogNameIncludeBinding\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.putArgs\nimport me.zhanghai.android.files.util.setTextWithSelection\nimport me.zhanghai.android.files.util.show\nimport me.zhanghai.android.files.util.takeIfNotEmpty\nimport me.zhanghai.android.libarchive.Archive\n\nclass CreateArchiveDialogFragment : FileNameDialogFragment() {\n    private val args by args<Args>()\n\n    override val binding: Binding\n        get() = super.binding as Binding\n\n    override val listener: Listener\n        get() = super.listener as Listener\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {\n        val dialog = super.onCreateDialog(savedInstanceState)\n\n        if (savedInstanceState == null) {\n            val files = args.files\n            var name: String? = null\n            if (files.size == 1) {\n                name = files.single().path.fileName.toString()\n            } else {\n                val parent = files.mapTo(mutableSetOf()) { it.path.parent }.singleOrNull()\n                if (parent != null && parent.nameCount > 0) {\n                    name = parent.fileName.toString()\n                }\n            }\n            name?.let { binding.nameEdit.setTextWithSelection(it) }\n        }\n        binding.typeGroup.setOnCheckedChangeListener { _, _ -> updatePasswordLayoutVisibility() }\n        updatePasswordLayoutVisibility()\n        return dialog\n    }\n\n    @StringRes\n    override val titleRes: Int = R.string.file_create_archive_title\n\n    override fun onInflateBinding(inflater: LayoutInflater): NameDialogFragment.Binding =\n        Binding.inflate(inflater)\n\n    override val name: String\n        get() {\n            val extension = when (val checkedId = binding.typeGroup.checkedRadioButtonId) {\n                R.id.zipRadio -> \"zip\"\n                R.id.tarXzRadio -> \"tar.xz\"\n                R.id.sevenZRadio -> \"7z\"\n                else -> throw AssertionError(checkedId)\n            }\n            return \"${super.name}.$extension\"\n        }\n\n    private val isPasswordSupported: Boolean\n        get() = when (val checkedId = binding.typeGroup.checkedRadioButtonId) {\n            R.id.zipRadio -> true\n            R.id.tarXzRadio, R.id.sevenZRadio -> false\n            else -> throw AssertionError(checkedId)\n        }\n\n    private fun updatePasswordLayoutVisibility() {\n        binding.passwordLayout.isGone = !isPasswordSupported\n    }\n\n    override fun onOk(name: String) {\n        val (format, filter) = when (val checkedId = binding.typeGroup.checkedRadioButtonId) {\n            R.id.zipRadio -> Archive.FORMAT_ZIP to Archive.FILTER_NONE\n            R.id.tarXzRadio -> Archive.FORMAT_TAR to Archive.FILTER_XZ\n            R.id.sevenZRadio -> Archive.FORMAT_7ZIP to Archive.FILTER_NONE\n            else -> throw AssertionError(checkedId)\n        }\n        val password = if (isPasswordSupported) {\n            binding.passwordEdit.text!!.toString().takeIfNotEmpty()\n        } else {\n            null\n        }\n        listener.archive(args.files, name, format, filter, password)\n    }\n\n    companion object {\n        fun show(files: FileItemSet, fragment: Fragment) {\n            CreateArchiveDialogFragment().putArgs(Args(files)).show(fragment)\n        }\n    }\n\n    @Parcelize\n    class Args(val files: FileItemSet) : ParcelableArgs\n\n    protected class Binding private constructor(\n        root: View,\n        nameLayout: TextInputLayout,\n        nameEdit: EditText,\n        val typeGroup: RadioGroup,\n        val passwordLayout: TextInputLayout,\n        val passwordEdit: TextInputEditText\n    ) : NameDialogFragment.Binding(root, nameLayout, nameEdit) {\n        companion object {\n            fun inflate(inflater: LayoutInflater): Binding {\n                val binding = CreateArchiveDialogBinding.inflate(inflater)\n                val bindingRoot = binding.root\n                val nameBinding = NameDialogNameIncludeBinding.bind(bindingRoot)\n                return Binding(\n                    bindingRoot, nameBinding.nameLayout, nameBinding.nameEdit, binding.typeGroup,\n                    binding.passwordLayout, binding.passwordEdit\n                )\n            }\n        }\n    }\n\n    interface Listener : FileNameDialogFragment.Listener {\n        fun archive(files: FileItemSet, name: String, format: Int, filter: Int, password: String?)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/CreateArchiveTypeRadioGroup.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.widget.RadioGroup\nimport me.zhanghai.android.files.settings.Settings\nimport me.zhanghai.android.files.util.valueCompat\n\nclass CreateArchiveTypeRadioGroup : RadioGroup {\n    private var onCheckedChangeListener: OnCheckedChangeListener? = null\n\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    init {\n        check(Settings.CREATE_ARCHIVE_TYPE.valueCompat)\n        super.setOnCheckedChangeListener { group, checkedId ->\n            Settings.CREATE_ARCHIVE_TYPE.putValue(checkedId)\n            onCheckedChangeListener?.onCheckedChanged(group, checkedId)\n        }\n    }\n\n    override fun setOnCheckedChangeListener(listener: OnCheckedChangeListener?) {\n        onCheckedChangeListener = listener\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/CreateDirectoryDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport androidx.annotation.StringRes\nimport androidx.fragment.app.Fragment\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.util.show\n\nclass CreateDirectoryDialogFragment : FileNameDialogFragment() {\n    override val listener: Listener\n        get() = super.listener as Listener\n\n    @StringRes\n    override val titleRes: Int = R.string.file_create_directory_title\n\n    override fun onOk(name: String) {\n        listener.createDirectory(name)\n    }\n\n    companion object {\n        fun show(fragment: Fragment) {\n            CreateDirectoryDialogFragment().show(fragment)\n        }\n    }\n\n    interface Listener : FileNameDialogFragment.Listener {\n        fun createDirectory(name: String)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/CreateFileDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport androidx.annotation.StringRes\nimport androidx.fragment.app.Fragment\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.util.show\n\nclass CreateFileDialogFragment : FileNameDialogFragment() {\n    override val listener: Listener\n        get() = super.listener as Listener\n\n    @StringRes\n    override val titleRes: Int = R.string.file_create_file_title\n\n    override fun onOk(name: String) {\n        listener.createFile(name)\n    }\n\n    companion object {\n        fun show(fragment: Fragment) {\n            CreateFileDialogFragment().show(fragment)\n        }\n    }\n\n    interface Listener : FileNameDialogFragment.Listener {\n        fun createFile(name: String)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/EditFileActivity.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.os.Bundle\nimport java8.nio.file.Path\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.app.AppActivity\nimport me.zhanghai.android.files.file.MimeType\nimport me.zhanghai.android.files.file.fileProviderUri\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.ParcelableParceler\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.createEditIntent\nimport me.zhanghai.android.files.util.startActivitySafe\n\n// Use a trampoline activity so that we can have a proper icon and title.\nclass EditFileActivity : AppActivity() {\n    private val args by args<Args>()\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        startActivitySafe(args.path.fileProviderUri.createEditIntent(args.mimeType))\n        finish()\n    }\n\n    @Parcelize\n    class Args(\n        val path: @WriteWith<ParcelableParceler> Path,\n        val mimeType: MimeType\n    ) : ParcelableArgs\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/FileItemExtensions.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.content.Context\nimport android.os.Build\nimport java8.nio.file.Path\nimport java8.nio.file.attribute.BasicFileAttributes\nimport java8.nio.file.attribute.FileTime\nimport me.zhanghai.android.files.file.FileItem\nimport me.zhanghai.android.files.file.MimeType\nimport me.zhanghai.android.files.file.getBrokenSymbolicLinkName\nimport me.zhanghai.android.files.file.getName\nimport me.zhanghai.android.files.file.isApk\nimport me.zhanghai.android.files.file.isImage\nimport me.zhanghai.android.files.file.isMedia\nimport me.zhanghai.android.files.file.isPdf\nimport me.zhanghai.android.files.provider.archive.createArchiveRootPath\nimport me.zhanghai.android.files.provider.document.documentSupportsThumbnail\nimport me.zhanghai.android.files.provider.document.isDocumentPath\nimport me.zhanghai.android.files.provider.ftp.isFtpPath\nimport me.zhanghai.android.files.provider.linux.isLinuxPath\nimport me.zhanghai.android.files.settings.Settings\nimport me.zhanghai.android.files.util.asFileName\nimport me.zhanghai.android.files.util.isGetPackageArchiveInfoCompatible\nimport me.zhanghai.android.files.util.isMediaMetadataRetrieverCompatible\nimport me.zhanghai.android.files.util.valueCompat\nimport java.text.CollationKey\n\nval FileItem.name: String\n    get() = path.name\n\nval FileItem.baseName: String\n    get() = if (attributes.isDirectory) name else name.asFileName().baseName\n\nval FileItem.extension: String\n    get() = if (attributes.isDirectory) \"\" else name.asFileName().extensions\n\nfun FileItem.getMimeTypeName(context: Context): String {\n        if (attributesNoFollowLinks.isSymbolicLink && isSymbolicLinkBroken) {\n            return MimeType.getBrokenSymbolicLinkName(context)\n        }\n        return mimeType.getName(extension, context)\n    }\n\nval FileItem.isArchiveFile: Boolean\n    get() = path.isArchiveFile(mimeType)\n\nval FileItem.isListable: Boolean\n    get() = attributes.isDirectory || isArchiveFile\n\nval FileItem.listablePath: Path\n    get() = if (isArchiveFile) path.createArchiveRootPath() else path\n\n// @see PathAttributesFetcher.fetch\nval FileItem.supportsThumbnail: Boolean\n    get() {\n        if (path.isDocumentPath && attributes.documentSupportsThumbnail) {\n            return true\n        }\n        if (path.isRemotePath) {\n            val shouldReadRemotePath = !path.isFtpPath\n                && Settings.READ_REMOTE_FILES_FOR_THUMBNAIL.valueCompat\n            if (!shouldReadRemotePath) {\n                return false\n            }\n        }\n        return when {\n            mimeType.isApk && path.isGetPackageArchiveInfoCompatible -> true\n            mimeType.isImage -> true\n            mimeType.isMedia && path.isMediaMetadataRetrieverCompatible -> true\n            mimeType.isPdf && (path.isLinuxPath || path.isDocumentPath) ->\n                Build.VERSION.SDK_INT >= Build.VERSION_CODES.P\n                    || Settings.SHOW_PDF_THUMBNAIL_PRE_28.valueCompat\n            else -> false\n        }\n    }\n\n// @see android.content.pm.parsing.ApkLiteParseUtils.parsePackageSplitNames\n// @see android.content.pm.parsing.ParsingPackageUtils.validateName\n// @see com.android.server.pm.PackageManagerService.getNextCodePath\nprivate const val PACKAGE_NAME_COMPONENT_PATTERN = \"[A-Za-z][0-9A-Z_a-z]*\"\nprivate const val PACKAGE_NAME_PATTERN =\n    \"$PACKAGE_NAME_COMPONENT_PATTERN(?:\\\\.$PACKAGE_NAME_COMPONENT_PATTERN)+\"\nprivate const val BASE64_URL_SAFE_CHARACTER_CLASS = \"[0-9A-Za-z\\\\-_]\"\nprivate const val BASE64_URL_SAFE_PATTERN = (\"(?:$BASE64_URL_SAFE_CHARACTER_CLASS{4})*\"\n    + \"(?:$BASE64_URL_SAFE_CHARACTER_CLASS{3}=|$BASE64_URL_SAFE_CHARACTER_CLASS{2}==)?\")\nprivate val APP_DIRECTORY_REGEX =\n    Regex(\"($PACKAGE_NAME_PATTERN)(?:-$BASE64_URL_SAFE_PATTERN)?\")\n\nval FileItem.appDirectoryPackageName: String?\n    get() {\n        if (!attributes.isDirectory) {\n            return null\n        }\n        return APP_DIRECTORY_REGEX.matchEntire(name)?.groupValues?.get(1)\n    }\n\nfun FileItem.createDummyArchiveRoot(): FileItem =\n    FileItem(\n        path.createArchiveRootPath(), DummyCollationKey(), DummyArchiveRootBasicFileAttributes(),\n        null, null, false, MimeType.DIRECTORY\n    )\n\n// Dummy collation key only to be added to the selection set, which may be used to determine file\n// type when confirming deletion.\nprivate class DummyCollationKey : CollationKey(\"\") {\n    override fun compareTo(other: CollationKey?): Int {\n        throw UnsupportedOperationException()\n    }\n\n    override fun toByteArray(): ByteArray {\n        throw UnsupportedOperationException()\n    }\n}\n\n// Dummy attributes only to be added to the selection set, which may be used to determine file\n// type when confirming deletion.\nprivate class DummyArchiveRootBasicFileAttributes : BasicFileAttributes {\n    override fun lastModifiedTime(): FileTime {\n        throw UnsupportedOperationException()\n    }\n\n    override fun lastAccessTime(): FileTime {\n        throw UnsupportedOperationException()\n    }\n\n    override fun creationTime(): FileTime {\n        throw UnsupportedOperationException()\n    }\n\n    override fun isRegularFile(): Boolean = false\n\n    override fun isDirectory(): Boolean = true\n\n    override fun isSymbolicLink(): Boolean = false\n\n    override fun isOther(): Boolean = false\n\n    override fun size(): Long {\n        throw UnsupportedOperationException()\n    }\n\n    override fun fileKey(): Any {\n        throw UnsupportedOperationException()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/FileItemSet.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.compat.writeParcelableListCompat\nimport me.zhanghai.android.files.file.FileItem\nimport me.zhanghai.android.files.util.LinkedMapSet\nimport me.zhanghai.android.files.util.readParcelableListCompat\n\nclass FileItemSet() : LinkedMapSet<Path, FileItem>(FileItem::path), Parcelable {\n    constructor(parcel: Parcel) : this() {\n        addAll(parcel.readParcelableListCompat())\n    }\n\n    override fun writeToParcel(parcel: Parcel, flags: Int) {\n        parcel.writeParcelableListCompat(toList(), flags)\n    }\n\n    override fun describeContents(): Int = 0\n\n    companion object CREATOR : Parcelable.Creator<FileItemSet> {\n        override fun createFromParcel(parcel: Parcel): FileItemSet = FileItemSet(parcel)\n\n        override fun newArray(size: Int): Array<FileItemSet?> = arrayOfNulls(size)\n    }\n}\n\nfun fileItemSetOf(vararg files: FileItem) = FileItemSet().apply { addAll(files) }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/FileListActivity.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Bundle\nimport android.view.KeyEvent\nimport android.view.View\nimport androidx.activity.result.contract.ActivityResultContract\nimport androidx.fragment.app.commit\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.app.AppActivity\nimport me.zhanghai.android.files.file.MimeType\nimport me.zhanghai.android.files.util.createIntent\nimport me.zhanghai.android.files.util.extraPath\nimport me.zhanghai.android.files.util.putArgs\n\nclass FileListActivity : AppActivity() {\n    private lateinit var fragment: FileListFragment\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        // Calls ensureSubDecor().\n        findViewById<View>(android.R.id.content)\n        if (savedInstanceState == null) {\n            fragment = FileListFragment().putArgs(FileListFragment.Args(intent))\n            supportFragmentManager.commit { add(android.R.id.content, fragment) }\n        } else {\n            fragment = supportFragmentManager.findFragmentById(android.R.id.content)\n                as FileListFragment\n        }\n    }\n\n    override fun onKeyShortcut(keyCode: Int, event: KeyEvent): Boolean {\n        if (fragment.onKeyShortcut(keyCode, event)) {\n            return true\n        }\n        return super.onKeyUp(keyCode, event)\n    }\n\n    companion object {\n        fun createViewIntent(path: Path): Intent =\n            FileListActivity::class.createIntent()\n                .setAction(Intent.ACTION_VIEW)\n                .apply { extraPath = path }\n    }\n\n    class OpenFileContract : ActivityResultContract<List<MimeType>, Path?>() {\n        override fun createIntent(context: Context, input: List<MimeType>): Intent =\n            FileListActivity::class.createIntent()\n                .setAction(Intent.ACTION_OPEN_DOCUMENT)\n                .setType(MimeType.ANY.value)\n                .addCategory(Intent.CATEGORY_OPENABLE)\n                .putExtra(Intent.EXTRA_MIME_TYPES, input.map { it.value }.toTypedArray())\n\n        override fun parseResult(resultCode: Int, intent: Intent?): Path? =\n            if (resultCode == RESULT_OK) intent?.extraPath else null\n    }\n\n    class CreateFileContract : ActivityResultContract<Triple<MimeType, String?, Path?>, Path?>() {\n        override fun createIntent(\n            context: Context,\n            input: Triple<MimeType, String?, Path?>\n        ): Intent =\n            FileListActivity::class.createIntent()\n                .setAction(Intent.ACTION_CREATE_DOCUMENT)\n                .setType(input.first.value)\n                .addCategory(Intent.CATEGORY_OPENABLE)\n                .apply {\n                    input.second?.let { putExtra(Intent.EXTRA_TITLE, it) }\n                    input.third?.let { extraPath = it }\n                }\n\n        override fun parseResult(resultCode: Int, intent: Intent?): Path? =\n            if (resultCode == RESULT_OK) intent?.extraPath else null\n    }\n\n    class OpenDirectoryContract : ActivityResultContract<Path?, Path?>() {\n        override fun createIntent(context: Context, input: Path?): Intent =\n            FileListActivity::class.createIntent()\n                .setAction(Intent.ACTION_OPEN_DOCUMENT_TREE)\n                .apply { input?.let { extraPath = it } }\n\n        override fun parseResult(resultCode: Int, intent: Intent?): Path? =\n            if (resultCode == RESULT_OK) intent?.extraPath else null\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/FileListAdapter.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.text.TextUtils\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.ImageButton\nimport android.widget.ImageView\nimport android.widget.TextView\nimport androidx.appcompat.widget.PopupMenu\nimport androidx.core.view.isVisible\nimport androidx.recyclerview.widget.DiffUtil\nimport androidx.recyclerview.widget.RecyclerView\nimport coil.dispose\nimport coil.load\nimport java8.nio.file.Path\nimport me.zhanghai.android.fastscroll.PopupTextProvider\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.coil.AppIconPackageName\nimport me.zhanghai.android.files.compat.foregroundCompat\nimport me.zhanghai.android.files.compat.getDrawableCompat\nimport me.zhanghai.android.files.compat.isSingleLineCompat\nimport me.zhanghai.android.files.databinding.FileItemGridBinding\nimport me.zhanghai.android.files.databinding.FileItemListBinding\nimport me.zhanghai.android.files.file.FileItem\nimport me.zhanghai.android.files.file.fileSize\nimport me.zhanghai.android.files.file.formatShort\nimport me.zhanghai.android.files.file.iconRes\nimport me.zhanghai.android.files.file.isApk\nimport me.zhanghai.android.files.provider.archive.isArchivePath\nimport me.zhanghai.android.files.provider.common.isEncrypted\nimport me.zhanghai.android.files.settings.Settings\nimport me.zhanghai.android.files.ui.AnimatedListAdapter\nimport me.zhanghai.android.files.ui.CheckableForegroundLinearLayout\nimport me.zhanghai.android.files.ui.CheckableItemBackground\nimport me.zhanghai.android.files.util.isMaterial3Theme\nimport me.zhanghai.android.files.util.layoutInflater\nimport me.zhanghai.android.files.util.valueCompat\nimport java.util.Locale\n\nclass FileListAdapter(\n    private val listener: Listener\n) : AnimatedListAdapter<FileItem, FileListAdapter.ViewHolder>(CALLBACK), PopupTextProvider {\n    private var isSearching = false\n\n    private lateinit var _viewType: FileViewType\n    var viewType: FileViewType\n        get() = _viewType\n        set(value) {\n            _viewType = value\n            if (!isSearching) {\n                super.replace(list, true)\n            }\n        }\n\n    private lateinit var _sortOptions: FileSortOptions\n    var sortOptions: FileSortOptions\n        get() = _sortOptions\n        set(value) {\n            _sortOptions = value\n            if (!isSearching) {\n                val sortedList = list.sortedWith(value.createComparator())\n                super.replace(sortedList, true)\n                rebuildFilePositionMap()\n            }\n        }\n\n    var pickOptions: PickOptions? = null\n        set(value) {\n            field = value\n            notifyItemRangeChanged(0, itemCount, PAYLOAD_STATE_CHANGED)\n        }\n\n    private val selectedFiles = fileItemSetOf()\n\n    private val filePositionMap = mutableMapOf<Path, Int>()\n\n    private lateinit var _nameEllipsize: TextUtils.TruncateAt\n    var nameEllipsize: TextUtils.TruncateAt\n        get() = _nameEllipsize\n        set(value) {\n            _nameEllipsize = value\n            notifyItemRangeChanged(0, itemCount, PAYLOAD_STATE_CHANGED)\n        }\n\n    fun replaceSelectedFiles(files: FileItemSet) {\n        val changedFiles = fileItemSetOf()\n        val iterator = selectedFiles.iterator()\n        while (iterator.hasNext()) {\n            val file = iterator.next()\n            if (file !in files) {\n                iterator.remove()\n                changedFiles.add(file)\n            }\n        }\n        for (file in files) {\n            if (file !in selectedFiles) {\n                selectedFiles.add(file)\n                changedFiles.add(file)\n            }\n        }\n        for (file in changedFiles) {\n            val position = filePositionMap[file.path]\n            position?.let { notifyItemChanged(it, PAYLOAD_STATE_CHANGED) }\n        }\n    }\n\n    private fun selectFile(file: FileItem) {\n        if (!isFileSelectable(file)) {\n            return\n        }\n        val selected = file in selectedFiles\n        val pickOptions = pickOptions\n        if (!selected && pickOptions != null && !pickOptions.allowMultiple) {\n            listener.clearSelectedFiles()\n        }\n        listener.selectFile(file, !selected)\n    }\n\n    fun selectAllFiles() {\n        val files = fileItemSetOf()\n        for (index in 0..<itemCount) {\n            val file = getItem(index)\n            if (isFileSelectable(file)) {\n                files.add(file)\n            }\n        }\n        listener.selectFiles(files, true)\n    }\n\n    private fun isFileSelectable(file: FileItem): Boolean {\n        val pickOptions = pickOptions ?: return true\n        return when (pickOptions.mode) {\n            PickOptions.Mode.OPEN_FILE, PickOptions.Mode.CREATE_FILE ->\n                !file.attributes.isDirectory &&\n                    pickOptions.mimeTypes.any { it.match(file.mimeType) }\n            PickOptions.Mode.OPEN_DIRECTORY -> file.attributes.isDirectory\n        }\n    }\n\n    override fun clear() {\n        super.clear()\n\n        rebuildFilePositionMap()\n    }\n\n    @Deprecated(\"\", ReplaceWith(\"replaceListAndSearching(list, searching)\"))\n    override fun replace(list: List<FileItem>, clear: Boolean) {\n        throw UnsupportedOperationException()\n    }\n\n    fun replaceListAndIsSearching(list: List<FileItem>, isSearching: Boolean) {\n        val clear = this.isSearching != isSearching\n        this.isSearching = isSearching\n        val sortedList = if (!isSearching) list.sortedWith(sortOptions.createComparator()) else list\n        super.replace(sortedList, clear)\n        rebuildFilePositionMap()\n    }\n\n    private fun rebuildFilePositionMap() {\n        filePositionMap.clear()\n        for (index in 0..<itemCount) {\n            val file = getItem(index)\n            filePositionMap[file.path] = index\n        }\n    }\n\n    override fun getItemViewType(position: Int): Int = viewType.ordinal\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {\n        val viewType = FileViewType.entries[viewType]\n        val inflater = parent.context.layoutInflater\n        val holder = when (viewType) {\n            FileViewType.LIST -> ViewHolder(FileItemListBinding.inflate(inflater, parent, false))\n            FileViewType.GRID -> ViewHolder(FileItemGridBinding.inflate(inflater, parent, false))\n        }\n        return holder.apply {\n            itemLayout.apply {\n                val context = context\n                val isMaterial3Theme = context.isMaterial3Theme\n                if (viewType == FileViewType.GRID && isMaterial3Theme) {\n                    foregroundCompat =\n                        context.getDrawableCompat(R.drawable.file_item_grid_foreground_material3)\n                }\n                background = if (viewType == FileViewType.GRID && isMaterial3Theme) {\n                    CheckableItemBackground.create(4f, 12f, context)\n                } else {\n                    CheckableItemBackground.create(0f, 0f, context)\n                }\n            }\n            thumbnailOutlineView?.apply {\n                val context = context\n                if (context.isMaterial3Theme) {\n                    background = context.getDrawableCompat(\n                        R.drawable.file_item_grid_thumbnail_outline_material3\n                    )\n                }\n            }\n            popupMenu = PopupMenu(menuButton.context, menuButton)\n                .apply { inflate(R.menu.file_item) }\n            menuButton.setOnClickListener { popupMenu.show() }\n        }\n    }\n\n    override fun onBindViewHolder(holder: ViewHolder, position: Int) {\n        throw UnsupportedOperationException()\n    }\n\n    override fun onBindViewHolder(holder: ViewHolder, position: Int, payloads: List<Any>) {\n        val file = getItem(position)\n        val isDirectory = file.attributes.isDirectory\n        val isEnabled = isFileSelectable(file) || isDirectory\n        holder.itemLayout.isEnabled = isEnabled\n        holder.menuButton.isEnabled = isEnabled\n        val menu = holder.popupMenu.menu\n        val path = file.path\n        val hasPickOptions = pickOptions != null\n        val isReadOnly = path.fileSystem.isReadOnly\n        menu.findItem(R.id.action_cut).isVisible = !hasPickOptions && !isReadOnly\n        menu.findItem(R.id.action_copy).isVisible = !hasPickOptions\n        val checked = file in selectedFiles\n        holder.itemLayout.isChecked = checked\n        holder.nameText.apply {\n            if (isSingleLineCompat) {\n                val nameEllipsize = nameEllipsize\n                ellipsize = nameEllipsize\n                isSelected = nameEllipsize == TextUtils.TruncateAt.MARQUEE\n            }\n        }\n        if (payloads.isNotEmpty()) {\n            return\n        }\n        bindViewHolderAnimation(holder)\n        holder.itemLayout.apply {\n            setOnClickListener {\n                if (selectedFiles.isEmpty()) {\n                    listener.openFile(file)\n                } else {\n                    selectFile(file)\n                }\n            }\n            setOnLongClickListener {\n                if (selectedFiles.isEmpty()) {\n                    selectFile(file)\n                } else {\n                    listener.openFile(file)\n                }\n                true\n            }\n        }\n        holder.iconLayout.setOnClickListener { selectFile(file) }\n        val iconRes = file.mimeType.iconRes\n        holder.iconImage.apply {\n            isVisible = true\n            setImageResource(iconRes)\n        }\n        holder.directoryThumbnailImage?.isVisible = isDirectory\n        holder.thumbnailOutlineView?.isVisible = !isDirectory\n        val supportsThumbnail = file.supportsThumbnail\n        val shouldLoadThumbnailIcon = supportsThumbnail && holder.thumbnailIconImage != null &&\n            file.mimeType.isApk\n        val attributes = file.attributes\n        holder.thumbnailIconImage?.apply {\n            dispose()\n            isVisible = !isDirectory\n            setImageResource(iconRes)\n            if (shouldLoadThumbnailIcon) {\n                load(path to attributes)\n            }\n        }\n        holder.thumbnailImage.apply {\n            dispose()\n            setImageDrawable(null)\n            val shouldLoadThumbnail = supportsThumbnail && !shouldLoadThumbnailIcon\n            isVisible = shouldLoadThumbnail\n            if (shouldLoadThumbnail) {\n                load(path to attributes) {\n                    listener { _, _ ->\n                        val iconImage = holder.thumbnailIconImage ?: holder.iconImage\n                        iconImage.isVisible = false\n                    }\n                }\n            }\n        }\n        holder.appIconBadgeImage.apply {\n            dispose()\n            setImageDrawable(null)\n            val appDirectoryPackageName = file.appDirectoryPackageName\n            val hasAppIconBadge = appDirectoryPackageName != null\n            isVisible = hasAppIconBadge\n            if (hasAppIconBadge) {\n                load(AppIconPackageName(appDirectoryPackageName!!))\n            }\n        }\n        holder.badgeImage.apply {\n            val badgeIconRes = if (file.attributesNoFollowLinks.isSymbolicLink) {\n                if (file.isSymbolicLinkBroken) {\n                    R.drawable.error_badge_icon_18dp\n                } else {\n                    R.drawable.symbolic_link_badge_icon_18dp\n                }\n            } else if (file.attributesNoFollowLinks.isEncrypted()) {\n                R.drawable.encrypted_badge_icon_18dp\n            } else {\n                null\n            }\n            val hasBadge = badgeIconRes != null\n            isVisible = hasBadge\n            if (hasBadge) {\n                setImageResource(badgeIconRes!!)\n            } else {\n                setImageDrawable(null)\n            }\n        }\n        holder.nameText.text = file.name\n        holder.descriptionText?.text = if (isDirectory) {\n            null\n        } else {\n            val context = holder.descriptionText!!.context\n            val lastModificationTime = attributes.lastModifiedTime().toInstant()\n                .formatShort(context)\n            val size = attributes.fileSize.formatHumanReadable(context)\n            val descriptionSeparator = context.getString(R.string.file_item_description_separator)\n            listOf(lastModificationTime, size).joinToString(descriptionSeparator)\n        }\n        val isArchivePath = path.isArchivePath\n        menu.findItem(R.id.action_copy)\n            .setTitle(if (isArchivePath) R.string.file_item_action_extract else R.string.copy)\n        menu.findItem(R.id.action_delete).isVisible = !isReadOnly\n        menu.findItem(R.id.action_rename).isVisible = !isReadOnly\n        menu.findItem(R.id.action_extract).isVisible = file.isArchiveFile\n        menu.findItem(R.id.action_archive).isVisible = !isArchivePath\n        menu.findItem(R.id.action_add_bookmark).isVisible = isDirectory\n        holder.popupMenu.setOnMenuItemClickListener {\n            when (it.itemId) {\n                R.id.action_open_with -> {\n                    listener.openFileWith(file)\n                    true\n                }\n                R.id.action_cut -> {\n                    listener.cutFile(file)\n                    true\n                }\n                R.id.action_copy -> {\n                    listener.copyFile(file)\n                    true\n                }\n                R.id.action_delete -> {\n                    listener.confirmDeleteFile(file)\n                    true\n                }\n                R.id.action_rename -> {\n                    listener.showRenameFileDialog(file)\n                    true\n                }\n                R.id.action_extract -> {\n                    listener.extractFile(file)\n                    true\n                }\n                R.id.action_archive -> {\n                    listener.showCreateArchiveDialog(file)\n                    true\n                }\n                R.id.action_share -> {\n                    listener.shareFile(file)\n                    true\n                }\n                R.id.action_copy_path -> {\n                    listener.copyPath(file)\n                    true\n                }\n                R.id.action_add_bookmark -> {\n                    listener.addBookmark(file)\n                    true\n                }\n                R.id.action_create_shortcut -> {\n                    listener.createShortcut(file)\n                    true\n                }\n                R.id.action_properties -> {\n                    listener.showPropertiesDialog(file)\n                    true\n                }\n                else -> false\n            }\n        }\n    }\n\n    override fun getPopupText(view: View, position: Int): CharSequence {\n        val file = getItem(position)\n        return when (sortOptions.by) {\n            FileSortOptions.By.NAME -> file.name.take(1).uppercase(Locale.getDefault())\n            FileSortOptions.By.TYPE -> file.extension.uppercase(Locale.getDefault())\n            FileSortOptions.By.SIZE -> file.attributes.fileSize.formatHumanReadable(view.context)\n            FileSortOptions.By.LAST_MODIFIED ->\n                file.attributes.lastModifiedTime().toInstant().formatShort(view.context)\n        }\n    }\n\n    override val isAnimationEnabled: Boolean\n        get() = Settings.FILE_LIST_ANIMATION.valueCompat\n\n    companion object {\n        private val PAYLOAD_STATE_CHANGED = Any()\n\n        private val CALLBACK = object : DiffUtil.ItemCallback<FileItem>() {\n            override fun areItemsTheSame(oldItem: FileItem, newItem: FileItem): Boolean =\n                oldItem.path == newItem.path\n\n            override fun areContentsTheSame(oldItem: FileItem, newItem: FileItem): Boolean =\n                oldItem == newItem\n        }\n    }\n\n    class ViewHolder private constructor(\n        root: View,\n        val itemLayout: CheckableForegroundLinearLayout,\n        val iconLayout: View,\n        val iconImage: ImageView,\n        val directoryThumbnailImage: ImageView?,\n        val thumbnailOutlineView: View?,\n        val thumbnailIconImage: ImageView?,\n        val thumbnailImage: ImageView,\n        val appIconBadgeImage: ImageView,\n        val badgeImage: ImageView,\n        val nameText: TextView,\n        val descriptionText: TextView?,\n        val menuButton: ImageButton\n    ) : RecyclerView.ViewHolder(root) {\n        constructor(binding: FileItemListBinding) : this(\n            binding.root,\n            binding.itemLayout,\n            binding.iconLayout,\n            binding.iconImage,\n            null,\n            null,\n            null,\n            binding.thumbnailImage,\n            binding.appIconBadgeImage,\n            binding.badgeImage,\n            binding.nameText,\n            binding.descriptionText,\n            binding.menuButton\n        )\n\n        constructor(binding: FileItemGridBinding) : this(\n            binding.root,\n            binding.itemLayout,\n            binding.iconLayout,\n            binding.iconImage,\n            binding.directoryThumbnailImage,\n            binding.thumbnailOutlineView,\n            binding.thumbnailIconImage,\n            binding.thumbnailImage,\n            binding.appIconBadgeImage,\n            binding.badgeImage,\n            binding.nameText,\n            null,\n            binding.menuButton\n        )\n\n        lateinit var popupMenu: PopupMenu\n    }\n\n    interface Listener {\n        fun clearSelectedFiles()\n        fun selectFile(file: FileItem, selected: Boolean)\n        fun selectFiles(files: FileItemSet, selected: Boolean)\n        fun openFile(file: FileItem)\n        fun openFileWith(file: FileItem)\n        fun cutFile(file: FileItem)\n        fun copyFile(file: FileItem)\n        fun confirmDeleteFile(file: FileItem)\n        fun showRenameFileDialog(file: FileItem)\n        fun extractFile(file: FileItem)\n        fun showCreateArchiveDialog(file: FileItem)\n        fun shareFile(file: FileItem)\n        fun copyPath(file: FileItem)\n        fun addBookmark(file: FileItem)\n        fun createShortcut(file: FileItem)\n        fun showPropertiesDialog(file: FileItem)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/FileListFragment.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.app.Activity\nimport android.content.ClipData\nimport android.content.Context\nimport android.content.Intent\nimport android.content.pm.PackageManager\nimport android.net.Uri\nimport android.os.Build\nimport android.os.Bundle\nimport android.os.Environment\nimport android.os.Handler\nimport android.os.Looper\nimport android.text.TextUtils\nimport android.view.KeyCharacterMap\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.widget.EditText\nimport android.widget.ProgressBar\nimport android.widget.TextView\nimport androidx.activity.OnBackPressedCallback\nimport androidx.activity.result.contract.ActivityResultContract\nimport androidx.activity.result.contract.ActivityResultContracts\nimport androidx.annotation.RequiresApi\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.appcompat.widget.SearchView\nimport androidx.appcompat.widget.Toolbar\nimport androidx.core.content.pm.ShortcutInfoCompat\nimport androidx.core.content.pm.ShortcutManagerCompat\nimport androidx.core.graphics.drawable.IconCompat\nimport androidx.core.view.GravityCompat\nimport androidx.core.view.isVisible\nimport androidx.core.view.updatePaddingRelative\nimport androidx.drawerlayout.widget.DrawerLayout\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.commit\nimport androidx.lifecycle.LifecycleOwner\nimport androidx.recyclerview.widget.GridLayoutManager\nimport androidx.recyclerview.widget.RecyclerView\nimport androidx.swiperefreshlayout.widget.SwipeRefreshLayout\nimport com.leinardi.android.speeddial.SpeedDialView\nimport java8.nio.file.Path\nimport java8.nio.file.Paths\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.app.application\nimport me.zhanghai.android.files.app.clipboardManager\nimport me.zhanghai.android.files.compat.checkSelfPermissionCompat\nimport me.zhanghai.android.files.compat.setGroupDividerEnabledCompat\nimport me.zhanghai.android.files.databinding.FileListFragmentAppBarIncludeBinding\nimport me.zhanghai.android.files.databinding.FileListFragmentBinding\nimport me.zhanghai.android.files.databinding.FileListFragmentBottomBarIncludeBinding\nimport me.zhanghai.android.files.databinding.FileListFragmentContentIncludeBinding\nimport me.zhanghai.android.files.databinding.FileListFragmentIncludeBinding\nimport me.zhanghai.android.files.databinding.FileListFragmentSpeedDialIncludeBinding\nimport me.zhanghai.android.files.file.FileItem\nimport me.zhanghai.android.files.file.MimeType\nimport me.zhanghai.android.files.file.asMimeTypeOrNull\nimport me.zhanghai.android.files.file.extension\nimport me.zhanghai.android.files.file.fileProviderUri\nimport me.zhanghai.android.files.file.isApk\nimport me.zhanghai.android.files.file.isImage\nimport me.zhanghai.android.files.filejob.FileJobService\nimport me.zhanghai.android.files.filelist.FileSortOptions.By\nimport me.zhanghai.android.files.filelist.FileSortOptions.Order\nimport me.zhanghai.android.files.fileproperties.FilePropertiesDialogFragment\nimport me.zhanghai.android.files.navigation.BookmarkDirectories\nimport me.zhanghai.android.files.navigation.BookmarkDirectory\nimport me.zhanghai.android.files.navigation.NavigationFragment\nimport me.zhanghai.android.files.navigation.NavigationRootMapLiveData\nimport me.zhanghai.android.files.provider.archive.createArchiveRootPath\nimport me.zhanghai.android.files.provider.archive.isArchivePath\nimport me.zhanghai.android.files.provider.linux.isLinuxPath\nimport me.zhanghai.android.files.settings.Settings\nimport me.zhanghai.android.files.terminal.Terminal\nimport me.zhanghai.android.files.ui.AppBarLayoutExpandHackListener\nimport me.zhanghai.android.files.ui.CoordinatorAppBarLayout\nimport me.zhanghai.android.files.ui.DrawerLayoutOnBackPressedCallback\nimport me.zhanghai.android.files.ui.FixQueryChangeSearchView\nimport me.zhanghai.android.files.ui.OverlayToolbarActionMode\nimport me.zhanghai.android.files.ui.PersistentBarLayout\nimport me.zhanghai.android.files.ui.PersistentBarLayoutToolbarActionMode\nimport me.zhanghai.android.files.ui.PersistentDrawerLayout\nimport me.zhanghai.android.files.ui.ScrollingViewOnApplyWindowInsetsListener\nimport me.zhanghai.android.files.ui.SpeedDialViewOnBackPressedCallback\nimport me.zhanghai.android.files.ui.ThemedFastScroller\nimport me.zhanghai.android.files.ui.ToolbarActionMode\nimport me.zhanghai.android.files.util.DebouncedRunnable\nimport me.zhanghai.android.files.util.Failure\nimport me.zhanghai.android.files.util.Loading\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.Stateful\nimport me.zhanghai.android.files.util.Success\nimport me.zhanghai.android.files.util.addOnBackPressedCallback\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.asFileName\nimport me.zhanghai.android.files.util.asFileNameOrNull\nimport me.zhanghai.android.files.util.checkSelfPermission\nimport me.zhanghai.android.files.util.copyText\nimport me.zhanghai.android.files.util.create\nimport me.zhanghai.android.files.util.createInstallPackageIntent\nimport me.zhanghai.android.files.util.createIntent\nimport me.zhanghai.android.files.util.createManageAppAllFilesAccessPermissionIntent\nimport me.zhanghai.android.files.util.createSendStreamIntent\nimport me.zhanghai.android.files.util.createViewIntent\nimport me.zhanghai.android.files.util.extraPath\nimport me.zhanghai.android.files.util.extraPathList\nimport me.zhanghai.android.files.util.fadeToVisibilityUnsafe\nimport me.zhanghai.android.files.util.getDimensionDp\nimport me.zhanghai.android.files.util.getQuantityString\nimport me.zhanghai.android.files.util.hasSw600Dp\nimport me.zhanghai.android.files.util.isOrientationLandscape\nimport me.zhanghai.android.files.util.putArgs\nimport me.zhanghai.android.files.util.setOnEditorConfirmActionListener\nimport me.zhanghai.android.files.util.showToast\nimport me.zhanghai.android.files.util.startActivitySafe\nimport me.zhanghai.android.files.util.supportsExternalStorageManager\nimport me.zhanghai.android.files.util.takeIfNotEmpty\nimport me.zhanghai.android.files.util.valueCompat\nimport me.zhanghai.android.files.util.viewModels\nimport me.zhanghai.android.files.util.withChooser\nimport me.zhanghai.android.files.viewer.image.ImageViewerActivity\nimport kotlin.math.roundToInt\n\nclass FileListFragment : Fragment(), BreadcrumbLayout.Listener, FileListAdapter.Listener,\n    ConfirmReplaceFileDialogFragment.Listener, OpenApkDialogFragment.Listener,\n    ConfirmDeleteFilesDialogFragment.Listener, CreateArchiveDialogFragment.Listener,\n    RenameFileDialogFragment.Listener, CreateFileDialogFragment.Listener,\n    CreateDirectoryDialogFragment.Listener, NavigateToPathDialogFragment.Listener,\n    NavigationFragment.Listener, ShowRequestAllFilesAccessRationaleDialogFragment.Listener,\n    ShowRequestNotificationPermissionRationaleDialogFragment.Listener,\n    ShowRequestNotificationPermissionInSettingsRationaleDialogFragment.Listener,\n    ShowRequestStoragePermissionRationaleDialogFragment.Listener,\n    ShowRequestStoragePermissionInSettingsRationaleDialogFragment.Listener {\n    private val requestAllFilesAccessLauncher = registerForActivityResult(\n        RequestAllFilesAccessContract(), this::onRequestAllFilesAccessResult\n    )\n    private val requestStoragePermissionLauncher = registerForActivityResult(\n        ActivityResultContracts.RequestPermission(), this::onRequestStoragePermissionResult\n    )\n    private val requestStoragePermissionInSettingsLauncher = registerForActivityResult(\n        RequestPermissionInSettingsContract(android.Manifest.permission.WRITE_EXTERNAL_STORAGE),\n        this::onRequestStoragePermissionInSettingsResult\n    )\n    @RequiresApi(Build.VERSION_CODES.TIRAMISU)\n    private val requestNotificationPermissionLauncher = registerForActivityResult(\n        ActivityResultContracts.RequestPermission(), this::onRequestNotificationPermissionResult\n    )\n    @RequiresApi(Build.VERSION_CODES.TIRAMISU)\n    private val requestNotificationPermissionInSettingsLauncher = registerForActivityResult(\n        RequestPermissionInSettingsContract(android.Manifest.permission.POST_NOTIFICATIONS),\n        this::onRequestNotificationPermissionInSettingsResult\n    )\n\n    private val args by args<Args>()\n    private val argsPath by lazy { args.intent.extraPath }\n\n    private val viewModel by viewModels { { FileListViewModel() } }\n\n    private lateinit var binding: Binding\n\n    private lateinit var navigationFragment: NavigationFragment\n\n    private lateinit var menuBinding: MenuBinding\n\n    private lateinit var overlayActionMode: ToolbarActionMode\n\n    private lateinit var bottomActionMode: ToolbarActionMode\n\n    private lateinit var layoutManager: GridLayoutManager\n\n    private lateinit var adapter: FileListAdapter\n\n    private val debouncedSearchRunnable = DebouncedRunnable(Handler(Looper.getMainLooper()), 1000) {\n        if (!isResumed || !viewModel.isSearchViewExpanded) {\n            return@DebouncedRunnable\n        }\n        val query = viewModel.searchViewQuery\n        if (query.isEmpty()) {\n            return@DebouncedRunnable\n        }\n        viewModel.search(query)\n    }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        setHasOptionsMenu(true)\n    }\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View =\n        Binding.inflate(inflater, container, false)\n            .also { binding = it }\n            .root\n\n    override fun onActivityCreated(savedInstanceState: Bundle?) {\n        super.onActivityCreated(savedInstanceState)\n\n        if (savedInstanceState == null) {\n            navigationFragment = NavigationFragment()\n            childFragmentManager.commit { add(R.id.navigationFragment, navigationFragment) }\n        } else {\n            navigationFragment = childFragmentManager.findFragmentById(R.id.navigationFragment)\n                as NavigationFragment\n        }\n        navigationFragment.listener = this\n        val activity = requireActivity() as AppCompatActivity\n        activity.setTitle(R.string.file_list_title)\n        activity.setSupportActionBar(binding.toolbar)\n        overlayActionMode = OverlayToolbarActionMode(binding.overlayToolbar)\n        bottomActionMode = PersistentBarLayoutToolbarActionMode(\n            binding.persistentBarLayout, binding.bottomBarLayout, binding.bottomToolbar\n        )\n        val contentLayoutInitialPaddingBottom = binding.contentLayout.paddingBottom\n        binding.appBarLayout.addOnOffsetChangedListener { _, verticalOffset ->\n            binding.contentLayout.updatePaddingRelative(\n                bottom = contentLayoutInitialPaddingBottom +\n                    binding.appBarLayout.totalScrollRange + verticalOffset\n            )\n        }\n        binding.appBarLayout.syncBackgroundColorTo(binding.overlayToolbar)\n        binding.breadcrumbLayout.setListener(this)\n        if (!(activity.hasSw600Dp && activity.isOrientationLandscape)) {\n            binding.swipeRefreshLayout.setProgressViewEndTarget(\n                true, binding.swipeRefreshLayout.progressViewEndOffset\n            )\n        }\n        binding.swipeRefreshLayout.setOnRefreshListener { this.refresh() }\n        layoutManager = GridLayoutManager(activity, 1)\n        binding.recyclerView.layoutManager = layoutManager\n        adapter = FileListAdapter(this)\n        binding.recyclerView.adapter = adapter\n        val fastScroller = ThemedFastScroller.create(binding.recyclerView)\n        binding.recyclerView.setOnApplyWindowInsetsListener(\n            ScrollingViewOnApplyWindowInsetsListener(binding.recyclerView, fastScroller)\n        )\n        binding.speedDialView.inflate(R.menu.file_list_speed_dial)\n        binding.speedDialView.setOnActionSelectedListener {\n            when (it.id) {\n                R.id.action_create_file -> showCreateFileDialog()\n                R.id.action_create_directory -> showCreateDirectoryDialog()\n            }\n            // Returning false causes the speed dial to close without animation.\n            //return false\n            binding.speedDialView.close()\n            true\n        }\n\n        val viewLifecycleOwner = viewLifecycleOwner\n        addOnBackPressedCallback(\n            object : OnBackPressedCallback(false) {\n                override fun handleOnBackPressed() {\n                    viewModel.navigateUp()\n                }\n            }\n                .also { callback ->\n                    viewModel.breadcrumbLiveData.observe(viewLifecycleOwner) {\n                        callback.isEnabled = viewModel.canNavigateUpBreadcrumb\n                    }\n                }\n        )\n        addOnBackPressedCallback(overlayActionMode.onBackPressedCallback)\n        addOnBackPressedCallback(SpeedDialViewOnBackPressedCallback(binding.speedDialView))\n        binding.drawerLayout?.let {\n            addOnBackPressedCallback(DrawerLayoutOnBackPressedCallback(it))\n        }\n\n        if (!viewModel.hasTrail) {\n            var path = argsPath\n            val intent = args.intent\n            var pickOptions: PickOptions? = null\n            when (val action = intent.action) {\n                Intent.ACTION_GET_CONTENT, Intent.ACTION_OPEN_DOCUMENT,\n                Intent.ACTION_CREATE_DOCUMENT -> {\n                    val mode = if (action == Intent.ACTION_CREATE_DOCUMENT) {\n                        PickOptions.Mode.CREATE_FILE\n                    } else {\n                        PickOptions.Mode.OPEN_FILE\n                    }\n                    val mimeType = intent.type?.asMimeTypeOrNull() ?: MimeType.ANY\n                    val fileName = if (mode == PickOptions.Mode.CREATE_FILE) {\n                        intent.getStringExtra(Intent.EXTRA_TITLE)?.asFileNameOrNull()?.value\n                            ?: mimeType.extension?.let { \"file.$it\" } ?: \"file\"\n                    } else {\n                        null\n                    }\n                    val readOnly = action == Intent.ACTION_GET_CONTENT\n                    val extraMimeTypes = if (mode == PickOptions.Mode.OPEN_FILE) {\n                        intent.getStringArrayExtra(Intent.EXTRA_MIME_TYPES)\n                            ?.mapNotNull { it.asMimeTypeOrNull() }?.takeIfNotEmpty()\n                    } else {\n                        null\n                    }\n                    val mimeTypes = extraMimeTypes ?: listOf(mimeType)\n                    val localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false)\n                    val allowMultiple = mode != PickOptions.Mode.CREATE_FILE &&\n                        intent.getBooleanExtra(Intent.EXTRA_ALLOW_MULTIPLE, false)\n                    pickOptions =\n                        PickOptions(mode, fileName, readOnly, mimeTypes, localOnly, allowMultiple)\n                }\n                Intent.ACTION_OPEN_DOCUMENT_TREE -> {\n                    val localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false)\n                    pickOptions = PickOptions(\n                        PickOptions.Mode.OPEN_DIRECTORY, null, false, emptyList(), localOnly, false\n                    )\n                }\n                ACTION_VIEW_DOWNLOADS ->\n                    path = Paths.get(\n                        Environment.getExternalStoragePublicDirectory(\n                            Environment.DIRECTORY_DOWNLOADS\n                        ).path\n                    )\n                else ->\n                    if (path != null) {\n                        val mimeType = intent.type?.asMimeTypeOrNull()\n                        if (mimeType != null && path.isArchiveFile(mimeType)) {\n                            path = path.createArchiveRootPath()\n                        }\n                    }\n            }\n            if (path == null) {\n                path = Settings.FILE_LIST_DEFAULT_DIRECTORY.valueCompat\n            }\n            viewModel.resetTo(path)\n            if (pickOptions != null) {\n                viewModel.pickOptions = pickOptions\n            }\n        }\n        viewModel.currentPathLiveData.observe(viewLifecycleOwner) { onCurrentPathChanged(it) }\n        viewModel.searchViewExpandedLiveData.observe(viewLifecycleOwner) {\n            onSearchViewExpandedChanged(it)\n        }\n        viewModel.breadcrumbLiveData.observe(viewLifecycleOwner) {\n            binding.breadcrumbLayout.setData(it)\n        }\n        viewModel.viewTypeLiveData.observe(viewLifecycleOwner) { onViewTypeChanged(it) }\n        // Live data only calls observeForever() on its sources when it is active, so we have to\n        // make view type live data active first (so that it can load its initial value) before we\n        // register another observer that needs to get the view type.\n        if (binding.persistentDrawerLayout != null) {\n            Settings.FILE_LIST_PERSISTENT_DRAWER_OPEN.observe(viewLifecycleOwner) {\n                onPersistentDrawerOpenChanged(it)\n            }\n        }\n        viewModel.sortOptionsLiveData.observe(viewLifecycleOwner) { onSortOptionsChanged(it) }\n        viewModel.viewSortPathSpecificLiveData.observe(viewLifecycleOwner) {\n            onViewSortPathSpecificChanged(it)\n        }\n        viewModel.pickOptionsLiveData.observe(viewLifecycleOwner) { onPickOptionsChanged(it) }\n        viewModel.selectedFilesLiveData.observe(viewLifecycleOwner) { onSelectedFilesChanged(it) }\n        viewModel.pasteStateLiveData.observe(viewLifecycleOwner) { onPasteStateChanged(it) }\n        Settings.FILE_NAME_ELLIPSIZE.observe(viewLifecycleOwner) { onFileNameEllipsizeChanged(it) }\n        viewModel.fileListLiveData.observe(viewLifecycleOwner) { onFileListChanged(it) }\n        Settings.FILE_LIST_SHOW_HIDDEN_FILES.observe(viewLifecycleOwner) {\n            onShowHiddenFilesChanged(it)\n        }\n    }\n\n    override fun onResume() {\n        super.onResume()\n\n        if (!viewModel.isNotificationPermissionRequested) {\n            ensureStorageAccess()\n        }\n        if (!viewModel.isStorageAccessRequested) {\n            ensureNotificationPermission()\n        }\n    }\n\n    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {\n        super.onCreateOptionsMenu(menu, inflater)\n\n        menuBinding = MenuBinding.inflate(menu, inflater)\n        menuBinding.viewSortItem.subMenu!!.setGroupDividerEnabledCompat(true)\n        setUpSearchView()\n    }\n\n    private fun setUpSearchView() {\n        val searchView = menuBinding.searchItem.actionView as FixQueryChangeSearchView\n        // MenuItem.OnActionExpandListener.onMenuItemActionExpand() is called before SearchView\n        // resets the query.\n        searchView.setOnSearchClickListener {\n            viewModel.isSearchViewExpanded = true\n            searchView.setQuery(viewModel.searchViewQuery, false)\n            debouncedSearchRunnable()\n        }\n        // SearchView.OnCloseListener.onClose() is not always called.\n        menuBinding.searchItem.setOnActionExpandListener(object : MenuItem.OnActionExpandListener {\n            override fun onMenuItemActionExpand(item: MenuItem): Boolean = true\n\n            override fun onMenuItemActionCollapse(item: MenuItem): Boolean {\n                viewModel.isSearchViewExpanded = false\n                viewModel.stopSearching()\n                return true\n            }\n        })\n        searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {\n            override fun onQueryTextSubmit(query: String): Boolean {\n                debouncedSearchRunnable.cancel()\n                viewModel.search(query)\n                return true\n            }\n\n            override fun onQueryTextChange(query: String): Boolean {\n                if (searchView.shouldIgnoreQueryChange) {\n                    return false\n                }\n                viewModel.searchViewQuery = query\n                debouncedSearchRunnable()\n                return false\n            }\n        })\n        if (viewModel.isSearchViewExpanded) {\n            menuBinding.searchItem.expandActionView()\n        }\n    }\n\n    private fun collapseSearchView() {\n        if (this::menuBinding.isInitialized && menuBinding.searchItem.isActionViewExpanded) {\n            menuBinding.searchItem.collapseActionView()\n        }\n    }\n\n    override fun onPrepareOptionsMenu(menu: Menu) {\n        super.onPrepareOptionsMenu(menu)\n\n        updateViewSortMenuItems()\n        updateSelectAllMenuItem()\n        updateShowHiddenFilesMenuItem()\n    }\n\n    override fun onOptionsItemSelected(item: MenuItem): Boolean {\n        return when (item.itemId) {\n            android.R.id.home -> {\n                binding.drawerLayout?.openDrawer(GravityCompat.START)\n                if (binding.persistentDrawerLayout != null) {\n                    Settings.FILE_LIST_PERSISTENT_DRAWER_OPEN.putValue(\n                        !Settings.FILE_LIST_PERSISTENT_DRAWER_OPEN.valueCompat\n                    )\n                }\n                true\n            }\n            R.id.action_view_list -> {\n                viewModel.viewType = FileViewType.LIST\n                true\n            }\n            R.id.action_view_grid -> {\n                viewModel.viewType = FileViewType.GRID\n                true\n            }\n            R.id.action_sort_by_name -> {\n                viewModel.setSortBy(By.NAME)\n                true\n            }\n            R.id.action_sort_by_type -> {\n                viewModel.setSortBy(By.TYPE)\n                true\n            }\n            R.id.action_sort_by_size -> {\n                viewModel.setSortBy(By.SIZE)\n                true\n            }\n            R.id.action_sort_by_last_modified -> {\n                viewModel.setSortBy(By.LAST_MODIFIED)\n                true\n            }\n            R.id.action_sort_order_ascending -> {\n                viewModel.setSortOrder(\n                    if (!menuBinding.sortOrderAscendingItem.isChecked) {\n                        Order.ASCENDING\n                    } else {\n                        Order.DESCENDING\n                    }\n                )\n                true\n            }\n            R.id.action_sort_directories_first -> {\n                viewModel.setSortDirectoriesFirst(!menuBinding.sortDirectoriesFirstItem.isChecked)\n                true\n            }\n            R.id.action_view_sort_path_specific -> {\n                viewModel.isViewSortPathSpecific = !menuBinding.viewSortPathSpecificItem.isChecked\n                true\n            }\n            R.id.action_new_task -> {\n                newTask()\n                true\n            }\n            R.id.action_navigate_up -> {\n                navigateUp()\n                true\n            }\n            R.id.action_navigate_to -> {\n                showNavigateToPathDialog()\n                true\n            }\n            R.id.action_refresh -> {\n                refresh()\n                true\n            }\n            R.id.action_select_all -> {\n                selectAllFiles()\n                true\n            }\n            R.id.action_show_hidden_files -> {\n                setShowHiddenFiles(!menuBinding.showHiddenFilesItem.isChecked)\n                true\n            }\n            R.id.action_share -> {\n                share()\n                true\n            }\n            R.id.action_copy_path -> {\n                copyPath()\n                true\n            }\n            R.id.action_open_in_terminal -> {\n                openInTerminal()\n                true\n            }\n            R.id.action_add_bookmark -> {\n                addBookmark()\n                true\n            }\n            R.id.action_create_shortcut -> {\n                createShortcut()\n                true\n            }\n            else -> super.onOptionsItemSelected(item)\n        }\n    }\n\n    fun onKeyShortcut(keyCode: Int, event: KeyEvent): Boolean {\n        if (bottomActionMode.isActive) {\n            val menu = bottomActionMode.menu\n            menu.setQwertyMode(\n                KeyCharacterMap.load(event.deviceId).keyboardType != KeyCharacterMap.NUMERIC\n            )\n            if (menu.performShortcut(keyCode, event, 0)) {\n                return true\n            }\n        }\n        if (overlayActionMode.isActive) {\n            val menu = overlayActionMode.menu\n            menu.setQwertyMode(\n                KeyCharacterMap.load(event.deviceId).keyboardType != KeyCharacterMap.NUMERIC\n            )\n            if (menu.performShortcut(keyCode, event, 0)) {\n                return true\n            }\n        }\n        return false\n    }\n\n    private fun onPersistentDrawerOpenChanged(open: Boolean) {\n        binding.persistentDrawerLayout?.let {\n            if (open) {\n                it.openDrawer(GravityCompat.START)\n            } else {\n                it.closeDrawer(GravityCompat.START)\n            }\n        }\n        updateSpanCount()\n    }\n\n    private fun onCurrentPathChanged(path: Path) {\n        updateOverlayToolbar()\n        updateBottomToolbar()\n    }\n\n    private fun onSearchViewExpandedChanged(expanded: Boolean) {\n        updateViewSortMenuItems()\n    }\n\n    private fun onFileListChanged(stateful: Stateful<List<FileItem>>) {\n        val files = stateful.value\n        val isSearching = viewModel.searchState.isSearching\n        when {\n            stateful is Failure -> binding.toolbar.setSubtitle(R.string.error)\n            stateful is Loading && !isSearching -> binding.toolbar.setSubtitle(R.string.loading)\n            else -> binding.toolbar.subtitle = getSubtitle(files!!)\n        }\n        val hasFiles = !files.isNullOrEmpty()\n        binding.swipeRefreshLayout.isRefreshing = stateful is Loading && (hasFiles || isSearching)\n        binding.progress.fadeToVisibilityUnsafe(stateful is Loading && !(hasFiles || isSearching))\n        binding.errorText.fadeToVisibilityUnsafe(stateful is Failure && !hasFiles)\n        val throwable = (stateful as? Failure)?.throwable\n        if (throwable != null) {\n            throwable.printStackTrace()\n            val error = throwable.toString()\n            if (hasFiles) {\n                showToast(error)\n            } else {\n                binding.errorText.text = error\n            }\n        }\n        binding.emptyView.fadeToVisibilityUnsafe(stateful is Success && !hasFiles)\n        if (files != null) {\n            updateAdapterFileList()\n        } else {\n            // This resets animation as well.\n            adapter.clear()\n        }\n        if (stateful is Success) {\n            viewModel.pendingState?.let { layoutManager.onRestoreInstanceState(it) }\n        }\n    }\n\n    private fun getSubtitle(files: List<FileItem>): String {\n        val directoryCount = files.count { it.attributes.isDirectory }\n        val fileCount = files.size - directoryCount\n        val directoryCountText = if (directoryCount > 0) {\n            getQuantityString(\n                R.plurals.file_list_subtitle_directory_count_format, directoryCount, directoryCount\n            )\n        } else {\n            null\n        }\n        val fileCountText = if (fileCount > 0) {\n            getQuantityString(\n                R.plurals.file_list_subtitle_file_count_format, fileCount, fileCount\n            )\n        } else {\n            null\n        }\n        return when {\n            !directoryCountText.isNullOrEmpty() && !fileCountText.isNullOrEmpty() ->\n                (directoryCountText + getString(R.string.file_list_subtitle_separator)\n                    + fileCountText)\n            !directoryCountText.isNullOrEmpty() -> directoryCountText\n            !fileCountText.isNullOrEmpty() -> fileCountText\n            else -> getString(R.string.empty)\n        }\n    }\n\n    private fun onViewTypeChanged(viewType: FileViewType) {\n        updateSpanCount()\n        adapter.viewType = viewType\n        updateViewSortMenuItems()\n    }\n\n    private fun updateSpanCount() {\n        layoutManager.spanCount = when (viewModel.viewType) {\n            FileViewType.LIST -> 1\n            FileViewType.GRID -> {\n                var widthDp = resources.configuration.screenWidthDp\n                val persistentDrawerLayout = binding.persistentDrawerLayout\n                if (persistentDrawerLayout != null &&\n                    persistentDrawerLayout.isDrawerOpen(GravityCompat.START)) {\n                    widthDp -= getDimensionDp(R.dimen.navigation_max_width).roundToInt()\n                }\n                (widthDp / 180).coerceAtLeast(2)\n            }\n        }\n    }\n\n    private fun onSortOptionsChanged(sortOptions: FileSortOptions) {\n        adapter.sortOptions = sortOptions\n        updateViewSortMenuItems()\n    }\n\n    private fun onViewSortPathSpecificChanged(pathSpecific: Boolean) {\n        updateViewSortMenuItems()\n    }\n\n    private fun updateViewSortMenuItems() {\n        if (!this::menuBinding.isInitialized) {\n            return\n        }\n        val searchViewExpanded = viewModel.isSearchViewExpanded\n        menuBinding.viewSortItem.isVisible = !searchViewExpanded\n        if (searchViewExpanded) {\n            return\n        }\n        val viewType = viewModel.viewType\n        val checkedViewTypeItem = when (viewType) {\n            FileViewType.LIST -> menuBinding.viewListItem\n            FileViewType.GRID -> menuBinding.viewGridItem\n        }\n        checkedViewTypeItem.isChecked = true\n        val sortOptions = viewModel.sortOptions\n        val checkedSortByItem = when (sortOptions.by) {\n            By.NAME -> menuBinding.sortByNameItem\n            By.TYPE -> menuBinding.sortByTypeItem\n            By.SIZE -> menuBinding.sortBySizeItem\n            By.LAST_MODIFIED -> menuBinding.sortByLastModifiedItem\n        }\n        checkedSortByItem.isChecked = true\n        menuBinding.sortOrderAscendingItem.isChecked = sortOptions.order == Order.ASCENDING\n        menuBinding.sortDirectoriesFirstItem.isChecked = sortOptions.isDirectoriesFirst\n        menuBinding.viewSortPathSpecificItem.isChecked = viewModel.isViewSortPathSpecific\n    }\n\n    private fun navigateUp() {\n        collapseSearchView()\n        viewModel.navigateUp()\n    }\n\n    private fun showNavigateToPathDialog() {\n        NavigateToPathDialogFragment.show(currentPath, this)\n    }\n\n    private fun newTask() {\n        openInNewTask(currentPath)\n    }\n\n    private fun refresh() {\n        viewModel.reload()\n    }\n\n    private fun setShowHiddenFiles(showHiddenFiles: Boolean) {\n        Settings.FILE_LIST_SHOW_HIDDEN_FILES.putValue(showHiddenFiles)\n    }\n\n    private fun onShowHiddenFilesChanged(showHiddenFiles: Boolean) {\n        updateAdapterFileList()\n        updateShowHiddenFilesMenuItem()\n    }\n\n    private fun updateAdapterFileList() {\n        var files = viewModel.fileListStateful.value ?: return\n        if (!Settings.FILE_LIST_SHOW_HIDDEN_FILES.valueCompat) {\n            files = files.filterNot { it.isHidden }\n        }\n        adapter.replaceListAndIsSearching(files, viewModel.searchState.isSearching)\n    }\n\n    private fun updateShowHiddenFilesMenuItem() {\n        if (!this::menuBinding.isInitialized) {\n            return\n        }\n        val showHiddenFiles = Settings.FILE_LIST_SHOW_HIDDEN_FILES.valueCompat\n        menuBinding.showHiddenFilesItem.isChecked = showHiddenFiles\n    }\n\n    private fun share() {\n        shareFile(currentPath, MimeType.DIRECTORY)\n    }\n\n    private fun copyPath() {\n        copyPath(currentPath)\n    }\n\n    private fun openInTerminal() {\n        val path = currentPath\n        if (path.isLinuxPath) {\n            Terminal.open(path.toFile().path, requireContext())\n        } else {\n            // TODO\n        }\n    }\n\n    override fun navigateTo(path: Path) {\n        collapseSearchView()\n        val state = layoutManager.onSaveInstanceState()\n        viewModel.navigateTo(state!!, path)\n    }\n\n    override fun copyPath(path: Path) {\n        clipboardManager.copyText(path.toUserFriendlyString(), requireContext())\n    }\n\n    override fun openInNewTask(path: Path) {\n        val intent = FileListActivity.createViewIntent(path)\n            .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT or Intent.FLAG_ACTIVITY_MULTIPLE_TASK)\n        startActivitySafe(intent)\n    }\n\n    private fun onPickOptionsChanged(pickOptions: PickOptions?) {\n        val title = if (pickOptions == null) {\n            getString(R.string.file_list_title)\n        } else {\n            val count = if (pickOptions.allowMultiple) Int.MAX_VALUE else 1\n            when (pickOptions.mode) {\n                PickOptions.Mode.OPEN_FILE ->\n                    getQuantityString(R.plurals.file_list_title_open_file, count)\n                PickOptions.Mode.CREATE_FILE -> getString(R.string.file_list_title_create_file)\n                PickOptions.Mode.OPEN_DIRECTORY ->\n                    getQuantityString(R.plurals.file_list_title_open_directory, count)\n            }\n        }\n        requireActivity().title = title\n        updateSelectAllMenuItem()\n        updateOverlayToolbar()\n        updateBottomToolbar()\n        adapter.pickOptions = pickOptions\n    }\n\n    private fun updateSelectAllMenuItem() {\n        if (!this::menuBinding.isInitialized) {\n            return\n        }\n        val pickOptions = viewModel.pickOptions\n        menuBinding.selectAllItem.isVisible = pickOptions == null || pickOptions.allowMultiple\n    }\n\n    private fun pickFiles(files: FileItemSet) {\n        pickPaths(files.mapTo(linkedSetOf()) { it.path })\n    }\n\n    private fun pickPaths(paths: LinkedHashSet<Path>) {\n        val intent = Intent().apply {\n            val pickOptions = viewModel.pickOptions!!\n            if (paths.size == 1) {\n                val path = paths.single()\n                data = path.fileProviderUri\n                extraPath = path\n            } else {\n                val mimeTypes = pickOptions.mimeTypes.map { it.value }\n                val items = paths.map { ClipData.Item(it.fileProviderUri) }\n                clipData = ClipData::class.create(null, mimeTypes, items)\n                extraPathList = paths.toList()\n            }\n            var flags =\n                Intent.FLAG_GRANT_READ_URI_PERMISSION or\n                    Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION\n            if (!pickOptions.readOnly) {\n                flags = flags or Intent.FLAG_GRANT_WRITE_URI_PERMISSION\n            }\n            if (pickOptions.mode == PickOptions.Mode.OPEN_DIRECTORY) {\n                flags = flags or Intent.FLAG_GRANT_PREFIX_URI_PERMISSION\n            }\n            addFlags(flags)\n        }\n        requireActivity().run {\n            setResult(Activity.RESULT_OK, intent)\n            finish()\n        }\n    }\n\n    private fun onSelectedFilesChanged(files: FileItemSet) {\n        updateOverlayToolbar()\n        adapter.replaceSelectedFiles(files)\n    }\n\n    private fun updateOverlayToolbar() {\n        val files = viewModel.selectedFiles\n        if (files.isEmpty()) {\n            if (overlayActionMode.isActive) {\n                overlayActionMode.finish()\n            }\n            return\n        }\n        val pickOptions = viewModel.pickOptions\n        if (pickOptions != null) {\n            overlayActionMode.title = getString(R.string.file_list_select_title_format, files.size)\n            overlayActionMode.setMenuResource(R.menu.file_list_pick)\n            val menu = overlayActionMode.menu\n            val isOpen = when (pickOptions.mode) {\n                PickOptions.Mode.OPEN_FILE, PickOptions.Mode.OPEN_DIRECTORY -> true\n                PickOptions.Mode.CREATE_FILE -> false\n            }\n            menu.findItem(R.id.action_open).isVisible = isOpen\n            menu.findItem(R.id.action_create).isVisible = !isOpen\n            menu.findItem(R.id.action_select_all).isVisible = pickOptions.allowMultiple\n        } else {\n            overlayActionMode.title = getString(R.string.file_list_select_title_format, files.size)\n            overlayActionMode.setMenuResource(R.menu.file_list_select)\n            val menu = overlayActionMode.menu\n            val isAnyFileReadOnly = files.any { it.path.fileSystem.isReadOnly }\n            menu.findItem(R.id.action_cut).isVisible = !isAnyFileReadOnly\n            val areAllFilesArchivePaths = files.all { it.path.isArchivePath }\n            menu.findItem(R.id.action_copy)\n                .setIcon(\n                    if (areAllFilesArchivePaths) {\n                        R.drawable.extract_icon_control_normal_24dp\n                    } else {\n                        R.drawable.copy_icon_control_normal_24dp\n                    }\n                )\n                .setTitle(\n                    if (areAllFilesArchivePaths) {\n                        R.string.file_list_select_action_extract\n                    } else {\n                        R.string.copy\n                    }\n                )\n            menu.findItem(R.id.action_delete).isVisible = !isAnyFileReadOnly\n            val areAllFilesArchiveFiles = files.all { it.isArchiveFile }\n            menu.findItem(R.id.action_extract).isVisible = areAllFilesArchiveFiles\n            val isCurrentPathReadOnly = viewModel.currentPath.fileSystem.isReadOnly\n            menu.findItem(R.id.action_archive).isVisible = !isCurrentPathReadOnly\n        }\n        if (!overlayActionMode.isActive) {\n            binding.appBarLayout.setExpanded(true)\n            binding.appBarLayout.addOnOffsetChangedListener(\n                AppBarLayoutExpandHackListener(binding.recyclerView)\n            )\n            overlayActionMode.start(object : ToolbarActionMode.Callback {\n                override fun onToolbarActionModeMenuItemClicked(\n                    toolbarActionMode: ToolbarActionMode,\n                    item: MenuItem\n                ): Boolean = onOverlayActionModeMenuItemClicked(item)\n\n                override fun onToolbarActionModeFinished(toolbarActionMode: ToolbarActionMode) {\n                    onOverlayActionModeFinished()\n                }\n            })\n        }\n    }\n\n    private fun onOverlayActionModeMenuItemClicked(item: MenuItem): Boolean =\n        when (item.itemId) {\n            R.id.action_open -> {\n                pickFiles(viewModel.selectedFiles)\n                true\n            }\n            R.id.action_create -> {\n                confirmReplaceFile(viewModel.selectedFiles.single())\n                true\n            }\n            R.id.action_cut -> {\n                cutFiles(viewModel.selectedFiles)\n                true\n            }\n            R.id.action_copy -> {\n                copyFiles(viewModel.selectedFiles)\n                true\n            }\n            R.id.action_delete -> {\n                confirmDeleteFiles(viewModel.selectedFiles)\n                true\n            }\n            R.id.action_extract -> {\n                extractFiles(viewModel.selectedFiles)\n                true\n            }\n            R.id.action_archive -> {\n                showCreateArchiveDialog(viewModel.selectedFiles)\n                true\n            }\n            R.id.action_share -> {\n                shareFiles(viewModel.selectedFiles)\n                true\n            }\n            R.id.action_select_all -> {\n                selectAllFiles()\n                true\n            }\n            else -> false\n        }\n\n    private fun onOverlayActionModeFinished() {\n        viewModel.clearSelectedFiles()\n    }\n\n    private fun confirmReplaceFile(file: FileItem, setFileName: Boolean = true) {\n        if (setFileName) {\n            val fileName = file.name\n            binding.bottomCreateFileNameEdit.setText(fileName)\n            binding.bottomCreateFileNameEdit.setSelection(\n                0, fileName.asFileName().baseName.length\n            )\n        }\n        ConfirmReplaceFileDialogFragment.show(file, this)\n    }\n\n    override fun replaceFile(file: FileItem) {\n        pickFiles(fileItemSetOf(file))\n    }\n\n    private fun cutFiles(files: FileItemSet) {\n        viewModel.addToPasteState(false, files)\n        viewModel.selectFiles(files, false)\n    }\n\n    private fun copyFiles(files: FileItemSet) {\n        viewModel.addToPasteState(true, files)\n        viewModel.selectFiles(files, false)\n    }\n\n    private fun confirmDeleteFiles(files: FileItemSet) {\n        ConfirmDeleteFilesDialogFragment.show(files, this)\n    }\n\n    override fun deleteFiles(files: FileItemSet) {\n        FileJobService.delete(makePathListForJob(files), requireContext())\n        viewModel.selectFiles(files, false)\n    }\n\n    private fun extractFiles(files: FileItemSet) {\n        copyFiles(files.mapTo(fileItemSetOf()) { it.createDummyArchiveRoot() })\n        viewModel.selectFiles(files, false)\n    }\n\n    private fun showCreateArchiveDialog(files: FileItemSet) {\n        CreateArchiveDialogFragment.show(files, this)\n    }\n\n    override fun archive(\n        files: FileItemSet,\n        name: String,\n        format: Int,\n        filter: Int,\n        password: String?\n    ) {\n        val archiveFile = viewModel.currentPath.resolve(name)\n        FileJobService.archive(\n            makePathListForJob(files), archiveFile, format, filter, password, requireContext()\n        )\n        viewModel.selectFiles(files, false)\n    }\n\n    private fun shareFiles(files: FileItemSet) {\n        shareFiles(files.map { it.path }, files.map { it.mimeType })\n        viewModel.selectFiles(files, false)\n    }\n\n    private fun selectAllFiles() {\n        adapter.selectAllFiles()\n    }\n\n    private fun onPasteStateChanged(pasteState: PasteState) {\n        updateBottomToolbar()\n    }\n\n    private fun updateBottomToolbar() {\n        val pickOptions = viewModel.pickOptions\n        if (pickOptions != null) {\n            bottomActionMode.setMenuResource(R.menu.file_list_pick_bottom)\n            val menu = bottomActionMode.menu\n            when (pickOptions.mode) {\n                PickOptions.Mode.CREATE_FILE -> {\n                    bottomActionMode.title = null\n                    binding.bottomCreateFileNameEdit.isVisible = true\n                    val createMenuItem = menu.findItem(R.id.action_create)\n                    binding.bottomCreateFileNameEdit.setOnEditorConfirmActionListener {\n                        onBottomActionModeMenuItemClicked(createMenuItem)\n                    }\n                    if (!viewModel.isCreateFileNameEditInitialized) {\n                        val fileName = pickOptions.fileName!!\n                        binding.bottomCreateFileNameEdit.setText(fileName)\n                        binding.bottomCreateFileNameEdit.setSelection(\n                            0, fileName.asFileName().baseName.length\n                        )\n                        binding.bottomCreateFileNameEdit.requestFocus()\n                        viewModel.isCreateFileNameEditInitialized = true\n                    }\n                    menu.findItem(R.id.action_open).isVisible = false\n                    createMenuItem.isVisible = true\n                }\n                PickOptions.Mode.OPEN_DIRECTORY -> {\n                    val path = viewModel.currentPath\n                    val navigationRoot = NavigationRootMapLiveData.valueCompat[path]\n                    val name = navigationRoot?.getName(requireContext()) ?: path.name\n                    bottomActionMode.title =\n                        getString(R.string.file_list_open_current_directory_format, name)\n                    binding.bottomCreateFileNameEdit.isVisible = false\n                    menu.findItem(R.id.action_open).isVisible = true\n                    menu.findItem(R.id.action_create).isVisible = false\n                }\n                else -> {\n                    if (bottomActionMode.isActive) {\n                        bottomActionMode.finish()\n                    }\n                    return\n                }\n            }\n        } else {\n            val pasteState = viewModel.pasteState\n            val files = pasteState.files\n            if (files.isEmpty()) {\n                if (bottomActionMode.isActive) {\n                    bottomActionMode.finish()\n                }\n                return\n            }\n            val areAllFilesArchivePaths = files.all { it.path.isArchivePath }\n            bottomActionMode.title = getString(\n                if (pasteState.copy) {\n                    if (areAllFilesArchivePaths) {\n                        R.string.file_list_paste_extract_title_format\n                    } else {\n                        R.string.file_list_paste_copy_title_format\n                    }\n                } else {\n                    R.string.file_list_paste_move_title_format\n                }, files.size\n            )\n            binding.bottomCreateFileNameEdit.isVisible = false\n            bottomActionMode.setMenuResource(R.menu.file_list_paste)\n            val isCurrentPathReadOnly = viewModel.currentPath.fileSystem.isReadOnly\n            bottomActionMode.menu.findItem(R.id.action_paste)\n                .setTitle(\n                    if (areAllFilesArchivePaths) R.string.file_list_paste_action_extract_here else R.string.paste\n                )\n                .isEnabled = !isCurrentPathReadOnly\n        }\n        if (!bottomActionMode.isActive) {\n            bottomActionMode.start(object : ToolbarActionMode.Callback {\n                override fun onToolbarNavigationIconClicked(toolbarActionMode: ToolbarActionMode) {\n                    onBottomToolbarNavigationIconClicked()\n                }\n\n                override fun onToolbarActionModeMenuItemClicked(\n                    toolbarActionMode: ToolbarActionMode,\n                    item: MenuItem\n                ): Boolean = onBottomActionModeMenuItemClicked(item)\n\n                override fun onToolbarActionModeFinished(toolbarActionMode: ToolbarActionMode) {\n                    onBottomActionModeFinished()\n                }\n            })\n        }\n    }\n\n    private fun onBottomToolbarNavigationIconClicked() {\n        val pickOptions = viewModel.pickOptions\n        if (pickOptions != null) {\n            requireActivity().finish()\n        } else {\n            bottomActionMode.finish()\n        }\n    }\n\n    private fun onBottomActionModeMenuItemClicked(item: MenuItem): Boolean =\n        when (item.itemId) {\n            R.id.action_open -> {\n                pickPaths(linkedSetOf(viewModel.currentPath))\n                true\n            }\n            R.id.action_create -> {\n                val fileName = binding.bottomCreateFileNameEdit.text.toString()\n                if (fileName.isEmpty()) {\n                    showToast(R.string.file_list_create_file_name_error_empty)\n                } else if (fileName.asFileNameOrNull() == null) {\n                    showToast(R.string.file_list_create_file_name_error_invalid)\n                } else {\n                    val file = getFileWithName(fileName)\n                    if (file != null) {\n                        confirmReplaceFile(file, false)\n                    } else {\n                        val path = viewModel.currentPath.resolve(fileName)\n                        pickPaths(linkedSetOf(path))\n                    }\n                }\n                true\n            }\n            R.id.action_paste -> {\n                pasteFiles(currentPath)\n                true\n            }\n            else -> false\n        }\n\n    private fun onBottomActionModeFinished() {\n        val pickOptions = viewModel.pickOptions\n        if (pickOptions == null) {\n            viewModel.clearPasteState()\n        }\n    }\n\n    private fun pasteFiles(targetDirectory: Path) {\n        val pasteState = viewModel.pasteState\n        if (viewModel.pasteState.copy) {\n            FileJobService.copy(\n                makePathListForJob(pasteState.files), targetDirectory, requireContext()\n            )\n        } else {\n            FileJobService.move(\n                makePathListForJob(pasteState.files), targetDirectory, requireContext()\n            )\n        }\n        viewModel.clearPasteState()\n    }\n\n    private fun makePathListForJob(files: FileItemSet): List<Path> =\n        files.map { it.path }.sortedBy { it.toUri() }\n\n    private fun onFileNameEllipsizeChanged(fileNameEllipsize: TextUtils.TruncateAt) {\n        adapter.nameEllipsize = fileNameEllipsize\n    }\n\n    override fun clearSelectedFiles() {\n        viewModel.clearSelectedFiles()\n    }\n\n    override fun selectFile(file: FileItem, selected: Boolean) {\n        viewModel.selectFile(file, selected)\n    }\n\n    override fun selectFiles(files: FileItemSet, selected: Boolean) {\n        viewModel.selectFiles(files, selected)\n    }\n\n    override fun openFile(file: FileItem) {\n        val pickOptions = viewModel.pickOptions\n        if (pickOptions != null) {\n            if (file.attributes.isDirectory) {\n                navigateTo(file.path)\n            } else {\n                when (pickOptions.mode) {\n                    PickOptions.Mode.OPEN_FILE -> pickFiles(fileItemSetOf(file))\n                    PickOptions.Mode.CREATE_FILE -> confirmReplaceFile(file)\n                    PickOptions.Mode.OPEN_DIRECTORY -> {}\n                }\n            }\n            return\n        }\n        if (file.mimeType.isApk) {\n            openApk(file)\n            return\n        }\n        if (file.isListable) {\n            navigateTo(file.listablePath)\n            return\n        }\n        openFileWithIntent(file, false)\n    }\n\n    private fun openApk(file: FileItem) {\n        if (!file.isListable) {\n            installApk(file)\n            return\n        }\n        when (Settings.OPEN_APK_DEFAULT_ACTION.valueCompat) {\n            OpenApkDefaultAction.INSTALL -> installApk(file)\n            OpenApkDefaultAction.VIEW -> viewApk(file)\n            OpenApkDefaultAction.ASK -> OpenApkDialogFragment.show(file, this)\n        }\n    }\n\n    override fun installApk(file: FileItem) {\n        val path = file.path\n        val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n            if (!path.isArchivePath) path.fileProviderUri else null\n        } else {\n            // PackageInstaller only supports file URI before N.\n            if (path.isLinuxPath) Uri.fromFile(path.toFile()) else null\n        }\n        if (uri != null) {\n            startActivitySafe(uri.createInstallPackageIntent())\n        } else {\n            FileJobService.installApk(path, requireContext())\n        }\n    }\n\n    override fun viewApk(file: FileItem) {\n        navigateTo(file.listablePath)\n    }\n\n    override fun openFileWith(file: FileItem) {\n        openFileWithIntent(file, true)\n    }\n\n    private fun openFileWithIntent(file: FileItem, withChooser: Boolean) {\n        val path = file.path\n        val mimeType = file.mimeType\n        if (path.isArchivePath) {\n            FileJobService.open(path, mimeType, withChooser, requireContext())\n        } else {\n            val intent = path.fileProviderUri.createViewIntent(mimeType)\n                .addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)\n                .apply {\n                    extraPath = path\n                    maybeAddImageViewerActivityExtras(this, path, mimeType)\n                }\n                .let {\n                    if (withChooser) {\n                        it.withChooser(\n                            EditFileActivity::class.createIntent()\n                                .putArgs(EditFileActivity.Args(path, mimeType)),\n                            OpenFileAsDialogActivity::class.createIntent()\n                                .putArgs(OpenFileAsDialogFragment.Args(path))\n                        )\n                    } else {\n                        it\n                    }\n                }\n            startActivitySafe(intent)\n        }\n    }\n\n    private fun maybeAddImageViewerActivityExtras(intent: Intent, path: Path, mimeType: MimeType) {\n        if (!mimeType.isImage) {\n            return\n        }\n        var paths = mutableListOf<Path>()\n        // We need the ordered list from our adapter instead of the list from FileListLiveData.\n        for (index in 0..<adapter.itemCount) {\n            val file = adapter.getItem(index)\n            val filePath = file.path\n            if (file.mimeType.isImage || filePath == path) {\n                paths.add(filePath)\n            }\n        }\n        var position = paths.indexOf(path)\n        if (position == -1) {\n            return\n        }\n        // HACK: Don't send too many paths to avoid TransactionTooLargeException.\n        if (paths.size > IMAGE_VIEWER_ACTIVITY_PATH_LIST_SIZE_MAX) {\n            val start = (position - IMAGE_VIEWER_ACTIVITY_PATH_LIST_SIZE_MAX / 2)\n                .coerceIn(0, paths.size - IMAGE_VIEWER_ACTIVITY_PATH_LIST_SIZE_MAX)\n            paths = paths.subList(start, start + IMAGE_VIEWER_ACTIVITY_PATH_LIST_SIZE_MAX)\n            position -= start\n        }\n        ImageViewerActivity.putExtras(intent, paths, position)\n    }\n\n    override fun cutFile(file: FileItem) {\n        cutFiles(fileItemSetOf(file))\n    }\n\n    override fun copyFile(file: FileItem) {\n        copyFiles(fileItemSetOf(file))\n    }\n\n    override fun confirmDeleteFile(file: FileItem) {\n        confirmDeleteFiles(fileItemSetOf(file))\n    }\n\n    override fun showRenameFileDialog(file: FileItem) {\n        RenameFileDialogFragment.show(file, this)\n    }\n\n    override fun hasFileWithName(name: String): Boolean = getFileWithName(name) != null\n\n    private fun getFileWithName(name: String): FileItem? {\n        val fileListData = viewModel.fileListStateful\n        if (fileListData !is Success) {\n            return null\n        }\n        return fileListData.value.find { it.name == name }\n    }\n\n    override fun renameFile(file: FileItem, newName: String) {\n        FileJobService.rename(file.path, newName, requireContext())\n        viewModel.selectFile(file, false)\n    }\n\n    override fun extractFile(file: FileItem) {\n        copyFile(file.createDummyArchiveRoot())\n    }\n\n    override fun showCreateArchiveDialog(file: FileItem) {\n        showCreateArchiveDialog(fileItemSetOf(file))\n    }\n\n    override fun shareFile(file: FileItem) {\n        shareFile(file.path, file.mimeType)\n    }\n\n    private fun shareFile(path: Path, mimeType: MimeType) {\n        shareFiles(listOf(path), listOf(mimeType))\n    }\n\n    private fun shareFiles(paths: List<Path>, mimeTypes: List<MimeType>) {\n        val uris = paths.map { it.fileProviderUri }\n        val intent = uris.createSendStreamIntent(mimeTypes)\n            .withChooser()\n        startActivitySafe(intent)\n    }\n\n    override fun copyPath(file: FileItem) {\n        copyPath(file.path)\n    }\n\n    override fun addBookmark(file: FileItem) {\n        addBookmark(file.path)\n    }\n\n    private fun addBookmark() {\n        addBookmark(currentPath)\n    }\n\n    private fun addBookmark(path: Path) {\n        BookmarkDirectories.add(BookmarkDirectory(null, path))\n        showToast(R.string.file_add_bookmark_success)\n    }\n\n    override fun createShortcut(file: FileItem) {\n        createShortcut(file.path, file.mimeType)\n    }\n\n    private fun createShortcut() {\n        createShortcut(currentPath, MimeType.DIRECTORY)\n    }\n\n    private fun createShortcut(path: Path, mimeType: MimeType) {\n        val context = requireContext()\n        val isDirectory = mimeType == MimeType.DIRECTORY\n        val shortcutInfo = ShortcutInfoCompat.Builder(context, path.toString())\n            .setShortLabel(path.name)\n            .setIntent(\n                if (isDirectory) {\n                    FileListActivity.createViewIntent(path)\n                        .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)\n                } else {\n                    OpenFileActivity.createIntent(path, mimeType)\n                }\n            )\n            .setIcon(\n                IconCompat.createWithResource(\n                    context, if (isDirectory) {\n                        R.mipmap.directory_shortcut_icon\n                    } else {\n                        R.mipmap.file_shortcut_icon\n                    }\n                )\n            )\n            .build()\n        ShortcutManagerCompat.requestPinShortcut(context, shortcutInfo, null)\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {\n            showToast(R.string.shortcut_created)\n        }\n    }\n\n    override fun showPropertiesDialog(file: FileItem) {\n        FilePropertiesDialogFragment.show(file, this)\n    }\n\n    private fun showCreateFileDialog() {\n        CreateFileDialogFragment.show(this)\n    }\n\n    override fun createFile(name: String) {\n        val path = currentPath.resolve(name)\n        FileJobService.create(path, false, requireContext())\n    }\n\n    private fun showCreateDirectoryDialog() {\n        CreateDirectoryDialogFragment.show(this)\n    }\n\n    override fun createDirectory(name: String) {\n        val path = currentPath.resolve(name)\n        FileJobService.create(path, true, requireContext())\n    }\n\n    override val currentPath: Path\n        get() = viewModel.currentPath\n\n    override fun navigateToRoot(path: Path) {\n        collapseSearchView()\n        viewModel.resetTo(path)\n    }\n\n    override fun navigateToDefaultRoot() {\n        navigateToRoot(Settings.FILE_LIST_DEFAULT_DIRECTORY.valueCompat)\n    }\n\n    override fun observeCurrentPath(owner: LifecycleOwner, observer: (Path) -> Unit) {\n        viewModel.currentPathLiveData.observe(owner, observer)\n    }\n\n    override fun closeNavigationDrawer() {\n        binding.drawerLayout?.closeDrawer(GravityCompat.START)\n    }\n\n    private fun ensureStorageAccess() {\n        if (viewModel.isStorageAccessRequested) {\n            return\n        }\n        if (Environment::class.supportsExternalStorageManager()) {\n            if (!Environment.isExternalStorageManager()) {\n                ShowRequestAllFilesAccessRationaleDialogFragment.show(this)\n                viewModel.isStorageAccessRequested = true\n            }\n        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n            if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) !=\n                PackageManager.PERMISSION_GRANTED\n            ) {\n                if (shouldShowRequestPermissionRationale(\n                        android.Manifest.permission.WRITE_EXTERNAL_STORAGE\n                    )) {\n                    ShowRequestStoragePermissionRationaleDialogFragment.show(this)\n                } else {\n                    requestStoragePermission()\n                }\n                viewModel.isStorageAccessRequested = true\n            }\n        }\n    }\n\n    override fun onShowRequestAllFilesAccessRationaleResult(shouldRequest: Boolean) {\n        if (shouldRequest) {\n            requestAllFilesAccess()\n        } else {\n            viewModel.isStorageAccessRequested = false\n            // This isn't an onActivityResult() callback so it's not delivered before calling\n            // onResume(), and we need to do this manually.\n            ensureNotificationPermission()\n        }\n    }\n\n    private fun requestAllFilesAccess() {\n        requestAllFilesAccessLauncher.launch(Unit)\n    }\n\n    private fun onRequestAllFilesAccessResult(isGranted: Boolean) {\n        viewModel.isStorageAccessRequested = false\n        if (isGranted) {\n            refresh()\n        }\n    }\n\n    override fun onShowRequestStoragePermissionRationaleResult(shouldRequest: Boolean) {\n        if (shouldRequest) {\n            requestStoragePermission()\n        } else {\n            viewModel.isStorageAccessRequested = false\n        }\n    }\n\n    private fun requestStoragePermission() {\n        requestStoragePermissionLauncher.launch(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)\n    }\n\n    private fun onRequestStoragePermissionResult(isGranted: Boolean) {\n        if (isGranted) {\n            viewModel.isStorageAccessRequested = false\n            refresh()\n        } else if (shouldShowRequestPermissionRationale(\n            android.Manifest.permission.WRITE_EXTERNAL_STORAGE\n        )) {\n            ShowRequestStoragePermissionRationaleDialogFragment.show(this)\n        } else {\n            ShowRequestStoragePermissionInSettingsRationaleDialogFragment.show(this)\n        }\n    }\n\n    override fun onShowRequestStoragePermissionInSettingsRationaleResult(shouldRequest: Boolean) {\n        if (shouldRequest) {\n            requestStoragePermissionInSettings()\n        } else {\n            viewModel.isStorageAccessRequested = false\n        }\n    }\n\n    private fun requestStoragePermissionInSettings() {\n        requestStoragePermissionInSettingsLauncher.launch(Unit)\n    }\n\n    private fun onRequestStoragePermissionInSettingsResult(isGranted: Boolean) {\n        viewModel.isStorageAccessRequested = false\n        if (isGranted) {\n            refresh()\n        }\n    }\n\n    private fun ensureNotificationPermission() {\n        if (viewModel.isNotificationPermissionRequested) {\n            return\n        }\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {\n            if (checkSelfPermission(android.Manifest.permission.POST_NOTIFICATIONS) !=\n                PackageManager.PERMISSION_GRANTED) {\n                if (shouldShowRequestPermissionRationale(\n                        android.Manifest.permission.POST_NOTIFICATIONS\n                    )) {\n                    ShowRequestNotificationPermissionRationaleDialogFragment.show(this)\n                } else {\n                    requestNotificationPermission()\n                }\n                viewModel.isNotificationPermissionRequested = true\n            }\n        }\n    }\n\n    @RequiresApi(Build.VERSION_CODES.TIRAMISU)\n    override fun onShowRequestNotificationPermissionRationaleResult(shouldRequest: Boolean) {\n        if (shouldRequest) {\n            requestNotificationPermission()\n        } else {\n            viewModel.isNotificationPermissionRequested = false\n        }\n    }\n\n    @RequiresApi(Build.VERSION_CODES.TIRAMISU)\n    private fun requestNotificationPermission() {\n        requestNotificationPermissionLauncher.launch(android.Manifest.permission.POST_NOTIFICATIONS)\n    }\n\n    @RequiresApi(Build.VERSION_CODES.TIRAMISU)\n    private fun onRequestNotificationPermissionResult(isGranted: Boolean) {\n        if (isGranted) {\n            viewModel.isNotificationPermissionRequested = false\n        } else if (shouldShowRequestPermissionRationale(\n            android.Manifest.permission.POST_NOTIFICATIONS\n        )) {\n            ShowRequestNotificationPermissionRationaleDialogFragment.show(this)\n        } else {\n            ShowRequestNotificationPermissionInSettingsRationaleDialogFragment.show(this)\n        }\n    }\n\n    @RequiresApi(Build.VERSION_CODES.TIRAMISU)\n    override fun onShowRequestNotificationPermissionInSettingsRationaleResult(\n        shouldRequest: Boolean\n    ) {\n        if (shouldRequest) {\n            requestNotificationPermissionInSettings()\n        } else {\n            viewModel.isNotificationPermissionRequested = false\n        }\n    }\n\n    @RequiresApi(Build.VERSION_CODES.TIRAMISU)\n    private fun requestNotificationPermissionInSettings() {\n        requestNotificationPermissionInSettingsLauncher.launch(Unit)\n    }\n\n    @RequiresApi(Build.VERSION_CODES.TIRAMISU)\n    private fun onRequestNotificationPermissionInSettingsResult(isGranted: Boolean) {\n        if (isGranted) {\n            viewModel.isNotificationPermissionRequested = false\n        }\n    }\n\n    companion object {\n        private const val ACTION_VIEW_DOWNLOADS =\n            \"me.zhanghai.android.files.intent.action.VIEW_DOWNLOADS\"\n\n        private const val IMAGE_VIEWER_ACTIVITY_PATH_LIST_SIZE_MAX = 1000\n    }\n\n    private class RequestAllFilesAccessContract : ActivityResultContract<Unit, Boolean>() {\n        @RequiresApi(Build.VERSION_CODES.R)\n        override fun createIntent(context: Context, input: Unit): Intent =\n            Environment::class.createManageAppAllFilesAccessPermissionIntent(context.packageName)\n\n        @RequiresApi(Build.VERSION_CODES.R)\n        override fun parseResult(resultCode: Int, intent: Intent?): Boolean =\n            Environment.isExternalStorageManager()\n    }\n\n    private class RequestPermissionInSettingsContract(private val permissionName: String)\n        : ActivityResultContract<Unit, Boolean>() {\n        override fun createIntent(context: Context, input: Unit): Intent =\n            Intent(\n                android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS,\n                Uri.fromParts(\"package\", context.packageName, null)\n            )\n\n        override fun parseResult(resultCode: Int, intent: Intent?): Boolean =\n            application.checkSelfPermissionCompat(permissionName) ==\n                PackageManager.PERMISSION_GRANTED\n    }\n\n    @Parcelize\n    class Args(val intent: Intent) : ParcelableArgs\n\n    private class Binding private constructor(\n        val root: View,\n        val drawerLayout: DrawerLayout? = null,\n        val persistentDrawerLayout: PersistentDrawerLayout? = null,\n        val persistentBarLayout: PersistentBarLayout,\n        val appBarLayout: CoordinatorAppBarLayout,\n        val toolbar: Toolbar,\n        val overlayToolbar: Toolbar,\n        val breadcrumbLayout: BreadcrumbLayout,\n        val contentLayout: ViewGroup,\n        val progress: ProgressBar,\n        val errorText: TextView,\n        val emptyView: View,\n        val swipeRefreshLayout: SwipeRefreshLayout,\n        val recyclerView: RecyclerView,\n        val bottomBarLayout: ViewGroup,\n        val bottomToolbar: Toolbar,\n        val bottomCreateFileNameEdit: EditText,\n        val speedDialView: SpeedDialView\n    ) {\n        companion object {\n            fun inflate(\n                inflater: LayoutInflater,\n                root: ViewGroup?,\n                attachToRoot: Boolean\n            ): Binding {\n                val binding = FileListFragmentBinding.inflate(inflater, root, attachToRoot)\n                val bindingRoot = binding.root\n                val includeBinding = FileListFragmentIncludeBinding.bind(bindingRoot)\n                val appBarBinding = FileListFragmentAppBarIncludeBinding.bind(bindingRoot)\n                val contentBinding = FileListFragmentContentIncludeBinding.bind(bindingRoot)\n                val bottomBarBinding = FileListFragmentBottomBarIncludeBinding.bind(bindingRoot)\n                val speedDialBinding = FileListFragmentSpeedDialIncludeBinding.bind(bindingRoot)\n                return Binding(\n                    bindingRoot, includeBinding.drawerLayout, includeBinding.persistentDrawerLayout,\n                    includeBinding.persistentBarLayout, appBarBinding.appBarLayout,\n                    appBarBinding.toolbar, appBarBinding.overlayToolbar,\n                    appBarBinding.breadcrumbLayout, contentBinding.contentLayout,\n                    contentBinding.progress, contentBinding.errorText, contentBinding.emptyView,\n                    contentBinding.swipeRefreshLayout, contentBinding.recyclerView,\n                    bottomBarBinding.bottomBarLayout, bottomBarBinding.bottomToolbar,\n                    bottomBarBinding.bottomCreateFileNameEdit, speedDialBinding.speedDialView\n                )\n            }\n        }\n    }\n\n    private class MenuBinding private constructor(\n        val menu: Menu,\n        val searchItem: MenuItem,\n        val viewSortItem: MenuItem,\n        val viewListItem: MenuItem,\n        val viewGridItem: MenuItem,\n        val sortByNameItem: MenuItem,\n        val sortByTypeItem: MenuItem,\n        val sortBySizeItem: MenuItem,\n        val sortByLastModifiedItem: MenuItem,\n        val sortOrderAscendingItem: MenuItem,\n        val sortDirectoriesFirstItem: MenuItem,\n        val viewSortPathSpecificItem: MenuItem,\n        val selectAllItem: MenuItem,\n        val showHiddenFilesItem: MenuItem\n    ) {\n        companion object {\n            fun inflate(menu: Menu, inflater: MenuInflater): MenuBinding {\n                inflater.inflate(R.menu.file_list, menu)\n                return MenuBinding(\n                    menu, menu.findItem(R.id.action_search), menu.findItem(R.id.action_view_sort),\n                    menu.findItem(R.id.action_view_list), menu.findItem(R.id.action_view_grid),\n                    menu.findItem(R.id.action_sort_by_name),\n                    menu.findItem(R.id.action_sort_by_type),\n                    menu.findItem(R.id.action_sort_by_size),\n                    menu.findItem(R.id.action_sort_by_last_modified),\n                    menu.findItem(R.id.action_sort_order_ascending),\n                    menu.findItem(R.id.action_sort_directories_first),\n                    menu.findItem(R.id.action_view_sort_path_specific),\n                    menu.findItem(R.id.action_select_all),\n                    menu.findItem(R.id.action_show_hidden_files)\n                )\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/FileListLiveData.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.os.AsyncTask\nimport java8.nio.file.DirectoryIteratorException\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.file.FileItem\nimport me.zhanghai.android.files.file.loadFileItem\nimport me.zhanghai.android.files.provider.common.newDirectoryStream\nimport me.zhanghai.android.files.util.CloseableLiveData\nimport me.zhanghai.android.files.util.Failure\nimport me.zhanghai.android.files.util.Loading\nimport me.zhanghai.android.files.util.Stateful\nimport me.zhanghai.android.files.util.Success\nimport me.zhanghai.android.files.util.valueCompat\nimport java.io.IOException\nimport java.util.concurrent.ExecutorService\nimport java.util.concurrent.Future\n\nclass FileListLiveData(private val path: Path) : CloseableLiveData<Stateful<List<FileItem>>>() {\n    private var future: Future<Unit>? = null\n\n    private val observer: PathObserver\n\n    @Volatile\n    private var isChangedWhileInactive = false\n\n    init {\n        loadValue()\n        observer = PathObserver(path) { onChangeObserved() }\n    }\n\n    fun loadValue() {\n        future?.cancel(true)\n        value = Loading(value?.value)\n        future = (AsyncTask.THREAD_POOL_EXECUTOR as ExecutorService).submit<Unit> {\n            val value = try {\n                path.newDirectoryStream().use { directoryStream ->\n                    val fileList = mutableListOf<FileItem>()\n                    for (path in directoryStream) {\n                        try {\n                            fileList.add(path.loadFileItem())\n                        } catch (e: DirectoryIteratorException) {\n                            // TODO: Ignoring such a file can be misleading and we need to support\n                            //  files without information.\n                            e.printStackTrace()\n                        } catch (e: IOException) {\n                            e.printStackTrace()\n                        }\n                    }\n                    Success(fileList as List<FileItem>)\n                }\n            } catch (e: Exception) {\n                Failure(valueCompat.value, e)\n            }\n            postValue(value)\n        }\n    }\n\n    private fun onChangeObserved() {\n        if (hasActiveObservers()) {\n            loadValue()\n        } else {\n            isChangedWhileInactive = true\n        }\n    }\n\n    override fun onActive() {\n        if (isChangedWhileInactive) {\n            loadValue()\n            isChangedWhileInactive = false\n        }\n    }\n\n    override fun close() {\n        observer.close()\n        future?.cancel(true)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/FileListViewModel.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.os.Parcelable\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.MediatorLiveData\nimport androidx.lifecycle.MutableLiveData\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.map\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.file.FileItem\nimport me.zhanghai.android.files.filelist.FileSortOptions.By\nimport me.zhanghai.android.files.filelist.FileSortOptions.Order\nimport me.zhanghai.android.files.provider.archive.archiveRefresh\nimport me.zhanghai.android.files.provider.archive.isArchivePath\nimport me.zhanghai.android.files.util.CloseableLiveData\nimport me.zhanghai.android.files.util.Stateful\nimport me.zhanghai.android.files.util.valueCompat\nimport java.io.Closeable\n\n// TODO: Use SavedStateHandle to save state.\nclass FileListViewModel : ViewModel() {\n    private val trailLiveData = TrailLiveData()\n    val hasTrail: Boolean\n        get() = trailLiveData.value != null\n    val pendingState: Parcelable?\n        get() = trailLiveData.valueCompat.pendingState\n\n    fun navigateTo(lastState: Parcelable, path: Path) = trailLiveData.navigateTo(lastState, path)\n\n    fun resetTo(path: Path) = trailLiveData.resetTo(path)\n\n    fun navigateUp(): Boolean = trailLiveData.navigateUp()\n\n    val currentPathLiveData = trailLiveData.map { it.currentPath }\n    val currentPath: Path\n        get() = currentPathLiveData.valueCompat\n\n    private val _searchStateLiveData = MutableLiveData(SearchState(false, \"\"))\n    val searchStateLiveData: LiveData<SearchState> = _searchStateLiveData\n    val searchState: SearchState\n        get() = _searchStateLiveData.valueCompat\n\n    fun search(query: String) {\n        val searchState = _searchStateLiveData.valueCompat\n        if (searchState.isSearching && searchState.query == query) {\n            return\n        }\n        _searchStateLiveData.value = SearchState(true, query)\n    }\n\n    fun stopSearching() {\n        val searchState = _searchStateLiveData.valueCompat\n        if (!searchState.isSearching) {\n            return\n        }\n        _searchStateLiveData.value = SearchState(false, \"\")\n    }\n\n    private val _fileListLiveData =\n        FileListSwitchMapLiveData(currentPathLiveData, _searchStateLiveData)\n    val fileListLiveData: LiveData<Stateful<List<FileItem>>>\n        get() = _fileListLiveData\n    val fileListStateful: Stateful<List<FileItem>>\n        get() = _fileListLiveData.valueCompat\n\n    fun reload() {\n        val path = currentPath\n        if (path.isArchivePath) {\n            path.archiveRefresh()\n        }\n        _fileListLiveData.reload()\n    }\n\n    val searchViewExpandedLiveData = MutableLiveData(false)\n    var isSearchViewExpanded: Boolean\n        get() = searchViewExpandedLiveData.valueCompat\n        set(value) {\n            if (searchViewExpandedLiveData.valueCompat == value) {\n                return\n            }\n            searchViewExpandedLiveData.value = value\n        }\n\n    private val _searchViewQueryLiveData = MutableLiveData(\"\")\n    var searchViewQuery: String\n        get() = _searchViewQueryLiveData.valueCompat\n        set(value) {\n            if (_searchViewQueryLiveData.valueCompat == value) {\n                return\n            }\n            _searchViewQueryLiveData.value = value\n        }\n\n    val breadcrumbLiveData: LiveData<BreadcrumbData> = BreadcrumbLiveData(trailLiveData)\n    val canNavigateUpBreadcrumb: Boolean\n        get() = breadcrumbLiveData.valueCompat.selectedIndex > 0\n\n    private val _viewTypeLiveData = FileViewTypeLiveData(currentPathLiveData)\n    val viewTypeLiveData: LiveData<FileViewType> = _viewTypeLiveData\n    var viewType: FileViewType\n        get() = _viewTypeLiveData.valueCompat\n        set(value) {\n            _viewTypeLiveData.putValue(value)\n        }\n\n    private val _sortOptionsLiveData = FileSortOptionsLiveData(currentPathLiveData)\n    val sortOptionsLiveData: LiveData<FileSortOptions> = _sortOptionsLiveData\n    val sortOptions: FileSortOptions\n        get() = _sortOptionsLiveData.valueCompat\n\n    fun setSortBy(by: By) = _sortOptionsLiveData.putBy(by)\n\n    fun setSortOrder(order: Order) = _sortOptionsLiveData.putOrder(order)\n\n    fun setSortDirectoriesFirst(isDirectoriesFirst: Boolean) =\n        _sortOptionsLiveData.putIsDirectoriesFirst(isDirectoriesFirst)\n\n    private val _viewSortPathSpecificLiveData =\n        FileViewSortPathSpecificLiveData(currentPathLiveData)\n    val viewSortPathSpecificLiveData: LiveData<Boolean>\n        get() = _viewSortPathSpecificLiveData\n    var isViewSortPathSpecific: Boolean\n        get() = _viewSortPathSpecificLiveData.valueCompat\n        set(value) {\n            _viewSortPathSpecificLiveData.putValue(value)\n        }\n\n    private val _pickOptionsLiveData = MutableLiveData<PickOptions?>()\n    val pickOptionsLiveData: LiveData<PickOptions?>\n        get() = _pickOptionsLiveData\n    var pickOptions: PickOptions?\n        get() = _pickOptionsLiveData.value\n        set(value) {\n            _pickOptionsLiveData.value = value\n        }\n\n    var isCreateFileNameEditInitialized: Boolean = false\n\n    private val _selectedFilesLiveData = MutableLiveData(fileItemSetOf())\n    val selectedFilesLiveData: LiveData<FileItemSet>\n        get() = _selectedFilesLiveData\n    val selectedFiles: FileItemSet\n        get() = _selectedFilesLiveData.valueCompat\n\n    fun selectFile(file: FileItem, selected: Boolean) {\n        selectFiles(fileItemSetOf(file), selected)\n    }\n\n    fun selectFiles(files: FileItemSet, selected: Boolean) {\n        val selectedFiles = _selectedFilesLiveData.valueCompat\n        if (selectedFiles === files) {\n            if (!selected && selectedFiles.isNotEmpty()) {\n                selectedFiles.clear()\n                _selectedFilesLiveData.value = selectedFiles\n            }\n            return\n        }\n        var changed = false\n        for (file in files) {\n            changed = changed or if (selected) {\n                selectedFiles.add(file)\n            } else {\n                selectedFiles.remove(file)\n            }\n        }\n        if (changed) {\n            _selectedFilesLiveData.value = selectedFiles\n        }\n    }\n\n    fun replaceSelectedFiles(files: FileItemSet) {\n        val selectedFiles = _selectedFilesLiveData.valueCompat\n        if (selectedFiles == files) {\n            return\n        }\n        selectedFiles.clear()\n        selectedFiles.addAll(files)\n        _selectedFilesLiveData.value = selectedFiles\n    }\n\n    fun clearSelectedFiles() {\n        val selectedFiles = _selectedFilesLiveData.valueCompat\n        if (selectedFiles.isEmpty()) {\n            return\n        }\n        selectedFiles.clear()\n        _selectedFilesLiveData.value = selectedFiles\n    }\n\n    val pasteStateLiveData: LiveData<PasteState> = _pasteStateLiveData\n    val pasteState: PasteState\n        get() = _pasteStateLiveData.valueCompat\n\n    fun addToPasteState(copy: Boolean, files: FileItemSet) {\n        val pasteState = _pasteStateLiveData.valueCompat\n        var changed = false\n        if (pasteState.copy != copy) {\n            changed = pasteState.files.isNotEmpty()\n            pasteState.files.clear()\n            pasteState.copy = copy\n        }\n        changed = changed or pasteState.files.addAll(files)\n        if (changed) {\n            _pasteStateLiveData.value = pasteState\n        }\n    }\n\n    fun clearPasteState() {\n        val pasteState = _pasteStateLiveData.valueCompat\n        if (pasteState.files.isEmpty()) {\n            return\n        }\n        pasteState.files.clear()\n        _pasteStateLiveData.value = pasteState\n    }\n\n    private val _isRequestingStorageAccessLiveData = MutableLiveData(false)\n    var isStorageAccessRequested: Boolean\n        get() = _isRequestingStorageAccessLiveData.valueCompat\n        set(value) {\n            _isRequestingStorageAccessLiveData.value = value\n        }\n\n    private val _isRequestingNotificationPermissionLiveData = MutableLiveData(false)\n    var isNotificationPermissionRequested: Boolean\n        get() = _isRequestingNotificationPermissionLiveData.valueCompat\n        set(value) {\n            _isRequestingNotificationPermissionLiveData.value = value\n        }\n\n    override fun onCleared() {\n        _fileListLiveData.close()\n    }\n\n    companion object {\n        private val _pasteStateLiveData = MutableLiveData(PasteState())\n    }\n\n    private class FileListSwitchMapLiveData(\n        private val pathLiveData: LiveData<Path>,\n        private val searchStateLiveData: LiveData<SearchState>\n    ) : MediatorLiveData<Stateful<List<FileItem>>>(), Closeable {\n        private var liveData: CloseableLiveData<Stateful<List<FileItem>>>? = null\n\n        init {\n            addSource(pathLiveData) { updateSource() }\n            addSource(searchStateLiveData) { updateSource() }\n        }\n\n        private fun updateSource() {\n            liveData?.let {\n                removeSource(it)\n                it.close()\n            }\n            val path = pathLiveData.valueCompat\n            val searchState = searchStateLiveData.valueCompat\n            val liveData = if (searchState.isSearching) {\n                SearchFileListLiveData(path, searchState.query)\n            } else {\n                FileListLiveData(path)\n            }\n            this.liveData = liveData\n            addSource(liveData) { value = it }\n        }\n\n        fun reload() {\n            when (val liveData = liveData) {\n                is FileListLiveData -> liveData.loadValue()\n                is SearchFileListLiveData -> liveData.loadValue()\n            }\n        }\n\n        override fun close() {\n            liveData?.let {\n                removeSource(it)\n                it.close()\n                this.liveData = null\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/FileNameDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.util.asFileNameOrNull\n\nabstract class FileNameDialogFragment : NameDialogFragment() {\n    override val listener: Listener\n        get() = super.listener as Listener\n\n    override fun isNameValid(name: String): Boolean {\n        if (!super.isNameValid(name)) {\n            return false\n        }\n        if (name.isEmpty()) {\n            binding.nameLayout.error = getString(R.string.file_name_error_empty)\n            return false\n        }\n        if (name.asFileNameOrNull() == null) {\n            binding.nameLayout.error = getString(R.string.file_name_error_invalid)\n            return false\n        }\n        val listener = listener\n        if (listener.hasFileWithName(name)) {\n            binding.nameLayout.error = getString(R.string.file_name_error_already_exists)\n            return false\n        }\n        return true\n    }\n\n    interface Listener : NameDialogFragment.Listener {\n        fun hasFileWithName(name: String): Boolean\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/FileSortOptions.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.os.Parcelable\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.compat.reversedCompat\nimport me.zhanghai.android.files.file.FileItem\n\n@Parcelize\ndata class FileSortOptions(\n    val by: By,\n    val order: Order,\n    val isDirectoriesFirst: Boolean\n) : Parcelable {\n    fun createComparator(): Comparator<FileItem> {\n        var comparator = compareBy<FileItem> {\n            NAME_UNIMPORTANT_PREFIXES.any { prefix -> it.name.startsWith(prefix) }\n        }.thenBy { it.nameCollationKey }\n        when (by) {\n            // Nothing to do.\n            By.NAME -> {}\n            By.TYPE ->\n                comparator = compareBy<FileItem, String>(String.CASE_INSENSITIVE_ORDER) {\n                    it.extension\n                }.then(comparator)\n            By.SIZE -> comparator = compareBy<FileItem> { it.attributes.size() }.then(comparator)\n            By.LAST_MODIFIED ->\n                comparator = compareBy<FileItem> { it.attributes.lastModifiedTime() }\n                    .then(comparator)\n        }\n        when (order) {\n            Order.ASCENDING -> {}\n            Order.DESCENDING -> comparator = comparator.reversedCompat()\n        }\n        if (isDirectoriesFirst) {\n            val isDirectoryComparator = compareBy<FileItem> { it.attributes.isDirectory }\n                .reversedCompat()\n            comparator = isDirectoryComparator.then(comparator)\n        }\n        return comparator\n    }\n\n    companion object {\n        // Same behavior as Nautilus.\n        private val NAME_UNIMPORTANT_PREFIXES = listOf(\".\", \"#\")\n    }\n\n    enum class By {\n        NAME,\n        TYPE,\n        SIZE,\n        LAST_MODIFIED\n    }\n\n    enum class Order {\n        ASCENDING,\n        DESCENDING\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/FileSortOptionsLiveData.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.MediatorLiveData\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.filelist.FileSortOptions.By\nimport me.zhanghai.android.files.filelist.FileSortOptions.Order\nimport me.zhanghai.android.files.settings.PathSettings\nimport me.zhanghai.android.files.settings.SettingLiveData\nimport me.zhanghai.android.files.settings.Settings\nimport me.zhanghai.android.files.util.valueCompat\n\nclass FileSortOptionsLiveData(pathLiveData: LiveData<Path>) : MediatorLiveData<FileSortOptions>() {\n    private lateinit var pathSortOptionsLiveData: SettingLiveData<FileSortOptions?>\n\n    private fun loadValue() {\n        if (!this::pathSortOptionsLiveData.isInitialized) {\n            // Not yet initialized.\n            return\n        }\n        val value = pathSortOptionsLiveData.value ?: Settings.FILE_LIST_SORT_OPTIONS.valueCompat\n        if (this.value != value) {\n            this.value = value\n        }\n    }\n\n    fun putBy(by: By) {\n        putValue(valueCompat.copy(by = by))\n    }\n\n    fun putOrder(order: Order) {\n        putValue(valueCompat.copy(order = order))\n    }\n\n    fun putIsDirectoriesFirst(isDirectoriesFirst: Boolean) {\n        putValue(valueCompat.copy(isDirectoriesFirst = isDirectoriesFirst))\n    }\n\n    private fun putValue(value: FileSortOptions) {\n        if (pathSortOptionsLiveData.value != null) {\n            pathSortOptionsLiveData.putValue(value)\n        } else {\n            Settings.FILE_LIST_SORT_OPTIONS.putValue(value)\n        }\n    }\n\n    init {\n        addSource(Settings.FILE_LIST_SORT_OPTIONS) { loadValue() }\n        addSource(pathLiveData) { path: Path ->\n            if (this::pathSortOptionsLiveData.isInitialized) {\n                removeSource(pathSortOptionsLiveData)\n            }\n            pathSortOptionsLiveData = PathSettings.getFileListSortOptions(path)\n            addSource(pathSortOptionsLiveData) { loadValue() }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/FileViewSortPathSpecificLiveData.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.MediatorLiveData\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.settings.PathSettings\nimport me.zhanghai.android.files.settings.SettingLiveData\nimport me.zhanghai.android.files.settings.Settings\nimport me.zhanghai.android.files.util.valueCompat\n\nclass FileViewSortPathSpecificLiveData(pathLiveData: LiveData<Path>) : MediatorLiveData<Boolean>() {\n    private lateinit var pathViewTypeLiveData: SettingLiveData<FileViewType?>\n    private lateinit var pathSortOptionsLiveData: SettingLiveData<FileSortOptions?>\n\n    private fun loadValue() {\n        val value = pathViewTypeLiveData.value != null || pathSortOptionsLiveData.value != null\n        if (this.value != value) {\n            this.value = value\n        }\n    }\n\n    fun putValue(value: Boolean) {\n        if (value) {\n            if (pathViewTypeLiveData.value == null) {\n                pathViewTypeLiveData.putValue(Settings.FILE_LIST_VIEW_TYPE.valueCompat)\n            }\n            if (pathSortOptionsLiveData.value == null) {\n                pathSortOptionsLiveData.putValue(Settings.FILE_LIST_SORT_OPTIONS.valueCompat)\n            }\n        } else {\n            if (pathViewTypeLiveData.value != null) {\n                pathViewTypeLiveData.putValue(null)\n            }\n            if (pathSortOptionsLiveData.value != null) {\n                pathSortOptionsLiveData.putValue(null)\n            }\n        }\n    }\n\n    init {\n        addSource(pathLiveData) { path: Path ->\n            if (this::pathViewTypeLiveData.isInitialized) {\n                removeSource(pathViewTypeLiveData)\n            }\n            if (this::pathSortOptionsLiveData.isInitialized) {\n                removeSource(pathSortOptionsLiveData)\n            }\n            pathViewTypeLiveData = PathSettings.getFileListViewType(path)\n            pathSortOptionsLiveData = PathSettings.getFileListSortOptions(path)\n            addSource(pathViewTypeLiveData) { loadValue() }\n            addSource(pathSortOptionsLiveData) { loadValue() }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/FileViewType.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nenum class FileViewType {\n    LIST,\n    GRID\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/FileViewTypeLiveData.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.MediatorLiveData\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.settings.PathSettings\nimport me.zhanghai.android.files.settings.SettingLiveData\nimport me.zhanghai.android.files.settings.Settings\nimport me.zhanghai.android.files.util.valueCompat\n\nclass FileViewTypeLiveData(pathLiveData: LiveData<Path>) : MediatorLiveData<FileViewType>() {\n    private lateinit var pathViewTypeLiveData: SettingLiveData<FileViewType?>\n\n    private fun loadValue() {\n        if (!this::pathViewTypeLiveData.isInitialized) {\n            // Not yet initialized.\n            return\n        }\n        val value = pathViewTypeLiveData.value ?: Settings.FILE_LIST_VIEW_TYPE.valueCompat\n        if (this.value != value) {\n            this.value = value\n        }\n    }\n\n    fun putValue(value: FileViewType) {\n        if (pathViewTypeLiveData.value != null) {\n            pathViewTypeLiveData.putValue(value)\n        } else {\n            Settings.FILE_LIST_VIEW_TYPE.putValue(value)\n        }\n    }\n\n    init {\n        addSource(Settings.FILE_LIST_VIEW_TYPE) { loadValue() }\n        addSource(pathLiveData) { path: Path ->\n            if (this::pathViewTypeLiveData.isInitialized) {\n                removeSource(pathViewTypeLiveData)\n            }\n            pathViewTypeLiveData = PathSettings.getFileListViewType(path)\n            addSource(pathViewTypeLiveData) { loadValue() }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/NameDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.app.Dialog\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.WindowManager\nimport android.widget.EditText\nimport androidx.annotation.StringRes\nimport androidx.appcompat.app.AlertDialog\nimport androidx.appcompat.app.AppCompatDialogFragment\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport com.google.android.material.textfield.TextInputLayout\nimport me.zhanghai.android.files.databinding.NameDialogBinding\nimport me.zhanghai.android.files.databinding.NameDialogNameIncludeBinding\nimport me.zhanghai.android.files.util.hideTextInputLayoutErrorOnTextChange\nimport me.zhanghai.android.files.util.layoutInflater\nimport me.zhanghai.android.files.util.setOnEditorConfirmActionListener\n\nabstract class NameDialogFragment : AppCompatDialogFragment() {\n    private lateinit var _binding: Binding\n    protected open val binding: Binding\n        get() = _binding\n\n    protected open val listener: Listener\n        get() = requireParentFragment() as Listener\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =\n        MaterialAlertDialogBuilder(requireContext(), theme)\n            .setTitle(titleRes)\n            .apply {\n                _binding = onInflateBinding(context.layoutInflater)\n                if (savedInstanceState == null) {\n                    val initialName = initialName\n                    if (initialName != null) {\n                        binding.nameEdit.setText(initialName)\n                        binding.nameEdit.setSelection(0, initialName.length)\n                    }\n                }\n                binding.nameEdit.hideTextInputLayoutErrorOnTextChange(binding.nameLayout)\n                binding.nameEdit.setOnEditorConfirmActionListener { onOk() }\n                setView(binding.root)\n            }\n            .setPositiveButton(android.R.string.ok, null)\n            .setNegativeButton(android.R.string.cancel, null)\n            .create()\n            .apply {\n                window!!.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)\n                // Override the listener here so that we have control over when to close the dialog.\n                setOnShowListener {\n                    getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { onOk() }\n                }\n            }\n\n    @get:StringRes\n    protected abstract val titleRes: Int\n\n    protected open val initialName: String? = null\n\n    protected open fun onInflateBinding(inflater: LayoutInflater): Binding =\n        Binding.inflate(inflater)\n\n    private fun onOk() {\n        val name = name\n        if (!isNameValid(name)) {\n            return\n        }\n        onOk(name)\n        dismiss()\n    }\n\n    protected open val name: String\n        get() = binding.nameEdit.text.toString().trim()\n\n    protected open fun isNameValid(name: String): Boolean {\n        if (name == initialName) {\n            dismiss()\n            return false\n        }\n        return true\n    }\n\n    protected abstract fun onOk(name: String)\n\n    protected open class Binding protected constructor(\n        val root: View,\n        val nameLayout: TextInputLayout,\n        val nameEdit: EditText\n    ) {\n        companion object {\n            fun inflate(inflater: LayoutInflater): Binding {\n                val binding = NameDialogBinding.inflate(inflater)\n                val bindingRoot = binding.root\n                val nameBinding = NameDialogNameIncludeBinding.bind(bindingRoot)\n                return Binding(bindingRoot, nameBinding.nameLayout, nameBinding.nameEdit)\n            }\n        }\n    }\n\n    interface Listener\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/NavigateToPathDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport androidx.annotation.StringRes\nimport androidx.fragment.app.Fragment\nimport java8.nio.file.Path\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.ParcelableParceler\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.putArgs\nimport me.zhanghai.android.files.util.show\n\nclass NavigateToPathDialogFragment : PathDialogFragment() {\n    private val args by args<Args>()\n\n    override val listener: Listener\n        get() = super.listener as Listener\n\n    @StringRes\n    override val titleRes: Int = R.string.file_list_navigate_to_title\n\n    override val initialName: String?\n        get() = args.path.toUserFriendlyString()\n\n    override fun onOk(path: Path) {\n        listener.navigateTo(path)\n    }\n\n    companion object {\n        fun show(path: Path, fragment: Fragment) {\n            NavigateToPathDialogFragment().putArgs(Args(path)).show(fragment)\n        }\n    }\n\n    @Parcelize\n    class Args(val path: @WriteWith<ParcelableParceler> Path) : ParcelableArgs\n\n    interface Listener : NameDialogFragment.Listener {\n        fun navigateTo(path: Path)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/OpenApkDefaultAction.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nenum class OpenApkDefaultAction {\n    INSTALL,\n    VIEW,\n    ASK\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/OpenApkDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.app.Dialog\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatDialogFragment\nimport androidx.fragment.app.Fragment\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.file.FileItem\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.putArgs\nimport me.zhanghai.android.files.util.show\n\nclass OpenApkDialogFragment : AppCompatDialogFragment() {\n    private val args by args<Args>()\n\n    private val listener: Listener\n        get() = requireParentFragment() as Listener\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {\n        return MaterialAlertDialogBuilder(requireContext(), theme)\n            .setMessage(R.string.file_open_apk_message)\n            .setPositiveButton(R.string.install) { _, _ -> listener.installApk(args.file) }\n            // While semantically incorrect, this places the two most expected actions side by side.\n            .setNegativeButton(R.string.view) { _, _ -> listener.viewApk(args.file) }\n            .setNeutralButton(android.R.string.cancel, null)\n            .create()\n    }\n\n    companion object {\n        fun show(file: FileItem, fragment: Fragment) {\n            OpenApkDialogFragment().putArgs(Args(file)).show(fragment)\n        }\n    }\n\n    @Parcelize\n    class Args(val file: FileItem) : ParcelableArgs\n\n    interface Listener {\n        fun installApk(file: FileItem)\n        fun viewApk(file: FileItem)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/OpenFileActivity.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.content.Intent\nimport android.os.Bundle\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.app.AppActivity\nimport me.zhanghai.android.files.app.application\nimport me.zhanghai.android.files.file.MimeType\nimport me.zhanghai.android.files.file.asMimeTypeOrNull\nimport me.zhanghai.android.files.file.fileProviderUri\nimport me.zhanghai.android.files.filejob.FileJobService\nimport me.zhanghai.android.files.provider.archive.isArchivePath\nimport me.zhanghai.android.files.util.createViewIntent\nimport me.zhanghai.android.files.util.extraPath\nimport me.zhanghai.android.files.util.startActivitySafe\n\nclass OpenFileActivity : AppActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        val intent = intent\n        val path = intent.extraPath\n        val mimeType = intent.type?.asMimeTypeOrNull()\n        if (path != null && mimeType != null) {\n            openFile(path, mimeType)\n        }\n        finish()\n    }\n\n    private fun openFile(path: Path, mimeType: MimeType) {\n        if (path.isArchivePath) {\n            FileJobService.open(path, mimeType, false, this)\n        } else {\n            val intent = path.fileProviderUri.createViewIntent(mimeType)\n                .addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)\n                .apply { extraPath = path }\n            startActivitySafe(intent)\n        }\n    }\n\n    companion object {\n        private const val ACTION_OPEN_FILE = \"me.zhanghai.android.files.intent.action.OPEN_FILE\"\n\n        fun createIntent(path: Path, mimeType: MimeType): Intent =\n            Intent(ACTION_OPEN_FILE)\n                .setPackage(application.packageName)\n                .setType(mimeType.value)\n                .apply { extraPath = path }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/OpenFileAsDialogActivity.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.os.Bundle\nimport android.view.View\nimport androidx.fragment.app.commit\nimport me.zhanghai.android.files.app.AppActivity\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.putArgs\n\nclass OpenFileAsDialogActivity : AppActivity() {\n    private val args by args<OpenFileAsDialogFragment.Args>()\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        // Calls ensureSubDecor().\n        findViewById<View>(android.R.id.content)\n        if (savedInstanceState == null) {\n            val fragment = OpenFileAsDialogFragment().putArgs(args)\n            supportFragmentManager.commit {\n                add(fragment, OpenFileAsDialogFragment::class.java.name)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/OpenFileAsDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.app.Dialog\nimport android.content.DialogInterface\nimport android.content.Intent\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatDialogFragment\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport java8.nio.file.Path\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.file.MimeType\nimport me.zhanghai.android.files.file.asMimeType\nimport me.zhanghai.android.files.file.fileProviderUri\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.ParcelableParceler\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.createViewIntent\nimport me.zhanghai.android.files.util.extraPath\nimport me.zhanghai.android.files.util.finish\nimport me.zhanghai.android.files.util.startActivitySafe\nimport me.zhanghai.android.files.util.withChooser\n\nclass OpenFileAsDialogFragment : AppCompatDialogFragment() {\n    private val args by args<Args>()\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =\n        MaterialAlertDialogBuilder(requireContext(), theme)\n            .setTitle(getString(R.string.file_open_as_title_format, args.path.name))\n            .apply {\n                val items = FILE_TYPES.map { getString(it.first) }.toTypedArray<CharSequence>()\n                setItems(items) { _, which -> openAs(FILE_TYPES[which].second) }\n            }\n            .create()\n\n    private fun openAs(mimeType: MimeType) {\n        val intent = args.path.fileProviderUri.createViewIntent(mimeType)\n            .addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)\n            .apply { extraPath = args.path }\n            .withChooser()\n        startActivitySafe(intent)\n        finish()\n    }\n\n    override fun onCancel(dialog: DialogInterface) {\n        super.onCancel(dialog)\n\n        finish()\n    }\n\n    companion object {\n        private val FILE_TYPES = listOf(\n            R.string.file_open_as_type_text to \"text/plain\",\n            R.string.file_open_as_type_image to \"image/*\",\n            R.string.file_open_as_type_audio to \"audio/*\",\n            R.string.file_open_as_type_video to \"video/*\",\n            R.string.file_open_as_type_directory to MimeType.DIRECTORY.value,\n            R.string.file_open_as_type_any to \"*/*\"\n        ).map { it.first to it.second.asMimeType() }\n    }\n\n    @Parcelize\n    class Args(val path: @WriteWith<ParcelableParceler> Path) : ParcelableArgs\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/PasteState.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\n// TODO: Make immutable?\nclass PasteState(\n    var copy: Boolean = false,\n    val files: FileItemSet = fileItemSetOf()\n)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/PathDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.net.Uri\nimport java8.nio.file.Path\nimport java8.nio.file.Paths\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.storage.createOrLog\nimport me.zhanghai.android.files.util.asPathNameOrNull\nimport java.net.URI\n\nabstract class PathDialogFragment : NameDialogFragment() {\n    override fun isNameValid(name: String): Boolean {\n        if (!super.isNameValid(name)) {\n            return false\n        }\n        if (name.isEmpty()) {\n            binding.nameLayout.error = getString(R.string.file_list_path_error_empty)\n            return false\n        }\n        if (name.asPathNameOrNull() == null || name.toPathOrNull() == null) {\n            binding.nameLayout.error = getString(R.string.file_list_path_error_invalid)\n            return false\n        }\n        return true\n    }\n\n    final override fun onOk(name: String) {\n        onOk(name.toPathOrNull()!!)\n    }\n\n    private fun String.toPathOrNull(): Path? {\n        val uri = URI::class.createOrLog(toString())\n            // Also try to accept decoded path.\n            ?: Uri.parse(this).let {\n                URI::class.createOrLog(\n                    it.scheme, it.userInfo, it.host, it.port, it.path, it.query, it.fragment\n                )\n            }\n        if (uri != null) {\n            try {\n                return Paths.get(uri)\n            } catch (e: Exception) {\n                e.printStackTrace()\n            }\n        }\n        if (startsWith('/')) {\n            return Paths.get(this)\n        }\n        return null\n    }\n\n    abstract fun onOk(path: Path)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/PathExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.file.MimeType\nimport me.zhanghai.android.files.file.isSupportedArchive\nimport me.zhanghai.android.files.provider.archive.archiveFile\nimport me.zhanghai.android.files.provider.archive.isArchivePath\nimport me.zhanghai.android.files.provider.document.isDocumentPath\nimport me.zhanghai.android.files.provider.document.resolver.DocumentResolver\nimport me.zhanghai.android.files.provider.linux.isLinuxPath\n\nval Path.name: String\n    get() = fileName?.toString() ?: if (isArchivePath) archiveFile.fileName.toString() else \"/\"\n\nfun Path.toUserFriendlyString(): String = if (isLinuxPath) toFile().path else toUri().toString()\n\nfun Path.isArchiveFile(mimeType: MimeType): Boolean = !isArchivePath && mimeType.isSupportedArchive\n\nval Path.isLocalPath: Boolean\n    get() =\n        isLinuxPath || (isDocumentPath && DocumentResolver.isLocal(this as DocumentResolver.Path))\n\nval Path.isRemotePath: Boolean\n    get() = !isLocalPath\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/PathObserver.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.os.AsyncTask\nimport android.os.Handler\nimport android.os.Looper\nimport androidx.annotation.MainThread\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.provider.common.PathObservable\nimport me.zhanghai.android.files.provider.common.observe\nimport me.zhanghai.android.files.util.closeSafe\nimport java.io.Closeable\nimport java.io.IOException\n\nclass PathObserver(path: Path, @MainThread onChange: () -> Unit) : Closeable {\n    private var pathObservable: PathObservable? = null\n\n    private var closed = false\n    private val lock = Any()\n\n    init {\n        AsyncTask.THREAD_POOL_EXECUTOR.execute {\n            synchronized(lock) {\n                if (closed) {\n                    return@execute\n                }\n                pathObservable = try {\n                    path.observe(THROTTLE_INTERVAL_MILLIS)\n                } catch (e: UnsupportedOperationException) {\n                    // Ignored.\n                    return@execute\n                } catch (e: IOException) {\n                    // Ignored.\n                    e.printStackTrace()\n                    return@execute\n                }.apply {\n                    val mainHandler = Handler(Looper.getMainLooper())\n                    addObserver { mainHandler.post(onChange) }\n                }\n            }\n        }\n    }\n\n    override fun close() {\n        AsyncTask.THREAD_POOL_EXECUTOR.execute {\n            synchronized(lock) {\n                if (closed) {\n                    return@execute\n                }\n                closed = true\n                pathObservable?.closeSafe()\n            }\n        }\n    }\n\n    companion object {\n        private const val THROTTLE_INTERVAL_MILLIS = 1000L\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/PickOptions.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport me.zhanghai.android.files.file.MimeType\n\nclass PickOptions(\n    val mode: Mode,\n    val fileName: String?,\n    val readOnly: Boolean,\n    val mimeTypes: List<MimeType>,\n    val localOnly: Boolean,\n    val allowMultiple: Boolean\n) {\n    enum class Mode {\n        OPEN_FILE,\n        CREATE_FILE,\n        OPEN_DIRECTORY\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/RenameFileDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.app.Dialog\nimport android.os.Bundle\nimport androidx.annotation.StringRes\nimport androidx.fragment.app.Fragment\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.file.FileItem\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.putArgs\nimport me.zhanghai.android.files.util.show\n\nclass RenameFileDialogFragment : FileNameDialogFragment() {\n    private val args by args<Args>()\n\n    override val listener: Listener\n        get() = super.listener as Listener\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {\n        val dialog = super.onCreateDialog(savedInstanceState)\n\n        if (savedInstanceState == null) {\n            binding.nameEdit.setSelection(0, args.file.baseName.length)\n        }\n        return dialog\n    }\n\n    @StringRes\n    override val titleRes: Int = R.string.rename\n\n    override val initialName: String?\n        get() = args.file.name\n\n    override fun onOk(name: String) {\n        listener.renameFile(args.file, name)\n    }\n\n    companion object {\n        fun show(file: FileItem, fragment: Fragment) {\n            RenameFileDialogFragment().putArgs(Args(file)).show(fragment)\n        }\n    }\n\n    @Parcelize\n    class Args(val file: FileItem) : ParcelableArgs\n\n    interface Listener : FileNameDialogFragment.Listener {\n        fun renameFile(file: FileItem, newName: String)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/SearchFileListLiveData.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.os.AsyncTask\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.file.FileItem\nimport me.zhanghai.android.files.file.loadFileItem\nimport me.zhanghai.android.files.provider.common.search\nimport me.zhanghai.android.files.util.CloseableLiveData\nimport me.zhanghai.android.files.util.Failure\nimport me.zhanghai.android.files.util.Loading\nimport me.zhanghai.android.files.util.Stateful\nimport me.zhanghai.android.files.util.Success\nimport me.zhanghai.android.files.util.valueCompat\nimport java.io.IOException\nimport java.util.concurrent.ExecutorService\nimport java.util.concurrent.Future\n\nclass SearchFileListLiveData(\n    private val path: Path,\n    private val query: String\n) : CloseableLiveData<Stateful<List<FileItem>>>() {\n    private var future: Future<Unit>? = null\n\n    init {\n        loadValue()\n    }\n\n    fun loadValue() {\n        future?.cancel(true)\n        value = Loading(emptyList())\n        future = (AsyncTask.THREAD_POOL_EXECUTOR as ExecutorService).submit<Unit> {\n            val fileList = mutableListOf<FileItem>()\n            try {\n                path.search(query, INTERVAL_MILLIS) { paths: List<Path> ->\n                    for (path in paths) {\n                        val fileItem = try {\n                            path.loadFileItem()\n                        } catch (e: IOException) {\n                            e.printStackTrace()\n                            // TODO: Support file without information.\n                            continue\n                        }\n                        fileList.add(fileItem)\n                    }\n                    postValue(Loading(fileList.toList()))\n                }\n                postValue(Success(fileList))\n            } catch (e: Exception) {\n                // TODO: Retrieval of previous value is racy.\n                postValue(Failure(valueCompat.value, e))\n            }\n        }\n    }\n\n    override fun close() {\n        future?.cancel(true)\n    }\n\n    companion object {\n        private const val INTERVAL_MILLIS = 500L\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/SearchState.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nclass SearchState(val isSearching: Boolean, val query: String)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/ShowRequestAllFilesAccessRationaleDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.app.Dialog\nimport android.content.DialogInterface\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatDialogFragment\nimport androidx.fragment.app.Fragment\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.util.show\n\nclass ShowRequestAllFilesAccessRationaleDialogFragment : AppCompatDialogFragment() {\n    private val listener: Listener\n        get() = requireParentFragment() as Listener\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {\n        return MaterialAlertDialogBuilder(requireContext(), theme)\n            .setMessage(R.string.all_files_access_rationale_message)\n            .setPositiveButton(android.R.string.ok) { _, _ ->\n                listener.onShowRequestAllFilesAccessRationaleResult(true)\n            }\n            .setNegativeButton(android.R.string.cancel) { _, _ ->\n                listener.onShowRequestAllFilesAccessRationaleResult(false)\n            }\n            .create()\n    }\n\n    override fun onCancel(dialog: DialogInterface) {\n        super.onCancel(dialog)\n\n        listener.onShowRequestAllFilesAccessRationaleResult(false)\n    }\n\n    companion object {\n        fun show(fragment: Fragment) {\n            ShowRequestAllFilesAccessRationaleDialogFragment().show(fragment)\n        }\n    }\n\n    interface Listener {\n        fun onShowRequestAllFilesAccessRationaleResult(shouldRequest: Boolean)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/ShowRequestNotificationPermissionInSettingsRationaleDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.app.Dialog\nimport android.content.DialogInterface\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatDialogFragment\nimport androidx.fragment.app.Fragment\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.util.show\n\nclass ShowRequestNotificationPermissionInSettingsRationaleDialogFragment : AppCompatDialogFragment() {\n    private val listener: Listener\n        get() = requireParentFragment() as Listener\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {\n        return MaterialAlertDialogBuilder(requireContext(), theme)\n            .setMessage(R.string.notification_permission_permanently_denied_message)\n            .setPositiveButton(R.string.open_settings) { _, _ ->\n                listener.onShowRequestNotificationPermissionInSettingsRationaleResult(true)\n            }\n            .setNegativeButton(android.R.string.cancel) { _, _ ->\n                listener.onShowRequestNotificationPermissionInSettingsRationaleResult(false)\n            }\n            .create()\n    }\n\n    override fun onCancel(dialog: DialogInterface) {\n        super.onCancel(dialog)\n\n        listener.onShowRequestNotificationPermissionInSettingsRationaleResult(false)\n    }\n\n    companion object {\n        fun show(fragment: Fragment) {\n            ShowRequestNotificationPermissionInSettingsRationaleDialogFragment().show(fragment)\n        }\n    }\n\n    interface Listener {\n        fun onShowRequestNotificationPermissionInSettingsRationaleResult(shouldRequest: Boolean)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/ShowRequestNotificationPermissionRationaleDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.app.Dialog\nimport android.content.DialogInterface\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatDialogFragment\nimport androidx.fragment.app.Fragment\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.util.show\n\nclass ShowRequestNotificationPermissionRationaleDialogFragment : AppCompatDialogFragment() {\n    private val listener: Listener\n        get() = requireParentFragment() as Listener\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {\n        return MaterialAlertDialogBuilder(requireContext(), theme)\n            .setMessage(R.string.notification_permission_rationale_message)\n            .setPositiveButton(android.R.string.ok) { _, _ ->\n                listener.onShowRequestNotificationPermissionRationaleResult(true)\n            }\n            .setNegativeButton(android.R.string.cancel) { _, _ ->\n                listener.onShowRequestNotificationPermissionRationaleResult(false)\n            }\n            .create()\n    }\n\n    override fun onCancel(dialog: DialogInterface) {\n        super.onCancel(dialog)\n\n        listener.onShowRequestNotificationPermissionRationaleResult(false)\n    }\n\n    companion object {\n        fun show(fragment: Fragment) {\n            ShowRequestNotificationPermissionRationaleDialogFragment().show(fragment)\n        }\n    }\n\n    interface Listener {\n        fun onShowRequestNotificationPermissionRationaleResult(shouldRequest: Boolean)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/ShowRequestStoragePermissionInSettingsRationaleDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.app.Dialog\nimport android.content.DialogInterface\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatDialogFragment\nimport androidx.fragment.app.Fragment\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.util.show\n\nclass ShowRequestStoragePermissionInSettingsRationaleDialogFragment : AppCompatDialogFragment() {\n    private val listener: Listener\n        get() = requireParentFragment() as Listener\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {\n        return MaterialAlertDialogBuilder(requireContext(), theme)\n            .setMessage(R.string.storage_permission_permanently_denied_message)\n            .setPositiveButton(R.string.open_settings) { _, _ ->\n                listener.onShowRequestStoragePermissionInSettingsRationaleResult(true)\n            }\n            .setNegativeButton(android.R.string.cancel) { _, _ ->\n                listener.onShowRequestStoragePermissionInSettingsRationaleResult(false)\n            }\n            .create()\n    }\n\n    override fun onCancel(dialog: DialogInterface) {\n        super.onCancel(dialog)\n\n        listener.onShowRequestStoragePermissionInSettingsRationaleResult(false)\n    }\n\n    companion object {\n        fun show(fragment: Fragment) {\n            ShowRequestStoragePermissionInSettingsRationaleDialogFragment().show(fragment)\n        }\n    }\n\n    interface Listener {\n        fun onShowRequestStoragePermissionInSettingsRationaleResult(shouldRequest: Boolean)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/ShowRequestStoragePermissionRationaleDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.app.Dialog\nimport android.content.DialogInterface\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatDialogFragment\nimport androidx.fragment.app.Fragment\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.util.show\n\nclass ShowRequestStoragePermissionRationaleDialogFragment : AppCompatDialogFragment() {\n    private val listener: Listener\n        get() = requireParentFragment() as Listener\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {\n        return MaterialAlertDialogBuilder(requireContext(), theme)\n            .setMessage(R.string.storage_permission_rationale_message)\n            .setPositiveButton(android.R.string.ok) { _, _ ->\n                listener.onShowRequestStoragePermissionRationaleResult(true)\n            }\n            .setNegativeButton(android.R.string.cancel) { _, _ ->\n                listener.onShowRequestStoragePermissionRationaleResult(false)\n            }\n            .create()\n    }\n\n    override fun onCancel(dialog: DialogInterface) {\n        super.onCancel(dialog)\n\n        listener.onShowRequestStoragePermissionRationaleResult(false)\n    }\n\n    companion object {\n        fun show(fragment: Fragment) {\n            ShowRequestStoragePermissionRationaleDialogFragment().show(fragment)\n        }\n    }\n\n    interface Listener {\n        fun onShowRequestStoragePermissionRationaleResult(shouldRequest: Boolean)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/TrailData.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.os.Parcelable\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.provider.archive.archiveFile\nimport me.zhanghai.android.files.provider.archive.isArchivePath\n\nclass TrailData private constructor(\n    val trail: List<Path>,\n    private val states: MutableList<Parcelable?>,\n    val currentIndex: Int\n) {\n    fun navigateTo(lastState: Parcelable, path: Path): TrailData {\n        val newTrail = createTrail(path)\n        val newStates = mutableListOf<Parcelable?>()\n        val newIndex = newTrail.size - 1\n        var isPrefix = true\n        for (index in newTrail.indices) {\n            if (isPrefix && index < trail.size) {\n                if (newTrail[index] == trail[index]) {\n                    newStates.add(if (index != currentIndex) states[index] else lastState)\n                } else {\n                    isPrefix = false\n                    newStates.add(null)\n                }\n            } else {\n                newStates.add(null)\n            }\n        }\n        if (isPrefix) {\n            for (index in newTrail.size..<trail.size) {\n                newTrail.add(trail[index])\n                newStates.add(if (index != currentIndex) states[index] else lastState)\n            }\n        }\n        return TrailData(newTrail, newStates, newIndex)\n    }\n\n    fun navigateUp(): TrailData? {\n        if (currentIndex == 0) {\n            return null\n        }\n        val newIndex = currentIndex - 1\n        return TrailData(trail, states, newIndex)\n    }\n\n    val pendingState: Parcelable?\n        get() = states.set(currentIndex, null)\n\n    val currentPath: Path\n        get() = trail[currentIndex]\n\n    companion object {\n        fun of(path: Path): TrailData {\n            val trail: List<Path> = createTrail(path)\n            val states = MutableList<Parcelable?>(trail.size) { null }\n            val index = trail.size - 1\n            return TrailData(trail, states, index)\n        }\n\n        private fun createTrail(path: Path): MutableList<Path> {\n            var path = path\n            val trail = mutableListOf<Path>()\n            val archiveFile = if (path.isArchivePath) path.archiveFile else null\n            while (true) {\n                trail.add(path)\n                path = path.parent ?: break\n            }\n            trail.reverse()\n            if (archiveFile != null) {\n                val archiveFileParent = archiveFile.parent\n                if (archiveFileParent != null) {\n                    trail.addAll(0, createTrail(archiveFileParent))\n                }\n            }\n            return trail\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/filelist/TrailLiveData.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.filelist\n\nimport android.os.Parcelable\nimport androidx.lifecycle.LiveData\nimport java8.nio.file.Path\n\nclass TrailLiveData : LiveData<TrailData>() {\n    fun navigateTo(lastState: Parcelable, path: Path) {\n        val oldTrailData = value\n        if (oldTrailData == null) {\n            resetTo(path)\n            return\n        }\n        value = oldTrailData.navigateTo(lastState, path)\n    }\n\n    fun resetTo(path: Path) {\n        value = TrailData.of(path)\n    }\n\n    fun navigateUp(): Boolean {\n        val oldTrailData = value ?: return false\n        val trailData = oldTrailData.navigateUp() ?: return false\n        value = trailData\n        return true\n    }\n\n    fun reload() {\n        value = value\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/FileLiveData.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties\n\nimport android.os.AsyncTask\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.file.FileItem\nimport me.zhanghai.android.files.file.loadFileItem\nimport me.zhanghai.android.files.util.Failure\nimport me.zhanghai.android.files.util.Loading\nimport me.zhanghai.android.files.util.Stateful\nimport me.zhanghai.android.files.util.Success\nimport me.zhanghai.android.files.util.valueCompat\n\nclass FileLiveData private constructor(\n    path: Path,\n    file: FileItem?\n) : PathObserverLiveData<Stateful<FileItem>>(path) {\n    constructor(path: Path) : this(path, null)\n\n    constructor(file: FileItem) : this(file.path, file)\n\n    init {\n        if (file != null) {\n            value = Success(file)\n        } else {\n            loadValue()\n        }\n        observe()\n    }\n\n    override fun loadValue() {\n        value = Loading(value?.value)\n        AsyncTask.THREAD_POOL_EXECUTOR.execute {\n            val value = try {\n                val file = path.loadFileItem()\n                Success(file)\n            } catch (e: Exception) {\n                Failure(valueCompat.value, e)\n            }\n            postValue(value)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/FilePropertiesDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties\n\nimport android.app.Dialog\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.view.WindowManager\nimport androidx.appcompat.app.AppCompatDialogFragment\nimport androidx.fragment.app.Fragment\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.databinding.FilePropertiesDialogBinding\nimport me.zhanghai.android.files.file.FileItem\nimport me.zhanghai.android.files.filelist.name\nimport me.zhanghai.android.files.fileproperties.apk.FilePropertiesApkTabFragment\nimport me.zhanghai.android.files.fileproperties.audio.FilePropertiesAudioTabFragment\nimport me.zhanghai.android.files.fileproperties.basic.FilePropertiesBasicTabFragment\nimport me.zhanghai.android.files.fileproperties.checksum.FilePropertiesChecksumTabFragment\nimport me.zhanghai.android.files.fileproperties.image.FilePropertiesImageTabFragment\nimport me.zhanghai.android.files.fileproperties.permission.FilePropertiesPermissionTabFragment\nimport me.zhanghai.android.files.fileproperties.video.FilePropertiesVideoTabFragment\nimport me.zhanghai.android.files.ui.TabFragmentPagerAdapter\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.layoutInflater\nimport me.zhanghai.android.files.util.putArgs\nimport me.zhanghai.android.files.util.show\nimport me.zhanghai.android.files.util.viewModels\n\nclass FilePropertiesDialogFragment : AppCompatDialogFragment() {\n    private val args by args<Args>()\n\n    private val viewModel by viewModels { { FilePropertiesFileViewModel(args.file) } }\n\n    private lateinit var binding: FilePropertiesDialogBinding\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =\n        MaterialAlertDialogBuilder(requireContext(), theme)\n            .setTitle(getString(R.string.file_properties_title_format, args.file.name))\n            .apply {\n                binding = FilePropertiesDialogBinding.inflate(context.layoutInflater)\n                setView(binding.root)\n            }\n            .setPositiveButton(android.R.string.ok, null)\n            .create()\n\n    // HACK: Work around child FragmentManager requiring a view.\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View = binding.root\n\n    override fun onActivityCreated(savedInstanceState: Bundle?) {\n        super.onActivityCreated(savedInstanceState)\n\n        // Initialize the shared file view model before child fragments are created.\n        viewModel.fileLiveData\n        val tabs = mutableListOf<Pair<Int, () -> Fragment>>()\n            .apply {\n                add(R.string.file_properties_basic to { FilePropertiesBasicTabFragment() })\n                if (FilePropertiesPermissionTabFragment.isAvailable(args.file)) {\n                    add(\n                        R.string.file_properties_permission\n                            to { FilePropertiesPermissionTabFragment() }\n                    )\n                }\n                if (FilePropertiesImageTabFragment.isAvailable(args.file)) {\n                    add(\n                        R.string.file_properties_image to {\n                            FilePropertiesImageTabFragment().putArgs(\n                                FilePropertiesImageTabFragment.Args(\n                                    args.file.path, args.file.mimeType\n                                )\n                            )\n                        }\n                    )\n                }\n                if (FilePropertiesAudioTabFragment.isAvailable(args.file)) {\n                    add(\n                        R.string.file_properties_audio to {\n                            FilePropertiesAudioTabFragment().putArgs(\n                                FilePropertiesAudioTabFragment.Args(args.file.path)\n                            )\n                        }\n                    )\n                }\n                if (FilePropertiesVideoTabFragment.isAvailable(args.file)) {\n                    add(\n                        R.string.file_properties_video to {\n                            FilePropertiesVideoTabFragment().putArgs(\n                                FilePropertiesVideoTabFragment.Args(args.file.path)\n                            )\n                        }\n                    )\n                }\n                if (FilePropertiesApkTabFragment.isAvailable(args.file)) {\n                    add(\n                        R.string.file_properties_apk to {\n                            FilePropertiesApkTabFragment().putArgs(\n                                FilePropertiesApkTabFragment.Args(args.file.path)\n                            )\n                        }\n                    )\n                }\n                if (FilePropertiesChecksumTabFragment.isAvailable(args.file)) {\n                    add(\n                        R.string.file_properties_checksum to {\n                            FilePropertiesChecksumTabFragment().putArgs(\n                                FilePropertiesChecksumTabFragment.Args(args.file.path)\n                            )\n                        }\n                    )\n                }\n            }\n            .map { getString(it.first) to it.second }\n            .toTypedArray()\n        val tabAdapter = TabFragmentPagerAdapter(childFragmentManager, *tabs)\n        binding.viewPager.offscreenPageLimit = tabAdapter.count - 1\n        binding.viewPager.adapter = tabAdapter\n        binding.tabLayout.setupWithViewPager(binding.viewPager)\n    }\n\n    override fun onStart() {\n        super.onStart()\n\n        // AlertDialog (its AlertController) adds FLAG_ALT_FOCUSABLE_IM when the initial custom\n        // view doesn't have any view that returns true for onCheckIsTextEditor().\n        requireDialog().window!!.clearFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM)\n    }\n\n    companion object {\n        fun show(file: FileItem, fragment: Fragment) {\n            FilePropertiesDialogFragment().putArgs(Args(file)).show(fragment)\n        }\n    }\n\n    @Parcelize\n    class Args(val file: FileItem): ParcelableArgs\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/FilePropertiesFileViewModel.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties\n\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.ViewModel\nimport me.zhanghai.android.files.file.FileItem\nimport me.zhanghai.android.files.util.Stateful\n\nclass FilePropertiesFileViewModel(file: FileItem) : ViewModel() {\n    private val _fileLiveData = FileLiveData(file)\n    val fileLiveData: LiveData<Stateful<FileItem>>\n        get() = _fileLiveData\n\n    fun reload() {\n        _fileLiveData.loadValue()\n    }\n\n    override fun onCleared() {\n        _fileLiveData.close()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/FilePropertiesTabFragment.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.LinearLayout\nimport android.widget.TextView\nimport androidx.annotation.StringRes\nimport androidx.core.view.forEach\nimport androidx.fragment.app.Fragment\nimport androidx.viewbinding.ViewBinding\nimport me.zhanghai.android.files.databinding.FilePropertiesTabFragmentBinding\nimport me.zhanghai.android.files.databinding.FilePropertiesTabItemBinding\nimport me.zhanghai.android.files.util.Failure\nimport me.zhanghai.android.files.util.Loading\nimport me.zhanghai.android.files.util.Stateful\nimport me.zhanghai.android.files.util.fadeToVisibilityUnsafe\nimport me.zhanghai.android.files.util.layoutInflater\nimport me.zhanghai.android.files.util.showToast\n\nabstract class FilePropertiesTabFragment : Fragment() {\n    protected lateinit var binding: FilePropertiesTabFragmentBinding\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View =\n        FilePropertiesTabFragmentBinding.inflate(inflater, container, false)\n            .also { binding = it }\n            .root\n\n    override fun onActivityCreated(savedInstanceState: Bundle?) {\n        super.onActivityCreated(savedInstanceState)\n\n        binding.swipeRefreshLayout.setOnRefreshListener { refresh() }\n    }\n\n    abstract fun refresh()\n\n    protected inline fun <T> bindView(stateful: Stateful<T>, block: ViewBuilder.(T) -> Unit) {\n        val value = stateful.value\n        val hasValue = value != null\n        binding.progress.fadeToVisibilityUnsafe(stateful is Loading && !hasValue)\n        binding.swipeRefreshLayout.isRefreshing = stateful is Loading && hasValue\n        binding.errorText.fadeToVisibilityUnsafe(stateful is Failure && !hasValue)\n        if (stateful is Failure) {\n            stateful.throwable.printStackTrace()\n            val error = stateful.throwable.toString()\n            if (hasValue) {\n                showToast(error)\n            } else {\n                binding.errorText.text = error\n            }\n        }\n        binding.scrollView.fadeToVisibilityUnsafe(hasValue)\n        if (value != null) {\n            ViewBuilder(binding.linearLayout).apply {\n                block(value)\n                build()\n            }\n        }\n    }\n\n    protected class ViewBuilder(val linearLayout: LinearLayout) {\n        private val scrapViews = mutableMapOf<Class<out ViewBinding>, MutableList<ViewBinding>>()\n\n        init {\n            linearLayout.forEach { view ->\n                val binding = view.tag as ViewBinding\n                scrapViews.getOrPut(binding.javaClass) { mutableListOf() } += binding\n            }\n            linearLayout.removeAllViews()\n        }\n\n        @Suppress(\"UNCHECKED_CAST\")\n        fun <T : ViewBinding> getScrapItemBinding(bindingClass: Class<T>): T? =\n            scrapViews[bindingClass]?.removeLastOrNull() as T?\n\n        fun addView(binding: ViewBinding) {\n            linearLayout.addView(binding.root)\n        }\n\n        fun addItemView(\n            hint: String,\n            text: String,\n            onClickListener: ((View) -> Unit)? = null\n        ): TextView {\n            val itemBinding =\n                getScrapItemBinding(FilePropertiesTabItemBinding::class.java)?.also { addView(it) }\n                    ?: FilePropertiesTabItemBinding.inflate(\n                        linearLayout.context.layoutInflater, linearLayout, true\n                    )\n                        .also { it.root.tag = it }\n            itemBinding.textInputLayout.hint = hint\n            itemBinding.textInputLayout.setDropDown(onClickListener != null)\n            itemBinding.text.setText(text)\n            itemBinding.text.setTextIsSelectable(onClickListener == null)\n            itemBinding.text.setOnClickListener(onClickListener?.let { View.OnClickListener(it) })\n            return itemBinding.text\n        }\n\n        fun addItemView(\n            @StringRes hintRes: Int,\n            text: String,\n            onClickListener: ((View) -> Unit)? = null\n        ): TextView = addItemView(linearLayout.context.getString(hintRes), text, onClickListener)\n\n        fun build() {\n            scrapViews.clear()\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/MediaMetadataRetrieverExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties\n\nimport android.media.MediaMetadataRetriever\nimport java.text.ParsePosition\nimport java.text.SimpleDateFormat\nimport java.time.Instant\nimport java.util.Locale\nimport java.util.TimeZone\nimport kotlin.math.max\nimport me.zhanghai.android.files.util.takeIfNotBlank\n\nfun MediaMetadataRetriever.extractMetadataNotBlank(keyCode: Int): String? =\n    extractMetadata(keyCode)?.takeIfNotBlank()\n\nprivate val dateFormat = SimpleDateFormat(\"yyyyMMdd'T'HHmmss\", Locale.US)\n    .apply { timeZone = TimeZone.getTimeZone(\"UTC\") }\n\n// @see com.android.providers.media.scan.ModernMediaScanner.parseOptionalDate\nval MediaMetadataRetriever.date: Instant?\n    get() {\n        val date = extractMetadataNotBlank(MediaMetadataRetriever.METADATA_KEY_DATE) ?: return null\n        return dateFormat.parse(date, ParsePosition(0))?.time?.let { Instant.ofEpochMilli(it) }\n    }\n\n// @see android.media.cts.MediaRecorderTest.checkLocationInFile\nval MediaMetadataRetriever.location: Pair<Float, Float>?\n    get() {\n        var location = extractMetadataNotBlank(MediaMetadataRetriever.METADATA_KEY_LOCATION)\n            ?: return null\n        if (location.endsWith('/')) {\n            location = location.dropLast(1)\n        }\n        val index = max(location.lastIndexOf('+'), location.lastIndexOf('-'))\n        if (index <= 0) {\n            return null\n        }\n        val latitude = location.substring(0, index).toFloatOrNull() ?: return null\n        val longitude = location.substring(index).toFloatOrNull() ?: return null\n        return latitude to longitude\n    }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/PathObserverLiveData.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties\n\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.filelist.PathObserver\nimport me.zhanghai.android.files.util.CloseableLiveData\n\nabstract class PathObserverLiveData<T>(protected val path: Path) : CloseableLiveData<T>() {\n    private lateinit var observer: PathObserver\n\n    @Volatile\n    private var changedWhileInactive = false\n\n    protected fun observe() {\n        observer = PathObserver(path) { onChangeObserved() }\n    }\n\n    abstract fun loadValue()\n\n    private fun onChangeObserved() {\n        if (hasActiveObservers()) {\n            loadValue()\n        } else {\n            changedWhileInactive = true\n        }\n    }\n\n    override fun onActive() {\n        if (changedWhileInactive) {\n            loadValue()\n            changedWhileInactive = false\n        }\n    }\n\n    override fun close() {\n        observer.close()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/apk/ApkInfo.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.apk\n\nimport android.content.pm.PackageInfo\n\nclass ApkInfo(\n    val packageInfo: PackageInfo,\n    val label: String,\n    val signingCertificateDigests: List<String>,\n    val pastSigningCertificateDigests: List<String>\n)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/apk/ApkInfoLiveData.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.apk\n\nimport android.content.pm.PackageManager\nimport android.os.AsyncTask\nimport android.os.Build\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.app.packageManager\nimport me.zhanghai.android.files.fileproperties.PathObserverLiveData\nimport me.zhanghai.android.files.util.Failure\nimport me.zhanghai.android.files.util.Loading\nimport me.zhanghai.android.files.util.Stateful\nimport me.zhanghai.android.files.util.Success\nimport me.zhanghai.android.files.util.getPackageArchiveInfoCompat\nimport me.zhanghai.android.files.util.sha1Digest\nimport me.zhanghai.android.files.util.toHexString\nimport me.zhanghai.android.files.util.valueCompat\nimport java.io.IOException\n\nclass ApkInfoLiveData(path: Path) : PathObserverLiveData<Stateful<ApkInfo>>(path) {\n    init {\n        loadValue()\n        observe()\n    }\n\n    override fun loadValue() {\n        value = Loading(value?.value)\n        AsyncTask.THREAD_POOL_EXECUTOR.execute {\n            val value = try {\n                // We must always pass in PackageManager.GET_SIGNATURES for\n                // PackageManager.getPackageArchiveInfo() to call\n                // PackageParser.collectCertificates().\n                @Suppress(\"DEPRECATION\")\n                var packageInfoFlags = (PackageManager.GET_PERMISSIONS\n                    or PackageManager.GET_SIGNATURES)\n                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {\n                    packageInfoFlags = packageInfoFlags or PackageManager.GET_SIGNING_CERTIFICATES\n                }\n                val (packageInfo, closeable) =\n                    packageManager.getPackageArchiveInfoCompat(path, packageInfoFlags)\n                val apkInfo = closeable.use {\n                    val applicationInfo = packageInfo?.applicationInfo\n                        ?: throw IOException(\"ApplicationInfo is null\")\n                    val label = applicationInfo.loadLabel(packageManager).toString()\n                    val signingCertificates = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {\n                        // PackageInfo.signatures returns only the oldest certificate if there are\n                        // past certificates on P and above for compatibility.\n                        packageInfo.signingInfo?.apkContentsSigners\n                    } else {\n                        @Suppress(\"DEPRECATION\")\n                        packageInfo.signatures\n                    } ?: emptyArray()\n                    val signingCertificateDigests = signingCertificates\n                        .map { it.toByteArray().sha1Digest().toHexString() }\n                    val pastSigningCertificates =\n                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {\n                            val signingInfo = packageInfo.signingInfo\n                            // SigningInfo.getSigningCertificateHistory() may return the current\n                            // certificate if there are no past certificates.\n                            if (signingInfo?.hasPastSigningCertificates() == true) {\n                                // SigningInfo.getSigningCertificateHistory() also returns the\n                                // current certificate.\n                                signingInfo.signingCertificateHistory?.toMutableList()\n                                    ?.apply { removeAll(signingCertificates) }\n                            } else {\n                                null\n                            }\n                        } else {\n                            null\n                        } ?: emptyList()\n                    val pastSigningCertificateDigests = pastSigningCertificates\n                        .map { it.toByteArray().sha1Digest().toHexString() }\n                    ApkInfo(\n                        packageInfo, label, signingCertificateDigests, pastSigningCertificateDigests\n                    )\n                }\n                Success(apkInfo)\n            } catch (e: Exception) {\n                Failure(valueCompat.value, e)\n            }\n            postValue(value)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/apk/FilePropertiesApkTabFragment.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.apk\n\nimport android.os.Build\nimport java8.nio.file.Path\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.compat.longVersionCodeCompat\nimport me.zhanghai.android.files.file.FileItem\nimport me.zhanghai.android.files.file.isApk\nimport me.zhanghai.android.files.fileproperties.FilePropertiesTabFragment\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.ParcelableParceler\nimport me.zhanghai.android.files.util.Stateful\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.getQuantityString\nimport me.zhanghai.android.files.util.getStringArray\nimport me.zhanghai.android.files.util.isGetPackageArchiveInfoCompatible\nimport me.zhanghai.android.files.util.viewModels\n\nclass FilePropertiesApkTabFragment : FilePropertiesTabFragment() {\n    private val args by args<Args>()\n\n    private val viewModel by viewModels { { FilePropertiesApkTabViewModel(args.path) } }\n\n    override fun onResume() {\n        super.onResume()\n\n        viewModel.apkInfoLiveData.observe(viewLifecycleOwner) { onApkInfoChanged(it) }\n    }\n\n    override fun refresh() {\n        viewModel.reload()\n    }\n\n    private fun onApkInfoChanged(stateful: Stateful<ApkInfo>) {\n        bindView(stateful) { apkInfo ->\n            addItemView(R.string.file_properties_apk_label, apkInfo.label)\n            val packageInfo = apkInfo.packageInfo\n            addItemView(R.string.file_properties_apk_package_name, packageInfo.packageName)\n            addItemView(\n                R.string.file_properties_apk_version, getString(\n                    R.string.file_properties_apk_version_format, packageInfo.versionName,\n                    packageInfo.longVersionCodeCompat\n                )\n            )\n            val applicationInfo = packageInfo.applicationInfo!!\n            // PackageParser didn't return minSdkVersion before N, so it's hard to implement a\n            // compat version.\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n                addItemView(\n                    R.string.file_properties_apk_min_sdk_version,\n                    getSdkVersionText(applicationInfo.minSdkVersion)\n                )\n            }\n            addItemView(\n                R.string.file_properties_apk_target_sdk_version,\n                getSdkVersionText(applicationInfo.targetSdkVersion)\n            )\n            val requestedPermissionsSize = packageInfo.requestedPermissions?.size ?: 0\n            addItemView(\n                R.string.file_properties_apk_requested_permissions,\n                if (requestedPermissionsSize == 0) {\n                    getString(R.string.file_properties_apk_requested_permissions_zero)\n                } else {\n                    getQuantityString(\n                        R.plurals.file_properties_apk_requested_permissions_positive_format,\n                        requestedPermissionsSize, requestedPermissionsSize\n                    )\n                }, if (requestedPermissionsSize == 0) {\n                    null\n                } else {\n                    {\n                        PermissionListDialogFragment.show(\n                            packageInfo.requestedPermissions!!, this@FilePropertiesApkTabFragment\n                        )\n                    }\n                }\n            )\n            addItemView(\n                R.string.file_properties_apk_signature_digests,\n                if (apkInfo.signingCertificateDigests.isNotEmpty()) {\n                    apkInfo.signingCertificateDigests.joinToString(\"\\n\")\n                } else {\n                    getString(R.string.file_properties_apk_signature_digests_empty)\n                }\n            )\n            if (apkInfo.pastSigningCertificateDigests.isNotEmpty()) {\n                addItemView(\n                    R.string.file_properties_apk_past_signature_digests,\n                    apkInfo.pastSigningCertificateDigests.joinToString(\"\\n\")\n                )\n            }\n        }\n    }\n\n    private fun getSdkVersionText(sdkVersion: Int): String {\n        val names = getStringArray(R.array.file_properites_apk_sdk_version_names)\n        val codeNames = getStringArray(R.array.file_properites_apk_sdk_version_codenames)\n        return getString(\n            R.string.file_properites_apk_sdk_version_format,\n            names[sdkVersion.coerceIn(names.indices)],\n            codeNames[sdkVersion.coerceIn(codeNames.indices)], sdkVersion\n        )\n    }\n\n    companion object {\n        fun isAvailable(file: FileItem): Boolean =\n            file.mimeType.isApk && file.path.isGetPackageArchiveInfoCompatible\n    }\n\n    @Parcelize\n    class Args(val path: @WriteWith<ParcelableParceler> Path) : ParcelableArgs\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/apk/FilePropertiesApkTabViewModel.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.apk\n\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.ViewModel\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.util.Stateful\n\nclass FilePropertiesApkTabViewModel(path: Path) : ViewModel() {\n    private val _apkInfoLiveData = ApkInfoLiveData(path)\n    val apkInfoLiveData: LiveData<Stateful<ApkInfo>>\n        get() = _apkInfoLiveData\n\n    fun reload() {\n        _apkInfoLiveData.loadValue()\n    }\n\n    override fun onCleared() {\n        _apkInfoLiveData.close()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/apk/PermissionItem.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.apk\n\nimport android.content.pm.PermissionInfo\n\nclass PermissionItem(\n    val name: String,\n    val permissionInfo: PermissionInfo?,\n    val label: String?,\n    val description: String?\n)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/apk/PermissionListAdapter.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.apk\n\nimport android.content.pm.PermissionInfo\nimport android.view.ViewGroup\nimport androidx.core.view.isVisible\nimport androidx.recyclerview.widget.RecyclerView\nimport me.zhanghai.android.files.app.clipboardManager\nimport me.zhanghai.android.files.compat.protectionCompat\nimport me.zhanghai.android.files.databinding.PermissionItemBinding\nimport me.zhanghai.android.files.ui.SimpleAdapter\nimport me.zhanghai.android.files.util.copyText\nimport me.zhanghai.android.files.util.isBold\nimport me.zhanghai.android.files.util.layoutInflater\nimport java.util.Locale\n\nclass PermissionListAdapter : SimpleAdapter<PermissionItem, PermissionListAdapter.ViewHolder>() {\n    override val hasStableIds: Boolean\n        get() = true\n\n    override fun getItemId(position: Int): Long = getItem(position).name.hashCode().toLong()\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder =\n        ViewHolder(PermissionItemBinding.inflate(parent.context.layoutInflater, parent, false))\n\n    override fun onBindViewHolder(holder: ViewHolder, position: Int) {\n        val binding = holder.binding\n        val permission = getItem(position)\n        val name = permission.name\n        binding.root.setOnLongClickListener {\n            clipboardManager.copyText(name, binding.root.context)\n            true\n        }\n        val label = permission.label\n        binding.labelText.text = label?.capitalize(Locale.getDefault()) ?: name\n        binding.labelText.isBold = (permission.permissionInfo?.protectionCompat\n            == PermissionInfo.PROTECTION_DANGEROUS)\n        binding.nameText.isVisible = label != null\n        binding.nameText.text = name\n        binding.descriptionText.text = permission.description\n    }\n\n    class ViewHolder(val binding: PermissionItemBinding) : RecyclerView.ViewHolder(binding.root)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/apk/PermissionListDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.apk\n\nimport android.app.Dialog\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatDialogFragment\nimport androidx.fragment.app.Fragment\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.databinding.PermissionListDialogBinding\nimport me.zhanghai.android.files.util.Failure\nimport me.zhanghai.android.files.util.Loading\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.Stateful\nimport me.zhanghai.android.files.util.Success\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.fadeInUnsafe\nimport me.zhanghai.android.files.util.fadeOutUnsafe\nimport me.zhanghai.android.files.util.fadeToVisibilityUnsafe\nimport me.zhanghai.android.files.util.getQuantityString\nimport me.zhanghai.android.files.util.layoutInflater\nimport me.zhanghai.android.files.util.putArgs\nimport me.zhanghai.android.files.util.show\nimport me.zhanghai.android.files.util.viewModels\n\nclass PermissionListDialogFragment : AppCompatDialogFragment() {\n    private val args by args<Args>()\n\n    private val viewModel by viewModels { { PermissionListViewModel(args.permissionNames) } }\n\n    private lateinit var binding: PermissionListDialogBinding\n\n    private lateinit var adapter: PermissionListAdapter\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =\n        MaterialAlertDialogBuilder(requireContext(), theme)\n            .apply {\n                val permissionsSize = args.permissionNames.size\n                setTitle(\n                    getQuantityString(\n                        R.plurals.file_properties_apk_requested_permissions_positive_format,\n                        permissionsSize, permissionsSize\n                    )\n                )\n\n                binding = PermissionListDialogBinding.inflate(context.layoutInflater)\n                binding.recyclerView.layoutManager = LinearLayoutManager(context)\n                adapter = PermissionListAdapter()\n                binding.recyclerView.adapter = adapter\n                setView(binding.root)\n\n                viewModel.permissionListLiveData.observe(this@PermissionListDialogFragment) {\n                    onPermissionListChanged(it)\n                }\n            }\n            .setPositiveButton(android.R.string.ok, null)\n            .create()\n\n    private fun onPermissionListChanged(stateful: Stateful<List<PermissionItem>>) {\n        when (stateful) {\n            is Loading -> {\n                binding.progress.fadeInUnsafe()\n                binding.errorText.fadeOutUnsafe()\n                binding.emptyView.fadeOutUnsafe()\n                adapter.clear()\n            }\n            is Failure -> {\n                binding.progress.fadeOutUnsafe()\n                binding.errorText.fadeInUnsafe()\n                binding.errorText.text = stateful.throwable.toString()\n                binding.emptyView.fadeOutUnsafe()\n                adapter.clear()\n            }\n            is Success -> {\n                binding.progress.fadeOutUnsafe()\n                binding.errorText.fadeOutUnsafe()\n                binding.emptyView.fadeToVisibilityUnsafe(stateful.value.isEmpty())\n                adapter.replace(stateful.value)\n            }\n        }\n    }\n\n    companion object {\n        fun show(permissionNames: Array<String>, fragment: Fragment) {\n            PermissionListDialogFragment().putArgs(Args(permissionNames)).show(fragment)\n        }\n    }\n\n    @Parcelize\n    class Args(val permissionNames: Array<String>) : ParcelableArgs\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/apk/PermissionListLiveData.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.apk\n\nimport android.os.AsyncTask\nimport androidx.lifecycle.MutableLiveData\nimport me.zhanghai.android.files.app.packageManager\nimport me.zhanghai.android.files.util.Failure\nimport me.zhanghai.android.files.util.Loading\nimport me.zhanghai.android.files.util.Stateful\nimport me.zhanghai.android.files.util.Success\nimport me.zhanghai.android.files.util.getPermissionInfoOrNull\nimport me.zhanghai.android.files.util.valueCompat\n\nclass PermissionListLiveData(\n    private val permissionNames: Array<String>\n) : MutableLiveData<Stateful<List<PermissionItem>>>() {\n    init {\n        loadValue()\n    }\n\n    private fun loadValue() {\n        value = Loading(value?.value)\n        AsyncTask.THREAD_POOL_EXECUTOR.execute {\n            val value = try {\n                val permissions = permissionNames.map { name ->\n                    val packageManager = packageManager\n                    val permissionInfo = packageManager.getPermissionInfoOrNull(name, 0)\n                    val label = permissionInfo?.loadLabel(packageManager)?.toString()\n                        .takeIf { it != name }\n                    val description = permissionInfo?.loadDescription(packageManager)?.toString()\n                    PermissionItem(name, permissionInfo, label, description)\n                }\n                Success(permissions)\n            } catch (e: Exception) {\n                Failure(valueCompat.value, e)\n            }\n            postValue(value)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/apk/PermissionListViewModel.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.apk\n\nimport androidx.lifecycle.ViewModel\n\nclass PermissionListViewModel(permissionNames: Array<String>) : ViewModel() {\n    val permissionListLiveData = PermissionListLiveData(permissionNames)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/audio/AudioInfo.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.audio\n\nimport java.time.Duration\n\n// @see com.android.providers.media.scan.ModernMediaScanner.scanItemAudio\n// @see com.android.documentsui.inspector.MediaView.showAudioData\n// @see https://github.com/GNOME/nautilus/blob/c73ad94a72f8e9a989b01858018de74182d17f0e/extensions/audio-video-properties/bacon-video-widget-properties.c#L89\nclass AudioInfo(\n    val title: String?,\n    val artist: String?,\n    val album: String?,\n    val albumArtist: String?,\n    val composer: String?,\n    val discNumber: String?,\n    val trackNumber: String?,\n    val year: String?,\n    val genre: String?,\n    val duration: Duration?,\n    val bitRate: Int?,\n    val sampleRate: Int?\n)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/audio/AudioInfoLiveData.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.audio\n\nimport android.media.MediaMetadataRetriever\nimport android.os.AsyncTask\nimport android.os.Build\nimport java.time.Duration\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.compat.METADATA_KEY_SAMPLERATE\nimport me.zhanghai.android.files.compat.use\nimport me.zhanghai.android.files.fileproperties.PathObserverLiveData\nimport me.zhanghai.android.files.fileproperties.extractMetadataNotBlank\nimport me.zhanghai.android.files.util.Failure\nimport me.zhanghai.android.files.util.Loading\nimport me.zhanghai.android.files.util.Stateful\nimport me.zhanghai.android.files.util.Success\nimport me.zhanghai.android.files.util.setDataSource\nimport me.zhanghai.android.files.util.valueCompat\n\nclass AudioInfoLiveData(path: Path) : PathObserverLiveData<Stateful<AudioInfo>>(path) {\n    init {\n        loadValue()\n        observe()\n    }\n\n    override fun loadValue() {\n        value = Loading(value?.value)\n        AsyncTask.THREAD_POOL_EXECUTOR.execute {\n            val value = try {\n                val audioInfo = MediaMetadataRetriever().use { retriever ->\n                    retriever.setDataSource(path)\n                    val title = retriever.extractMetadataNotBlank(\n                        MediaMetadataRetriever.METADATA_KEY_TITLE\n                    )\n                    val artist = retriever.extractMetadataNotBlank(\n                        MediaMetadataRetriever.METADATA_KEY_ARTIST\n                    )\n                    val album = retriever.extractMetadataNotBlank(\n                        MediaMetadataRetriever.METADATA_KEY_ALBUM\n                    )\n                    val albumArtist = retriever.extractMetadataNotBlank(\n                        MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST\n                    )\n                    val composer = retriever.extractMetadataNotBlank(\n                        MediaMetadataRetriever.METADATA_KEY_COMPOSER\n                    )\n                    val discNumber = retriever.extractMetadataNotBlank(\n                        MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER\n                    )\n                    val trackNumber = retriever.extractMetadataNotBlank(\n                        MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER\n                    )\n                    val year = retriever.extractMetadataNotBlank(\n                        MediaMetadataRetriever.METADATA_KEY_YEAR\n                    )\n                    val genre = retriever.extractMetadataNotBlank(\n                        MediaMetadataRetriever.METADATA_KEY_GENRE\n                    )\n                    val duration = retriever.extractMetadataNotBlank(\n                        MediaMetadataRetriever.METADATA_KEY_DURATION\n                    )?.toLongOrNull()?.let { Duration.ofMillis(it) }\n                    val bitRate = retriever.extractMetadataNotBlank(\n                        MediaMetadataRetriever.METADATA_KEY_BITRATE\n                    )?.toIntOrNull()\n                    val sampleRate = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n                        retriever.extractMetadataNotBlank(\n                            MediaMetadataRetriever::class.METADATA_KEY_SAMPLERATE\n                        )?.toIntOrNull()\n                    } else {\n                        null\n                    }\n                    AudioInfo(\n                        title, artist, album, albumArtist, composer, discNumber, trackNumber, year,\n                        genre, duration, bitRate, sampleRate\n                    )\n                }\n                Success(audioInfo)\n            } catch (e: Exception) {\n                Failure(valueCompat.value, e)\n            }\n            postValue(value)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/audio/FilePropertiesAudioTabFragment.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.audio\n\nimport java8.nio.file.Path\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.file.FileItem\nimport me.zhanghai.android.files.file.format\nimport me.zhanghai.android.files.file.isAudio\nimport me.zhanghai.android.files.fileproperties.FilePropertiesTabFragment\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.ParcelableParceler\nimport me.zhanghai.android.files.util.Stateful\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.isMediaMetadataRetrieverCompatible\nimport me.zhanghai.android.files.util.viewModels\n\nclass FilePropertiesAudioTabFragment : FilePropertiesTabFragment() {\n    private val args by args<Args>()\n\n    private val viewModel by viewModels { { FilePropertiesAudioTabViewModel(args.path) } }\n\n    override fun onResume() {\n        super.onResume()\n\n        viewModel.audioInfoLiveData.observe(viewLifecycleOwner) { onAudioInfoChanged(it) }\n    }\n\n    override fun refresh() {\n        viewModel.reload()\n    }\n\n    private fun onAudioInfoChanged(stateful: Stateful<AudioInfo>) {\n        bindView(stateful) { audioInfo ->\n            if (audioInfo.title != null) {\n                addItemView(R.string.file_properties_media_title, audioInfo.title)\n            }\n            if (audioInfo.artist != null) {\n                addItemView(R.string.file_properties_audio_artist, audioInfo.artist)\n            }\n            if (audioInfo.album != null) {\n                addItemView(R.string.file_properties_audio_album, audioInfo.album)\n            }\n            if (audioInfo.albumArtist != null) {\n                addItemView(R.string.file_properties_audio_album_artist, audioInfo.albumArtist)\n            }\n            if (audioInfo.composer != null) {\n                addItemView(R.string.file_properties_audio_composer, audioInfo.composer)\n            }\n            if (audioInfo.discNumber != null) {\n                addItemView(R.string.file_properties_audio_disc_number, audioInfo.discNumber)\n            }\n            if (audioInfo.trackNumber != null) {\n                addItemView(R.string.file_properties_audio_track_number, audioInfo.trackNumber)\n            }\n            if (audioInfo.year != null) {\n                addItemView(R.string.file_properties_audio_year, audioInfo.year)\n            }\n            if (audioInfo.genre != null) {\n                addItemView(R.string.file_properties_audio_genre, audioInfo.genre)\n            }\n            if (audioInfo.duration != null) {\n                addItemView(R.string.file_properties_media_duration, audioInfo.duration.format())\n            }\n            if (audioInfo.bitRate != null) {\n                addItemView(\n                    R.string.file_properties_media_bit_rate, getString(\n                        R.string.file_properties_media_bit_rate_format, audioInfo.bitRate / 1000\n                    )\n                )\n            }\n            if (audioInfo.sampleRate != null) {\n                addItemView(\n                    R.string.file_properties_audio_sample_rate, getString(\n                        R.string.file_properties_audio_sample_rate_format, audioInfo.sampleRate\n                    )\n                )\n            }\n        }\n    }\n\n    companion object {\n        fun isAvailable(file: FileItem): Boolean =\n            file.mimeType.isAudio && file.path.isMediaMetadataRetrieverCompatible\n    }\n\n    @Parcelize\n    class Args(val path: @WriteWith<ParcelableParceler> Path) : ParcelableArgs\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/audio/FilePropertiesAudioTabViewModel.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.audio\n\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.ViewModel\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.util.Stateful\n\nclass FilePropertiesAudioTabViewModel(path: Path) : ViewModel() {\n    private val _audioInfoLiveData = AudioInfoLiveData(path)\n    val audioInfoLiveData: LiveData<Stateful<AudioInfo>>\n        get() = _audioInfoLiveData\n\n    fun reload() {\n        _audioInfoLiveData.loadValue()\n    }\n\n    override fun onCleared() {\n        _audioInfoLiveData.close()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/basic/FilePropertiesBasicTabFragment.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.basic\n\nimport android.os.Bundle\nimport androidx.lifecycle.lifecycleScope\nimport java8.nio.file.FileVisitResult\nimport java8.nio.file.FileVisitor\nimport java8.nio.file.Files\nimport java8.nio.file.Path\nimport java8.nio.file.attribute.BasicFileAttributes\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.Job\nimport kotlinx.coroutines.isActive\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.runBlocking\nimport kotlinx.coroutines.withContext\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.file.FileItem\nimport me.zhanghai.android.files.file.asFileSize\nimport me.zhanghai.android.files.file.fileSize\nimport me.zhanghai.android.files.file.formatLong\nimport me.zhanghai.android.files.filelist.getMimeTypeName\nimport me.zhanghai.android.files.filelist.name\nimport me.zhanghai.android.files.filelist.toUserFriendlyString\nimport me.zhanghai.android.files.fileproperties.FilePropertiesFileViewModel\nimport me.zhanghai.android.files.fileproperties.FilePropertiesTabFragment\nimport me.zhanghai.android.files.provider.archive.ArchiveFileAttributes\nimport me.zhanghai.android.files.provider.archive.archiveFile\nimport me.zhanghai.android.files.provider.archive.isArchivePath\nimport me.zhanghai.android.files.util.Stateful\nimport me.zhanghai.android.files.util.getQuantityString\nimport me.zhanghai.android.files.util.viewModels\nimport java.io.IOException\n\nclass FilePropertiesBasicTabFragment : FilePropertiesTabFragment() {\n    private val viewModel by viewModels<FilePropertiesFileViewModel>({ requireParentFragment() })\n\n    private var contentJob: Job? = null\n\n    override fun onActivityCreated(savedInstanceState: Bundle?) {\n        super.onActivityCreated(savedInstanceState)\n\n        viewModel.fileLiveData.observe(viewLifecycleOwner) { onFileChanged(it) }\n    }\n\n    override fun refresh() {\n        viewModel.reload()\n    }\n\n    private fun onFileChanged(stateful: Stateful<FileItem>) {\n        contentJob?.cancel()\n        contentJob = null\n        bindView(stateful) { file ->\n            addItemView(R.string.file_properties_basic_name, file.name)\n            val path = file.path\n            if (path.isArchivePath) {\n                val archiveFile = path.archiveFile\n                addItemView(\n                    R.string.file_properties_basic_archive_file, archiveFile.toUserFriendlyString()\n                )\n                val attributes = file.attributes as ArchiveFileAttributes\n                addItemView(R.string.file_properties_basic_archive_entry, attributes.entryName())\n            } else {\n                val parentPath = path.parent\n                if (parentPath != null) {\n                    addItemView(\n                        R.string.file_properties_basic_parent_directory, parentPath.toString()\n                    )\n                }\n            }\n            addItemView(R.string.file_properties_basic_type, getTypeText(file))\n            val symbolicLinkTarget = file.symbolicLinkTarget\n            if (symbolicLinkTarget != null) {\n                addItemView(R.string.file_properties_basic_symbolic_link_target, symbolicLinkTarget)\n            }\n            if (file.attributes.isDirectory) {\n                val textView = addItemView(\n                    R.string.file_properties_basic_contents, getDirectoryContentsText(0, 0)\n                )\n                contentJob = viewLifecycleOwner.lifecycleScope.launch {\n                    getDirectoryContents(\n                        path,\n                        GET_DIRECTORY_CONTENTS_INTERVAL_MILLIS\n                    ) { (count, size) -> textView.text = getDirectoryContentsText(count, size) }\n                }\n            } else {\n                addItemView(R.string.file_properties_basic_size, getSizeText(file))\n            }\n            val lastModificationTime = file.attributes.lastModifiedTime().toInstant().formatLong()\n            addItemView(R.string.file_properties_basic_last_modification_time, lastModificationTime)\n        }\n    }\n\n    private fun getTypeText(file: FileItem): String {\n        val typeFormatRes = if (file.attributesNoFollowLinks.isSymbolicLink\n            && !file.isSymbolicLinkBroken) {\n            R.string.file_properties_basic_type_symbolic_link_format\n        } else {\n            R.string.file_properties_basic_type_format\n        }\n        return getString(typeFormatRes, file.getMimeTypeName(requireContext()), file.mimeType.value)\n    }\n\n    private suspend fun getDirectoryContents(\n        directory: Path,\n        intervalMillis: Long,\n        listener: (Pair<Int, Long>) -> Unit\n    ) {\n        withContext(Dispatchers.IO) {\n            var count = 0\n            var size = 0L\n            fun notifyListener() {\n                // kotlinc: Suspension functions can be called only within coroutine body\n                //withContext(Dispatchers.Main.immediate) {\n                // We need to keep our job alive to know if it has been canceled before notifying\n                // our listener on main thread.\n                //mainExecutor.execute {\n                runBlocking(Dispatchers.Main.immediate) {\n                    if (this@withContext.isActive) {\n                        listener(count to size)\n                    }\n                }\n            }\n            Files.walkFileTree(directory, object : FileVisitor<Path> {\n                private var lastTimeMillis = System.currentTimeMillis()\n\n                override fun preVisitDirectory(\n                    directory: Path,\n                    attributes: BasicFileAttributes\n                ): FileVisitResult = visit(directory, attributes, null)\n\n                override fun visitFile(\n                    file: Path,\n                    attributes: BasicFileAttributes\n                ): FileVisitResult = visit(file, attributes, null)\n\n                override fun visitFileFailed(\n                    file: Path,\n                    exception: IOException\n                ): FileVisitResult = visit(file, null, exception)\n\n                override fun postVisitDirectory(\n                    directory: Path,\n                    exception: IOException?\n                ): FileVisitResult = visit(null, null, exception)\n\n                private fun visit(\n                    path: Path?,\n                    attributes: BasicFileAttributes?,\n                    exception: IOException?\n                ): FileVisitResult {\n                    if (!isActive) {\n                        return FileVisitResult.TERMINATE\n                    }\n                    if (path == directory) {\n                        return FileVisitResult.CONTINUE\n                    }\n                    path?.let { ++count }\n                    attributes?.let { size += it.size() }\n                    exception?.printStackTrace()\n                    val currentTimeMillis = System.currentTimeMillis()\n                    if (currentTimeMillis >= lastTimeMillis + intervalMillis) {\n                        notifyListener()\n                        lastTimeMillis = currentTimeMillis\n                    }\n                    return FileVisitResult.CONTINUE\n                }\n            })\n            notifyListener()\n        }\n    }\n\n    private fun getDirectoryContentsText(count: Int, size: Long): String =\n        if (count == 0) {\n            getString(R.string.empty)\n        } else {\n            val fileSize = size.asFileSize()\n            val context = requireContext()\n            val sizeText = if (fileSize.isHumanReadableInBytes) {\n                fileSize.formatInBytes(context)\n            } else {\n                fileSize.formatHumanReadable(context)\n            }\n            getQuantityString(\n                R.plurals.file_properties_basic_contents_format, count, count, sizeText\n            )\n        }\n\n    private fun getSizeText(file: FileItem): String {\n        val size = file.attributes.fileSize\n        val context = requireContext()\n        val sizeInBytes = size.formatInBytes(context)\n        return if (size.isHumanReadableInBytes) {\n            sizeInBytes\n        } else {\n            val humanReadableSize = size.formatHumanReadable(context)\n            getString(\n                R.string.file_properties_basic_size_with_human_readable_format, humanReadableSize,\n                sizeInBytes\n            )\n        }\n    }\n\n    companion object {\n        private const val GET_DIRECTORY_CONTENTS_INTERVAL_MILLIS = 200L\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/checksum/ChecksumInfo.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.checksum\n\nimport androidx.annotation.StringRes\nimport me.zhanghai.android.files.R\nimport java.security.MessageDigest\n\nclass ChecksumInfo(val checksums: Map<Algorithm, String>) {\n    enum class Algorithm(@StringRes val nameRes: Int) {\n        CRC32(R.string.file_properties_checksum_crc32),\n        MD5(R.string.file_properties_checksum_md5),\n        SHA1(R.string.file_properties_checksum_sha_1),\n        SHA256(R.string.file_properties_checksum_sha_256),\n        SHA512(R.string.file_properties_checksum_sha_512);\n\n        fun createMessageDigest(): MessageDigest =\n            when (this) {\n                CRC32 -> Crc32MessageDigest()\n                MD5 -> MessageDigest.getInstance(\"MD5\")\n                SHA1 -> MessageDigest.getInstance(\"SHA-1\")\n                SHA256 -> MessageDigest.getInstance(\"SHA-256\")\n                SHA512 -> MessageDigest.getInstance(\"SHA-512\")\n            }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/checksum/ChecksumInfoLiveData.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.checksum\n\nimport android.os.AsyncTask\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.fileproperties.PathObserverLiveData\nimport me.zhanghai.android.files.provider.common.newInputStream\nimport me.zhanghai.android.files.util.Failure\nimport me.zhanghai.android.files.util.Loading\nimport me.zhanghai.android.files.util.Stateful\nimport me.zhanghai.android.files.util.Success\nimport me.zhanghai.android.files.util.toHexString\nimport me.zhanghai.android.files.util.valueCompat\nimport java.util.concurrent.ExecutorService\nimport java.util.concurrent.Future\n\nclass ChecksumInfoLiveData(path: Path) : PathObserverLiveData<Stateful<ChecksumInfo>>(path) {\n    private var future: Future<Unit>? = null\n\n    init {\n        loadValue()\n        observe()\n    }\n\n    override fun loadValue() {\n        future?.cancel(true)\n        value = Loading(value?.value)\n        future = (AsyncTask.THREAD_POOL_EXECUTOR as ExecutorService).submit<Unit> {\n            val value = try {\n                val messageDigests =\n                    ChecksumInfo.Algorithm.entries.associateWith { it.createMessageDigest() }\n                path.newInputStream().use { inputStream ->\n                    val buffer = ByteArray(DEFAULT_BUFFER_SIZE)\n                    while (true) {\n                        val readSize = inputStream.read(buffer)\n                        if (readSize == -1) {\n                            break\n                        }\n                        messageDigests.values.forEach { it.update(buffer, 0, readSize) }\n                    }\n                }\n                val checksumInfo = ChecksumInfo(\n                    messageDigests.mapValues { it.value.digest().toHexString() }\n                )\n                Success(checksumInfo)\n            } catch (e: Exception) {\n                Failure(valueCompat.value, e)\n            }\n            postValue(value)\n        }\n    }\n\n    override fun close() {\n        super.close()\n\n        future?.cancel(true)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/checksum/Crc32MessageDigest.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.checksum\n\nimport java.security.MessageDigest\nimport java.util.zip.CRC32\n\nclass Crc32MessageDigest : MessageDigest(\"CRC32\") {\n    private val crc32 = CRC32()\n\n    override fun engineUpdate(input: Byte) {\n        crc32.update(input.toInt())\n    }\n\n    override fun engineUpdate(input: ByteArray, offset: Int, length: Int) {\n        crc32.update(input, offset, length)\n    }\n\n    override fun engineDigest(): ByteArray {\n        val value = crc32.value\n        crc32.reset()\n        return ByteArray(4).apply {\n            this[0] = (value ushr 24).toByte()\n            this[1] = (value ushr 16).toByte()\n            this[2] = (value ushr 8).toByte()\n            this[3] = value.toByte()\n        }\n    }\n\n    override fun engineReset() {\n        crc32.reset()\n    }\n}"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/checksum/FilePropertiesChecksumTabFragment.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.checksum\n\nimport androidx.core.widget.doAfterTextChanged\nimport java8.nio.file.Path\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.databinding.FilePropertiesChecksumCompareItemBinding\nimport me.zhanghai.android.files.file.FileItem\nimport me.zhanghai.android.files.fileproperties.FilePropertiesTabFragment\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.ParcelableParceler\nimport me.zhanghai.android.files.util.Stateful\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.layoutInflater\nimport me.zhanghai.android.files.util.viewModels\n\nclass FilePropertiesChecksumTabFragment : FilePropertiesTabFragment() {\n    private val args by args<Args>()\n\n    private val viewModel by viewModels { { FilePropertiesChecksumTabViewModel(args.path) } }\n\n    override fun onResume() {\n        super.onResume()\n\n        viewModel.checksumInfoLiveData.observe(viewLifecycleOwner) { onChecksumInfoChanged(it) }\n    }\n\n    override fun refresh() {\n        viewModel.reload()\n    }\n\n    private fun onChecksumInfoChanged(stateful: Stateful<ChecksumInfo>) {\n        bindView(stateful) { checksumInfo ->\n            checksumInfo.checksums.forEach { addItemView(it.key.nameRes, it.value) }\n            addCompareEdit(checksumInfo)\n        }\n    }\n\n    private fun ViewBuilder.addCompareEdit(checksumInfo: ChecksumInfo) {\n        val binding = getScrapItemBinding(FilePropertiesChecksumCompareItemBinding::class.java)\n            ?.also { addView(it) }\n            ?: FilePropertiesChecksumCompareItemBinding.inflate(\n                linearLayout.context.layoutInflater, linearLayout, true\n            )\n                .also { it.root.tag = it }\n        binding.compareEdit.doAfterTextChanged { editable ->\n            val text = editable!!.toString().trim()\n            if (text.isEmpty()) {\n                binding.compareLayout.helperText = null\n                binding.compareLayout.error = null\n                return@doAfterTextChanged\n            }\n            val matchingAlgorithm = checksumInfo.checksums.firstNotNullOfOrNull {\n                if (it.value.equals(text, true)) it.key else null\n            }\n            if (matchingAlgorithm != null) {\n                binding.compareLayout.helperText =\n                    getString(\n                        R.string.file_properties_checksum_compare_match_format,\n                        getString(matchingAlgorithm.nameRes)\n                    )\n                return@doAfterTextChanged\n            }\n            val prefixMatchingAlgorithm = checksumInfo.checksums.firstNotNullOfOrNull {\n                if (it.value.startsWith(text, true)) it.key else null\n            }\n            if (prefixMatchingAlgorithm != null) {\n                binding.compareLayout.helperText =\n                    getString(\n                        R.string.file_properties_checksum_compare_prefix_match_format,\n                        getString(prefixMatchingAlgorithm.nameRes)\n                    )\n                return@doAfterTextChanged\n            }\n            binding.compareLayout.error =\n                getString(R.string.file_properties_checksum_compare_no_match)\n        }\n    }\n\n    companion object {\n        fun isAvailable(file: FileItem): Boolean = file.attributes.isRegularFile\n    }\n\n    @Parcelize\n    class Args(val path: @WriteWith<ParcelableParceler> Path) : ParcelableArgs\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/checksum/FilePropertiesChecksumTabViewModel.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.checksum\n\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.ViewModel\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.util.Stateful\n\nclass FilePropertiesChecksumTabViewModel(path: Path) : ViewModel() {\n    private val _checksumInfoLiveData = ChecksumInfoLiveData(path)\n    val checksumInfoLiveData: LiveData<Stateful<ChecksumInfo>>\n        get() = _checksumInfoLiveData\n\n    fun reload() {\n        _checksumInfoLiveData.loadValue()\n    }\n\n    override fun onCleared() {\n        _checksumInfoLiveData.close()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/image/ExifInterfaceExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.image\n\nimport android.annotation.SuppressLint\nimport androidx.exifinterface.media.ExifInterface\nimport java.time.Duration\nimport java.time.Instant\nimport java.time.ZoneId\nimport java.time.ZoneOffset\nimport kotlin.math.roundToLong\nimport me.zhanghai.android.files.util.takeIfNotBlank\n\nfun ExifInterface.getAttributeNotBlank(tag: String): String? =\n    getAttribute(tag)?.takeIfNotBlank()\n\nfun ExifInterface.getAttributeDoubleOrNull(tag: String): Double? =\n    getAttributeDouble(tag, Double.NaN).takeIf { !it.isNaN() }\n\nfun ExifInterface.getAttributeIntOrNull(tag: String): Int? =\n    getAttributeInt(tag, -1).takeIf { it != -1 || getAttributeInt(tag, 0) == -1 }\n\nval ExifInterface.gpsAltitude: Double?\n    get() = getAltitude(Double.NaN).takeIf { !it.isNaN() }\n\n/* @see com.android.providers.media.scan.ModernMediaScanner.parseOptionalDateTaken */\n@SuppressLint(\"RestrictedApi\")\nfun ExifInterface.inferDateTimeOriginal(lastModifiedTime: Instant): Instant? {\n    val dateTimeOriginal = dateTimeOriginal?.let { Instant.ofEpochMilli(it) } ?: return null\n    if (getAttributeNotBlank(ExifInterface.TAG_OFFSET_TIME_ORIGINAL) != null) {\n        return dateTimeOriginal\n    }\n    val gpsDateTime = gpsDateTime?.let { Instant.ofEpochMilli(it) }\n    if (gpsDateTime != null) {\n        dateTimeOriginal.withTimezoneInferredFrom(gpsDateTime)?.let { return it }\n    }\n    dateTimeOriginal.withTimezoneInferredFrom(lastModifiedTime)?.let { return it }\n    // We don't have any timezone information, pretend that it's in the current timezone which is\n    // still better than in UTC.\n    return dateTimeOriginal\n        .atOffset(ZoneOffset.UTC)\n        .toLocalDateTime()\n        .atZone(ZoneId.systemDefault())\n        .toInstant()\n}\n\nprivate fun Instant.withTimezoneInferredFrom(other: Instant): Instant? {\n    val smallestZone = Duration.ofMinutes(15)\n    val offset = Duration.between(this, other)\n    if (offset.abs() < Duration.ofDays(1)) {\n        val smallestZoneMillis = smallestZone.toMillis()\n        val rounded = Duration.ofMillis(\n            (offset.toMillis().toDouble() / smallestZoneMillis).roundToLong() * smallestZoneMillis\n        )\n        return this + rounded\n    }\n    return null\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/image/FilePropertiesImageTabFragment.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.image\n\nimport android.content.Intent\nimport android.location.Geocoder\nimport androidx.lifecycle.lifecycleScope\nimport java8.nio.file.Path\nimport kotlinx.coroutines.Job\nimport kotlinx.coroutines.isActive\nimport kotlinx.coroutines.launch\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.file.FileItem\nimport me.zhanghai.android.files.file.MimeType\nimport me.zhanghai.android.files.file.formatLong\nimport me.zhanghai.android.files.file.isImage\nimport me.zhanghai.android.files.filelist.name\nimport me.zhanghai.android.files.fileproperties.FilePropertiesTabFragment\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.ParcelableParceler\nimport me.zhanghai.android.files.util.Stateful\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.awaitGetFromLocation\nimport me.zhanghai.android.files.util.createViewLocation\nimport me.zhanghai.android.files.util.isGeocoderPresent\nimport me.zhanghai.android.files.util.startActivitySafe\nimport me.zhanghai.android.files.util.userFriendlyString\nimport me.zhanghai.android.files.util.viewModels\nimport kotlin.math.pow\nimport kotlin.math.roundToInt\n\nclass FilePropertiesImageTabFragment : FilePropertiesTabFragment() {\n    private val args by args<Args>()\n\n    private val viewModel by viewModels {\n        { FilePropertiesImageTabViewModel(args.path, args.mimeType) }\n    }\n\n    private var addressJob: Job? = null\n\n    override fun onResume() {\n        super.onResume()\n\n        viewModel.imageInfoLiveData.observe(viewLifecycleOwner) { onImageInfoChanged(it) }\n    }\n\n    override fun refresh() {\n        viewModel.reload()\n    }\n\n    private fun onImageInfoChanged(stateful: Stateful<ImageInfo>) {\n        addressJob?.cancel()\n        addressJob = null\n        bindView(stateful) { imageInfo ->\n            addItemView(\n                R.string.file_properties_media_dimensions, if (imageInfo.dimensions != null) {\n                    getString(\n                        R.string.file_properties_media_dimensions_format,\n                        imageInfo.dimensions.width, imageInfo.dimensions.height\n                    )\n                } else {\n                    getString(R.string.unknown)\n                }\n            )\n            val exifInfo = imageInfo.exifInfo\n            if (exifInfo != null) {\n                if (exifInfo.dateTimeOriginal != null) {\n                    addItemView(\n                        R.string.file_properties_media_date_time,\n                        exifInfo.dateTimeOriginal.formatLong()\n                    )\n                }\n                if (exifInfo.gpsCoordinates != null) {\n                    addItemView(\n                        R.string.file_properties_media_coordinates, getString(\n                            R.string.file_properties_media_coordinates_format,\n                            exifInfo.gpsCoordinates.first, exifInfo.gpsCoordinates.second\n                        )\n                    ) {\n                        startActivitySafe(\n                            Intent::class.createViewLocation(\n                                exifInfo.gpsCoordinates.first.toFloat(),\n                                exifInfo.gpsCoordinates.second.toFloat(), args.path.name\n                            )\n                        )\n                    }\n                    if (isGeocoderPresent) {\n                        val textView = addItemView(\n                            R.string.file_properties_media_address, getString(R.string.loading)\n                        )\n                        val geocoder = Geocoder(requireContext())\n                        addressJob = viewLifecycleOwner.lifecycleScope.launch {\n                            val address = try {\n                                geocoder.awaitGetFromLocation(\n                                    exifInfo.gpsCoordinates.first, exifInfo.gpsCoordinates.second, 1\n                                ).first()\n                            } catch (e: Exception) {\n                                null\n                            }\n                            if (isActive) {\n                                textView.text = address?.userFriendlyString\n                                    ?: getString(R.string.unknown)\n                            }\n                        }\n                    }\n                }\n                if (exifInfo.gpsAltitude != null) {\n                    addItemView(\n                        R.string.file_properties_image_gps_altitude, getString(\n                            R.string.file_properties_image_gps_altitude_format, exifInfo.gpsAltitude\n                        )\n                    )\n                }\n                val equipment = getEquipment(exifInfo.make, exifInfo.model)\n                if (equipment != null) {\n                    addItemView(R.string.file_properties_image_equipment, equipment)\n                }\n                if (exifInfo.fNumber != null) {\n                    addItemView(\n                        R.string.file_properties_image_f_number, getString(\n                            R.string.file_properties_image_f_number_format, exifInfo.fNumber\n                        )\n                    )\n                }\n                if (exifInfo.shutterSpeedValue != null) {\n                    addItemView(\n                        R.string.file_properties_image_shutter_speed,\n                        getShutterSpeedText(exifInfo.shutterSpeedValue)\n                    )\n                }\n                if (exifInfo.focalLength != null) {\n                    addItemView(\n                        R.string.file_properties_image_focal_length, getString(\n                            R.string.file_properties_image_focal_length_format, exifInfo.focalLength\n                        )\n                    )\n                }\n                if (exifInfo.photographicSensitivity != null) {\n                    addItemView(\n                        R.string.file_properties_image_photographic_sensitivity, getString(\n                            R.string.file_properties_image_photographic_sensitivity_format,\n                            exifInfo.photographicSensitivity\n                        )\n                    )\n                }\n                if (exifInfo.software != null) {\n                    addItemView(R.string.file_properties_image_software, exifInfo.software)\n                }\n                if (exifInfo.description != null) {\n                    addItemView(R.string.file_properties_image_description, exifInfo.description)\n                }\n                if (exifInfo.artist != null) {\n                    addItemView(R.string.file_properties_image_artist, exifInfo.artist)\n                }\n                if (exifInfo.copyright != null) {\n                    addItemView(R.string.file_properties_image_copyright, exifInfo.copyright)\n                }\n            }\n        }\n    }\n\n    private fun getEquipment(make: String?, model: String?): String? =\n        when {\n            make != null && model != null -> {\n                if (model.startsWith(make, true)) {\n                    model\n                } else {\n                    getString(R.string.file_properties_image_equipment_format, make, model)\n                }\n            }\n            make != null -> make\n            model != null -> model\n            else -> null\n        }\n\n    // @see com.android.documentsui.inspector.MediaView.formatShutterSpeed\n    private fun getShutterSpeedText(value: Double): String =\n        if (value <= 0) {\n            val shutterSpeed = 2.0.pow(-1 * value)\n            ((shutterSpeed * 10.0).roundToInt() / 10.0).toString()\n        } else {\n            val approximateDenominator = 2.0.pow(value).toInt() + 1\n            getString(\n                R.string.file_properties_image_shutter_speed_with_denominator_format,\n                approximateDenominator\n            )\n        }\n\n    companion object {\n        fun isAvailable(file: FileItem): Boolean = file.mimeType.isImage\n    }\n\n    @Parcelize\n    class Args(\n        val path: @WriteWith<ParcelableParceler> Path,\n        val mimeType: MimeType\n    ) : ParcelableArgs\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/image/FilePropertiesImageTabViewModel.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.image\n\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.ViewModel\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.file.MimeType\nimport me.zhanghai.android.files.util.Stateful\n\nclass FilePropertiesImageTabViewModel(path: Path, mimeType: MimeType) : ViewModel() {\n    private val _imageInfoLiveData = ImageInfoLiveData(path, mimeType)\n    val imageInfoLiveData: LiveData<Stateful<ImageInfo>>\n        get() = _imageInfoLiveData\n\n    fun reload() {\n        _imageInfoLiveData.loadValue()\n    }\n\n    override fun onCleared() {\n        _imageInfoLiveData.close()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/image/ImageInfo.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.image\n\nimport android.util.Size\nimport java.time.Instant\n\nclass ImageInfo(\n    val dimensions: Size?,\n    val exifInfo: ExifInfo?\n)\n\n// @see com.android.documentsui.inspector.MediaView\n// @see https://github.com/GNOME/nautilus/blob/c73ad94a72f8e9a989b01858018de74182d17f0e/extensions/image-properties/nautilus-image-properties-page.c#L198\nclass ExifInfo(\n    val dateTimeOriginal: Instant?,\n    val gpsCoordinates: Pair<Double, Double>?,\n    val gpsAltitude: Double?,\n    val make: String?,\n    val model: String?,\n    val fNumber: Double?,\n    val shutterSpeedValue: Double?,\n    val focalLength: Double?,\n    val photographicSensitivity: Int?,\n    val software: String?,\n    val description: String?,\n    val artist: String?,\n    val copyright: String?\n)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/image/ImageInfoLiveData.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.image\n\nimport android.graphics.BitmapFactory\nimport android.os.AsyncTask\nimport android.util.Size\nimport androidx.exifinterface.media.ExifInterface\nimport com.caverock.androidsvg.SVG\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.file.MimeType\nimport me.zhanghai.android.files.fileproperties.PathObserverLiveData\nimport me.zhanghai.android.files.provider.common.getLastModifiedTime\nimport me.zhanghai.android.files.provider.common.newInputStream\nimport me.zhanghai.android.files.util.Failure\nimport me.zhanghai.android.files.util.Loading\nimport me.zhanghai.android.files.util.Stateful\nimport me.zhanghai.android.files.util.Success\nimport me.zhanghai.android.files.util.valueCompat\nimport okio.buffer\nimport okio.source\nimport kotlin.math.roundToInt\n\nclass ImageInfoLiveData(\n    path: Path,\n    private val mimeType: MimeType\n) : PathObserverLiveData<Stateful<ImageInfo>>(path) {\n    init {\n        loadValue()\n        observe()\n    }\n\n    override fun loadValue() {\n        value = Loading(value?.value)\n        AsyncTask.THREAD_POOL_EXECUTOR.execute {\n            val value = try {\n                val imageInfo = when (mimeType) {\n                    MimeType.IMAGE_SVG_XML -> {\n                        val svg = path.newInputStream()\n                            // It seems we need Okio for SVG parser to work for files with entities.\n                            // Something weird is going on with buffering and mark/reset.\n                            //.buffer()\n                            //.use { SVG.getFromInputStream(it) }\n                            .source()\n                            .buffer()\n                            .use { SVG.getFromInputStream(it.inputStream()) }\n                        val width = svg.documentWidth\n                        val height = svg.documentHeight\n                        val dimensions = if (width != -1f && height != -1f) {\n                            Size(width.roundToInt(), height.roundToInt())\n                        } else {\n                            val viewBox = svg.documentViewBox\n                            if (viewBox != null) {\n                                Size(viewBox.width().roundToInt(), viewBox.height().roundToInt())\n                            } else {\n                                null\n                            }\n                        }\n                        ImageInfo(dimensions, null)\n                    }\n                    else -> {\n                        val bitmapOptions = BitmapFactory.Options()\n                            .apply { inJustDecodeBounds = true }\n                        path.newInputStream()\n                            .buffered()\n                            .use { BitmapFactory.decodeStream(it, null, bitmapOptions) }\n                        val width = bitmapOptions.outWidth\n                        val height = bitmapOptions.outHeight\n                        val dimensions = if (width != -1 && height != -1) {\n                            Size(width, height)\n                        } else {\n                            null\n                        }\n                        val exifInfo = try {\n                            val lastModifiedTime = path.getLastModifiedTime().toInstant()\n                            path.newInputStream().buffered().use {\n                                val exifInterface = ExifInterface(it)\n                                val dateTimeOriginal =\n                                    exifInterface.inferDateTimeOriginal(lastModifiedTime)\n                                val gpsCoordinates = exifInterface.latLong?.let { it[0] to it[1] }\n                                val gpsAltitude = exifInterface.gpsAltitude\n                                val make =\n                                    exifInterface.getAttributeNotBlank(ExifInterface.TAG_MAKE)\n                                val model =\n                                    exifInterface.getAttributeNotBlank(ExifInterface.TAG_MODEL)\n                                val fNumber = exifInterface.getAttributeDoubleOrNull(\n                                    ExifInterface.TAG_F_NUMBER\n                                )\n                                val shutterSpeedValue = exifInterface.getAttributeDoubleOrNull(\n                                    ExifInterface.TAG_SHUTTER_SPEED_VALUE\n                                )\n                                val focalLength = exifInterface.getAttributeDoubleOrNull(\n                                    ExifInterface.TAG_FOCAL_LENGTH\n                                )\n                                val photographicSensitivity = exifInterface.getAttributeIntOrNull(\n                                    ExifInterface.TAG_PHOTOGRAPHIC_SENSITIVITY\n                                )\n                                val software =\n                                    exifInterface.getAttributeNotBlank(ExifInterface.TAG_SOFTWARE)\n                                val description = exifInterface.getAttributeNotBlank(\n                                    ExifInterface.TAG_IMAGE_DESCRIPTION\n                                ) ?: exifInterface.getAttributeNotBlank(\n                                    ExifInterface.TAG_USER_COMMENT\n                                )\n                                val artist =\n                                    exifInterface.getAttributeNotBlank(ExifInterface.TAG_ARTIST)\n                                val copyright =\n                                    exifInterface.getAttributeNotBlank(ExifInterface.TAG_COPYRIGHT)\n                                ExifInfo(\n                                    dateTimeOriginal, gpsCoordinates, gpsAltitude, make,\n                                    model, fNumber, shutterSpeedValue, focalLength,\n                                    photographicSensitivity, software, description, artist,\n                                    copyright\n                                )\n                            }\n                        } catch (e: Exception) {\n                            e.printStackTrace()\n                            null\n                        }\n                        ImageInfo(dimensions, exifInfo)\n                    }\n                }\n                Success(imageInfo)\n            } catch (e: Exception) {\n                Failure(valueCompat.value, e)\n            }\n            postValue(value)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/permission/FilePropertiesPermissionTabFragment.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.permission\n\nimport android.os.Bundle\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.file.FileItem\nimport me.zhanghai.android.files.fileproperties.FilePropertiesFileViewModel\nimport me.zhanghai.android.files.fileproperties.FilePropertiesTabFragment\nimport me.zhanghai.android.files.provider.common.PosixFileAttributes\nimport me.zhanghai.android.files.provider.common.PosixPrincipal\nimport me.zhanghai.android.files.provider.common.toInt\nimport me.zhanghai.android.files.provider.common.toModeString\nimport me.zhanghai.android.files.util.Stateful\nimport me.zhanghai.android.files.util.viewModels\n\nclass FilePropertiesPermissionTabFragment : FilePropertiesTabFragment() {\n    private val viewModel by viewModels<FilePropertiesFileViewModel>({ requireParentFragment() })\n\n    override fun onActivityCreated(savedInstanceState: Bundle?) {\n        super.onActivityCreated(savedInstanceState)\n\n        viewModel.fileLiveData.observe(viewLifecycleOwner) { onFileChanged(it) }\n    }\n\n    override fun refresh() {\n        viewModel.reload()\n    }\n\n    private fun onFileChanged(stateful: Stateful<FileItem>) {\n        bindView(stateful) { file ->\n            val attributes = file.attributes as PosixFileAttributes\n            val owner = attributes.owner()\n            addItemView(\n                R.string.file_properties_permission_owner, getPrincipalText(owner), owner?.let {\n                    { SetOwnerDialogFragment.show(file, this@FilePropertiesPermissionTabFragment) }\n                }\n            )\n            val group = attributes.group()\n            addItemView(\n                R.string.file_properties_permission_group, getPrincipalText(group), group?.let {\n                    { SetGroupDialogFragment.show(file, this@FilePropertiesPermissionTabFragment) }\n                }\n            )\n            val mode = attributes.mode()\n            addItemView(\n                R.string.file_properties_permission_mode, if (mode != null) {\n                    getString(\n                        R.string.file_properties_permission_mode_format, mode.toModeString(),\n                        mode.toInt()\n                    )\n                } else {\n                    getString(R.string.unknown)\n                }, if (mode != null && !attributes.isSymbolicLink) {\n                    { SetModeDialogFragment.show(file, this@FilePropertiesPermissionTabFragment) }\n                } else {\n                    null\n                }\n            )\n            val seLinuxContext = attributes.seLinuxContext()\n            if (seLinuxContext != null) {\n                addItemView(\n                    R.string.file_properties_permission_selinux_context,\n                    if (seLinuxContext.isNotEmpty()) {\n                        seLinuxContext.toString()\n                    } else {\n                        getString(R.string.empty_placeholder)\n                    }\n                ) {\n                    SetSeLinuxContextDialogFragment.show(\n                        file, this@FilePropertiesPermissionTabFragment\n                    )\n                }\n            }\n        }\n    }\n\n    private fun getPrincipalText(principal: PosixPrincipal?) =\n        if (principal != null) {\n            if (principal.name != null) {\n                getString(\n                    R.string.file_properties_permission_principal_format, principal.name,\n                    principal.id\n                )\n            } else {\n                principal.id.toString()\n            }\n        } else {\n            getString(R.string.unknown)\n        }\n\n    companion object {\n        fun isAvailable(file: FileItem): Boolean {\n            val attributes = file.attributes\n            return attributes is PosixFileAttributes && (attributes.owner() != null\n                || attributes.group() != null || attributes.mode() != null\n                || attributes.seLinuxContext() != null)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/permission/GroupListAdapter.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.permission\n\nimport androidx.annotation.DrawableRes\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.util.SelectionLiveData\n\nclass GroupListAdapter(\n    selectionLiveData: SelectionLiveData<Int>\n) : PrincipalListAdapter(selectionLiveData) {\n    @DrawableRes\n    override val principalIconRes: Int = R.drawable.people_icon_control_normal_24dp\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/permission/GroupListLiveData.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.permission\n\nimport me.zhanghai.android.files.provider.linux.syscall.Syscall.endgrent\nimport me.zhanghai.android.files.provider.linux.syscall.Syscall.getgrent\nimport me.zhanghai.android.files.provider.linux.syscall.Syscall.setgrent\n\nclass GroupListLiveData : PrincipalListLiveData() {\n    override val androidPrincipals: MutableList<PrincipalItem>\n        @Throws(Exception::class)\n        get() {\n            val groups = mutableListOf<PrincipalItem>()\n            setgrent()\n            try {\n                while (true) {\n                    val structGroup = getgrent() ?: break\n                    val group = PrincipalItem(\n                        structGroup.gr_gid, structGroup.gr_name?.toString(), emptyList(),\n                        emptyList()\n                    )\n                    groups.add(group)\n                }\n            } finally {\n                endgrent()\n            }\n            return groups\n        }\n\n    /*\n     * @see https://android.googlesource.com/platform/bionic/+/android10-release/libc/bionic/grp_grd.cpp\n     *      print_app_name_from_gid()\n     */\n    override fun getAppPrincipalName(uid: Int): String {\n        val userId = uid / AID_USER_OFFSET\n        val appId = uid % AID_USER_OFFSET\n        return when {\n            appId > AID_ISOLATED_START -> \"u${userId}_i${appId - AID_ISOLATED_START}\"\n            userId == 0 && appId in AID_SHARED_GID_START..AID_SHARED_GID_END ->\n                \"all_a${appId - AID_SHARED_GID_START}\"\n            appId in AID_CACHE_GID_START..AID_CACHE_GID_END ->\n                \"u${userId}_a${appId - AID_CACHE_GID_START}_cache\"\n            else -> \"u${userId}_a${appId - AID_APP_START}\"\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/permission/ModeBitListAdapter.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.permission\n\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.BaseAdapter\nimport me.zhanghai.android.files.databinding.ModeBitItemBinding\nimport me.zhanghai.android.files.provider.common.PosixFileModeBit\nimport me.zhanghai.android.files.util.layoutInflater\n\nclass ModeBitListAdapter(\n    private val modeBits: List<PosixFileModeBit>,\n    private val modeBitNames: Array<String>\n) : BaseAdapter() {\n    var mode: Set<PosixFileModeBit> = emptySet()\n        set(value) {\n            field = value\n            notifyDataSetChanged()\n        }\n\n    override fun getCount(): Int = modeBits.size\n\n    override fun getItem(position: Int): PosixFileModeBit = modeBits[position]\n\n    override fun hasStableIds(): Boolean = true\n\n    override fun getItemId(position: Int): Long = getItem(position).ordinal.toLong()\n\n    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {\n        val modeBit = getItem(position)\n        val binding = convertView?.tag as ModeBitItemBinding?\n            ?: ModeBitItemBinding.inflate(parent.context.layoutInflater, parent, false)\n                .apply { root.tag = this }\n        binding.modeBitCheck.text = modeBitNames[position]\n        binding.modeBitCheck.isChecked = modeBit in mode\n        return binding.root\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/permission/PrincipalItem.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.permission\n\nimport android.content.pm.ApplicationInfo\n\nclass PrincipalItem(\n    val id: Int,\n    val name: String?,\n    val applicationInfos: List<ApplicationInfo>,\n    val applicationLabels: List<String>\n)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/permission/PrincipalListAdapter.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.permission\n\nimport android.view.ViewGroup\nimport androidx.annotation.DrawableRes\nimport androidx.recyclerview.widget.RecyclerView\nimport coil.dispose\nimport coil.load\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.coil.ignoreError\nimport me.zhanghai.android.files.compat.getDrawableCompat\nimport me.zhanghai.android.files.databinding.PrincipalItemBinding\nimport me.zhanghai.android.files.ui.SimpleAdapter\nimport me.zhanghai.android.files.util.SelectionLiveData\nimport me.zhanghai.android.files.util.layoutInflater\n\nabstract class PrincipalListAdapter(\n    private val selectionLiveData: SelectionLiveData<Int>\n) : SimpleAdapter<PrincipalItem, PrincipalListAdapter.ViewHolder>() {\n    override val hasStableIds: Boolean\n        get() = true\n\n    override fun getItemId(position: Int): Long = getItem(position).id.toLong()\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder =\n        ViewHolder(PrincipalItemBinding.inflate(parent.context.layoutInflater, parent, false))\n\n    override fun onBindViewHolder(holder: ViewHolder, position: Int) {\n        throw UnsupportedOperationException()\n    }\n\n    override fun onBindViewHolder(holder: ViewHolder, position: Int, payloads: List<Any>) {\n        val principal = getItem(position)\n        val binding = holder.binding\n        binding.itemLayout.isChecked = selectionLiveData.value == principal.id\n        if (payloads.isNotEmpty()) {\n            return\n        }\n        binding.itemLayout.setOnClickListener {\n            selectionLiveData.setValue(principal.id)\n        }\n        val icon = binding.iconImage.context.getDrawableCompat(principalIconRes)\n        val applicationInfo = principal.applicationInfos.firstOrNull()\n        if (applicationInfo != null) {\n            binding.iconImage.load(applicationInfo) {\n                placeholder(icon)\n                ignoreError()\n            }\n        } else {\n            binding.iconImage.dispose()\n            binding.iconImage.setImageDrawable(icon)\n        }\n        binding.principalText.text = if (principal.name != null) {\n            binding.principalText.context.getString(\n                R.string.file_properties_permission_principal_format, principal.name, principal.id\n            )\n        } else {\n            principal.id.toString()\n        }\n        binding.labelText.text = principal.applicationLabels.firstOrNull()\n            ?: binding.labelText.resources.getString(\n                R.string.file_properties_permission_set_principal_system\n            )\n    }\n\n    @get:DrawableRes\n    protected abstract val principalIconRes: Int\n\n    fun findPositionByPrincipalId(id: Int): Int = findPositionById(id.toLong())\n\n    class ViewHolder(val binding: PrincipalItemBinding) : RecyclerView.ViewHolder(binding.root)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/permission/PrincipalListLiveData.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.permission\n\nimport android.content.pm.ApplicationInfo\nimport android.os.AsyncTask\nimport androidx.lifecycle.MutableLiveData\nimport me.zhanghai.android.files.app.packageManager\nimport me.zhanghai.android.files.util.Failure\nimport me.zhanghai.android.files.util.Loading\nimport me.zhanghai.android.files.util.Stateful\nimport me.zhanghai.android.files.util.Success\nimport me.zhanghai.android.files.util.valueCompat\n\nabstract class PrincipalListLiveData : MutableLiveData<Stateful<List<PrincipalItem>>>() {\n    init {\n        loadValue()\n    }\n\n    private fun loadValue() {\n        value = Loading(value?.value)\n        AsyncTask.THREAD_POOL_EXECUTOR.execute {\n            val value = try {\n                val principals = androidPrincipals\n                val androidIds = principals.mapTo(mutableSetOf()) { it.id }\n                val installedApplicationInfos = packageManager.getInstalledApplications(0)\n                val uidApplicationInfoMap = mutableMapOf<Int, MutableList<ApplicationInfo>>()\n                for (applicationInfo in installedApplicationInfos) {\n                    val uid = applicationInfo.uid\n                    if (uid in androidIds) {\n                        continue\n                    }\n                    uidApplicationInfoMap.getOrPut(uid) { mutableListOf() }.add(applicationInfo)\n                }\n                for ((uid, applicationInfos) in uidApplicationInfoMap) {\n                    val principal = PrincipalItem(\n                        uid, getAppPrincipalName(uid), applicationInfos,\n                        applicationInfos.map { it.loadLabel(packageManager).toString() }\n                    )\n                    principals.add(principal)\n                }\n                principals.sortBy { it.id }\n                Success(principals as List<PrincipalItem>)\n            } catch (e: Exception) {\n                Failure(valueCompat.value, e)\n            }\n            postValue(value)\n        }\n    }\n\n    @get:Throws(Exception::class)\n    protected abstract val androidPrincipals: MutableList<PrincipalItem>\n\n    protected abstract fun getAppPrincipalName(uid: Int): String\n\n    companion object {\n        @JvmStatic\n        protected val AID_USER_OFFSET = 100000\n        @JvmStatic\n        protected val AID_APP_START = 10000\n        @JvmStatic\n        protected val AID_CACHE_GID_START = 20000\n        @JvmStatic\n        protected val AID_CACHE_GID_END = 29999\n        @JvmStatic\n        protected val AID_SHARED_GID_START = 50000\n        @JvmStatic\n        protected val AID_SHARED_GID_END = 59999\n        @JvmStatic\n        protected val AID_ISOLATED_START = 99000\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/permission/SetGroupDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.permission\n\nimport androidx.annotation.StringRes\nimport androidx.fragment.app.Fragment\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.file.FileItem\nimport me.zhanghai.android.files.filejob.FileJobService\nimport me.zhanghai.android.files.provider.common.PosixFileAttributes\nimport me.zhanghai.android.files.provider.common.PosixGroup\nimport me.zhanghai.android.files.provider.common.toByteString\nimport me.zhanghai.android.files.util.SelectionLiveData\nimport me.zhanghai.android.files.util.putArgs\nimport me.zhanghai.android.files.util.show\nimport me.zhanghai.android.files.util.viewModels\n\nclass SetGroupDialogFragment : SetPrincipalDialogFragment() {\n    override val viewModel: SetPrincipalViewModel by viewModels { { SetGroupViewModel() } }\n\n    @StringRes\n    override val titleRes: Int = R.string.file_properties_permission_set_group_title\n\n    override fun createAdapter(selectionLiveData: SelectionLiveData<Int>): PrincipalListAdapter =\n        GroupListAdapter(selectionLiveData)\n\n    override val PosixFileAttributes.principal\n        get() = group()!!\n\n    override fun setPrincipal(path: Path, principal: PrincipalItem, recursive: Boolean) {\n        val group = PosixGroup(principal.id, principal.name?.toByteString())\n        FileJobService.setGroup(path, group, recursive, requireContext())\n    }\n\n    companion object {\n        fun show(file: FileItem, fragment: Fragment) {\n            SetGroupDialogFragment().putArgs(Args(file)).show(fragment)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/permission/SetGroupViewModel.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.permission\n\nclass SetGroupViewModel : SetPrincipalViewModel(GroupListLiveData())\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/permission/SetModeDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.permission\n\nimport android.app.Dialog\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatDialogFragment\nimport androidx.core.view.isVisible\nimport androidx.fragment.app.Fragment\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.compat.ListFormatterCompat\nimport me.zhanghai.android.files.databinding.SetModeDialogBinding\nimport me.zhanghai.android.files.file.FileItem\nimport me.zhanghai.android.files.filejob.FileJobService\nimport me.zhanghai.android.files.provider.common.PosixFileAttributes\nimport me.zhanghai.android.files.provider.common.PosixFileModeBit\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.getStringArray\nimport me.zhanghai.android.files.util.layoutInflater\nimport me.zhanghai.android.files.util.putArgs\nimport me.zhanghai.android.files.util.show\nimport me.zhanghai.android.files.util.viewModels\n\nclass SetModeDialogFragment : AppCompatDialogFragment() {\n    private val args by args<Args>()\n\n    private val viewModel by viewModels { { SetModeViewModel(argsMode) } }\n\n    private lateinit var binding: SetModeDialogBinding\n\n    private lateinit var normalModeBitNames: Array<String>\n    private lateinit var ownerAdapter: ModeBitListAdapter\n    private lateinit var groupAdapter: ModeBitListAdapter\n    private lateinit var othersAdapter: ModeBitListAdapter\n    private lateinit var specialModeBitNames: Array<String>\n    private lateinit var specialAdapter: ModeBitListAdapter\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =\n        MaterialAlertDialogBuilder(requireContext(), theme)\n            .setTitle(R.string.file_properties_permission_set_mode_title)\n            .apply {\n                binding = SetModeDialogBinding.inflate(context.layoutInflater)\n                binding.ownerText.setOnClickListener { binding.ownerDropDown.show() }\n                val isDirectory = args.file.attributes.isDirectory\n                normalModeBitNames = getStringArray(\n                    if (isDirectory) {\n                        R.array.file_properties_permission_set_mode_normal_mode_bits_directory\n                    } else {\n                        R.array.file_properties_permission_set_mode_normal_mode_bits_file\n                    }\n                )\n                ownerAdapter = ModeBitListAdapter(OWNER_MODE_BITS, normalModeBitNames)\n                binding.ownerDropDown.setAdapter(ownerAdapter)\n                binding.ownerDropDown.setOnItemClickListener { _, _, position, _ ->\n                    viewModel.toggleModeBit(ownerAdapter.getItem(position))\n                }\n                binding.groupText.setOnClickListener { binding.groupDropDown.show() }\n                groupAdapter = ModeBitListAdapter(GROUP_MODE_BITS, normalModeBitNames)\n                binding.groupDropDown.setAdapter(groupAdapter)\n                binding.groupDropDown.setOnItemClickListener { _, _, position, _ ->\n                    viewModel.toggleModeBit(groupAdapter.getItem(position))\n                }\n                binding.othersText.setOnClickListener { binding.othersDropDown.show() }\n                othersAdapter = ModeBitListAdapter(OTHERS_MODE_BITS, normalModeBitNames)\n                binding.othersDropDown.setAdapter(othersAdapter)\n                binding.othersDropDown.setOnItemClickListener { _, _, position, _ ->\n                    viewModel.toggleModeBit(othersAdapter.getItem(position))\n                }\n                binding.specialText.setOnClickListener { binding.specialDropDown.show() }\n                specialModeBitNames = getStringArray(\n                    R.array.file_properties_permission_set_mode_special_mode_bits\n                )\n                specialAdapter = ModeBitListAdapter(SPECIAL_MODE_BITS, specialModeBitNames)\n                binding.specialDropDown.setAdapter(specialAdapter)\n                binding.specialDropDown.setOnItemClickListener { _, _, position, _ ->\n                    viewModel.toggleModeBit(specialAdapter.getItem(position))\n                }\n                binding.recursiveCheck.isVisible = isDirectory\n                binding.recursiveCheck.setOnCheckedChangeListener { _, isChecked ->\n                    binding.uppercaseXCheck.isEnabled = isChecked\n                }\n                binding.uppercaseXCheck.isVisible = isDirectory\n                if (savedInstanceState == null) {\n                    binding.uppercaseXCheck.isEnabled = false\n                    binding.uppercaseXCheck.isChecked = true\n                }\n                setView(binding.root)\n\n                viewModel.modeLiveData.observe(this@SetModeDialogFragment) { onModeChanged(it) }\n            }\n            .setPositiveButton(android.R.string.ok) { _, _ -> setMode() }\n            .setNegativeButton(android.R.string.cancel, null)\n            .create()\n\n    private fun onModeChanged(mode: Set<PosixFileModeBit>) {\n        binding.ownerText.setText(getModeString(OWNER_MODE_BITS, normalModeBitNames))\n        ownerAdapter.mode = mode\n        binding.groupText.setText(getModeString(GROUP_MODE_BITS, normalModeBitNames))\n        groupAdapter.mode = mode\n        binding.othersText.setText(getModeString(OTHERS_MODE_BITS, normalModeBitNames))\n        othersAdapter.mode = mode\n        binding.specialText.setText(getModeString(SPECIAL_MODE_BITS, specialModeBitNames))\n        specialAdapter.mode = mode\n    }\n\n    private fun getModeString(\n        modeBits: List<PosixFileModeBit>,\n        modeBitNames: Array<String>\n    ): String {\n        val mode = viewModel.mode\n        val checkedNames = mutableListOf<String>()\n        for (index in modeBits.indices) {\n            if (modeBits[index] in mode) {\n                checkedNames += modeBitNames[index]\n            }\n        }\n        return if (checkedNames.isEmpty()) {\n            getString(R.string.none)\n        } else {\n            ListFormatterCompat.format(checkedNames)\n        }\n    }\n\n    private fun setMode() {\n        val mode = viewModel.mode\n        val recursive = binding.recursiveCheck.isChecked\n        if (!recursive) {\n            if (mode == argsMode) {\n                return\n            }\n        }\n        val uppercaseX = binding.uppercaseXCheck.isChecked\n        FileJobService.setMode(args.file.path, mode, recursive, uppercaseX, requireContext())\n    }\n\n    private val argsMode: Set<PosixFileModeBit>\n        get() {\n            val attributes = args.file.attributes as PosixFileAttributes\n            return attributes.mode()!!\n        }\n\n    companion object {\n        private val OWNER_MODE_BITS = listOf(\n            PosixFileModeBit.OWNER_READ,\n            PosixFileModeBit.OWNER_WRITE,\n            PosixFileModeBit.OWNER_EXECUTE\n        )\n        private val GROUP_MODE_BITS = listOf(\n            PosixFileModeBit.GROUP_READ,\n            PosixFileModeBit.GROUP_WRITE,\n            PosixFileModeBit.GROUP_EXECUTE\n        )\n        private val OTHERS_MODE_BITS = listOf(\n            PosixFileModeBit.OTHERS_READ,\n            PosixFileModeBit.OTHERS_WRITE,\n            PosixFileModeBit.OTHERS_EXECUTE\n        )\n        private val SPECIAL_MODE_BITS = listOf(\n            PosixFileModeBit.SET_USER_ID,\n            PosixFileModeBit.SET_GROUP_ID,\n            PosixFileModeBit.STICKY\n        )\n\n        fun show(file: FileItem, fragment: Fragment) {\n            SetModeDialogFragment().putArgs(Args(file)).show(fragment)\n        }\n    }\n\n    @Parcelize\n    class Args(val file: FileItem) : ParcelableArgs\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/permission/SetModeViewModel.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.permission\n\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.MutableLiveData\nimport androidx.lifecycle.ViewModel\nimport me.zhanghai.android.files.provider.common.PosixFileModeBit\nimport me.zhanghai.android.files.util.toEnumSet\nimport me.zhanghai.android.files.util.valueCompat\n\nclass SetModeViewModel(mode: Set<PosixFileModeBit>) : ViewModel() {\n    private val _modeLiveData: MutableLiveData<Set<PosixFileModeBit>> = MutableLiveData(mode)\n    val modeLiveData: LiveData<Set<PosixFileModeBit>>\n        get() = _modeLiveData\n    val mode: Set<PosixFileModeBit>\n        get() = _modeLiveData.valueCompat\n\n    fun toggleModeBit(modeBit: PosixFileModeBit) {\n        val mode = _modeLiveData.valueCompat.toEnumSet()\n        if (modeBit in mode) {\n            mode -= modeBit\n        } else {\n            mode += modeBit\n        }\n        _modeLiveData.value = mode\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/permission/SetOwnerDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.permission\n\nimport androidx.annotation.StringRes\nimport androidx.fragment.app.Fragment\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.file.FileItem\nimport me.zhanghai.android.files.filejob.FileJobService\nimport me.zhanghai.android.files.provider.common.PosixFileAttributes\nimport me.zhanghai.android.files.provider.common.PosixPrincipal\nimport me.zhanghai.android.files.provider.common.PosixUser\nimport me.zhanghai.android.files.provider.common.toByteString\nimport me.zhanghai.android.files.util.SelectionLiveData\nimport me.zhanghai.android.files.util.putArgs\nimport me.zhanghai.android.files.util.show\nimport me.zhanghai.android.files.util.viewModels\n\nclass SetOwnerDialogFragment : SetPrincipalDialogFragment() {\n    override val viewModel: SetPrincipalViewModel by viewModels { { SetOwnerViewModel() } }\n\n    @StringRes\n    override val titleRes: Int = R.string.file_properties_permission_set_owner_title\n\n    override fun createAdapter(selectionLiveData: SelectionLiveData<Int>): PrincipalListAdapter =\n        UserListAdapter(selectionLiveData)\n\n    override val PosixFileAttributes.principal: PosixPrincipal\n        get() = owner()!!\n\n    override fun setPrincipal(path: Path, principal: PrincipalItem, recursive: Boolean) {\n        val owner = PosixUser(principal.id, principal.name?.toByteString())\n        FileJobService.setOwner(path, owner, recursive, requireContext())\n    }\n\n    companion object {\n        fun show(file: FileItem, fragment: Fragment) {\n            SetOwnerDialogFragment().putArgs(Args(file)).show(fragment)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/permission/SetOwnerViewModel.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.permission\n\nclass SetOwnerViewModel : SetPrincipalViewModel(UserListLiveData())\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/permission/SetPrincipalDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.permission\n\nimport android.app.Dialog\nimport android.os.Bundle\nimport androidx.annotation.StringRes\nimport androidx.appcompat.app.AppCompatDialogFragment\nimport androidx.core.view.isVisible\nimport androidx.core.widget.doAfterTextChanged\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport androidx.recyclerview.widget.RecyclerView\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport java8.nio.file.Path\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.databinding.SetPrincipalDialogBinding\nimport me.zhanghai.android.files.file.FileItem\nimport me.zhanghai.android.files.provider.common.PosixFileAttributes\nimport me.zhanghai.android.files.provider.common.PosixPrincipal\nimport me.zhanghai.android.files.util.Failure\nimport me.zhanghai.android.files.util.Loading\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.SelectionLiveData\nimport me.zhanghai.android.files.util.Stateful\nimport me.zhanghai.android.files.util.Success\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.fadeInUnsafe\nimport me.zhanghai.android.files.util.fadeOutUnsafe\nimport me.zhanghai.android.files.util.fadeToVisibilityUnsafe\nimport me.zhanghai.android.files.util.layoutInflater\nimport me.zhanghai.android.files.util.valueCompat\n\nabstract class SetPrincipalDialogFragment : AppCompatDialogFragment() {\n    private val args by args<Args>()\n\n    protected abstract val viewModel: SetPrincipalViewModel\n\n    private lateinit var binding: SetPrincipalDialogBinding\n\n    private lateinit var adapter: PrincipalListAdapter\n\n    private var pendingScrollToId: Int? = null\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =\n        MaterialAlertDialogBuilder(requireContext(), theme)\n            .setTitle(titleRes)\n            .apply {\n                val selectionLiveData = viewModel.selectionLiveData\n                if (selectionLiveData.value == null) {\n                    val id = argsPrincipalId\n                    selectionLiveData.value = id\n                    pendingScrollToId = id\n                }\n\n                binding = SetPrincipalDialogBinding.inflate(context.layoutInflater)\n                binding.filterEdit.doAfterTextChanged { viewModel.filter = it!!.toString() }\n                binding.recyclerView.layoutManager = LinearLayoutManager(context)\n                adapter = createAdapter(selectionLiveData)\n                binding.recyclerView.adapter = adapter\n                binding.recursiveCheck.isVisible = args.file.attributes.isDirectory\n                setView(binding.root)\n\n                viewModel.filteredPrincipalListLiveData.observe(this@SetPrincipalDialogFragment) {\n                    onFilteredPrincipalListChanged(it)\n                }\n                selectionLiveData.observe(this@SetPrincipalDialogFragment, adapter)\n            }\n            .setPositiveButton(android.R.string.ok) { _, _ -> setPrincipal() }\n            .setNegativeButton(android.R.string.cancel, null)\n            .create()\n\n    @get:StringRes\n    protected abstract val titleRes: Int\n\n    protected abstract fun createAdapter(\n        selectionLiveData: SelectionLiveData<Int>\n    ): PrincipalListAdapter\n\n    private fun onFilteredPrincipalListChanged(stateful: Stateful<List<PrincipalItem>>) {\n        when (stateful) {\n            is Loading -> {\n                binding.progress.fadeInUnsafe()\n                binding.errorText.fadeOutUnsafe()\n                binding.emptyView.fadeOutUnsafe()\n                adapter.clear()\n            }\n            is Failure -> {\n                binding.progress.fadeOutUnsafe()\n                binding.errorText.fadeInUnsafe()\n                binding.errorText.text = stateful.throwable.toString()\n                binding.emptyView.fadeOutUnsafe()\n                adapter.clear()\n            }\n            is Success -> {\n                binding.progress.fadeOutUnsafe()\n                binding.errorText.fadeOutUnsafe()\n                binding.emptyView.fadeToVisibilityUnsafe(stateful.value.isEmpty())\n                adapter.replace(stateful.value)\n                pendingScrollToId?.let {\n                    val position = adapter.findPositionByPrincipalId(it)\n                    if (position != RecyclerView.NO_POSITION) {\n                        binding.recyclerView.scrollToPosition(position)\n                    }\n                    this.pendingScrollToId = null\n                }\n            }\n        }\n    }\n\n    private fun setPrincipal() {\n        val id = viewModel.selectionLiveData.valueCompat\n        val recursive = binding.recursiveCheck.isChecked\n        if (!recursive) {\n            if (id == argsPrincipalId) {\n                return\n            }\n        }\n        val principalListStateful = viewModel.principalListStateful\n        if (principalListStateful !is Success) {\n            return\n        }\n        val principal = principalListStateful.value.find { it.id == id } ?: return\n        setPrincipal(args.file.path, principal, recursive)\n    }\n\n    private val argsPrincipalId: Int\n        get() {\n            val attributes = args.file.attributes as PosixFileAttributes\n            return attributes.principal.id\n        }\n\n    protected abstract val PosixFileAttributes.principal: PosixPrincipal\n\n    protected abstract fun setPrincipal(path: Path, principal: PrincipalItem, recursive: Boolean)\n\n    @Parcelize\n    class Args(val file: FileItem) : ParcelableArgs\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/permission/SetPrincipalViewModel.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.permission\n\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.MediatorLiveData\nimport androidx.lifecycle.MutableLiveData\nimport androidx.lifecycle.ViewModel\nimport me.zhanghai.android.files.util.SelectionLiveData\nimport me.zhanghai.android.files.util.Stateful\nimport me.zhanghai.android.files.util.Success\nimport me.zhanghai.android.files.util.valueCompat\n\nabstract class SetPrincipalViewModel(\n    private val principalListLiveData: MutableLiveData<Stateful<List<PrincipalItem>>>\n) : ViewModel() {\n    val principalListStateful: Stateful<List<PrincipalItem>>\n        get() = principalListLiveData.valueCompat\n\n    private val filterLiveData = MutableLiveData(\"\")\n    var filter: String\n        get() = filterLiveData.valueCompat\n        set(value) {\n            if (filterLiveData.valueCompat != value) {\n                filterLiveData.value = value\n            }\n        }\n\n    val filteredPrincipalListLiveData: LiveData<Stateful<List<PrincipalItem>>> =\n        FilteredPrincipalListLiveData(principalListLiveData, filterLiveData)\n\n    val selectionLiveData = SelectionLiveData<Int>()\n\n    private class FilteredPrincipalListLiveData(\n        private val principalListLiveData: LiveData<Stateful<List<PrincipalItem>>>,\n        private val filterLiveData: LiveData<String>\n    ) : MediatorLiveData<Stateful<List<PrincipalItem>>>() {\n        init {\n            addSource(principalListLiveData) { loadValue() }\n            addSource(filterLiveData) { loadValue() }\n        }\n\n        private fun loadValue() {\n            var principalListStateful = principalListLiveData.valueCompat\n            val filter = filterLiveData.valueCompat\n            if (principalListStateful is Success && filter.isNotEmpty()) {\n                principalListStateful = Success(\n                    principalListStateful.value.filter { it.applyFilter(filter) }\n                )\n            }\n            value = principalListStateful\n        }\n\n        private fun PrincipalItem.applyFilter(filter: String): Boolean =\n            (filter in id.toString() || (name != null && filter in name)\n                || applicationInfos.any { filter in it.packageName }\n                || applicationLabels.any { filter in it })\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/permission/SetSeLinuxContextDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.permission\n\nimport android.app.Dialog\nimport android.os.Bundle\nimport android.view.WindowManager\nimport androidx.appcompat.app.AppCompatDialogFragment\nimport androidx.core.view.isVisible\nimport androidx.fragment.app.Fragment\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.databinding.SetSelinuxContextDialogBinding\nimport me.zhanghai.android.files.file.FileItem\nimport me.zhanghai.android.files.filejob.FileJobService\nimport me.zhanghai.android.files.provider.common.PosixFileAttributes\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.layoutInflater\nimport me.zhanghai.android.files.util.putArgs\nimport me.zhanghai.android.files.util.show\n\nclass SetSeLinuxContextDialogFragment : AppCompatDialogFragment() {\n    private val args by args<Args>()\n\n    private lateinit var binding: SetSelinuxContextDialogBinding\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =\n        MaterialAlertDialogBuilder(requireContext(), theme)\n            .setTitle(R.string.file_properties_permission_set_selinux_context_title)\n            .apply {\n                binding = SetSelinuxContextDialogBinding.inflate(context.layoutInflater)\n                if (savedInstanceState == null) {\n                    binding.seLinuxContextEdit.setText(argsSeLinuxContext)\n                }\n                binding.recursiveCheck.isVisible = args.file.attributes.isDirectory\n                setView(binding.root)\n            }\n            .setPositiveButton(android.R.string.ok) { _, _ -> setSeLinuxContext() }\n            .setNegativeButton(android.R.string.cancel, null)\n            .setNeutralButton(\n                R.string.file_properties_permission_set_selinux_context_restore\n            ) { _, _ -> restoreSeLinuxContext() }\n            .create()\n            .apply {\n                window!!.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)\n            }\n\n    private fun setSeLinuxContext() {\n        val seLinuxContext = binding.seLinuxContextEdit.text.toString()\n        val recursive = binding.recursiveCheck.isChecked\n        if (!recursive) {\n            if (seLinuxContext == argsSeLinuxContext) {\n                return\n            }\n        }\n        FileJobService.setSeLinuxContext(\n            args.file.path, seLinuxContext, recursive, requireContext()\n        )\n    }\n\n    private val argsSeLinuxContext: String\n        get() {\n            val attributes = args.file.attributes as PosixFileAttributes\n            return attributes.seLinuxContext()?.toString().orEmpty()\n        }\n\n    private fun restoreSeLinuxContext() {\n        val recursive = binding.recursiveCheck.isChecked\n        FileJobService.restoreSeLinuxContext(args.file.path, recursive, requireContext())\n    }\n\n    companion object {\n        fun show(file: FileItem, fragment: Fragment) {\n            SetSeLinuxContextDialogFragment().putArgs(Args(file)).show(fragment)\n        }\n    }\n\n    @Parcelize\n    class Args(val file: FileItem) : ParcelableArgs\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/permission/UserListAdapter.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.permission\n\nimport androidx.annotation.DrawableRes\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.util.SelectionLiveData\n\nclass UserListAdapter(\n    selectionLiveData: SelectionLiveData<Int>\n) : PrincipalListAdapter(selectionLiveData) {\n    @DrawableRes\n    override val principalIconRes: Int = R.drawable.person_icon_control_normal_24dp\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/permission/UserListLiveData.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.permission\n\nimport me.zhanghai.android.files.provider.linux.syscall.Syscall.endpwent\nimport me.zhanghai.android.files.provider.linux.syscall.Syscall.getpwent\nimport me.zhanghai.android.files.provider.linux.syscall.Syscall.setpwent\n\nclass UserListLiveData : PrincipalListLiveData() {\n    override val androidPrincipals: MutableList<PrincipalItem>\n        @Throws(Exception::class)\n        get() {\n            val users = mutableListOf<PrincipalItem>()\n            setpwent()\n            try {\n                while (true) {\n                    val passwd = getpwent() ?: break\n                    val user = PrincipalItem(\n                        passwd.pw_uid, passwd.pw_name?.toString(), emptyList(), emptyList()\n                    )\n                    users.add(user)\n                }\n            } finally {\n                endpwent()\n            }\n            return users\n        }\n\n    /*\n     * @see https://android.googlesource.com/platform/bionic/+/android10-release/libc/bionic/grp_pwd.cpp\n     *      print_app_name_from_uid()\n     */\n    override fun getAppPrincipalName(uid: Int): String {\n        val userId = uid / AID_USER_OFFSET\n        val appId = uid % AID_USER_OFFSET\n        return when {\n            appId > AID_ISOLATED_START -> \"u${userId}_i${appId - AID_ISOLATED_START}\"\n            else -> \"u${userId}_a${appId - AID_APP_START}\"\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/video/FilePropertiesVideoTabFragment.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.video\n\nimport android.content.Intent\nimport android.location.Geocoder\nimport androidx.lifecycle.lifecycleScope\nimport java8.nio.file.Path\nimport kotlinx.coroutines.Job\nimport kotlinx.coroutines.isActive\nimport kotlinx.coroutines.launch\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.file.FileItem\nimport me.zhanghai.android.files.file.format\nimport me.zhanghai.android.files.file.formatLong\nimport me.zhanghai.android.files.file.isVideo\nimport me.zhanghai.android.files.filelist.name\nimport me.zhanghai.android.files.fileproperties.FilePropertiesTabFragment\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.ParcelableParceler\nimport me.zhanghai.android.files.util.Stateful\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.awaitGetFromLocation\nimport me.zhanghai.android.files.util.createViewLocation\nimport me.zhanghai.android.files.util.isGeocoderPresent\nimport me.zhanghai.android.files.util.isMediaMetadataRetrieverCompatible\nimport me.zhanghai.android.files.util.startActivitySafe\nimport me.zhanghai.android.files.util.userFriendlyString\nimport me.zhanghai.android.files.util.viewModels\n\nclass FilePropertiesVideoTabFragment : FilePropertiesTabFragment() {\n    private val args by args<Args>()\n\n    private val viewModel by viewModels { { FilePropertiesVideoTabViewModel(args.path) } }\n\n    private var addressJob: Job? = null\n\n    override fun onResume() {\n        super.onResume()\n\n        viewModel.videoInfoLiveData.observe(viewLifecycleOwner) { onVideoInfoChanged(it) }\n    }\n\n    override fun refresh() {\n        viewModel.reload()\n    }\n\n    private fun onVideoInfoChanged(stateful: Stateful<VideoInfo>) {\n        addressJob?.cancel()\n        addressJob = null\n        bindView(stateful) { videoInfo ->\n            if (videoInfo.title != null) {\n                addItemView(R.string.file_properties_media_title, videoInfo.title)\n            }\n            if (videoInfo.dimensions != null) {\n                addItemView(\n                    R.string.file_properties_media_dimensions, getString(\n                        R.string.file_properties_media_dimensions_format,\n                        videoInfo.dimensions.width, videoInfo.dimensions.height\n                    )\n                )\n            }\n            if (videoInfo.duration != null) {\n                addItemView(R.string.file_properties_media_duration, videoInfo.duration.format())\n            }\n            if (videoInfo.date != null) {\n                addItemView(R.string.file_properties_media_date_time, videoInfo.date.formatLong())\n            }\n            if (videoInfo.location != null) {\n                addItemView(\n                    R.string.file_properties_media_coordinates, getString(\n                        R.string.file_properties_media_coordinates_format, videoInfo.location.first,\n                        videoInfo.location.second\n                    )\n                ) {\n                    startActivitySafe(\n                        Intent::class.createViewLocation(\n                            videoInfo.location.first, videoInfo.location.second, args.path.name\n                        )\n                    )\n                }\n                if (isGeocoderPresent) {\n                    val textView = addItemView(\n                        R.string.file_properties_media_address, getString(R.string.loading)\n                    )\n                    val geocoder = Geocoder(requireContext())\n                    addressJob = viewLifecycleOwner.lifecycleScope.launch {\n                        val address = try {\n                            geocoder.awaitGetFromLocation(\n                                videoInfo.location.first.toDouble(),\n                                videoInfo.location.second.toDouble(), 1\n                            ).first()\n                        } catch (e: Exception) {\n                            null\n                        }\n                        if (isActive) {\n                            textView.text = address?.userFriendlyString\n                                ?: getString(R.string.unknown)\n                        }\n                    }\n                }\n            }\n            if (videoInfo.bitRate != null) {\n                addItemView(\n                    R.string.file_properties_media_bit_rate, getString(\n                        R.string.file_properties_media_bit_rate_format, videoInfo.bitRate / 1000\n                    )\n                )\n            }\n        }\n    }\n\n    companion object {\n        fun isAvailable(file: FileItem): Boolean =\n            file.mimeType.isVideo && file.path.isMediaMetadataRetrieverCompatible\n    }\n\n    @Parcelize\n    class Args(val path: @WriteWith<ParcelableParceler> Path) : ParcelableArgs\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/video/FilePropertiesVideoTabViewModel.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.video\n\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.ViewModel\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.util.Stateful\n\nclass FilePropertiesVideoTabViewModel(path: Path) : ViewModel() {\n    private val _videoInfoLiveData = VideoInfoLiveData(path)\n    val videoInfoLiveData: LiveData<Stateful<VideoInfo>>\n        get() = _videoInfoLiveData\n\n    fun reload() {\n        _videoInfoLiveData.loadValue()\n    }\n\n    override fun onCleared() {\n        _videoInfoLiveData.close()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/video/VideoInfo.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.video\n\nimport android.util.Size\nimport java.time.Duration\nimport java.time.Instant\n\n// @see com.android.providers.media.scan.ModernMediaScanner.scanItemVideo\n// @see com.android.documentsui.inspector.MediaView.showVideoData\n// @see https://github.com/GNOME/nautilus/blob/c73ad94a72f8e9a989b01858018de74182d17f0e/extensions/audio-video-properties/bacon-video-widget-properties.c#L89\nclass VideoInfo(\n    val title: String?,\n    val dimensions: Size?,\n    val duration: Duration?,\n    val date: Instant?,\n    val location: Pair<Float, Float>?,\n    val bitRate: Long?\n)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/fileproperties/video/VideoInfoLiveData.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.fileproperties.video\n\nimport android.media.MediaMetadataRetriever\nimport android.os.AsyncTask\nimport android.util.Size\nimport java.time.Duration\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.compat.use\nimport me.zhanghai.android.files.fileproperties.PathObserverLiveData\nimport me.zhanghai.android.files.fileproperties.date\nimport me.zhanghai.android.files.fileproperties.extractMetadataNotBlank\nimport me.zhanghai.android.files.fileproperties.location\nimport me.zhanghai.android.files.util.Failure\nimport me.zhanghai.android.files.util.Loading\nimport me.zhanghai.android.files.util.Stateful\nimport me.zhanghai.android.files.util.Success\nimport me.zhanghai.android.files.util.setDataSource\nimport me.zhanghai.android.files.util.valueCompat\n\nclass VideoInfoLiveData(path: Path) : PathObserverLiveData<Stateful<VideoInfo>>(path) {\n    init {\n        loadValue()\n        observe()\n    }\n\n    override fun loadValue() {\n        value = Loading(value?.value)\n        AsyncTask.THREAD_POOL_EXECUTOR.execute {\n            val value = try {\n                val videoInfo = MediaMetadataRetriever().use { retriever ->\n                    retriever.setDataSource(path)\n                    val title = retriever.extractMetadataNotBlank(\n                        MediaMetadataRetriever.METADATA_KEY_TITLE\n                    )\n                    val width = retriever.extractMetadataNotBlank(\n                        MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH\n                    )?.toIntOrNull()\n                    val height = retriever.extractMetadataNotBlank(\n                        MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT\n                    )?.toIntOrNull()\n                    val dimensions = if (width != null && height != null) {\n                        Size(width, height)\n                    } else {\n                        null\n                    }\n                    val duration = retriever.extractMetadataNotBlank(\n                        MediaMetadataRetriever.METADATA_KEY_DURATION\n                    )?.toLongOrNull()?.let { Duration.ofMillis(it) }\n                    val date = retriever.date\n                    val location = retriever.location\n                    val bitRate = retriever.extractMetadataNotBlank(\n                        MediaMetadataRetriever.METADATA_KEY_BITRATE\n                    )?.toLongOrNull()\n                    VideoInfo(title, dimensions, duration, date, location, bitRate)\n                }\n                Success(videoInfo)\n            } catch (e: Exception) {\n                Failure(valueCompat.value, e)\n            }\n            postValue(value)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ftpserver/FtpServer.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ftpserver\n\nimport java8.nio.file.Path\nimport org.apache.ftpserver.ConnectionConfigFactory\nimport org.apache.ftpserver.FtpServer\nimport org.apache.ftpserver.FtpServerFactory\nimport org.apache.ftpserver.ftplet.FtpException\nimport org.apache.ftpserver.listener.ListenerFactory\nimport org.apache.ftpserver.usermanager.impl.BaseUser\nimport org.apache.ftpserver.usermanager.impl.WritePermission\n\nclass FtpServer(\n    private val username: String,\n    private val password: String?,\n    private val port: Int,\n    private val homeDirectory: Path,\n    private val writable: Boolean\n) {\n    private lateinit var server: FtpServer\n\n    @Throws(FtpException::class, RuntimeException::class)\n    fun start() {\n        server = FtpServerFactory()\n            .apply {\n                val listener = ListenerFactory()\n                    .apply { port = this@FtpServer.port }\n                    .createListener()\n                addListener(\"default\", listener)\n                val user = BaseUser().apply {\n                    name = username\n                    password = this@FtpServer.password\n                    authorities = if (writable) listOf(WritePermission()) else emptyList()\n                    homeDirectory = this@FtpServer.homeDirectory.toUri().toString()\n                }\n                userManager.save(user)\n                fileSystem = ProviderFileSystemFactory()\n                connectionConfig = ConnectionConfigFactory()\n                    .apply { isAnonymousLoginEnabled = true }\n                    .createConnectionConfig()\n            }\n            .createServer()\n        server.start()\n    }\n\n    fun stop() {\n        server.stop()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ftpserver/FtpServerActivity.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ftpserver\n\nimport android.os.Bundle\nimport android.view.View\nimport androidx.fragment.app.add\nimport androidx.fragment.app.commit\nimport me.zhanghai.android.files.app.AppActivity\n\nclass FtpServerActivity : AppActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        // Calls ensureSubDecor().\n        findViewById<View>(android.R.id.content)\n        if (savedInstanceState == null) {\n            supportFragmentManager.commit { add<FtpServerFragment>(android.R.id.content) }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ftpserver/FtpServerAddTilePreference.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ftpserver\n\nimport android.app.StatusBarManager\nimport android.content.Context\nimport android.os.Build\nimport android.util.AttributeSet\nimport androidx.annotation.AttrRes\nimport androidx.annotation.RequiresApi\nimport androidx.annotation.StyleRes\nimport androidx.preference.Preference\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.compat.getSystemServiceCompat\nimport me.zhanghai.android.files.compat.mainExecutorCompat\nimport me.zhanghai.android.files.util.requestAddTileService\nimport me.zhanghai.android.files.util.showToast\n\nclass FtpServerAddTilePreference : Preference {\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    constructor(\n        context: Context,\n        attrs: AttributeSet?,\n        @AttrRes defStyleAttr: Int,\n        @StyleRes defStyleRes: Int\n    ) : super(context, attrs, defStyleAttr, defStyleRes)\n\n    init {\n        isPersistent = false\n    }\n\n    @RequiresApi(Build.VERSION_CODES.TIRAMISU)\n    override fun onClick() {\n        val statusBarManager = context.getSystemServiceCompat(StatusBarManager::class.java)\n        statusBarManager.requestAddTileService(\n            FtpServerTileService::class.java, context.mainExecutorCompat\n        ) { result ->\n            val resultRes = when (result) {\n                StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_NOT_ADDED ->\n                    return@requestAddTileService\n                StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_ALREADY_ADDED ->\n                    R.string.ftp_server_add_tile_result_already_added\n                StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_ADDED ->\n                    R.string.ftp_server_add_tile_result_added\n                else -> R.string.ftp_server_add_tile_result_error\n            }\n            context.showToast(resultRes)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ftpserver/FtpServerFragment.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ftpserver\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.fragment.app.Fragment\nimport me.zhanghai.android.files.databinding.FtpServerFragmentBinding\n\nclass FtpServerFragment : Fragment() {\n    private lateinit var binding: FtpServerFragmentBinding\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View =\n        FtpServerFragmentBinding.inflate(inflater, container, false)\n            .also { binding = it }\n            .root\n\n    override fun onActivityCreated(savedInstanceState: Bundle?) {\n        super.onActivityCreated(savedInstanceState)\n\n        val activity = requireActivity() as AppCompatActivity\n        activity.setSupportActionBar(binding.toolbar)\n        activity.supportActionBar!!.setDisplayHomeAsUpEnabled(true)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ftpserver/FtpServerHomeDirectoryPreference.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ftpserver\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.settings.PathPreference\nimport me.zhanghai.android.files.settings.Settings\nimport me.zhanghai.android.files.util.valueCompat\n\nclass FtpServerHomeDirectoryPreference : PathPreference {\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    constructor(\n        context: Context,\n        attrs: AttributeSet?,\n        @AttrRes defStyleAttr: Int,\n        @StyleRes defStyleRes: Int\n    ) : super(context, attrs, defStyleAttr, defStyleRes)\n\n    override var persistedPath: Path\n        get() = Settings.FTP_SERVER_HOME_DIRECTORY.valueCompat\n        set(value) {\n            Settings.FTP_SERVER_HOME_DIRECTORY.putValue(value)\n        }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ftpserver/FtpServerNotification.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ftpserver\n\nimport android.app.PendingIntent\nimport android.app.Service\nimport android.os.Build\nimport androidx.core.app.NotificationCompat\nimport androidx.core.app.NotificationManagerCompat\nimport androidx.core.app.ServiceCompat\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.app.NotificationIds\nimport me.zhanghai.android.files.compat.stopForegroundCompat\nimport me.zhanghai.android.files.util.NotificationChannelTemplate\nimport me.zhanghai.android.files.util.NotificationTemplate\nimport me.zhanghai.android.files.util.createIntent\n\nval ftpServerServiceNotificationTemplate =\n    NotificationTemplate(\n        NotificationChannelTemplate(\n            \"ftp_server\",\n            R.string.notification_channel_ftp_server_name,\n            NotificationManagerCompat.IMPORTANCE_LOW,\n            descriptionRes = R.string.notification_channel_ftp_server_description,\n            showBadge = false\n        ),\n        colorRes = R.color.color_primary,\n        smallIcon = R.drawable.notification_icon,\n        contentTitleRes = R.string.ftp_server_notification_title,\n        ongoing = true,\n        onlyAlertOnce = true,\n        category = NotificationCompat.CATEGORY_SERVICE,\n        priority = NotificationCompat.PRIORITY_LOW\n    )\n\nclass FtpServerNotification(private val service: Service) {\n    private val receiver = FtpServerUrl.createChangeReceiver(service) { doStartForeground() }\n\n    fun startForeground() {\n        doStartForeground()\n        receiver.register()\n    }\n\n    private fun doStartForeground() {\n        val contextText = FtpServerUrl.getUrl()\n            ?: service.getString(R.string.ftp_server_notification_text_no_local_inet_address)\n        val contentIntent = FtpServerActivity::class.createIntent()\n        var pendingIntentFlags = PendingIntent.FLAG_UPDATE_CURRENT\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n            pendingIntentFlags = pendingIntentFlags or PendingIntent.FLAG_IMMUTABLE\n        }\n        val contentPendingIntent = PendingIntent.getActivity(\n            service, FtpServerActivity::class.hashCode(), contentIntent, pendingIntentFlags\n        )\n        val stopIntent = FtpServerReceiver.createIntent()\n        val stopPendingIntent = PendingIntent.getBroadcast(\n            service, FtpServerReceiver::class.hashCode(), stopIntent, pendingIntentFlags\n        )\n        val notification = ftpServerServiceNotificationTemplate.createBuilder(service)\n            .setContentText(contextText)\n            .setContentIntent(contentPendingIntent)\n            .addAction(\n                R.drawable.stop_icon_white_24dp, service.getString(R.string.stop), stopPendingIntent\n            )\n            .build()\n        service.startForeground(NotificationIds.FTP_SERVER, notification)\n    }\n\n    fun stopForeground() {\n        receiver.unregister()\n        service.stopForegroundCompat(ServiceCompat.STOP_FOREGROUND_REMOVE)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ftpserver/FtpServerPreferenceFragment.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ftpserver\n\nimport android.os.Bundle\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.ui.PreferenceFragmentCompat\n\nclass FtpServerPreferenceFragment : PreferenceFragmentCompat() {\n    override fun onCreatePreferencesFix(savedInstanceState: Bundle?, rootKey: String?) {\n        addPreferencesFromResource(R.xml.ftp_server)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ftpserver/FtpServerReceiver.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ftpserver\n\nimport android.content.BroadcastReceiver\nimport android.content.Context\nimport android.content.Intent\nimport me.zhanghai.android.files.app.application\n\nclass FtpServerReceiver : BroadcastReceiver() {\n    override fun onReceive(context: Context, intent: Intent) {\n        when (val action = intent.action) {\n            ACTION_STOP -> FtpServerService.stop(context)\n            else -> throw IllegalArgumentException(action)\n        }\n    }\n\n    companion object {\n        const val ACTION_STOP = \"stop\"\n\n        fun createIntent(): Intent =\n            Intent(application, FtpServerReceiver::class.java)\n                .setAction(ACTION_STOP)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ftpserver/FtpServerService.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ftpserver\n\nimport android.app.Service\nimport android.content.Context\nimport android.content.Intent\nimport android.os.IBinder\nimport androidx.annotation.WorkerThread\nimport androidx.core.content.ContextCompat\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.MutableLiveData\nimport me.zhanghai.android.files.compat.mainExecutorCompat\nimport me.zhanghai.android.files.settings.Settings\nimport me.zhanghai.android.files.util.WakeWifiLock\nimport me.zhanghai.android.files.util.showToast\nimport me.zhanghai.android.files.util.valueCompat\nimport java.util.concurrent.Executors\n\nclass FtpServerService : Service() {\n    private var state = State.STOPPED\n        set(value) {\n            field = value\n            _stateLiveData.value = value\n        }\n\n    private lateinit var wakeWifiLock: WakeWifiLock\n\n    private lateinit var notification: FtpServerNotification\n\n    private val executorService = Executors.newSingleThreadExecutor()\n\n    private var server: FtpServer? = null\n\n    override fun onCreate() {\n        super.onCreate()\n\n        wakeWifiLock = WakeWifiLock(FtpServerService::class.java.simpleName)\n        notification = FtpServerNotification(this)\n        executeStart()\n    }\n\n    override fun onBind(intent: Intent): IBinder? = null\n\n    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int = START_STICKY\n\n    override fun onDestroy() {\n        super.onDestroy()\n\n        executeStop()\n        executorService.shutdown()\n    }\n\n    private fun executeStart() {\n        if (state == State.STARTING || state == State.RUNNING) {\n            return\n        }\n        wakeWifiLock.isAcquired = true\n        notification.startForeground()\n        state = State.STARTING\n        executorService.execute { doStart() }\n    }\n\n    private fun onStartError(exception: Exception) {\n        state = State.STOPPED\n        showToast(exception.toString())\n        notification.stopForeground()\n        wakeWifiLock.isAcquired = false\n        stopSelf()\n    }\n\n    private fun executeStop() {\n        if (state == State.STOPPING || state == State.STOPPED) {\n            return\n        }\n        state = State.STOPPING\n        executorService.execute { doStop() }\n        notification.stopForeground()\n        wakeWifiLock.isAcquired = false\n    }\n\n    @WorkerThread\n    private fun postState(state: State) {\n        mainExecutorCompat.execute { this.state = state }\n    }\n\n    @WorkerThread\n    private fun doStart() {\n        if (server != null) {\n            return\n        }\n        val username: String?\n        val password: String?\n        if (Settings.FTP_SERVER_ANONYMOUS_LOGIN.valueCompat) {\n            username = USERNAME_ANONYMOUS\n            password = null\n        } else {\n            username = Settings.FTP_SERVER_USERNAME.valueCompat\n            password = Settings.FTP_SERVER_PASSWORD.valueCompat\n        }\n        val port = Settings.FTP_SERVER_PORT.valueCompat\n        val homeDirectory = Settings.FTP_SERVER_HOME_DIRECTORY.valueCompat\n        val writable = Settings.FTP_SERVER_WRITABLE.valueCompat\n        val server = FtpServer(username, password, port, homeDirectory, writable)\n        this.server = server\n        try {\n            server.start()\n        } catch (e: Exception) {\n            e.printStackTrace()\n            this.server = null\n            mainExecutorCompat.execute { onStartError(e) }\n            return\n        }\n        postState(State.RUNNING)\n    }\n\n    @WorkerThread\n    private fun doStop() {\n        val server = server ?: return\n        server.stop()\n        this.server = null\n        postState(State.STOPPED)\n    }\n\n    companion object {\n        const val USERNAME_ANONYMOUS = \"anonymous\"\n\n        private val _stateLiveData = MutableLiveData(State.STOPPED)\n        val stateLiveData: LiveData<State>\n            get() = _stateLiveData\n\n        fun start(context: Context) {\n            ContextCompat.startForegroundService(\n                context, Intent(context, FtpServerService::class.java)\n            )\n        }\n\n        fun stop(context: Context) {\n            context.stopService(Intent(context, FtpServerService::class.java))\n        }\n\n        fun toggle(context: Context) {\n            when (val state = _stateLiveData.valueCompat) {\n                State.STARTING, State.STOPPING -> {}\n                State.RUNNING -> stop(context)\n                State.STOPPED -> start(context)\n                else -> throw AssertionError(state)\n            }\n        }\n    }\n\n    enum class State {\n        STARTING,\n        RUNNING,\n        STOPPING,\n        STOPPED\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ftpserver/FtpServerStatePreference.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ftpserver\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport androidx.lifecycle.Observer\nimport androidx.preference.SwitchPreferenceCompat\nimport me.zhanghai.android.files.R\n\nclass FtpServerStatePreference : SwitchPreferenceCompat {\n    private val observer = Observer<FtpServerService.State> { onStateChanged(it) }\n\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    constructor(\n        context: Context,\n        attrs: AttributeSet?,\n        @AttrRes defStyleAttr: Int,\n        @StyleRes defStyleRes: Int\n    ) : super(context, attrs, defStyleAttr, defStyleRes)\n\n    init {\n        isPersistent = false\n    }\n\n    override fun onAttached() {\n        super.onAttached()\n\n        FtpServerService.stateLiveData.observeForever(observer)\n    }\n\n    override fun onDetached() {\n        super.onDetached()\n\n        FtpServerService.stateLiveData.removeObserver(observer)\n    }\n\n    private fun onStateChanged(state: FtpServerService.State) {\n        val summaryRes = when (state) {\n            FtpServerService.State.STARTING -> R.string.ftp_server_state_summary_starting\n            FtpServerService.State.RUNNING -> R.string.ftp_server_state_summary_running\n            FtpServerService.State.STOPPING -> R.string.ftp_server_state_summary_stopping\n            FtpServerService.State.STOPPED -> R.string.ftp_server_state_summary_stopped\n        }\n        summary = context.getString(summaryRes)\n        isChecked = state == FtpServerService.State.STARTING\n            || state == FtpServerService.State.RUNNING\n        isEnabled = !(state == FtpServerService.State.STARTING\n            || state == FtpServerService.State.STOPPING)\n    }\n\n    override fun onClick() {\n        FtpServerService.toggle(context)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ftpserver/FtpServerTileService.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ftpserver\n\nimport android.os.Build\nimport android.service.quicksettings.Tile\nimport android.service.quicksettings.TileService\nimport androidx.annotation.RequiresApi\nimport androidx.lifecycle.Observer\nimport me.zhanghai.android.files.compat.doWithStartForegroundServiceAllowed\n\n@RequiresApi(Build.VERSION_CODES.N)\nclass FtpServerTileService : TileService() {\n    private val observer = Observer<FtpServerService.State> { onFtpServerStateChanged(it) }\n\n    override fun onStartListening() {\n        super.onStartListening()\n\n        FtpServerService.stateLiveData.observeForever(observer)\n    }\n\n    override fun onStopListening() {\n        super.onStopListening()\n\n        FtpServerService.stateLiveData.removeObserver(observer)\n    }\n\n    private fun onFtpServerStateChanged(state: FtpServerService.State) {\n        val tile = qsTile\n        when (state) {\n            FtpServerService.State.STARTING,\n            FtpServerService.State.RUNNING -> tile.state = Tile.STATE_ACTIVE\n            FtpServerService.State.STOPPING -> tile.state = Tile.STATE_UNAVAILABLE\n            FtpServerService.State.STOPPED -> tile.state = Tile.STATE_INACTIVE\n        }\n        tile.updateTile()\n    }\n\n    override fun onClick() {\n        super.onClick()\n\n        if (isLocked) {\n            unlockAndRun { toggle() }\n        } else {\n            toggle()\n        }\n    }\n\n    private fun toggle() {\n        doWithStartForegroundServiceAllowed { FtpServerService.toggle(this) }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ftpserver/FtpServerUrl.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ftpserver\n\nimport android.content.BroadcastReceiver\nimport android.content.Context\nimport android.content.Intent\nimport android.content.IntentFilter\nimport android.net.ConnectivityManager\nimport me.zhanghai.android.files.settings.Settings\nimport me.zhanghai.android.files.util.RuntimeBroadcastReceiver\nimport me.zhanghai.android.files.util.getLocalAddress\nimport me.zhanghai.android.files.util.valueCompat\nimport java.net.InetAddress\n\nobject FtpServerUrl {\n    fun getUrl(): String? {\n        val localAddress = InetAddress::class.getLocalAddress() ?: return null\n        val username = if (!Settings.FTP_SERVER_ANONYMOUS_LOGIN.valueCompat) {\n            Settings.FTP_SERVER_USERNAME.valueCompat\n        } else {\n            null\n        }\n        val host = localAddress.hostAddress\n        val port = Settings.FTP_SERVER_PORT.valueCompat\n        return \"ftp://${if (username != null) \"$username@\" else \"\"}$host:$port/\"\n    }\n\n    fun createChangeReceiver(context: Context, onChange: () -> Unit): RuntimeBroadcastReceiver =\n        RuntimeBroadcastReceiver(\n            IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION), object : BroadcastReceiver() {\n                override fun onReceive(context: Context, intent: Intent) {\n                    onChange()\n                }\n            }, context\n        )\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ftpserver/FtpServerUrlPreference.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ftpserver\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.ContextMenu\nimport android.view.ContextMenu.ContextMenuInfo\nimport android.view.Menu\nimport android.view.View\nimport android.view.View.OnCreateContextMenuListener\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport androidx.lifecycle.Observer\nimport androidx.preference.Preference\nimport androidx.preference.PreferenceViewHolder\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.app.clipboardManager\nimport me.zhanghai.android.files.settings.Settings\nimport me.zhanghai.android.files.util.copyText\nimport me.zhanghai.android.files.util.valueCompat\n\nclass FtpServerUrlPreference : Preference {\n    private val observer = Observer<Any> { updateUrl() }\n    private val receiver = FtpServerUrl.createChangeReceiver(context) { updateUrl() }\n\n    private var url: String? = null\n\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    constructor(\n        context: Context,\n        attrs: AttributeSet?,\n        @AttrRes defStyleAttr: Int,\n        @StyleRes defStyleRes: Int\n    ) : super(context, attrs, defStyleAttr, defStyleRes)\n\n    init {\n        isPersistent = false\n        updateUrl()\n    }\n\n    override fun onAttached() {\n        super.onAttached()\n\n        Settings.FTP_SERVER_ANONYMOUS_LOGIN.observeForever(observer)\n        Settings.FTP_SERVER_USERNAME.observeForever(observer)\n        Settings.FTP_SERVER_PORT.observeForever(observer)\n        receiver.register()\n    }\n\n    override fun onDetached() {\n        super.onDetached()\n\n        Settings.FTP_SERVER_ANONYMOUS_LOGIN.removeObserver(observer)\n        Settings.FTP_SERVER_USERNAME.removeObserver(observer)\n        Settings.FTP_SERVER_PORT.removeObserver(observer)\n        receiver.unregister()\n    }\n\n    private fun updateUrl() {\n        url = FtpServerUrl.getUrl()\n        summary = url ?: context.getString(R.string.ftp_server_url_summary_no_local_inet_address)\n    }\n\n    override fun onBindViewHolder(holder: PreferenceViewHolder) {\n        super.onBindViewHolder(holder)\n\n        holder.itemView.setOnCreateContextMenuListener(object : OnCreateContextMenuListener {\n            override fun onCreateContextMenu(\n                menu: ContextMenu,\n                view: View,\n                menuInfo: ContextMenuInfo?\n            ) {\n                val url = url ?: return\n                menu.apply {\n                    setHeaderTitle(url)\n                    add(Menu.NONE, Menu.NONE, Menu.NONE, R.string.ftp_server_url_menu_copy_url)\n                        .setOnMenuItemClickListener {\n                            clipboardManager.copyText(url, context)\n                            true\n                        }\n                    if (!Settings.FTP_SERVER_ANONYMOUS_LOGIN.valueCompat) {\n                        val password = Settings.FTP_SERVER_PASSWORD.valueCompat\n                        if (password.isNotEmpty()) {\n                            add(\n                                Menu.NONE, Menu.NONE, Menu.NONE,\n                                R.string.ftp_server_url_menu_copy_password\n                            ).setOnMenuItemClickListener {\n                                clipboardManager.copyText(password, context)\n                                true\n                            }\n                        }\n                    }\n                }\n            }\n        })\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ftpserver/ProviderFileSystemFactory.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ftpserver\n\nimport org.apache.ftpserver.ftplet.FileSystemFactory\nimport org.apache.ftpserver.ftplet.FileSystemView\nimport org.apache.ftpserver.ftplet.User\n\nclass ProviderFileSystemFactory : FileSystemFactory {\n    override fun createFileSystemView(user: User): FileSystemView = ProviderFileSystemView(user)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ftpserver/ProviderFileSystemView.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ftpserver\n\nimport java8.nio.file.Paths\nimport me.zhanghai.android.files.provider.archive.isArchivePath\nimport org.apache.ftpserver.ftplet.FileSystemView\nimport org.apache.ftpserver.ftplet.User\nimport java.net.URI\n\nclass ProviderFileSystemView(private val user: User) : FileSystemView {\n    private val homeDirectory: ProviderFtpFile\n    private var workingDirectory: ProviderFtpFile\n\n    init {\n        val homeDirectoryPath = Paths.get(URI.create(user.homeDirectory))\n        homeDirectory = ProviderFtpFile(\n            homeDirectoryPath, homeDirectoryPath.relativize(homeDirectoryPath), user\n        )\n        workingDirectory = homeDirectory\n    }\n\n    override fun getHomeDirectory(): ProviderFtpFile = homeDirectory\n\n    override fun getWorkingDirectory(): ProviderFtpFile = workingDirectory\n\n    override fun changeWorkingDirectory(directoryString: String): Boolean {\n        val directory = getFile(directoryString)\n        if (!directory.isDirectory) {\n            return false\n        }\n        workingDirectory = directory\n        return true\n    }\n\n    override fun getFile(fileString: String): ProviderFtpFile {\n        val isAbsolute = fileString.startsWith(\"/\")\n        val homeDirectoryPath = homeDirectory.physicalFile\n        val parentPath = if (isAbsolute) homeDirectoryPath else workingDirectory.physicalFile\n        val relativeFileString = if (isAbsolute) fileString.drop(1) else fileString\n        val filePath = parentPath.resolve(relativeFileString).normalize()\n        if (!filePath.startsWith(homeDirectoryPath)) {\n            return homeDirectory\n        }\n        return ProviderFtpFile(filePath, homeDirectoryPath.relativize(filePath), user)\n    }\n\n    override fun isRandomAccessible(): Boolean =\n        // TODO: Better way of determining if the provider is random accessible.\n        !homeDirectory.physicalFile.isArchivePath\n\n    override fun dispose() {}\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ftpserver/ProviderFtpFile.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ftpserver\n\nimport java8.nio.file.Path\nimport java8.nio.file.StandardOpenOption\nimport java8.nio.file.attribute.FileTime\nimport java8.nio.file.attribute.PosixFileAttributeView\nimport me.zhanghai.android.files.provider.common.createDirectory\nimport me.zhanghai.android.files.provider.common.delete\nimport me.zhanghai.android.files.provider.common.exists\nimport me.zhanghai.android.files.provider.common.getFileAttributeView\nimport me.zhanghai.android.files.provider.common.getLastModifiedTime\nimport me.zhanghai.android.files.provider.common.getOwner\nimport me.zhanghai.android.files.provider.common.isDirectory\nimport me.zhanghai.android.files.provider.common.isReadable\nimport me.zhanghai.android.files.provider.common.isRegularFile\nimport me.zhanghai.android.files.provider.common.isWritable\nimport me.zhanghai.android.files.provider.common.moveTo\nimport me.zhanghai.android.files.provider.common.newByteChannel\nimport me.zhanghai.android.files.provider.common.newDirectoryStream\nimport me.zhanghai.android.files.provider.common.newInputStream\nimport me.zhanghai.android.files.provider.common.newOutputStream\nimport me.zhanghai.android.files.provider.common.setLastModifiedTime\nimport me.zhanghai.android.files.provider.common.size\nimport org.apache.ftpserver.ftplet.FtpFile\nimport org.apache.ftpserver.ftplet.User\nimport org.apache.ftpserver.usermanager.impl.WriteRequest\nimport java.io.IOException\nimport java.io.InputStream\nimport java.io.OutputStream\nimport java.nio.ByteBuffer\n\nclass ProviderFtpFile(\n    private val path: Path,\n    private val relativePath: Path,\n    private val user: User\n) : Comparable<ProviderFtpFile>, FtpFile {\n    override fun getAbsolutePath(): String {\n        val path = relativePath.toString()\n        return \"/$path\"\n    }\n\n    override fun getName(): String {\n        val name = relativePath.fileName.toString()\n        return if (name.isNotEmpty()) name else \"/\"\n    }\n\n    override fun isHidden(): Boolean = false\n\n    override fun isDirectory(): Boolean = path.isDirectory()\n\n    override fun isFile(): Boolean = path.isRegularFile()\n\n    override fun doesExist(): Boolean = path.exists()\n\n    override fun isReadable(): Boolean = path.isReadable\n\n    override fun isWritable(): Boolean {\n        if (user.authorize(WriteRequest(absolutePath)) == null) {\n            return false\n        }\n        return !path.exists() || path.isWritable\n    }\n\n    override fun isRemovable(): Boolean {\n        if (relativePath.nameCount == 1 && relativePath.getName(0).toString().isEmpty()) {\n            return false\n        }\n        if (user.authorize(WriteRequest(absolutePath)) == null) {\n            return false\n        }\n        return path.parent.isWritable\n    }\n\n    override fun getOwnerName(): String =\n        try {\n            path.getOwner().name\n        } catch (ignored: UnsupportedOperationException) {\n            null\n        } catch (e: IOException) {\n            e.printStackTrace()\n            null\n        } ?: \"user\"\n\n    override fun getGroupName(): String {\n        val attributeView = path.getFileAttributeView(PosixFileAttributeView::class.java)\n        return if (attributeView != null) {\n            try {\n                attributeView.readAttributes().group().name\n            } catch (e: IOException) {\n                e.printStackTrace()\n                null\n            }\n        } else {\n            null\n        } ?: \"group\"\n    }\n\n    override fun getLinkCount(): Int = if (isDirectory) 3 else 1\n\n    override fun getLastModified(): Long =\n        try {\n            path.getLastModifiedTime().toMillis()\n        } catch (e: IOException) {\n            e.printStackTrace()\n            0\n        }\n\n    override fun setLastModified(time: Long): Boolean =\n        if (!isWritable) {\n            false\n        } else {\n            try {\n                path.setLastModifiedTime(FileTime.fromMillis(time))\n                true\n            } catch (e: IOException) {\n                e.printStackTrace()\n                false\n            }\n        }\n\n    override fun getSize(): Long =\n        try {\n            path.size()\n        } catch (e: IOException) {\n            e.printStackTrace()\n            0\n        }\n\n    override fun getPhysicalFile(): Path = path\n\n    override fun mkdir(): Boolean =\n        if (!isWritable) {\n            false\n        } else {\n            try {\n                path.createDirectory()\n                true\n            } catch (e: IOException) {\n                e.printStackTrace()\n                false\n            }\n        }\n\n    override fun delete(): Boolean =\n        if (!isRemovable) {\n            false\n        } else {\n            try {\n                path.delete()\n                true\n            } catch (e: IOException) {\n                e.printStackTrace()\n                false\n            }\n        }\n\n    override fun move(destination: FtpFile): Boolean {\n        if (!(isRemovable && destination.isWritable)) {\n            return false\n        }\n        val targetPath = (destination as ProviderFtpFile).path\n        return try {\n            path.moveTo(targetPath)\n            true\n        } catch (e: IOException) {\n            e.printStackTrace()\n            false\n        }\n    }\n\n    override fun listFiles(): List<ProviderFtpFile>? {\n        val directoryStream = try {\n            path.newDirectoryStream()\n        } catch (e: IOException) {\n            e.printStackTrace()\n            return null\n        }\n        return directoryStream.map {\n            ProviderFtpFile(path.resolve(it), relativePath.resolve(it), user)\n        }.sorted()\n    }\n\n    @Throws(IOException::class)\n    override fun createOutputStream(offset: Long): OutputStream {\n        if (!isWritable) {\n            throw IOException(\"Not writable: $absolutePath\")\n        }\n        return if (offset == 0L) {\n            path.newOutputStream()\n        } else {\n            val channel = path.newByteChannel(StandardOpenOption.WRITE)\n            var successful = false\n            try {\n                val size = channel.size()\n                if (offset <= size) {\n                    if (offset < size) {\n                        channel.truncate(offset)\n                    }\n                    channel.position(offset)\n                } else {\n                    channel.position(offset - 1)\n                    channel.write(ByteBuffer.allocate(1))\n                }\n                val outputStream = channel.newOutputStream()\n                successful = true\n                outputStream\n            } finally {\n                if (!successful) {\n                    channel.close()\n                }\n            }\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun createInputStream(offset: Long): InputStream {\n        return if (offset == 0L) {\n            path.newInputStream()\n        } else {\n            val channel = path.newByteChannel()\n            var successful = false\n            try {\n                channel.position(offset)\n                val inputStream = channel.newInputStream()\n                successful = true\n                inputStream\n            } finally {\n                if (!successful) {\n                    channel.close()\n                }\n            }\n        }\n    }\n\n    override fun equals(other: Any?): Boolean {\n        if (this === other) {\n            return true\n        }\n        if (javaClass != other?.javaClass) {\n            return false\n        }\n        other as ProviderFtpFile\n        return path == other.path\n    }\n\n    override fun hashCode(): Int = path.hashCode()\n\n    override fun compareTo(other: ProviderFtpFile): Int = path.compareTo(other.path)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/hiddenapi/HiddenApi.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.hiddenapi\n\nimport android.os.Build\n\nobject HiddenApi {\n    fun disableHiddenApiChecks() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {\n            System.loadLibrary(\"hiddenapi\")\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/hiddenapi/RestrictedHiddenApi.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.hiddenapi\n\n@Retention(AnnotationRetention.SOURCE)\nannotation class RestrictedHiddenApi\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/navigation/BookmarkDirectories.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.navigation\n\nimport me.zhanghai.android.files.settings.Settings\nimport me.zhanghai.android.files.util.removeFirst\nimport me.zhanghai.android.files.util.valueCompat\n\nobject BookmarkDirectories {\n    fun add(bookmarkDirectory: BookmarkDirectory) {\n        val bookmarkDirectories = Settings.BOOKMARK_DIRECTORIES.valueCompat.toMutableList()\n            .apply { add(bookmarkDirectory) }\n        Settings.BOOKMARK_DIRECTORIES.putValue(bookmarkDirectories)\n    }\n\n    fun move(fromPosition: Int, toPosition: Int) {\n        val bookmarkDirectories = Settings.BOOKMARK_DIRECTORIES.valueCompat.toMutableList()\n            .apply { add(toPosition, removeAt(fromPosition)) }\n        Settings.BOOKMARK_DIRECTORIES.putValue(bookmarkDirectories)\n    }\n\n    fun replace(bookmarkDirectory: BookmarkDirectory) {\n        val bookmarkDirectories = Settings.BOOKMARK_DIRECTORIES.valueCompat.toMutableList()\n            .apply { this[indexOfFirst { it.id == bookmarkDirectory.id }] = bookmarkDirectory }\n        Settings.BOOKMARK_DIRECTORIES.putValue(bookmarkDirectories)\n    }\n\n    fun remove(bookmarkDirectory: BookmarkDirectory) {\n        val bookmarkDirectories = Settings.BOOKMARK_DIRECTORIES.valueCompat.toMutableList()\n            .apply { removeFirst { it.id == bookmarkDirectory.id } }\n        Settings.BOOKMARK_DIRECTORIES.putValue(bookmarkDirectories)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/navigation/BookmarkDirectory.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.navigation\n\nimport android.os.Parcelable\nimport java8.nio.file.Path\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.filelist.name\nimport me.zhanghai.android.files.util.ParcelableParceler\nimport me.zhanghai.android.files.util.takeIfNotEmpty\nimport java.util.Random\n\n@Parcelize\n// @see https://youtrack.jetbrains.com/issue/KT-24842\n// @Parcelize throws IllegalAccessError if the primary constructor is private.\ndata class BookmarkDirectory internal constructor(\n    val id: Long,\n    val customName: String?,\n    val path: @WriteWith<ParcelableParceler> Path\n) : Parcelable {\n    // We cannot simply use path.hashCode() as ID because different bookmark directories may have\n    // the same path.\n    constructor(customName: String?, path: Path) : this(Random().nextLong(), customName, path)\n\n    val defaultName: String\n        get() = path.name\n\n    val name: String\n        get() = customName?.takeIfNotEmpty() ?: defaultName\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/navigation/EditBookmarkDirectoryDialogActivity.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.navigation\n\nimport android.os.Bundle\nimport android.view.View\nimport androidx.fragment.app.commit\nimport me.zhanghai.android.files.app.AppActivity\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.putArgs\n\nclass EditBookmarkDirectoryDialogActivity : AppActivity() {\n    private val args by args<EditBookmarkDirectoryDialogFragment.Args>()\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        // Calls ensureSubDecor().\n        findViewById<View>(android.R.id.content)\n        if (savedInstanceState == null) {\n            val fragment = EditBookmarkDirectoryDialogFragment().putArgs(args)\n            supportFragmentManager.commit {\n                add(fragment, EditBookmarkDirectoryDialogFragment::class.java.name)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/navigation/EditBookmarkDirectoryDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.navigation\n\nimport android.app.Dialog\nimport android.content.DialogInterface\nimport android.os.Bundle\nimport android.view.WindowManager\nimport androidx.appcompat.app.AppCompatDialogFragment\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport java8.nio.file.Path\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.databinding.EditBookmarkDirectoryDialogBinding\nimport me.zhanghai.android.files.filelist.FileListActivity\nimport me.zhanghai.android.files.filelist.toUserFriendlyString\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.ParcelableParceler\nimport me.zhanghai.android.files.util.ParcelableState\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.finish\nimport me.zhanghai.android.files.util.getState\nimport me.zhanghai.android.files.util.launchSafe\nimport me.zhanghai.android.files.util.layoutInflater\nimport me.zhanghai.android.files.util.putState\nimport me.zhanghai.android.files.util.setTextWithSelection\n\nclass EditBookmarkDirectoryDialogFragment : AppCompatDialogFragment() {\n    private val openPathLauncher =\n        registerForActivityResult(FileListActivity.OpenDirectoryContract(), ::onOpenPathResult)\n\n    private val args by args<Args>()\n\n    private lateinit var path: Path\n\n    private lateinit var binding: EditBookmarkDirectoryDialogBinding\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        path = savedInstanceState?.getState<State>()?.path ?: args.bookmarkDirectory.path\n    }\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =\n        MaterialAlertDialogBuilder(requireContext(), theme)\n            .setTitle(R.string.navigation_edit_bookmark_directory_title)\n            .apply {\n                binding = EditBookmarkDirectoryDialogBinding.inflate(context.layoutInflater)\n                val bookmarkDirectory = args.bookmarkDirectory\n                binding.nameLayout.placeholderText = bookmarkDirectory.defaultName\n                if (savedInstanceState == null) {\n                    binding.nameEdit.setTextWithSelection(bookmarkDirectory.name)\n                }\n                updatePathText()\n                binding.pathText.setOnClickListener { onEditPath() }\n                setView(binding.root)\n            }\n            .setPositiveButton(android.R.string.ok) { _, _ -> save() }\n            .setNegativeButton(android.R.string.cancel) { dialog, _ -> dialog.cancel() }\n            .setNeutralButton(R.string.remove) { _, _ -> remove() }\n            .create()\n            .apply {\n                window!!.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)\n            }\n\n    override fun onSaveInstanceState(outState: Bundle) {\n        super.onSaveInstanceState(outState)\n\n        outState.putState(State(path))\n    }\n\n    private fun onEditPath() {\n        openPathLauncher.launchSafe(path, this)\n    }\n\n    private fun onOpenPathResult(result: Path?) {\n        result ?: return\n        path = result\n        updatePathText()\n    }\n\n    private fun updatePathText() {\n        binding.pathText.setText(path.toUserFriendlyString())\n    }\n\n    private fun save() {\n        val customName = binding.nameEdit.text.toString()\n            .takeIf { it.isNotEmpty() && it != binding.nameLayout.placeholderText }\n        val bookmarkDirectory = args.bookmarkDirectory.copy(customName = customName, path = path)\n        BookmarkDirectories.replace(bookmarkDirectory)\n        finish()\n    }\n\n    private fun remove() {\n        BookmarkDirectories.remove(args.bookmarkDirectory)\n        finish()\n    }\n\n    override fun onCancel(dialog: DialogInterface) {\n        super.onCancel(dialog)\n\n        finish()\n    }\n\n    @Parcelize\n    class Args(val bookmarkDirectory: BookmarkDirectory) : ParcelableArgs\n\n    @Parcelize\n    private class State(var path: @WriteWith<ParcelableParceler> Path) : ParcelableState\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/navigation/NavigationFragment.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.navigation\n\nimport android.content.Intent\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.fragment.app.Fragment\nimport androidx.lifecycle.LifecycleOwner\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.databinding.NavigationFragmentBinding\nimport me.zhanghai.android.files.util.startActivitySafe\n\nclass NavigationFragment : Fragment(), NavigationItem.Listener {\n    private lateinit var binding: NavigationFragmentBinding\n\n    private lateinit var adapter: NavigationListAdapter\n\n    lateinit var listener: Listener\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View =\n        NavigationFragmentBinding.inflate(inflater, container, false)\n            .also { binding = it }\n            .root\n\n    override fun onActivityCreated(savedInstanceState: Bundle?) {\n        super.onActivityCreated(savedInstanceState)\n\n        binding.recyclerView.setHasFixedSize(true)\n        // TODO: Needed?\n        //binding.recyclerView.setItemAnimator(new NoChangeAnimationItemAnimator())\n        val context = requireContext()\n        binding.recyclerView.layoutManager = LinearLayoutManager(context)\n        adapter = NavigationListAdapter(this, context)\n        binding.recyclerView.adapter = adapter\n\n        val viewLifecycleOwner = viewLifecycleOwner\n        NavigationItemListLiveData.observe(viewLifecycleOwner) { onNavigationItemsChanged(it) }\n        listener.observeCurrentPath(viewLifecycleOwner) { onCurrentPathChanged(it) }\n    }\n\n    private fun onNavigationItemsChanged(navigationItems: List<NavigationItem?>) {\n        adapter.replace(navigationItems)\n    }\n\n    private fun onCurrentPathChanged(path: Path) {\n        adapter.notifyCheckedChanged()\n    }\n\n    override val currentPath: Path\n        get() = listener.currentPath\n\n    override fun navigateTo(path: Path) {\n        listener.navigateTo(path)\n    }\n\n    override fun navigateToRoot(path: Path) {\n        listener.navigateToRoot(path)\n    }\n\n    override fun launchIntent(intent: Intent) {\n        startActivitySafe(intent)\n    }\n\n    override fun closeNavigationDrawer() {\n        listener.closeNavigationDrawer()\n    }\n\n    interface Listener {\n        val currentPath: Path\n        fun navigateTo(path: Path)\n        fun navigateToRoot(path: Path)\n        fun navigateToDefaultRoot()\n        fun observeCurrentPath(owner: LifecycleOwner, observer: (Path) -> Unit)\n        fun closeNavigationDrawer()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/navigation/NavigationItem.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.navigation\n\nimport android.content.Context\nimport android.content.Intent\nimport android.graphics.drawable.Drawable\nimport androidx.annotation.DrawableRes\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.compat.getDrawableCompat\n\nabstract class NavigationItem {\n    abstract val id: Long\n\n    fun getIcon(context: Context): Drawable = context.getDrawableCompat(iconRes!!)\n\n    @get:DrawableRes\n    protected abstract val iconRes: Int?\n\n    abstract fun getTitle(context: Context): String\n\n    open fun getSubtitle(context: Context): String? = null\n\n    open fun isChecked(listener: Listener): Boolean = false\n\n    abstract fun onClick(listener: Listener)\n\n    open fun onLongClick(listener: Listener): Boolean = false\n\n    interface Listener {\n        val currentPath: Path\n        fun navigateTo(path: Path)\n        fun navigateToRoot(path: Path)\n        fun launchIntent(intent: Intent)\n        fun closeNavigationDrawer()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/navigation/NavigationItemListLiveData.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.navigation\n\nimport androidx.lifecycle.MediatorLiveData\nimport me.zhanghai.android.files.settings.Settings\nimport me.zhanghai.android.files.storage.StorageVolumeListLiveData\n\nobject NavigationItemListLiveData : MediatorLiveData<List<NavigationItem?>>() {\n    init {\n        // Initialize value before we have any active observer.\n        loadValue()\n        addSource(Settings.STORAGES) { loadValue() }\n        addSource(StorageVolumeListLiveData) { loadValue() }\n        addSource(StandardDirectoriesLiveData) { loadValue() }\n        addSource(Settings.BOOKMARK_DIRECTORIES) { loadValue() }\n    }\n\n    private fun loadValue() {\n        value = navigationItems\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/navigation/NavigationItems.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.navigation\n\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Build\nimport android.os.Environment\nimport android.os.storage.StorageVolume\nimport androidx.annotation.DrawableRes\nimport androidx.annotation.Size\nimport androidx.annotation.StringRes\nimport java8.nio.file.Path\nimport java8.nio.file.Paths\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.about.AboutActivity\nimport me.zhanghai.android.files.compat.getDescriptionCompat\nimport me.zhanghai.android.files.compat.isPrimaryCompat\nimport me.zhanghai.android.files.compat.pathCompat\nimport me.zhanghai.android.files.file.JavaFile\nimport me.zhanghai.android.files.file.asFileSize\nimport me.zhanghai.android.files.ftpserver.FtpServerActivity\nimport me.zhanghai.android.files.settings.Settings\nimport me.zhanghai.android.files.settings.SettingsActivity\nimport me.zhanghai.android.files.settings.StandardDirectoryListActivity\nimport me.zhanghai.android.files.storage.AddStorageDialogActivity\nimport me.zhanghai.android.files.storage.FileSystemRoot\nimport me.zhanghai.android.files.storage.Storage\nimport me.zhanghai.android.files.storage.StorageVolumeListLiveData\nimport me.zhanghai.android.files.util.createIntent\nimport me.zhanghai.android.files.util.isMounted\nimport me.zhanghai.android.files.util.putArgs\nimport me.zhanghai.android.files.util.supportsExternalStorageManager\nimport me.zhanghai.android.files.util.valueCompat\n\nval navigationItems: List<NavigationItem?>\n    get() =\n        mutableListOf<NavigationItem?>().apply {\n            addAll(storageItems)\n            if (Environment::class.supportsExternalStorageManager()) {\n                // Starting with R, we can get read/write access to non-primary storage volumes with\n                // MANAGE_EXTERNAL_STORAGE. However before R, we only have read-only access to them\n                // and need to use the Storage Access Framework instead, so hide them in this case\n                // to avoid confusion.\n                addAll(storageVolumeItems)\n            }\n            add(AddStorageItem())\n            val standardDirectoryItems = standardDirectoryItems\n            if (standardDirectoryItems.isNotEmpty()) {\n                add(null)\n                addAll(standardDirectoryItems)\n            }\n            val bookmarkDirectoryItems = bookmarkDirectoryItems\n            if (bookmarkDirectoryItems.isNotEmpty()) {\n                add(null)\n                addAll(bookmarkDirectoryItems)\n            }\n            add(null)\n            addAll(menuItems)\n        }\n\nprivate val storageItems: List<NavigationItem>\n    @Size(min = 0)\n    get() =\n        Settings.STORAGES.valueCompat.filter { it.isVisible }.map {\n            if (it.path != null) PathStorageItem(it) else IntentStorageItem(it)\n        }\n\nprivate abstract class PathItem(val path: Path) : NavigationItem() {\n    override fun isChecked(listener: Listener): Boolean = listener.currentPath == path\n\n    override fun onClick(listener: Listener) {\n        if (this is NavigationRoot) {\n            listener.navigateToRoot(path)\n        } else {\n            listener.navigateTo(path)\n        }\n        listener.closeNavigationDrawer()\n    }\n}\n\nprivate class PathStorageItem(\n    private val storage: Storage\n) : PathItem(storage.path!!), NavigationRoot {\n    init {\n        require(storage.isVisible)\n    }\n\n    override val id: Long\n        get() = storage.id\n\n    override val iconRes: Int\n        @DrawableRes\n        get() = storage.iconRes\n\n    override fun getTitle(context: Context): String = storage.getName(context)\n\n    override fun getSubtitle(context: Context): String? =\n        storage.linuxPath?.let { getStorageSubtitle(it, context) }\n\n    override fun onLongClick(listener: Listener): Boolean {\n        listener.launchIntent(storage.createEditIntent())\n        return true\n    }\n\n    override fun getName(context: Context): String = getTitle(context)\n}\n\nprivate class IntentStorageItem(\n    private val storage: Storage\n) : NavigationItem() {\n    init {\n        require(storage.isVisible)\n    }\n\n    override val id: Long\n        get() = storage.id\n\n    override val iconRes: Int\n        @DrawableRes\n        get() = storage.iconRes\n\n    override fun getTitle(context: Context): String = storage.getName(context)\n\n    override fun onClick(listener: Listener) {\n        listener.launchIntent(storage.createIntent()!!)\n        listener.closeNavigationDrawer()\n    }\n\n    override fun onLongClick(listener: Listener): Boolean {\n        listener.launchIntent(storage.createEditIntent())\n        return true\n    }\n}\n\nprivate val storageVolumeItems: List<NavigationItem>\n    @Size(min = 0)\n    get() =\n        StorageVolumeListLiveData.valueCompat.filter { !it.isPrimaryCompat && it.isMounted }\n            .map { StorageVolumeItem(it) }\n\nprivate class StorageVolumeItem(\n    private val storageVolume: StorageVolume\n) : PathItem(Paths.get(storageVolume.pathCompat)), NavigationRoot {\n    override val id: Long\n        get() = storageVolume.hashCode().toLong()\n\n    override val iconRes: Int\n        @DrawableRes\n        get() = R.drawable.sd_card_icon_white_24dp\n\n    override fun getTitle(context: Context): String = storageVolume.getDescriptionCompat(context)\n\n    override fun getSubtitle(context: Context): String? =\n        getStorageSubtitle(storageVolume.pathCompat, context)\n\n    override fun getName(context: Context): String = getTitle(context)\n}\n\nprivate fun getStorageSubtitle(linuxPath: String, context: Context): String? {\n    var totalSpace = JavaFile.getTotalSpace(linuxPath)\n    val freeSpace: Long\n    when {\n        totalSpace != 0L -> freeSpace = JavaFile.getFreeSpace(linuxPath)\n        linuxPath == FileSystemRoot.LINUX_PATH -> {\n            // Root directory may not be an actual partition on legacy Android versions (can be\n            // a ramdisk instead). On modern Android the system partition will be mounted as\n            // root instead so let's try with the system partition again.\n            // @see https://source.android.com/devices/bootloader/system-as-root\n            val systemPath = Environment.getRootDirectory().path\n            totalSpace = JavaFile.getTotalSpace(systemPath)\n            freeSpace = JavaFile.getFreeSpace(systemPath)\n        }\n        else -> freeSpace = 0\n    }\n    if (totalSpace == 0L) {\n        return null\n    }\n    val freeSpaceString = freeSpace.asFileSize().formatHumanReadable(context)\n    val totalSpaceString = totalSpace.asFileSize().formatHumanReadable(context)\n    return context.getString(\n        R.string.navigation_storage_subtitle_format, freeSpaceString, totalSpaceString\n    )\n}\n\nprivate class AddStorageItem : NavigationItem() {\n    override val id: Long = R.string.navigation_add_storage.toLong()\n\n    @DrawableRes\n    override val iconRes: Int = R.drawable.add_icon_white_24dp\n\n    override fun getTitle(context: Context): String =\n        context.getString(R.string.navigation_add_storage)\n\n    override fun onClick(listener: Listener) {\n        listener.launchIntent(AddStorageDialogActivity::class.createIntent())\n    }\n}\n\nprivate val standardDirectoryItems: List<NavigationItem>\n    @Size(min = 0)\n    get() =\n        StandardDirectoriesLiveData.valueCompat\n            .filter { it.isEnabled }\n            .map { StandardDirectoryItem(it) }\n\nprivate class StandardDirectoryItem(\n    private val standardDirectory: StandardDirectory\n) : PathItem(Paths.get(getExternalStorageDirectory(standardDirectory.relativePath))) {\n    init {\n        require(standardDirectory.isEnabled)\n    }\n\n    override val id: Long\n        get() = standardDirectory.id\n\n    override val iconRes: Int\n        @DrawableRes\n        get() = standardDirectory.iconRes\n\n    override fun getTitle(context: Context): String = standardDirectory.getTitle(context)\n\n    override fun onLongClick(listener: Listener): Boolean {\n        listener.launchIntent(StandardDirectoryListActivity::class.createIntent())\n        return true\n    }\n}\n\nval standardDirectories: List<StandardDirectory>\n    get() {\n        val settingsMap = Settings.STANDARD_DIRECTORY_SETTINGS.valueCompat.associateBy { it.id }\n        return defaultStandardDirectories.map {\n            val settings = settingsMap[it.key]\n            if (settings != null) it.withSettings(settings) else it\n        }\n    }\n\nprivate const val relativePathSeparator = \":\"\n\nprivate val defaultStandardDirectories: List<StandardDirectory>\n    // HACK: Show QQ, TIM and WeChat standard directories based on whether the directory exists.\n    get() =\n        DEFAULT_STANDARD_DIRECTORIES.mapNotNull {\n            when (it.iconRes) {\n                R.drawable.qq_icon_white_24dp, R.drawable.tim_icon_white_24dp,\n                R.drawable.wechat_icon_white_24dp -> {\n                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {\n                        // Direct access to Android/data is blocked since Android 11.\n                        null\n                    } else {\n                        for (relativePath in it.relativePath.split(relativePathSeparator)) {\n                            val path = getExternalStorageDirectory(relativePath)\n                            if (JavaFile.isDirectory(path)) {\n                                return@mapNotNull it.copy(relativePath = relativePath)\n                            }\n                        }\n                        null\n                    }\n                }\n                else -> it\n            }\n        }\n\n// @see android.os.Environment#STANDARD_DIRECTORIES\nprivate val DEFAULT_STANDARD_DIRECTORIES = listOf(\n    StandardDirectory(\n        R.drawable.alarm_icon_white_24dp, R.string.navigation_standard_directory_alarms,\n        Environment.DIRECTORY_ALARMS, false\n    ),\n    StandardDirectory(\n        R.drawable.camera_icon_white_24dp, R.string.navigation_standard_directory_dcim,\n        Environment.DIRECTORY_DCIM, true\n    ),\n    StandardDirectory(\n        R.drawable.document_icon_white_24dp, R.string.navigation_standard_directory_documents,\n        Environment.DIRECTORY_DOCUMENTS, false),\n    StandardDirectory(\n        R.drawable.download_icon_white_24dp, R.string.navigation_standard_directory_downloads,\n        Environment.DIRECTORY_DOWNLOADS, true\n    ),\n    StandardDirectory(\n        R.drawable.video_icon_white_24dp, R.string.navigation_standard_directory_movies,\n        Environment.DIRECTORY_MOVIES, true\n    ),\n    StandardDirectory(\n        R.drawable.audio_icon_white_24dp, R.string.navigation_standard_directory_music,\n        Environment.DIRECTORY_MUSIC, true\n    ),\n    StandardDirectory(\n        R.drawable.notification_icon_white_24dp,\n        R.string.navigation_standard_directory_notifications, Environment.DIRECTORY_NOTIFICATIONS,\n        false\n    ),\n    StandardDirectory(\n        R.drawable.image_icon_white_24dp, R.string.navigation_standard_directory_pictures,\n        Environment.DIRECTORY_PICTURES, true\n    ),\n    StandardDirectory(\n        R.drawable.podcast_icon_white_24dp, R.string.navigation_standard_directory_podcasts,\n        Environment.DIRECTORY_PODCASTS, false\n    ),\n    StandardDirectory(\n        R.drawable.ringtone_icon_white_24dp, R.string.navigation_standard_directory_ringtones,\n        Environment.DIRECTORY_RINGTONES, false\n    ),\n    StandardDirectory(\n        R.drawable.qq_icon_white_24dp, R.string.navigation_standard_directory_qq,\n        listOf(\"Android/data/com.tencent.mobileqq/Tencent/QQfile_recv\", \"Tencent/QQfile_recv\")\n            .joinToString(relativePathSeparator), true\n    ),\n    StandardDirectory(\n        R.drawable.tim_icon_white_24dp, R.string.navigation_standard_directory_tim,\n        listOf(\"Android/data/com.tencent.tim/Tencent/TIMfile_recv\", \"Tencent/TIMfile_recv\")\n            .joinToString(relativePathSeparator), true\n    ),\n    StandardDirectory(\n        R.drawable.wechat_icon_white_24dp, R.string.navigation_standard_directory_wechat,\n        listOf(\"Android/data/com.tencent.mm/MicroMsg/Download\", \"Tencent/MicroMsg/Download\")\n            .joinToString(relativePathSeparator), true\n    )\n)\n\ninternal fun getExternalStorageDirectory(relativePath: String): String =\n    @Suppress(\"DEPRECATION\")\n    Environment.getExternalStoragePublicDirectory(relativePath).path\n\nprivate val bookmarkDirectoryItems: List<NavigationItem>\n    @Size(min = 0)\n    get() = Settings.BOOKMARK_DIRECTORIES.valueCompat.map { BookmarkDirectoryItem(it) }\n\nprivate class BookmarkDirectoryItem(\n    private val bookmarkDirectory: BookmarkDirectory\n) : PathItem(bookmarkDirectory.path) {\n    // We cannot simply use super.getId() because different bookmark directories may have\n    // the same path.\n    override val id: Long\n        get() = bookmarkDirectory.id\n\n    @DrawableRes\n    override val iconRes: Int = R.drawable.directory_icon_white_24dp\n\n    override fun getTitle(context: Context): String = bookmarkDirectory.name\n\n    override fun onLongClick(listener: Listener): Boolean {\n        listener.launchIntent(\n            EditBookmarkDirectoryDialogActivity::class.createIntent()\n                .putArgs(EditBookmarkDirectoryDialogFragment.Args(bookmarkDirectory))\n        )\n        return true\n    }\n}\n\nprivate val menuItems: List<NavigationItem>\n    @Size(3)\n    get() = listOf(\n        IntentMenuItem(\n            R.drawable.shared_directory_icon_white_24dp, R.string.navigation_ftp_server,\n            FtpServerActivity::class.createIntent()\n        ),\n        IntentMenuItem(\n            R.drawable.settings_icon_white_24dp, R.string.navigation_settings,\n            SettingsActivity::class.createIntent()\n        ),\n        IntentMenuItem(\n            R.drawable.about_icon_white_24dp, R.string.navigation_about,\n            AboutActivity::class.createIntent()\n        )\n    )\n\nprivate abstract class MenuItem(\n    @DrawableRes override val iconRes: Int,\n    @StringRes val titleRes: Int\n) : NavigationItem() {\n    override fun getTitle(context: Context): String = context.getString(titleRes)\n}\n\nprivate class IntentMenuItem(\n    @DrawableRes iconRes: Int,\n    @StringRes titleRes: Int,\n    private val intent: Intent\n) : MenuItem(iconRes, titleRes) {\n    override val id: Long\n        get() = intent.component.hashCode().toLong()\n\n    override fun onClick(listener: Listener) {\n        listener.launchIntent(intent)\n        listener.closeNavigationDrawer()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/navigation/NavigationListAdapter.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.navigation\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport android.content.res.ColorStateList\nimport android.graphics.Color\nimport android.graphics.drawable.Drawable\nimport android.graphics.drawable.InsetDrawable\nimport android.graphics.drawable.RippleDrawable\nimport android.util.TypedValue\nimport android.view.ViewGroup\nimport androidx.annotation.Px\nimport androidx.annotation.StyleRes\nimport androidx.core.content.res.ResourcesCompat\nimport androidx.core.view.updateLayoutParams\nimport androidx.core.view.updatePaddingRelative\nimport androidx.recyclerview.widget.RecyclerView\nimport com.google.android.material.shape.MaterialShapeDrawable\nimport com.google.android.material.shape.ShapeAppearanceModel\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.compat.foregroundCompat\nimport me.zhanghai.android.files.compat.obtainStyledAttributesCompat\nimport me.zhanghai.android.files.compat.setTextAppearanceCompat\nimport me.zhanghai.android.files.compat.use\nimport me.zhanghai.android.files.databinding.NavigationDividerItemBinding\nimport me.zhanghai.android.files.databinding.NavigationItemBinding\nimport me.zhanghai.android.files.ui.AutoMirrorDrawable\nimport me.zhanghai.android.files.ui.SimpleAdapter\nimport me.zhanghai.android.files.util.getColorStateListByAttr\nimport me.zhanghai.android.files.util.layoutInflater\n\nclass NavigationListAdapter(\n    private val listener: NavigationItem.Listener,\n    context: Context\n) : SimpleAdapter<NavigationItem?, RecyclerView.ViewHolder>() {\n    @SuppressLint(\"PrivateResource\", \"RestrictedApi\")\n    private val viewAttributes = context.obtainStyledAttributesCompat(\n        null, com.google.android.material.R.styleable.NavigationView,\n        com.google.android.material.R.attr.navigationViewStyle,\n        com.google.android.material.R.style.Widget_MaterialComponents_NavigationView\n    ).use { a ->\n        val itemShapeAppearance = a.getResourceId(\n            com.google.android.material.R.styleable.NavigationView_itemShapeAppearance, 0\n        )\n        val itemShapeAppearanceOverlay = a.getResourceId(\n            com.google.android.material.R.styleable.NavigationView_itemShapeAppearanceOverlay, 0\n        )\n        val itemShapeFillColor = a.getColorStateList(\n            com.google.android.material.R.styleable.NavigationView_itemShapeFillColor\n        )\n        val itemShapeInsetStart = a.getDimensionPixelSize(\n            com.google.android.material.R.styleable.NavigationView_itemShapeInsetStart, 0\n        )\n        val itemShapeInsetEnd = a.getDimensionPixelSize(\n            com.google.android.material.R.styleable.NavigationView_itemShapeInsetEnd, 0\n        )\n        val itemShapeInsetTop = a.getDimensionPixelSize(\n            com.google.android.material.R.styleable.NavigationView_itemShapeInsetTop, 0\n        )\n        val itemShapeInsetBottom = a.getDimensionPixelSize(\n            com.google.android.material.R.styleable.NavigationView_itemShapeInsetBottom, 0\n        )\n        val itemBackground = createItemShapeDrawable(\n            itemShapeAppearance, itemShapeAppearanceOverlay, itemShapeFillColor,\n            itemShapeInsetStart, itemShapeInsetEnd, itemShapeInsetTop, itemShapeInsetBottom, context\n        )\n        val controlHighlightColor = context.getColorStateListByAttr(\n            com.google.android.material.R.attr.colorControlHighlight\n        )\n        val itemForegroundMaskFillColor = ColorStateList.valueOf(Color.WHITE)\n        val itemForegroundMask = createItemShapeDrawable(\n            itemShapeAppearance, itemShapeAppearanceOverlay, itemForegroundMaskFillColor,\n            itemShapeInsetStart, itemShapeInsetEnd, itemShapeInsetTop, itemShapeInsetBottom, context\n        )\n        val itemForeground = RippleDrawable(controlHighlightColor, null, itemForegroundMask)\n        context.obtainStyledAttributesCompat(\n            null, R.styleable.NavigationViewExtra,\n            com.google.android.material.R.attr.navigationViewStyle, 0\n        ).use { a2 ->\n            ViewAttributes(\n                a.getDimensionPixelSize(\n                    com.google.android.material.R.styleable.NavigationView_itemHorizontalPadding, 0\n                ),\n                a.getDimensionPixelSize(\n                    com.google.android.material.R.styleable.NavigationView_itemVerticalPadding, 0\n                ),\n                itemBackground,\n                itemForeground,\n                a.getDimensionPixelSize(\n                    com.google.android.material.R.styleable.NavigationView_itemIconSize, 0\n                ),\n                a.getColorStateList(\n                    com.google.android.material.R.styleable.NavigationView_itemIconTint\n                ),\n                a.getDimensionPixelSize(\n                    com.google.android.material.R.styleable.NavigationView_itemIconPadding, 0\n                ),\n                a.getResourceId(\n                    com.google.android.material.R.styleable.NavigationView_itemTextAppearance,\n                    ResourcesCompat.ID_NULL\n                ),\n                a.getColorStateList(\n                    com.google.android.material.R.styleable.NavigationView_itemTextColor\n                ),\n                a2.getResourceId(\n                    R.styleable.NavigationViewExtra_itemSubtitleTextAppearance,\n                    ResourcesCompat.ID_NULL\n                ),\n                a2.getColorStateList(R.styleable.NavigationViewExtra_itemSubtitleTextColor),\n                a2.getDimension(R.styleable.NavigationViewExtra_itemSubtitleTextSize, 0f),\n                a.getDimensionPixelSize(\n                    com.google.android.material.R.styleable.NavigationView_dividerInsetStart, 0\n                ),\n                a.getDimensionPixelSize(\n                    com.google.android.material.R.styleable.NavigationView_dividerInsetEnd, 0\n                ),\n                a2.getDimensionPixelSize(R.styleable.NavigationViewExtra_dividerVerticalPadding, 0)\n            )\n        }\n    }\n\n    // @see com.google.android.material.navigation.NavigationView#createDefaultItemBackground\n    private fun createItemShapeDrawable(\n        @StyleRes shapeAppearance: Int,\n        @StyleRes shapeAppearanceOverlay: Int,\n        fillColor: ColorStateList?,\n        @Px insetStart: Int,\n        @Px insetEnd: Int,\n        @Px insetTop: Int,\n        @Px insetBottom: Int,\n        context: Context\n    ): Drawable {\n        val shapeAppearanceModel =\n            ShapeAppearanceModel.builder(context, shapeAppearance, shapeAppearanceOverlay).build()\n        val materialShapeDrawable = MaterialShapeDrawable(shapeAppearanceModel)\n            .apply { this.fillColor = fillColor }\n        return AutoMirrorDrawable(\n            InsetDrawable(materialShapeDrawable, insetStart, insetTop, insetEnd, insetBottom)\n        )\n    }\n\n    fun notifyCheckedChanged() {\n        notifyItemRangeChanged(0, itemCount, PAYLOAD_CHECKED_CHANGED)\n    }\n\n    override val hasStableIds: Boolean\n        get() = true\n\n    override fun getItemId(position: Int): Long =\n        getItem(position)?.id ?: list.subList(0, position).count { it == null }.toLong()\n\n    override fun getItemViewType(position: Int): Int {\n        val viewType = if (getItem(position) != null) ViewType.ITEM else ViewType.DIVIDER\n        return viewType.ordinal\n    }\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {\n        return when (ViewType.entries[viewType]) {\n            ViewType.ITEM ->\n                ItemHolder(\n                    NavigationItemBinding.inflate(parent.context.layoutInflater, parent, false)\n                ).apply {\n                    binding.itemLayout.updatePaddingRelative(\n                        viewAttributes.itemHorizontalPadding,\n                        viewAttributes.itemVerticalPadding,\n                        viewAttributes.itemHorizontalPadding,\n                        viewAttributes.itemVerticalPadding\n                    )\n                    binding.itemLayout.background =\n                        viewAttributes.itemBackground?.constantState?.newDrawable()\n                    binding.itemLayout.foregroundCompat =\n                        viewAttributes.itemForeground?.constantState?.newDrawable()\n                    binding.iconImage.updateLayoutParams {\n                        width = viewAttributes.itemIconSize\n                        height = viewAttributes.itemIconSize\n                    }\n                    binding.iconImage.imageTintList = viewAttributes.itemIconTint\n                    binding.textLayout.updateLayoutParams<ViewGroup.MarginLayoutParams> {\n                        marginStart = viewAttributes.itemIconPadding\n                    }\n                    if (viewAttributes.itemTextAppearance != ResourcesCompat.ID_NULL) {\n                        binding.titleText.setTextAppearanceCompat(viewAttributes.itemTextAppearance)\n                    }\n                    binding.titleText.setTextColor(viewAttributes.itemTextColor)\n                    if (viewAttributes.itemSubtitleTextAppearance != ResourcesCompat.ID_NULL) {\n                        binding.subtitleText.setTextAppearanceCompat(\n                            viewAttributes.itemSubtitleTextAppearance\n                        )\n                    }\n                    binding.subtitleText.setTextSize(\n                        TypedValue.COMPLEX_UNIT_PX, viewAttributes.itemSubtitleTextSize\n                    )\n                    binding.subtitleText.setTextColor(viewAttributes.itemSubtitleTextColor)\n                }\n            ViewType.DIVIDER ->\n                DividerHolder(\n                    NavigationDividerItemBinding.inflate(\n                        parent.context.layoutInflater, parent, false\n                    )\n                ).apply {\n                    binding.root.updatePaddingRelative(\n                        viewAttributes.dividerInsetStart,\n                        viewAttributes.dividerVerticalPadding,\n                        viewAttributes.dividerInsetEnd,\n                        viewAttributes.dividerVerticalPadding\n                    )\n                }\n        }\n    }\n\n    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {\n        throw UnsupportedOperationException()\n    }\n\n    override fun onBindViewHolder(\n        holder: RecyclerView.ViewHolder,\n        position: Int,\n        payloads: List<Any>\n    ) {\n        when (ViewType.entries[getItemViewType(position)]) {\n            ViewType.ITEM -> {\n                val item = getItem(position)!!\n                val binding = (holder as ItemHolder).binding\n                binding.itemLayout.isChecked = item.isChecked(listener)\n                if (payloads.isNotEmpty()) {\n                    return\n                }\n                binding.itemLayout.setOnClickListener { item.onClick(listener) }\n                binding.itemLayout.setOnLongClickListener { item.onLongClick(listener) }\n                binding.iconImage.setImageDrawable(item.getIcon(binding.iconImage.context))\n                binding.titleText.text = item.getTitle(binding.titleText.context)\n                binding.subtitleText.text = item.getSubtitle(binding.subtitleText.context)\n            }\n            ViewType.DIVIDER -> {}\n        }\n    }\n\n    companion object {\n        private val PAYLOAD_CHECKED_CHANGED = Any()\n    }\n\n    private class ViewAttributes(\n        @Px val itemHorizontalPadding: Int,\n        @Px val itemVerticalPadding: Int,\n        val itemBackground: Drawable?,\n        val itemForeground: Drawable?,\n        @Px val itemIconSize: Int,\n        val itemIconTint: ColorStateList?,\n        @Px val itemIconPadding: Int,\n        @StyleRes val itemTextAppearance: Int,\n        val itemTextColor: ColorStateList?,\n        @StyleRes val itemSubtitleTextAppearance: Int,\n        val itemSubtitleTextColor: ColorStateList?,\n        @Px val itemSubtitleTextSize: Float,\n        @Px val dividerInsetStart: Int,\n        @Px val dividerInsetEnd: Int,\n        @Px val dividerVerticalPadding: Int\n    )\n\n    private enum class ViewType {\n        ITEM,\n        DIVIDER\n    }\n\n    private class ItemHolder(val binding: NavigationItemBinding) : RecyclerView.ViewHolder(\n        binding.root\n    )\n\n    private class DividerHolder(\n        val binding: NavigationDividerItemBinding\n    ) : RecyclerView.ViewHolder(binding.root)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/navigation/NavigationRecyclerView.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.navigation\n\nimport android.content.Context\nimport android.graphics.Canvas\nimport android.graphics.Color\nimport android.util.AttributeSet\nimport android.view.WindowInsets\nimport androidx.annotation.AttrRes\nimport androidx.core.graphics.withSave\nimport androidx.core.view.updatePadding\nimport androidx.recyclerview.widget.RecyclerView\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.util.activity\nimport me.zhanghai.android.files.util.displayWidth\nimport me.zhanghai.android.files.util.getDimensionPixelSize\nimport me.zhanghai.android.files.util.getDimensionPixelSizeByAttr\nimport me.zhanghai.android.files.util.getDrawableByAttr\nimport me.zhanghai.android.files.util.isLayoutDirectionRtl\n\nclass NavigationRecyclerView : RecyclerView {\n    private val verticalPadding = context.getDimensionPixelSize(\n        com.google.android.material.R.dimen.design_navigation_padding_bottom\n    )\n    private val actionBarSize =\n        context.getDimensionPixelSizeByAttr(androidx.appcompat.R.attr.actionBarSize)\n    private val maxWidth = context.getDimensionPixelSize(R.dimen.navigation_max_width)\n    private var scrim = context.getDrawableByAttr(android.R.attr.statusBarColor)\n\n    private var insetStart = 0\n    private var insetTop = 0\n\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(\n        context: Context,\n        attrs: AttributeSet?,\n        @AttrRes defStyleAttr: Int\n    ) : super(context, attrs, defStyleAttr)\n\n    init {\n        updatePadding(top = verticalPadding, bottom = verticalPadding)\n        fitsSystemWindows = true\n        setWillNotDraw(false)\n    }\n\n    override fun onMeasure(widthSpec: Int, heightSpec: Int) {\n        var widthSpec = widthSpec\n        var width = (context.displayWidth - actionBarSize).coerceIn(0..insetStart + maxWidth)\n        when (MeasureSpec.getMode(widthSpec)) {\n            MeasureSpec.AT_MOST -> {\n                width = width.coerceAtMost(MeasureSpec.getSize(widthSpec))\n                widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY)\n            }\n            MeasureSpec.UNSPECIFIED ->\n                widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY)\n            MeasureSpec.EXACTLY -> {}\n        }\n        super.onMeasure(widthSpec, heightSpec)\n    }\n\n    override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets {\n        val isLayoutDirectionRtl = isLayoutDirectionRtl\n        insetStart = if (isLayoutDirectionRtl) {\n            insets.systemWindowInsetRight\n        } else {\n            insets.systemWindowInsetLeft\n        }\n        val paddingLeft = if (isLayoutDirectionRtl) 0 else insetStart\n        val paddingRight = if (isLayoutDirectionRtl) insetStart else 0\n        insetTop = insets.systemWindowInsetTop\n        setPadding(\n            paddingLeft, verticalPadding + insetTop, paddingRight,\n            verticalPadding + insets.systemWindowInsetBottom\n        )\n        requestLayout()\n        return insets.replaceSystemWindowInsets(\n            insets.systemWindowInsetLeft - paddingLeft, 0,\n            insets.systemWindowInsetRight - paddingRight, 0\n        )\n    }\n\n    override fun draw(canvas: Canvas) {\n        super.draw(canvas)\n\n        if (context.activity!!.window.statusBarColor == Color.TRANSPARENT) {\n            canvas.withSave {\n                canvas.translate(scrollX.toFloat(), scrollY.toFloat())\n                scrim.setBounds(0, 0, width, insetTop)\n                scrim.draw(canvas)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/navigation/NavigationRoot.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.navigation\n\nimport android.content.Context\nimport java8.nio.file.Path\n\ninterface NavigationRoot {\n    val path: Path\n\n    fun getName(context: Context): String\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/navigation/NavigationRootMapLiveData.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.navigation\n\nimport androidx.lifecycle.MediatorLiveData\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.util.valueCompat\n\nobject NavigationRootMapLiveData : MediatorLiveData<Map<Path, NavigationRoot>>() {\n    init {\n        // Initialize value before we have any active observer.\n        loadValue()\n        addSource(NavigationItemListLiveData) { loadValue() }\n    }\n\n    private fun loadValue() {\n        value = NavigationItemListLiveData.valueCompat\n            .mapNotNull { it as? NavigationRoot }\n            .associateBy { it.path }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/navigation/StandardDirectoriesLiveData.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.navigation\n\nimport androidx.lifecycle.MediatorLiveData\nimport me.zhanghai.android.files.settings.Settings\n\nobject StandardDirectoriesLiveData : MediatorLiveData<List<StandardDirectory>>() {\n    init {\n        // Initialize value before we have any active observer.\n        loadValue()\n        addSource(Settings.STANDARD_DIRECTORY_SETTINGS) { loadValue() }\n    }\n\n    private fun loadValue() {\n        value = standardDirectories\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/navigation/StandardDirectory.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.navigation\n\nimport android.content.Context\nimport androidx.annotation.DrawableRes\nimport androidx.annotation.StringRes\n\ndata class StandardDirectory internal constructor(\n    @DrawableRes val iconRes: Int,\n    @StringRes private val titleRes: Int,\n    private val customTitle: String?,\n    val relativePath: String,\n    val isEnabled: Boolean\n) {\n    constructor(\n        @DrawableRes iconRes: Int,\n        @StringRes titleRes: Int,\n        relativePath: String,\n        enabled: Boolean\n    ) : this(iconRes, titleRes, null, relativePath, enabled)\n\n    val id: Long\n        get() = relativePath.hashCode().toLong()\n\n    val key: String\n        get() = relativePath\n\n    fun getTitle(context: Context): String =\n        if (!customTitle.isNullOrEmpty()) customTitle else context.getString(titleRes)\n\n    fun withSettings(settings: StandardDirectorySettings): StandardDirectory =\n        StandardDirectory(iconRes, titleRes, settings.customTitle, relativePath, settings.isEnabled)\n\n    fun toSettings(): StandardDirectorySettings =\n        StandardDirectorySettings(relativePath, customTitle, isEnabled)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/navigation/StandardDirectorySettings.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.navigation\n\nimport android.os.Parcelable\nimport kotlinx.parcelize.Parcelize\n\n@Parcelize\ndata class StandardDirectorySettings(\n    val id: String,\n    val customTitle: String?,\n    val isEnabled: Boolean\n) : Parcelable\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/nonfree/CrashlyticsInitializer.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.nonfree\n\nimport android.annotation.SuppressLint\nimport android.content.pm.PackageManager\nimport android.content.pm.Signature\nimport android.os.Build\nimport com.google.firebase.crashlytics.FirebaseCrashlytics\nimport java.security.MessageDigest\nimport java.security.NoSuchAlgorithmException\nimport me.zhanghai.android.files.BuildConfig\nimport me.zhanghai.android.files.app.application\nimport me.zhanghai.android.files.app.packageManager\nimport me.zhanghai.android.files.util.getPackageInfoOrNull\n\nobject CrashlyticsInitializer {\n    private val HEX_CHARS = \"0123456789ABCDEF\".toCharArray()\n\n    fun initialize() {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {\n            return\n        }\n        if (BuildConfig.DEBUG) {\n            return\n        }\n        if (!verifyPackageName() || !verifySignature()) {\n            // Please, don't spam.\n            return\n        }\n        FirebaseCrashlytics.getInstance().isCrashlyticsCollectionEnabled = true\n    }\n\n    private fun verifyPackageName(): Boolean {\n        return application.packageName == \"me.zhanghai.android.files\"\n    }\n\n    @SuppressLint(\"PackageManagerGetSignatures\")\n    private fun verifySignature(): Boolean {\n        val packageInfo = packageManager.getPackageInfoOrNull(\n            application.packageName, PackageManager.GET_SIGNATURES\n        ) ?: return false\n        val signatures = packageInfo.signatures ?: return false\n        return signatures.size == 1 &&\n            computeCertificateFingerprint(signatures[0]) == \"87:3B:9B:60:C7:7C:F7:F3:CD:5F:AE:66\" +\n                \":D0:FE:11:2C:4A:86:97:3E:11:8E:E8:A2:9C:34:6C:4C:67:3C:97:F0\"\n    }\n\n    private fun computeCertificateFingerprint(certificate: Signature): String {\n        val messageDigest = try {\n            MessageDigest.getInstance(\"SHA-256\")\n        } catch (e: NoSuchAlgorithmException) {\n            throw AssertionError(e)\n        }\n        val digest = messageDigest.digest(certificate.toByteArray())\n        val chars = CharArray(3 * digest.size - 1)\n        for (index in digest.indices) {\n            val byte = digest[index].toInt() and 0xFF\n            chars[3 * index] = HEX_CHARS[byte ushr 4]\n            chars[3 * index + 1] = HEX_CHARS[byte and 0x0F]\n            if (index < digest.size - 1) {\n                chars[3 * index + 2] = ':'\n            }\n        }\n        return String(chars)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/FileSystemProviders.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider\n\nimport java8.nio.file.Files\nimport java8.nio.file.ProviderNotFoundException\nimport java8.nio.file.spi.FileSystemProvider\nimport me.zhanghai.android.files.provider.archive.ArchiveFileSystemProvider\nimport me.zhanghai.android.files.provider.common.AndroidFileTypeDetector\nimport me.zhanghai.android.files.provider.content.ContentFileSystemProvider\nimport me.zhanghai.android.files.provider.document.DocumentFileSystemProvider\nimport me.zhanghai.android.files.provider.ftp.FtpFileSystemProvider\nimport me.zhanghai.android.files.provider.ftp.FtpesFileSystemProvider\nimport me.zhanghai.android.files.provider.ftp.FtpsFileSystemProvider\nimport me.zhanghai.android.files.provider.linux.LinuxFileSystemProvider\nimport me.zhanghai.android.files.provider.root.isRunningAsRoot\nimport me.zhanghai.android.files.provider.sftp.SftpFileSystemProvider\nimport me.zhanghai.android.files.provider.smb.SmbFileSystemProvider\nimport me.zhanghai.android.files.provider.webdav.WebDavFileSystemProvider\nimport me.zhanghai.android.files.provider.webdav.WebDavsFileSystemProvider\n\nobject FileSystemProviders {\n    /**\n     * If set, WatchService implementations will skip processing any event data and simply send an\n     * overflow event to all the registered keys upon successful read from the inotify fd. This can\n     * help reducing the JNI and GC overhead when large amount of inotify events are generated.\n     * Simply sending an overflow event to all the keys is okay because we use only one key per\n     * service for WatchServicePathObservable.\n     */\n    @Volatile\n    var overflowWatchEvents = false\n\n    fun install() {\n        FileSystemProvider.installDefaultProvider(LinuxFileSystemProvider)\n        FileSystemProvider.installProvider(ArchiveFileSystemProvider)\n        if (!isRunningAsRoot) {\n            FileSystemProvider.installProvider(ContentFileSystemProvider)\n            FileSystemProvider.installProvider(DocumentFileSystemProvider)\n            FileSystemProvider.installProvider(FtpFileSystemProvider)\n            FileSystemProvider.installProvider(FtpsFileSystemProvider)\n            FileSystemProvider.installProvider(FtpesFileSystemProvider)\n            FileSystemProvider.installProvider(SftpFileSystemProvider)\n            FileSystemProvider.installProvider(SmbFileSystemProvider)\n            FileSystemProvider.installProvider(WebDavFileSystemProvider)\n            FileSystemProvider.installProvider(WebDavsFileSystemProvider)\n        }\n        Files.installFileTypeDetector(AndroidFileTypeDetector)\n    }\n\n    operator fun get(scheme: String): FileSystemProvider {\n        for (provider in FileSystemProvider.installedProviders()) {\n            if (provider.scheme.equals(scheme, ignoreCase = true)) {\n                return provider\n            }\n        }\n        throw ProviderNotFoundException(scheme)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/archive/ArchiveExceptionExtensions.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.archive\n\nimport android.system.OsConstants\nimport java8.nio.file.FileSystemException\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.provider.common.DelegateInputStream\nimport me.zhanghai.android.libarchive.ArchiveException\nimport java.io.IOException\nimport java.io.InputStream\nimport java.io.InterruptedIOException\n\n// See also libarchive/archive_platform.h .\nprivate const val ARCHIVE_ERRNO_MISC = -1\n\nfun ArchiveException.toFileSystemOrInterruptedIOException(file: Path): IOException =\n    when {\n        // See also ReadArchive.toArchiveException .\n        code == OsConstants.EINTR -> InterruptedIOException(message)\n        // See also libarchive/archive_read_support_format_zip.c .\n        code == ARCHIVE_ERRNO_MISC && (\n            message == \"Incorrect passphrase\" || message == \"Passphrase required for this entry\"\n        ) -> ArchivePasswordRequiredException(file, message)\n        else -> FileSystemException(file.toString(), null, message)\n    }.apply { initCause(this@toFileSystemOrInterruptedIOException) }\n\nclass ArchiveExceptionInputStream(\n    inputStream: InputStream,\n    private val file: Path\n) : DelegateInputStream(inputStream) {\n    @Throws(IOException::class)\n    override fun read(): Int =\n        try {\n            super.read()\n        } catch (e: ArchiveException) {\n            throw e.toFileSystemOrInterruptedIOException(file)\n        }\n\n    @Throws(IOException::class)\n    override fun read(b: ByteArray): Int =\n        try {\n            super.read(b)\n        } catch (e: ArchiveException) {\n            throw e.toFileSystemOrInterruptedIOException(file)\n        }\n\n    @Throws(IOException::class)\n    override fun read(b: ByteArray, off: Int, len: Int): Int =\n        try {\n            super.read(b, off, len)\n        } catch (e: ArchiveException) {\n            throw e.toFileSystemOrInterruptedIOException(file)\n        }\n\n    @Throws(IOException::class)\n    override fun skip(n: Long): Long = try {\n        super.skip(n)\n    } catch (e: ArchiveException) {\n        throw e.toFileSystemOrInterruptedIOException(file)\n    }\n\n    @Throws(IOException::class)\n    override fun available(): Int =\n        try {\n            super.available()\n        } catch (e: ArchiveException) {\n            throw e.toFileSystemOrInterruptedIOException(file)\n        }\n\n    @Throws(IOException::class)\n    override fun close() {\n        try {\n            super.close()\n        } catch (e: ArchiveException) {\n            throw e.toFileSystemOrInterruptedIOException(file)\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun reset() {\n        try {\n            super.reset()\n        } catch (e: ArchiveException) {\n            throw e.toFileSystemOrInterruptedIOException(file)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/archive/ArchiveFileAttributeView.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.archive\n\nimport java8.nio.file.Path\nimport java8.nio.file.attribute.FileTime\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.PosixFileAttributeView\nimport me.zhanghai.android.files.provider.common.PosixFileModeBit\nimport me.zhanghai.android.files.provider.common.PosixGroup\nimport me.zhanghai.android.files.provider.common.PosixUser\nimport java.io.IOException\n\ninternal class ArchiveFileAttributeView(private val path: Path) : PosixFileAttributeView {\n    override fun name(): String = NAME\n\n    @Throws(IOException::class)\n    override fun readAttributes(): ArchiveFileAttributes {\n        val fileSystem = path.fileSystem as ArchiveFileSystem\n        val entry = fileSystem.getEntry(path)\n        return ArchiveFileAttributes.from(fileSystem.archiveFile, entry)\n    }\n\n    override fun setTimes(\n        lastModifiedTime: FileTime?,\n        lastAccessTime: FileTime?,\n        createTime: FileTime?\n    ) {\n        throw UnsupportedOperationException()\n    }\n\n    override fun setOwner(owner: PosixUser) {\n        throw UnsupportedOperationException()\n    }\n\n    override fun setGroup(group: PosixGroup) {\n        throw UnsupportedOperationException()\n    }\n\n    override fun setMode(mode: Set<PosixFileModeBit>) {\n        throw UnsupportedOperationException()\n    }\n\n    override fun setSeLinuxContext(context: ByteString) {\n        throw UnsupportedOperationException()\n    }\n\n    override fun restoreSeLinuxContext() {\n        throw UnsupportedOperationException()\n    }\n\n    companion object {\n        private val NAME = ArchiveFileSystemProvider.scheme\n\n        val SUPPORTED_NAMES = setOf(\"basic\", \"posix\", NAME)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/archive/ArchiveFileAttributes.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.archive\n\nimport android.os.Parcelable\nimport java8.nio.file.Path\nimport java8.nio.file.attribute.FileTime\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.provider.archive.archiver.ReadArchive\nimport me.zhanghai.android.files.provider.common.AbstractPosixFileAttributes\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.EncryptedFileAttributes\nimport me.zhanghai.android.files.provider.common.FileTimeParceler\nimport me.zhanghai.android.files.provider.common.PosixFileModeBit\nimport me.zhanghai.android.files.provider.common.PosixFileType\nimport me.zhanghai.android.files.provider.common.PosixGroup\nimport me.zhanghai.android.files.provider.common.PosixUser\n\n@Parcelize\ninternal class ArchiveFileAttributes(\n    override val lastModifiedTime: @WriteWith<FileTimeParceler> FileTime,\n    override val lastAccessTime: @WriteWith<FileTimeParceler> FileTime,\n    override val creationTime: @WriteWith<FileTimeParceler> FileTime,\n    override val type: PosixFileType,\n    override val size: Long,\n    override val fileKey: Parcelable,\n    override val owner: PosixUser?,\n    override val group: PosixGroup?,\n    override val mode: Set<PosixFileModeBit>?,\n    override val seLinuxContext: ByteString?,\n    private val isEncrypted: Boolean,\n    private val entryName: String\n) : AbstractPosixFileAttributes(), EncryptedFileAttributes {\n    override fun isEncrypted(): Boolean = isEncrypted\n\n    fun entryName(): String = entryName\n\n    companion object {\n        fun from(archiveFile: Path, entry: ReadArchive.Entry): ArchiveFileAttributes {\n            val lastModifiedTime = entry.lastModifiedTime ?: FileTime.fromMillis(0)\n            val lastAccessTime = entry.lastAccessTime ?: lastModifiedTime\n            val creationTime = entry.creationTime ?: lastModifiedTime\n            val type = entry.type\n            val size = entry.size\n            val fileKey = ArchiveFileKey(archiveFile, entry.name)\n            val owner = entry.owner\n            val group = entry.group\n            val mode = entry.mode\n            val seLinuxContext = null\n            val isEncrypted = entry.isEncrypted\n            val entryName = entry.name\n            return ArchiveFileAttributes(\n                lastModifiedTime, lastAccessTime, creationTime, type, size, fileKey, owner, group,\n                mode, seLinuxContext, isEncrypted, entryName\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/archive/ArchiveFileKey.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.archive\n\nimport android.os.Parcelable\nimport java8.nio.file.Path\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.util.ParcelableParceler\n\n@Parcelize\ninternal data class ArchiveFileKey(\n    private val archiveFile: @WriteWith<ParcelableParceler> Path,\n    private val entryName: String\n) : Parcelable\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/archive/ArchiveFileStore.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.archive\n\nimport java8.nio.file.Path\nimport java8.nio.file.attribute.FileAttributeView\nimport me.zhanghai.android.files.file.MimeType\nimport me.zhanghai.android.files.file.guessFromPath\nimport me.zhanghai.android.files.provider.common.PosixFileStore\nimport me.zhanghai.android.files.provider.common.size\nimport java.io.IOException\n\ninternal class ArchiveFileStore(private val archiveFile: Path) : PosixFileStore() {\n    override fun refresh() {}\n\n    override fun name(): String = archiveFile.toString()\n\n    override fun type(): String = MimeType.guessFromPath(archiveFile.toString()).value\n\n    override fun isReadOnly(): Boolean = true\n\n    @Throws(IOException::class)\n    override fun setReadOnly(readOnly: Boolean) {\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun getTotalSpace(): Long = archiveFile.size()\n\n    override fun getUsableSpace(): Long = 0\n\n    override fun getUnallocatedSpace(): Long = 0\n\n    override fun supportsFileAttributeView(type: Class<out FileAttributeView>): Boolean =\n        ArchiveFileSystemProvider.supportsFileAttributeView(type)\n\n    override fun supportsFileAttributeView(name: String): Boolean =\n        name in ArchiveFileAttributeView.SUPPORTED_NAMES\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/archive/ArchiveFileSystem.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.archive\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport java8.nio.file.ClosedFileSystemException\nimport java8.nio.file.FileStore\nimport java8.nio.file.FileSystem\nimport java8.nio.file.NoSuchFileException\nimport java8.nio.file.NotDirectoryException\nimport java8.nio.file.NotLinkException\nimport java8.nio.file.Path\nimport java8.nio.file.PathMatcher\nimport java8.nio.file.WatchService\nimport java8.nio.file.attribute.UserPrincipalLookupService\nimport java8.nio.file.spi.FileSystemProvider\nimport me.zhanghai.android.files.provider.archive.archiver.ArchiveReader\nimport me.zhanghai.android.files.provider.archive.archiver.ReadArchive\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.ByteStringBuilder\nimport me.zhanghai.android.files.provider.common.ByteStringListPathCreator\nimport me.zhanghai.android.files.provider.common.IsDirectoryException\nimport me.zhanghai.android.files.provider.common.toByteString\nimport me.zhanghai.android.libarchive.ArchiveException\nimport java.io.IOException\nimport java.io.InputStream\n\ninternal class ArchiveFileSystem(\n    private val provider: ArchiveFileSystemProvider,\n    val archiveFile: Path\n) : FileSystem(), ByteStringListPathCreator, Parcelable {\n    val rootDirectory = ArchivePath(this, SEPARATOR_BYTE_STRING)\n\n    init {\n        if (!rootDirectory.isAbsolute) {\n            throw AssertionError(\"Root directory $rootDirectory must be absolute\")\n        }\n        if (rootDirectory.nameCount != 0) {\n            throw AssertionError(\"Root directory $rootDirectory must contain no names\")\n        }\n    }\n\n    val defaultDirectory: ArchivePath\n        get() = rootDirectory\n\n    private val lock = Any()\n\n    private var isOpen = true\n\n    private var passwords = listOf<String>()\n\n    private var isRefreshNeeded = true\n\n    private var entries: Map<Path, ReadArchive.Entry>? = null\n\n    private var tree: Map<Path, List<Path>>? = null\n\n    @Throws(IOException::class)\n    fun getEntry(path: Path): ReadArchive.Entry =\n        synchronized(lock) {\n            ensureEntriesLocked(path)\n            getEntryLocked(path)\n        }\n\n    @Throws(IOException::class)\n    private fun getEntryLocked(path: Path): ReadArchive.Entry =\n        synchronized(lock) {\n            entries!![path] ?: throw NoSuchFileException(path.toString())\n        }\n\n    @Throws(IOException::class)\n    fun newInputStream(file: Path): InputStream =\n        synchronized(lock) {\n            ensureEntriesLocked(file)\n            val entry = getEntryLocked(file)\n            if (entry.isDirectory) {\n                throw IsDirectoryException(file.toString())\n            }\n            val inputStream = try {\n                ArchiveReader.newInputStream(archiveFile, passwords, entry)\n            } catch (e: ArchiveException) {\n                throw e.toFileSystemOrInterruptedIOException(file)\n            } ?: throw NoSuchFileException(file.toString())\n            ArchiveExceptionInputStream(inputStream, file)\n        }\n\n    @Throws(IOException::class)\n    fun getDirectoryChildren(directory: Path): List<Path> =\n        synchronized(lock) {\n            ensureEntriesLocked(directory)\n            val entry = getEntryLocked(directory)\n            if (!entry.isDirectory) {\n                throw NotDirectoryException(directory.toString())\n            }\n            tree!![directory]!!\n        }\n\n    @Throws(IOException::class)\n    fun readSymbolicLink(link: Path): String =\n        synchronized(lock) {\n            ensureEntriesLocked(link)\n            val entry = getEntryLocked(link)\n            if (!entry.isSymbolicLink) {\n                throw NotLinkException(link.toString())\n            }\n            entry.symbolicLinkTarget.orEmpty()\n        }\n\n    fun addPassword(password: String) {\n        synchronized(lock) {\n            if (!isOpen) {\n                throw ClosedFileSystemException()\n            }\n            passwords += password\n        }\n    }\n\n    fun refresh() {\n        synchronized(lock) {\n            if (!isOpen) {\n                throw ClosedFileSystemException()\n            }\n            isRefreshNeeded = true\n        }\n    }\n\n    @Throws(IOException::class)\n    private fun ensureEntriesLocked(file: Path) {\n        if (!isOpen) {\n            throw ClosedFileSystemException()\n        }\n        if (isRefreshNeeded) {\n            val entriesAndTree = try {\n                ArchiveReader.readEntries(archiveFile, passwords, rootDirectory)\n            } catch (e: ArchiveException) {\n                throw e.toFileSystemOrInterruptedIOException(file)\n            }\n            entries = entriesAndTree.first\n            tree = entriesAndTree.second\n            isRefreshNeeded = false\n        }\n    }\n\n    override fun provider(): FileSystemProvider = provider\n\n    override fun close() {\n        synchronized(lock) {\n            if (!isOpen) {\n                return\n            }\n            provider.removeFileSystem(this)\n            isRefreshNeeded = false\n            entries = null\n            tree = null\n            isOpen = false\n        }\n    }\n\n    override fun isOpen(): Boolean = synchronized(lock) { isOpen }\n\n    override fun isReadOnly(): Boolean = true\n\n    override fun getSeparator(): String = SEPARATOR_STRING\n\n    override fun getRootDirectories(): Iterable<Path> = listOf(rootDirectory)\n\n    override fun getFileStores(): Iterable<FileStore> {\n        // TODO\n        throw UnsupportedOperationException()\n    }\n\n    override fun supportedFileAttributeViews(): Set<String> =\n        ArchiveFileAttributeView.SUPPORTED_NAMES\n\n    override fun getPath(first: String, vararg more: String): ArchivePath {\n        val path = ByteStringBuilder(first.toByteString())\n            .apply { more.forEach { append(SEPARATOR).append(it.toByteString()) } }\n            .toByteString()\n        return ArchivePath(this, path)\n    }\n\n    override fun getPath(first: ByteString, vararg more: ByteString): ArchivePath {\n        val path = ByteStringBuilder(first)\n            .apply { more.forEach { append(SEPARATOR).append(it) } }\n            .toByteString()\n        return ArchivePath(this, path)\n    }\n\n    override fun getPathMatcher(syntaxAndPattern: String): PathMatcher {\n        throw UnsupportedOperationException()\n    }\n\n    override fun getUserPrincipalLookupService(): UserPrincipalLookupService {\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun newWatchService(): WatchService {\n        // TODO\n        throw UnsupportedOperationException()\n    }\n\n    override fun equals(other: Any?): Boolean {\n        if (this === other) {\n            return true\n        }\n        if (javaClass != other?.javaClass) {\n            return false\n        }\n        other as ArchiveFileSystem\n        return archiveFile == other.archiveFile\n    }\n\n    override fun hashCode(): Int = archiveFile.hashCode()\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        dest.writeParcelable(archiveFile as Parcelable, flags)\n    }\n\n    companion object {\n        const val SEPARATOR = '/'.code.toByte()\n        private val SEPARATOR_BYTE_STRING = SEPARATOR.toByteString()\n        private const val SEPARATOR_STRING = SEPARATOR.toInt().toChar().toString()\n\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<ArchiveFileSystem> {\n            override fun createFromParcel(source: Parcel): ArchiveFileSystem {\n                val archiveFile = source.readParcelable<Parcelable>(Path::class.java.classLoader)\n                    as Path\n                return ArchiveFileSystemProvider.getOrNewFileSystem(archiveFile)\n            }\n\n            override fun newArray(size: Int): Array<ArchiveFileSystem?> = arrayOfNulls(size)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/archive/ArchiveFileSystemProvider.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.archive\n\nimport java8.nio.channels.FileChannel\nimport java8.nio.channels.SeekableByteChannel\nimport java8.nio.file.AccessDeniedException\nimport java8.nio.file.AccessMode\nimport java8.nio.file.CopyOption\nimport java8.nio.file.DirectoryStream\nimport java8.nio.file.FileStore\nimport java8.nio.file.FileSystem\nimport java8.nio.file.LinkOption\nimport java8.nio.file.OpenOption\nimport java8.nio.file.Path\nimport java8.nio.file.Paths\nimport java8.nio.file.ProviderMismatchException\nimport java8.nio.file.attribute.BasicFileAttributes\nimport java8.nio.file.attribute.FileAttribute\nimport java8.nio.file.attribute.FileAttributeView\nimport java8.nio.file.spi.FileSystemProvider\nimport me.zhanghai.android.files.provider.common.ByteStringPath\nimport me.zhanghai.android.files.provider.common.FileSystemCache\nimport me.zhanghai.android.files.provider.common.PathListDirectoryStream\nimport me.zhanghai.android.files.provider.common.PathObservable\nimport me.zhanghai.android.files.provider.common.PathObservableProvider\nimport me.zhanghai.android.files.provider.common.ReadOnlyFileSystemException\nimport me.zhanghai.android.files.provider.common.Searchable\nimport me.zhanghai.android.files.provider.common.WalkFileTreeSearchable\nimport me.zhanghai.android.files.provider.common.decodedPathByteString\nimport me.zhanghai.android.files.provider.common.decodedQueryByteString\nimport me.zhanghai.android.files.provider.common.isSameFile\nimport me.zhanghai.android.files.provider.common.toAccessModes\nimport me.zhanghai.android.files.provider.common.toByteString\nimport me.zhanghai.android.files.provider.common.toOpenOptions\nimport java.io.IOException\nimport java.io.InputStream\nimport java.net.URI\n\nobject ArchiveFileSystemProvider : FileSystemProvider(), PathObservableProvider, Searchable {\n    private const val SCHEME = \"archive\"\n\n    private val fileSystems = FileSystemCache<Path, ArchiveFileSystem>()\n\n    override fun getScheme(): String = SCHEME\n\n    override fun newFileSystem(uri: URI, env: Map<String, *>): FileSystem {\n        uri.requireSameScheme()\n        val archiveFile = uri.archiveFile\n        return fileSystems.create(archiveFile) { newFileSystem(archiveFile) }\n    }\n\n    override fun newFileSystem(file: Path, env: Map<String, *>): FileSystem = newFileSystem(file)\n\n    internal fun getOrNewFileSystem(archiveFile: Path): ArchiveFileSystem =\n        fileSystems.getOrCreate(archiveFile) { newFileSystem(archiveFile) }\n\n    private fun newFileSystem(archiveFile: Path): ArchiveFileSystem =\n        ArchiveFileSystem(this, archiveFile)\n\n    override fun getFileSystem(uri: URI): FileSystem {\n        uri.requireSameScheme()\n        val archiveFile = uri.archiveFile\n        return fileSystems[archiveFile]\n    }\n\n    internal fun removeFileSystem(fileSystem: ArchiveFileSystem) {\n        fileSystems.remove(fileSystem.archiveFile, fileSystem)\n    }\n\n    override fun getPath(uri: URI): Path {\n        uri.requireSameScheme()\n        val archiveFile = uri.archiveFile\n        val path = uri.decodedQueryByteString\n            ?: throw IllegalArgumentException(\"URI must have a query\")\n        return getOrNewFileSystem(archiveFile).getPath(path)\n    }\n\n    private fun URI.requireSameScheme() {\n        val scheme = scheme\n        require(scheme == SCHEME) { \"URI scheme $scheme must be $SCHEME\" }\n    }\n\n    private val URI.archiveFile: Path\n        get() {\n            val path = decodedPathByteString\n                ?: throw IllegalArgumentException(\"URI must have a path\")\n            // Drop the first character which is always a slash.\n            val archiveUri = URI.create(path.toString().drop(1))\n            return Paths.get(archiveUri)\n        }\n\n    @Throws(IOException::class)\n    override fun newInputStream(file: Path, vararg options: OpenOption): InputStream {\n        file as? ArchivePath ?: throw ProviderMismatchException(file.toString())\n        options.toOpenOptions().checkForArchive()\n        return file.fileSystem.newInputStream(file)\n    }\n\n    override fun newFileChannel(\n        file: Path,\n        options: Set<OpenOption>,\n        vararg attributes: FileAttribute<*>\n    ): FileChannel {\n        file as? ArchivePath ?: throw ProviderMismatchException(file.toString())\n        options.toOpenOptions().checkForArchive()\n        if (attributes.isNotEmpty()) {\n            throw UnsupportedOperationException(attributes.contentToString())\n        }\n        throw UnsupportedOperationException()\n    }\n\n    override fun newByteChannel(\n        file: Path,\n        options: Set<OpenOption>,\n        vararg attributes: FileAttribute<*>\n    ): SeekableByteChannel {\n        file as? ArchivePath ?: throw ProviderMismatchException(file.toString())\n        options.toOpenOptions().checkForArchive()\n        if (attributes.isNotEmpty()) {\n            throw UnsupportedOperationException(attributes.contentToString())\n        }\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun newDirectoryStream(\n        directory: Path,\n        filter: DirectoryStream.Filter<in Path>\n    ): DirectoryStream<Path> {\n        directory as? ArchivePath ?: throw ProviderMismatchException(directory.toString())\n        val children = directory.fileSystem.getDirectoryChildren(directory)\n        return PathListDirectoryStream(children, filter)\n    }\n\n    @Throws(IOException::class)\n    override fun createDirectory(directory: Path, vararg attributes: FileAttribute<*>) {\n        directory as? ArchivePath ?: throw ProviderMismatchException(directory.toString())\n        throw ReadOnlyFileSystemException(directory.toString())\n    }\n\n    @Throws(IOException::class)\n    override fun createSymbolicLink(link: Path, target: Path, vararg attributes: FileAttribute<*>) {\n        link as? ArchivePath ?: throw ProviderMismatchException(link.toString())\n        when (target) {\n            is ArchivePath, is ByteStringPath -> {}\n            else -> throw ProviderMismatchException(target.toString())\n        }\n        throw ReadOnlyFileSystemException(link.toString(), target.toString(), null)\n    }\n\n    @Throws(IOException::class)\n    override fun createLink(link: Path, existing: Path) {\n        link as? ArchivePath ?: throw ProviderMismatchException(link.toString())\n        existing as? ArchivePath ?: throw ProviderMismatchException(existing.toString())\n        throw ReadOnlyFileSystemException(link.toString(), existing.toString(), null)\n    }\n\n    @Throws(IOException::class)\n    override fun delete(path: Path) {\n        path as? ArchivePath ?: throw ProviderMismatchException(path.toString())\n        throw ReadOnlyFileSystemException(path.toString())\n    }\n\n    @Throws(IOException::class)\n    override fun readSymbolicLink(link: Path): Path {\n        link as? ArchivePath ?: throw ProviderMismatchException(link.toString())\n        val target = link.fileSystem.readSymbolicLink(link)\n        return ByteStringPath(target.toByteString())\n    }\n\n    @Throws(IOException::class)\n    override fun copy(source: Path, target: Path, vararg options: CopyOption) {\n        source as? ArchivePath ?: throw ProviderMismatchException(source.toString())\n        target as? ArchivePath ?: throw ProviderMismatchException(target.toString())\n        throw ReadOnlyFileSystemException(source.toString(), target.toString(), null)\n    }\n\n    @Throws(IOException::class)\n    override fun move(source: Path, target: Path, vararg options: CopyOption) {\n        source as? ArchivePath ?: throw ProviderMismatchException(source.toString())\n        target as? ArchivePath ?: throw ProviderMismatchException(target.toString())\n        throw ReadOnlyFileSystemException(source.toString(), target.toString(), null)\n    }\n\n    @Throws(IOException::class)\n    override fun isSameFile(path: Path, path2: Path): Boolean {\n        path as? ArchivePath ?: throw ProviderMismatchException(path.toString())\n        if (path == path2) {\n            return true\n        }\n        if (path2 !is ArchivePath) {\n            return false\n        }\n        val fileSystem = path.fileSystem\n        if (!fileSystem.archiveFile.isSameFile(path2.fileSystem.archiveFile)) {\n            return false\n        }\n        return path == fileSystem.getPath(path2.toString())\n    }\n\n    override fun isHidden(path: Path): Boolean {\n        path as? ArchivePath ?: throw ProviderMismatchException(path.toString())\n        return false\n    }\n\n    override fun getFileStore(path: Path): FileStore {\n        path as? ArchivePath ?: throw ProviderMismatchException(path.toString())\n        val archiveFile = path.fileSystem.archiveFile\n        return ArchiveFileStore(archiveFile)\n    }\n\n    @Throws(IOException::class)\n    override fun checkAccess(path: Path, vararg modes: AccessMode) {\n        path as? ArchivePath ?: throw ProviderMismatchException(path.toString())\n        val accessModes = modes.toAccessModes()\n        path.fileSystem.getEntry(path)\n        if (accessModes.write || accessModes.execute) {\n            throw AccessDeniedException(path.toString())\n        }\n    }\n\n    override fun <V : FileAttributeView> getFileAttributeView(\n        path: Path,\n        type: Class<V>,\n        vararg options: LinkOption\n    ): V? {\n        path as? ArchivePath ?: throw ProviderMismatchException(path.toString())\n        if (!supportsFileAttributeView(type)) {\n            return null\n        }\n        @Suppress(\"UNCHECKED_CAST\")\n        return getFileAttributeView(path) as V\n    }\n\n    internal fun supportsFileAttributeView(type: Class<out FileAttributeView>): Boolean =\n        type.isAssignableFrom(ArchiveFileAttributeView::class.java)\n\n    @Throws(IOException::class)\n    override fun <A : BasicFileAttributes> readAttributes(\n        path: Path,\n        type: Class<A>,\n        vararg options: LinkOption\n    ): A {\n        path as? ArchivePath ?: throw ProviderMismatchException(path.toString())\n        if (!type.isAssignableFrom(ArchiveFileAttributes::class.java)) {\n            throw UnsupportedOperationException(type.toString())\n        }\n        @Suppress(\"UNCHECKED_CAST\")\n        return getFileAttributeView(path).readAttributes() as A\n    }\n\n    private fun getFileAttributeView(path: ArchivePath): ArchiveFileAttributeView =\n        ArchiveFileAttributeView(path)\n\n    override fun readAttributes(\n        path: Path,\n        attributes: String,\n        vararg options: LinkOption\n    ): Map<String, Any> {\n        path as? ArchivePath ?: throw ProviderMismatchException(path.toString())\n        throw UnsupportedOperationException()\n    }\n\n    override fun setAttribute(\n        path: Path,\n        attribute: String,\n        value: Any,\n        vararg options: LinkOption\n    ) {\n        path as? ArchivePath ?: throw ProviderMismatchException(path.toString())\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun observe(path: Path, intervalMillis: Long): PathObservable {\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun search(\n        directory: Path,\n        query: String,\n        intervalMillis: Long,\n        listener: (List<Path>) -> Unit\n    ) {\n        directory as? ArchivePath ?: throw ProviderMismatchException(directory.toString())\n        WalkFileTreeSearchable.search(directory, query, intervalMillis, listener)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/archive/ArchivePasswordRequiredException.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.archive\n\nimport android.content.Context\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.fileaction.ArchivePasswordDialogActivity\nimport me.zhanghai.android.files.fileaction.ArchivePasswordDialogFragment\nimport me.zhanghai.android.files.provider.common.UserAction\nimport me.zhanghai.android.files.provider.common.UserActionRequiredException\nimport me.zhanghai.android.files.util.createIntent\nimport me.zhanghai.android.files.util.putArgs\nimport kotlin.coroutines.Continuation\nimport kotlin.coroutines.resume\n\nclass ArchivePasswordRequiredException(\n    private val file: Path,\n    reason: String?\n) :\n    UserActionRequiredException(file.toString(), null, reason) {\n\n    override fun getUserAction(continuation: Continuation<Boolean>, context: Context): UserAction {\n        return UserAction(\n            ArchivePasswordDialogActivity::class.createIntent().putArgs(\n                ArchivePasswordDialogFragment.Args(file) { continuation.resume(it) }\n            ), ArchivePasswordDialogFragment.getTitle(context),\n            ArchivePasswordDialogFragment.getMessage(file, context)\n        )\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/archive/ArchivePath.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.archive\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport java8.nio.file.LinkOption\nimport java8.nio.file.Path\nimport java8.nio.file.WatchEvent\nimport java8.nio.file.WatchKey\nimport java8.nio.file.WatchService\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.ByteStringListPath\nimport me.zhanghai.android.files.provider.common.toByteString\nimport me.zhanghai.android.files.provider.root.RootablePath\nimport me.zhanghai.android.files.util.readParcelable\nimport java.io.File\nimport java.io.IOException\n\ninternal class ArchivePath : ByteStringListPath<ArchivePath>, RootablePath {\n    private val fileSystem: ArchiveFileSystem\n\n    constructor(fileSystem: ArchiveFileSystem, path: ByteString) : super(\n        ArchiveFileSystem.SEPARATOR, path\n    ) {\n        this.fileSystem = fileSystem\n    }\n\n    private constructor(\n        fileSystem: ArchiveFileSystem,\n        absolute: Boolean,\n        segments: List<ByteString>\n    ) : super(ArchiveFileSystem.SEPARATOR, absolute, segments) {\n        this.fileSystem = fileSystem\n    }\n\n    override fun isPathAbsolute(path: ByteString): Boolean =\n        !path.isEmpty() && path[0] == ArchiveFileSystem.SEPARATOR\n\n    override fun createPath(path: ByteString): ArchivePath = ArchivePath(fileSystem, path)\n\n    override fun createPath(absolute: Boolean, segments: List<ByteString>): ArchivePath =\n        ArchivePath(fileSystem, absolute, segments)\n\n    override val uriPath: ByteString\n        // Prepend a slash character to make it a valid URI path, since we always have an (empty)\n        // authority.\n        get() = (\"/\" + fileSystem.archiveFile.toUri().toString()).toByteString()\n\n    override val uriQuery: ByteString?\n        get() = super.uriPath\n\n    override val defaultDirectory: ArchivePath\n        get() = fileSystem.defaultDirectory\n\n    override fun getFileSystem(): ArchiveFileSystem = fileSystem\n\n    override fun getRoot(): ArchivePath? = if (isAbsolute) fileSystem.rootDirectory else null\n\n    @Throws(IOException::class)\n    override fun toRealPath(vararg options: LinkOption): ArchivePath {\n        throw UnsupportedOperationException()\n    }\n\n    override fun toFile(): File {\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun register(\n        watcher: WatchService,\n        events: Array<WatchEvent.Kind<*>>,\n        vararg modifiers: WatchEvent.Modifier\n    ): WatchKey {\n        throw UnsupportedOperationException()\n    }\n\n    override fun isRootRequired(isAttributeAccess: Boolean): Boolean {\n        val archiveFile = fileSystem.archiveFile\n        return if (archiveFile is RootablePath) {\n            archiveFile.isRootRequired(isAttributeAccess)\n        } else {\n            false\n        }\n    }\n\n    private constructor(source: Parcel) : super(source) {\n        fileSystem = source.readParcelable()!!\n    }\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        super.writeToParcel(dest, flags)\n\n        dest.writeParcelable(fileSystem, flags)\n    }\n\n    companion object {\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<ArchivePath> {\n            override fun createFromParcel(source: Parcel): ArchivePath = ArchivePath(source)\n\n            override fun newArray(size: Int): Array<ArchivePath?> = arrayOfNulls(size)\n        }\n    }\n}\n\nval Path.isArchivePath: Boolean\n    get() = this is ArchivePath\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/archive/OpenOptionsArchiveExtensions.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.archive\n\nimport java8.nio.file.StandardOpenOption\nimport me.zhanghai.android.files.provider.common.OpenOptions\n\ninternal fun OpenOptions.checkForArchive() {\n    if (write) {\n        throw UnsupportedOperationException(StandardOpenOption.WRITE.toString())\n    }\n    if (append) {\n        throw UnsupportedOperationException(StandardOpenOption.APPEND.toString())\n    }\n    if (truncateExisting) {\n        throw UnsupportedOperationException(StandardOpenOption.TRUNCATE_EXISTING.toString())\n    }\n    if (create) {\n        throw UnsupportedOperationException(StandardOpenOption.CREATE.toString())\n    }\n    if (createNew) {\n        throw UnsupportedOperationException(StandardOpenOption.CREATE_NEW.toString())\n    }\n    if (deleteOnClose) {\n        throw UnsupportedOperationException(StandardOpenOption.DELETE_ON_CLOSE.toString())\n    }\n    if (sync) {\n        throw UnsupportedOperationException(StandardOpenOption.SYNC.toString())\n    }\n    if (dsync) {\n        throw UnsupportedOperationException(StandardOpenOption.DSYNC.toString())\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/archive/PathArchiveExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.archive\n\nimport java8.nio.file.Path\nimport java8.nio.file.ProviderMismatchException\n\nfun Path.archiveAddPassword(password: String) {\n    this as? ArchivePath ?: throw ProviderMismatchException(toString())\n    fileSystem.addPassword(password)\n}\n\nval Path.archiveFile: Path\n    get() {\n        this as? ArchivePath ?: throw ProviderMismatchException(toString())\n        return fileSystem.archiveFile\n    }\n\nfun Path.archiveRefresh() {\n    this as? ArchivePath ?: throw ProviderMismatchException(toString())\n    fileSystem.refresh()\n}\n\nfun Path.createArchiveRootPath(): Path =\n    ArchiveFileSystemProvider.getOrNewFileSystem(this).rootDirectory\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/archive/archiver/ArchiveReader.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.archive.archiver\n\nimport androidx.preference.PreferenceManager\nimport java8.nio.channels.SeekableByteChannel\nimport java8.nio.charset.StandardCharsets\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.provider.common.DelegateForceableSeekableByteChannel\nimport me.zhanghai.android.files.provider.common.DelegateInputStream\nimport me.zhanghai.android.files.provider.common.DelegateNonForceableSeekableByteChannel\nimport me.zhanghai.android.files.provider.common.ForceableChannel\nimport me.zhanghai.android.files.provider.common.PosixFileMode\nimport me.zhanghai.android.files.provider.common.PosixFileType\nimport me.zhanghai.android.files.provider.common.newByteChannel\nimport me.zhanghai.android.files.provider.common.newInputStream\nimport me.zhanghai.android.files.provider.root.isRunningAsRoot\nimport me.zhanghai.android.files.provider.root.rootContext\nimport me.zhanghai.android.files.settings.Settings\nimport me.zhanghai.android.files.util.valueCompat\nimport java.io.Closeable\nimport java.io.IOException\nimport java.io.InputStream\nimport java.nio.charset.Charset\n\nobject ArchiveReader {\n    @Throws(IOException::class)\n    fun readEntries(\n        file: Path,\n        passwords: List<String>,\n        rootPath: Path\n    ): Pair<Map<Path, ReadArchive.Entry>, Map<Path, List<Path>>> {\n        val entries = mutableMapOf<Path, ReadArchive.Entry>()\n        val rawEntries = readEntries(file, passwords)\n        for (entry in rawEntries) {\n            var path = rootPath.resolve(entry.name)\n            // Normalize an absolute path to prevent path traversal attack.\n            if (!path.isAbsolute) {\n                // TODO: Will this actually happen?\n                throw AssertionError(\"Path must be absolute: $path\")\n            }\n            if (path.nameCount > 0) {\n                path = path.normalize()\n                if (path.nameCount == 0) {\n                    // Don't allow a path to become the root path only after normalization.\n                    continue\n                }\n            } else {\n                if (!entry.isDirectory) {\n                    // Ignore a root path that's not a directory\n                    continue\n                }\n            }\n            entries.getOrPut(path) { entry }\n        }\n        entries.getOrPut(rootPath) { createDirectoryEntry(\"\") }\n        val tree = mutableMapOf<Path, MutableList<Path>>()\n        tree[rootPath] = mutableListOf()\n        val paths = entries.keys.toList()\n        for (path in paths) {\n            var path = path\n            while (true) {\n                val parentPath = path.parent ?: break\n                val entry = entries[path]!!\n                if (entry.isDirectory) {\n                    tree.getOrPut(path) { mutableListOf() }\n                }\n                tree.getOrPut(parentPath) { mutableListOf() }.add(path)\n                if (entries.containsKey(parentPath)) {\n                    break\n                }\n                entries[parentPath] = createDirectoryEntry(parentPath.toString())\n                path = parentPath\n            }\n        }\n        return entries to tree\n    }\n\n    private fun createDirectoryEntry(name: String): ReadArchive.Entry {\n        require(!name.endsWith(\"/\")) { \"name $name should not end with a slash\" }\n        return ReadArchive.Entry(\n            name, false, null, null, null, PosixFileType.DIRECTORY, 0, null, null,\n            PosixFileMode.DIRECTORY_DEFAULT, null\n        )\n    }\n\n    @Throws(IOException::class)\n    private fun readEntries(file: Path, passwords: List<String>): List<ReadArchive.Entry> {\n        val charset = archiveFileNameCharset\n        val (archive, closeable) = openArchive(file, passwords)\n        return closeable.use {\n            buildList {\n                while (true) {\n                    this += archive.readEntry(charset) ?: break\n                }\n            }\n        }\n    }\n\n    @Throws(IOException::class)\n    fun newInputStream(file: Path, passwords: List<String>, entry: ReadArchive.Entry): InputStream? {\n        val charset = archiveFileNameCharset\n        val (archive, closeable) = openArchive(file, passwords)\n        var successful = false\n        return try {\n            while (true) {\n                val currentEntry = archive.readEntry(charset) ?: break\n                if (currentEntry.name != entry.name) {\n                    continue\n                }\n                successful = true\n                break\n            }\n            if (successful) {\n                CloseableInputStream(archive.newDataInputStream(), closeable)\n            } else {\n                null\n            }\n        } finally {\n            if (!successful) {\n                closeable.close()\n            }\n        }\n    }\n\n    @Throws(IOException::class)\n    private fun openArchive(\n        file: Path,\n        passwords: List<String>\n    ): Pair<ReadArchive, ArchiveCloseable> {\n        val channel = try {\n            CacheSizeSeekableByteChannel(file.newByteChannel())\n        } catch (e: Exception) {\n            e.printStackTrace()\n            null\n        }\n        if (channel != null) {\n            var successful = false\n            try {\n                val archive = ReadArchive(channel, passwords)\n                successful = true\n                return archive to ArchiveCloseable(archive, channel)\n            } finally {\n                if (!successful) {\n                    channel.close()\n                }\n            }\n        }\n        val inputStream = file.newInputStream()\n        var successful = false\n        try {\n            val archive = ReadArchive(inputStream, passwords)\n            successful = true\n            return archive to ArchiveCloseable(archive, inputStream)\n        } finally {\n            if (!successful) {\n                inputStream.close()\n            }\n        }\n    }\n\n    // size() may be called repeatedly for ZIP and 7Z, so make it cached to improve performance.\n    private fun CacheSizeSeekableByteChannel(channel: SeekableByteChannel): SeekableByteChannel =\n        if (channel is ForceableChannel) {\n            CacheSizeForceableSeekableByteChannel(channel)\n        } else {\n            CacheSizeNonForceableSeekableByteChannel(channel)\n        }\n\n    private class CacheSizeNonForceableSeekableByteChannel(\n        channel: SeekableByteChannel\n    ) : DelegateNonForceableSeekableByteChannel(channel) {\n        private val size: Long by lazy { super.size() }\n\n        override fun size(): Long = size\n    }\n\n    private class CacheSizeForceableSeekableByteChannel(\n        channel: SeekableByteChannel\n    ) : DelegateForceableSeekableByteChannel(channel) {\n        private val size: Long by lazy { super.size() }\n\n        override fun size(): Long = size\n    }\n\n    private val archiveFileNameCharset: Charset\n        get() =\n            if (isRunningAsRoot) {\n                try {\n                    val sharedPreferences =\n                        PreferenceManager.getDefaultSharedPreferences(rootContext)\n                    val key = rootContext.getString(R.string.pref_key_archive_file_name_encoding)\n                    val defaultValue = rootContext.getString(\n                        R.string.pref_default_value_archive_file_name_encoding\n                    )\n                    Charset.forName(sharedPreferences.getString(key, defaultValue)!!)\n                } catch (e: Exception) {\n                    e.printStackTrace()\n                    StandardCharsets.UTF_8\n                }\n            } else {\n                Charset.forName(Settings.ARCHIVE_FILE_NAME_ENCODING.valueCompat)\n            }\n\n    private class ArchiveCloseable(\n        private val archive: ReadArchive,\n        private val closeable: Closeable\n    ) : Closeable {\n        override fun close() {\n            @Suppress(\"ConvertTryFinallyToUseCall\")\n            try {\n                archive.close()\n            } finally {\n                closeable.close()\n            }\n        }\n    }\n\n    private class CloseableInputStream(\n        inputStream: InputStream,\n        private val closeable: Closeable\n    ) : DelegateInputStream(inputStream) {\n        @Throws(IOException::class)\n        override fun close() {\n            super.close()\n\n            closeable.close()\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/archive/archiver/ArchiveWriter.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.archive.archiver\n\nimport java8.nio.channels.SeekableByteChannel\nimport java8.nio.file.LinkOption\nimport java8.nio.file.Path\nimport java8.nio.file.attribute.BasicFileAttributes\nimport me.zhanghai.android.files.provider.common.PosixFileAttributes\nimport me.zhanghai.android.files.provider.common.PosixFileMode\nimport me.zhanghai.android.files.provider.common.PosixFileType\nimport me.zhanghai.android.files.provider.common.copyTo\nimport me.zhanghai.android.files.provider.common.getLastModifiedTime\nimport me.zhanghai.android.files.provider.common.newInputStream\nimport me.zhanghai.android.files.provider.common.readAttributes\nimport me.zhanghai.android.files.provider.common.readSymbolicLinkByteString\nimport me.zhanghai.android.files.provider.common.size\nimport java.io.Closeable\nimport java.io.IOException\n\nclass ArchiveWriter @Throws(IOException::class) constructor(\n    channel: SeekableByteChannel,\n    format: Int,\n    filter: Int,\n    password: String?\n) : Closeable {\n    private val archive = WriteArchive(channel, format, filter, password)\n\n    @Throws(IOException::class)\n    fun write(file: Path, entryName: Path, intervalMillis: Long, listener: ((Long) -> Unit)?) {\n        val name = entryName.toString()\n        val lastModifiedTime = file.getLastModifiedTime(LinkOption.NOFOLLOW_LINKS)\n        val lastAccessTime = null\n        val creationTime = null\n        val attributes = file.readAttributes(\n            BasicFileAttributes::class.java, LinkOption.NOFOLLOW_LINKS\n        )\n        val type = when {\n            attributes is PosixFileAttributes -> attributes.type()\n            attributes.isDirectory -> PosixFileType.DIRECTORY\n            attributes.isSymbolicLink -> PosixFileType.SYMBOLIC_LINK\n            else -> PosixFileType.REGULAR_FILE\n        }\n        val size = file.size(LinkOption.NOFOLLOW_LINKS)\n        val posixAttributes = attributes as? PosixFileAttributes\n        val owner = posixAttributes?.owner()\n        val group = posixAttributes?.group()\n        val mode = posixAttributes?.mode() ?: when {\n            attributes.isDirectory -> PosixFileMode.DIRECTORY_DEFAULT\n            attributes.isSymbolicLink -> PosixFileMode.SYMBOLIC_LINK_DEFAULT\n            else -> PosixFileMode.FILE_DEFAULT\n        }\n        val symbolicLinkTarget = if (attributes.isSymbolicLink) {\n            file.readSymbolicLinkByteString().toString()\n        } else {\n            null\n        }\n        archive.Entry(\n            name, lastModifiedTime, lastAccessTime, creationTime, type, size, owner, group, mode,\n            symbolicLinkTarget\n        ).use { archive.writeEntry(it) }\n        if (type == PosixFileType.REGULAR_FILE) {\n            file.newInputStream(LinkOption.NOFOLLOW_LINKS).use { inputStream ->\n                inputStream.copyTo(archive.newDataOutputStream(), intervalMillis, listener)\n            }\n        } else {\n            listener?.invoke(attributes.size())\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun close() {\n        archive.close()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/archive/archiver/ReadArchive.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.archive.archiver\n\nimport android.system.OsConstants\nimport java.io.Closeable\nimport java.io.IOException\nimport java.io.InputStream\nimport java.io.InterruptedIOException\nimport java.nio.ByteBuffer\nimport java.nio.charset.Charset\nimport java.time.Instant\nimport java8.nio.channels.SeekableByteChannel\nimport java8.nio.charset.StandardCharsets\nimport java8.nio.file.attribute.FileTime\nimport me.zhanghai.android.files.provider.common.PosixFileMode\nimport me.zhanghai.android.files.provider.common.PosixFileModeBit\nimport me.zhanghai.android.files.provider.common.PosixFileType\nimport me.zhanghai.android.files.provider.common.PosixGroup\nimport me.zhanghai.android.files.provider.common.PosixUser\nimport me.zhanghai.android.files.provider.common.toByteString\nimport me.zhanghai.android.libarchive.Archive\nimport me.zhanghai.android.libarchive.ArchiveEntry\nimport me.zhanghai.android.libarchive.ArchiveException\n\nclass ReadArchive : Closeable {\n    private val archive = Archive.readNew()\n\n    @Throws(ArchiveException::class)\n    constructor(inputStream: InputStream, passwords: List<String>) {\n        var successful = false\n        try {\n            Archive.setCharset(archive, StandardCharsets.UTF_8.name().toByteArray())\n            Archive.readSupportFilterAll(archive)\n            Archive.readSupportFormatAll(archive)\n            Archive.readSetCallbackData(archive, null)\n            val buffer = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE)\n            Archive.readSetReadCallback<Any?>(archive) { _, _ ->\n                buffer.clear()\n                val bytesRead = try {\n                    inputStream.read(buffer.array())\n                } catch (e: IOException) {\n                    throw e.toArchiveException(\"InputStream.read\")\n                }\n                if (bytesRead != -1) {\n                    buffer.limit(bytesRead)\n                    buffer\n                } else {\n                    null\n                }\n            }\n            Archive.readSetSkipCallback<Any?>(archive) { _, _, request ->\n                try {\n                    inputStream.skip(request)\n                } catch (e: IOException) {\n                    throw e.toArchiveException(\"InputStream.skip\")\n                }\n            }\n            for (password in passwords) {\n                Archive.readAddPassphrase(archive, password.toByteArray())\n            }\n            Archive.readOpen1(archive)\n            successful = true\n        } finally {\n            if (!successful) {\n                close()\n            }\n        }\n    }\n\n    @Throws(ArchiveException::class)\n    constructor(channel: SeekableByteChannel, passwords: List<String>) {\n        var successful = false\n        try {\n            Archive.setCharset(archive, StandardCharsets.UTF_8.name().toByteArray())\n            Archive.readSupportFilterAll(archive)\n            Archive.readSupportFormatAll(archive)\n            Archive.readSetCallbackData(archive, null)\n            val buffer = ByteBuffer.allocateDirect(DEFAULT_BUFFER_SIZE)\n            Archive.readSetReadCallback<Any?>(archive) { _, _ ->\n                buffer.clear()\n                val bytesRead = try {\n                    channel.read(buffer)\n                } catch (e: IOException) {\n                    throw e.toArchiveException(\"SeekableByteChannel.read\")\n                }\n                if (bytesRead != -1) {\n                    buffer.flip()\n                    buffer\n                } else {\n                    null\n                }\n            }\n            Archive.readSetSkipCallback<Any?>(archive) { _, _, request ->\n                try {\n                    channel.position(channel.position() + request)\n                } catch (e: IOException) {\n                    throw e.toArchiveException(\"SeekableByteChannel.position\")\n                }\n                request\n            }\n            Archive.readSetSeekCallback<Any?>(archive) { _, _, offset, whence ->\n                val newPosition: Long\n                try {\n                    newPosition = when (whence) {\n                        OsConstants.SEEK_SET -> offset\n                        OsConstants.SEEK_CUR -> channel.position() + offset\n                        OsConstants.SEEK_END -> channel.size() + offset\n                        else -> throw ArchiveException(\n                            Archive.ERRNO_FATAL,\n                            \"Unknown whence $whence\"\n                        )\n                    }\n                    channel.position(newPosition)\n                } catch (e: IOException) {\n                    throw e.toArchiveException(\"SeekableByteChannel.position\")\n                }\n                newPosition\n            }\n            for (password in passwords) {\n                Archive.readAddPassphrase(archive, password.toByteArray())\n            }\n            Archive.readOpen1(archive)\n            successful = true\n        } finally {\n            if (!successful) {\n                close()\n            }\n        }\n    }\n\n    private fun IOException.toArchiveException(message: String): ArchiveException =\n        when (this) {\n            is InterruptedIOException -> ArchiveException(OsConstants.EINTR, message, this)\n            else -> ArchiveException(Archive.ERRNO_FATAL, message, this)\n        }\n\n    @Throws(ArchiveException::class)\n    fun readEntry(charset: Charset): Entry? {\n        val entry = Archive.readNextHeader(archive)\n        if (entry == 0L) {\n            return null\n        }\n        val name =\n            getEntryString(ArchiveEntry.pathnameUtf8(entry), ArchiveEntry.pathname(entry), charset)\n                ?: throw ArchiveException(\n                    Archive.ERRNO_FATAL, \"pathname == null && pathnameUtf8 == null\"\n                )\n        val isEncrypted = ArchiveEntry.isEncrypted(entry)\n        val stat = ArchiveEntry.stat(entry)\n        val lastModifiedTime = if (ArchiveEntry.mtimeIsSet(entry)) {\n            FileTime.from(\n                Instant.ofEpochSecond(stat.stMtim.tvSec, stat.stMtim.tvNsec)\n            )\n        } else {\n            null\n        }\n        val lastAccessTime = if (ArchiveEntry.atimeIsSet(entry)) {\n            FileTime.from(\n                Instant.ofEpochSecond(stat.stAtim.tvSec, stat.stAtim.tvNsec)\n            )\n        } else {\n            null\n        }\n        val creationTime = if (ArchiveEntry.birthtimeIsSet(entry)) {\n            FileTime.from(\n                Instant.ofEpochSecond(\n                    ArchiveEntry.birthtime(entry), ArchiveEntry.birthtimeNsec(entry)\n                )\n            )\n        } else {\n            null\n        }\n        val type = PosixFileType.fromMode(stat.stMode)\n        val size = stat.stSize\n        // TODO: There's no way to know if UID/GID is unset or root.\n        val owner = PosixUser(\n            stat.stUid, getEntryString(\n                ArchiveEntry.unameUtf8(entry), ArchiveEntry.uname(entry), charset\n            )?.toByteString()\n        )\n        val group = PosixGroup(\n            stat.stGid, getEntryString(\n                ArchiveEntry.gnameUtf8(entry), ArchiveEntry.gname(entry), charset\n            )?.toByteString()\n        )\n        val mode = PosixFileMode.fromInt(stat.stMode)\n        val symbolicLinkTarget =\n            getEntryString(ArchiveEntry.symlinkUtf8(entry), ArchiveEntry.symlink(entry), charset)\n        return Entry(\n            name, isEncrypted, lastModifiedTime, lastAccessTime, creationTime, type, size, owner,\n            group, mode, symbolicLinkTarget\n        )\n    }\n\n    private fun getEntryString(stringUtf8: String?, string: ByteArray?, charset: Charset): String? =\n        stringUtf8 ?: string?.toString(charset)\n\n    @Throws(ArchiveException::class)\n    fun newDataInputStream(): InputStream = DataInputStream()\n\n    @Throws(ArchiveException::class)\n    override fun close() {\n        Archive.readFree(archive)\n    }\n\n    class Entry(\n        val name: String,\n        val isEncrypted: Boolean,\n        val lastModifiedTime: FileTime?,\n        val lastAccessTime: FileTime?,\n        val creationTime: FileTime?,\n        val type: PosixFileType,\n        val size: Long,\n        val owner: PosixUser?,\n        val group: PosixGroup?,\n        val mode: Set<PosixFileModeBit>,\n        val symbolicLinkTarget: String?\n    ) {\n        val isDirectory: Boolean\n            get() = type == PosixFileType.DIRECTORY\n\n        val isSymbolicLink: Boolean\n            get() = type == PosixFileType.SYMBOLIC_LINK\n    }\n\n    private inner class DataInputStream : InputStream() {\n        private val oneByteBuffer = ByteBuffer.allocateDirect(1)\n\n        @Throws(IOException::class)\n        override fun read(): Int {\n            read(oneByteBuffer)\n            return if (oneByteBuffer.hasRemaining()) oneByteBuffer.get().toUByte().toInt() else -1\n        }\n\n        @Throws(IOException::class)\n        override fun read(b: ByteArray, off: Int, len: Int): Int {\n            val buffer = ByteBuffer.wrap(b, off, len)\n            read(buffer)\n            return if (buffer.hasRemaining()) buffer.remaining() else -1\n        }\n\n        @Throws(IOException::class)\n        private fun read(buffer: ByteBuffer) {\n            buffer.clear()\n            Archive.readData(archive, buffer)\n            buffer.flip()\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/archive/archiver/WriteArchive.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.archive.archiver\n\nimport java8.nio.channels.SeekableByteChannel\nimport java8.nio.charset.StandardCharsets\nimport java8.nio.file.attribute.FileTime\nimport me.zhanghai.android.files.provider.common.PosixFileModeBit\nimport me.zhanghai.android.files.provider.common.PosixFileType\nimport me.zhanghai.android.files.provider.common.PosixGroup\nimport me.zhanghai.android.files.provider.common.PosixUser\nimport me.zhanghai.android.files.provider.common.toInt\nimport me.zhanghai.android.libarchive.Archive\nimport me.zhanghai.android.libarchive.ArchiveEntry\nimport me.zhanghai.android.libarchive.ArchiveException\nimport java.io.Closeable\nimport java.io.IOException\nimport java.io.OutputStream\nimport java.nio.ByteBuffer\n\nclass WriteArchive @Throws(ArchiveException::class) constructor(\n    channel: SeekableByteChannel,\n    format: Int,\n    filter: Int,\n    password: String?\n) : Closeable {\n    private val archive = Archive.writeNew()\n\n    init {\n        var successful = false\n        try {\n            Archive.writeSetBytesPerBlock(archive, DEFAULT_BUFFER_SIZE)\n            Archive.writeSetBytesInLastBlock(archive, 1)\n            Archive.writeSetFormat(archive, format)\n            Archive.writeAddFilter(archive, filter)\n            if (password != null) {\n                require(format == Archive.FORMAT_ZIP)\n                Archive.writeSetPassphrase(archive, password.toByteArray())\n                Archive.writeSetFormatOption(\n                    archive, null, \"encryption\".toByteArray(), \"zipcrypt\".toByteArray()\n                )\n            }\n            Archive.writeOpen(\n                archive, null, null, { _, _, buffer -> channel.write(buffer) }, null\n            )\n            successful = true\n        } finally {\n            if (!successful) {\n                close()\n            }\n        }\n    }\n\n    @Throws(ArchiveException::class)\n    fun writeEntry(entry: Entry) {\n        Archive.writeHeader(archive, entry.entry)\n    }\n\n    @Throws(ArchiveException::class)\n    fun newDataOutputStream(): OutputStream = DataOutputStream()\n\n    @Throws(ArchiveException::class)\n    override fun close() {\n        Archive.writeFree(archive)\n    }\n\n    inner class Entry(\n        name: String,\n        lastModifiedTime: FileTime?,\n        lastAccessTime: FileTime?,\n        creationTime: FileTime?,\n        type: PosixFileType,\n        size: Long,\n        owner: PosixUser?,\n        group: PosixGroup?,\n        mode: Set<PosixFileModeBit>,\n        symbolicLinkTarget: String?\n    ) : Closeable {\n        internal val entry = ArchiveEntry.new2(archive)\n\n        init {\n            Archive.setCharset(archive, StandardCharsets.UTF_8.name().toByteArray())\n            ArchiveEntry.setPathname(entry, name.toByteArray())\n            if (lastModifiedTime != null) {\n                val lastModifiedTimeInstant = lastModifiedTime.toInstant()\n                ArchiveEntry.setMtime(\n                    entry, lastModifiedTimeInstant.epochSecond,\n                    lastModifiedTimeInstant.nano.toLong()\n                )\n            }\n            if (lastAccessTime != null) {\n                val lastAccessTimeInstant = lastAccessTime.toInstant()\n                ArchiveEntry.setAtime(\n                    entry, lastAccessTimeInstant.epochSecond, lastAccessTimeInstant.nano.toLong()\n                )\n            }\n            if (creationTime != null) {\n                val creationTimeInstant = creationTime.toInstant()\n                ArchiveEntry.setBirthtime(\n                    entry, creationTimeInstant.epochSecond, creationTimeInstant.nano.toLong()\n                )\n            }\n            ArchiveEntry.setFiletype(entry, type.mode)\n            ArchiveEntry.setSize(entry, size)\n            if (owner != null) {\n                ArchiveEntry.setUid(entry, owner.id.toLong())\n                val ownerName = owner.name\n                if (ownerName != null) {\n                    ArchiveEntry.setUname(entry, ownerName.toByteArray())\n                }\n            }\n            if (group != null) {\n                ArchiveEntry.setGid(entry, group.id.toLong())\n                val groupName = group.name\n                if (groupName != null) {\n                    ArchiveEntry.setGname(entry, groupName.toByteArray())\n                }\n            }\n            ArchiveEntry.setPerm(entry, mode.toInt())\n            if (symbolicLinkTarget != null) {\n                ArchiveEntry.setSymlink(entry, symbolicLinkTarget.toByteArray())\n            }\n        }\n\n        override fun close() {\n            ArchiveEntry.free(entry)\n        }\n    }\n\n    private inner class DataOutputStream : OutputStream() {\n        private val oneByteBuffer = ByteBuffer.allocateDirect(1)\n\n        @Throws(IOException::class)\n        override fun write(b: Int) {\n            oneByteBuffer.clear()\n            oneByteBuffer.put(b.toByte())\n            Archive.writeData(archive, oneByteBuffer)\n        }\n\n        @Throws(IOException::class)\n        override fun write(b: ByteArray, off: Int, len: Int) {\n            val buffer = ByteBuffer.wrap(b, off, len)\n            while (buffer.hasRemaining()) {\n                Archive.writeData(archive, buffer)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/AbstractBasicFileAttributes.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport android.os.Parcelable\nimport java8.nio.file.attribute.BasicFileAttributes\nimport java8.nio.file.attribute.FileTime\n\nabstract class AbstractBasicFileAttributes : BasicFileAttributes, Parcelable {\n    protected abstract val lastModifiedTime: FileTime\n    protected abstract val lastAccessTime: FileTime\n    protected abstract val creationTime: FileTime\n    protected abstract val type: BasicFileType\n    protected abstract val size: Long\n    protected abstract val fileKey: Parcelable\n\n    override fun lastModifiedTime(): FileTime = lastModifiedTime\n\n    override fun lastAccessTime(): FileTime = lastAccessTime\n\n    override fun creationTime(): FileTime = creationTime\n\n    override fun isRegularFile(): Boolean = type == BasicFileType.REGULAR_FILE\n\n    override fun isDirectory(): Boolean = type == BasicFileType.DIRECTORY\n\n    override fun isSymbolicLink(): Boolean = type == BasicFileType.SYMBOLIC_LINK\n\n    override fun isOther(): Boolean = type == BasicFileType.OTHER\n\n    override fun size(): Long = size\n\n    override fun fileKey(): Parcelable? = fileKey\n}\n\nenum class BasicFileType {\n    REGULAR_FILE,\n    DIRECTORY,\n    SYMBOLIC_LINK,\n    OTHER\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/AbstractContentProviderFileAttributes.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport android.os.Parcelable\nimport java8.nio.file.attribute.FileTime\n\nabstract class AbstractContentProviderFileAttributes : ContentProviderFileAttributes, Parcelable {\n    protected abstract val lastModifiedTime: FileTime\n    protected abstract val mimeType: String?\n    protected abstract val size: Long\n    protected abstract val fileKey: Parcelable\n\n    override fun lastModifiedTime(): FileTime = lastModifiedTime\n\n    override fun mimeType(): String? = mimeType\n\n    override fun size(): Long = size\n\n    override fun fileKey(): Parcelable = fileKey\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/AbstractFileByteChannel.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.channels.SeekableByteChannel\nimport kotlinx.coroutines.DelicateCoroutinesApi\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.GlobalScope\nimport kotlinx.coroutines.async\nimport kotlinx.coroutines.runInterruptible\nimport kotlinx.coroutines.withTimeout\nimport me.zhanghai.android.files.util.closeSafe\nimport java.io.Closeable\nimport java.io.IOException\nimport java.io.InterruptedIOException\nimport java.nio.ByteBuffer\nimport java.nio.channels.ClosedChannelException\nimport java.nio.channels.NonReadableChannelException\nimport java.util.concurrent.CancellationException\nimport java.util.concurrent.ExecutionException\nimport java.util.concurrent.Future\n\nabstract class AbstractFileByteChannel(\n    private val isAppend: Boolean,\n    private val shouldCancelRead: Boolean = true,\n    private val joinCancelledRead: Boolean = false\n) : ForceableChannel, SeekableByteChannel {\n    private var position = 0L\n    private val readBuffer = ReadBuffer()\n    private val ioLock = Any()\n\n    private var isOpen = true\n    private val closeLock = Any()\n\n    @Throws(IOException::class)\n    final override fun read(destination: ByteBuffer): Int {\n        ensureOpen()\n        if (isAppend) {\n            throw NonReadableChannelException()\n        }\n        val remaining = destination.remaining()\n        if (remaining == 0) {\n            return 0\n        }\n        return synchronized(ioLock) {\n            readBuffer.read(destination).also {\n                if (it != -1) {\n                    position += it\n                }\n            }\n        }\n    }\n\n    protected open fun onReadAsync(\n        position: Long,\n        size: Int,\n        timeoutMillis: Long\n    ): Future<ByteBuffer> =\n        @OptIn(DelicateCoroutinesApi::class)\n        GlobalScope.async(Dispatchers.IO) {\n            withTimeout(timeoutMillis) {\n                runInterruptible {\n                    onRead(position, size)\n                }\n            }\n        }\n            .asFuture()\n\n    @Throws(IOException::class)\n    protected open fun onRead(position: Long, size: Int): ByteBuffer {\n        throw NotImplementedError()\n    }\n\n    @Throws(IOException::class)\n    final override fun write(source: ByteBuffer): Int {\n        ensureOpen()\n        val remaining = source.remaining()\n        if (remaining == 0) {\n            return 0\n        }\n        synchronized(ioLock) {\n            if (isAppend) {\n                onAppend(source)\n                position = onSize()\n            } else {\n                onWrite(position, source)\n                position += remaining - source.remaining()\n            }\n            return remaining\n        }\n    }\n\n    @Throws(IOException::class)\n    protected abstract fun onWrite(position: Long, source: ByteBuffer)\n\n    @Throws(IOException::class)\n    protected open fun onAppend(source: ByteBuffer) {\n        val position = onSize()\n        onWrite(position, source)\n    }\n\n    @Throws(IOException::class)\n    final override fun position(): Long {\n        ensureOpen()\n        synchronized(ioLock) {\n            if (isAppend) {\n                position = onSize()\n            }\n            return position\n        }\n    }\n\n    final override fun position(newPosition: Long): SeekableByteChannel {\n        ensureOpen()\n        if (isAppend) {\n            // Ignored.\n            return this\n        }\n        synchronized(ioLock) {\n            readBuffer.reposition(position, newPosition)\n            position = newPosition\n        }\n        return this\n    }\n\n    @Throws(IOException::class)\n    final override fun size(): Long {\n        ensureOpen()\n        return onSize()\n    }\n\n    @Throws(IOException::class)\n    final override fun truncate(size: Long): SeekableByteChannel {\n        ensureOpen()\n        require(size >= 0)\n        synchronized(ioLock) {\n            val currentSize = onSize()\n            if (size >= currentSize) {\n                return this\n            }\n            onTruncate(size)\n            position = position.coerceAtMost(size)\n        }\n        return this\n    }\n\n    @Throws(IOException::class)\n    protected abstract fun onTruncate(size: Long)\n\n    @Throws(IOException::class)\n    protected abstract fun onSize(): Long\n\n    @Throws(IOException::class)\n    final override fun force(metaData: Boolean) {\n        ensureOpen()\n        synchronized(ioLock) {\n            onForce(metaData)\n        }\n    }\n\n    @Throws(IOException::class)\n    protected open fun onForce(metaData: Boolean) {}\n\n    @Throws(ClosedChannelException::class)\n    private fun ensureOpen() {\n        synchronized(closeLock) {\n            if (!isOpen) {\n                throw ClosedChannelException()\n            }\n        }\n    }\n\n    final override fun isOpen(): Boolean = synchronized(closeLock) { isOpen }\n\n    @Throws(IOException::class)\n    final override fun close() {\n        synchronized(closeLock) {\n            if (!isOpen) {\n                return\n            }\n            isOpen = false\n            synchronized(ioLock) {\n                readBuffer.closeSafe()\n                onClose()\n            }\n        }\n    }\n\n    protected fun setClosed() {\n        synchronized(closeLock) {\n            isOpen = false\n        }\n    }\n\n    @Throws(IOException::class)\n    protected open fun onClose() {}\n\n    private inner class ReadBuffer : Closeable {\n        private val buffer = ByteBuffer.allocate(BUFFER_SIZE).apply { limit(0) }\n        private var bufferedPosition = 0L\n\n        private var pendingRead: Future<ByteBuffer>? = null\n        private val pendingReadLock = Any()\n\n        @Throws(IOException::class)\n        fun read(destination: ByteBuffer): Int {\n            if (!buffer.hasRemaining()) {\n                readIntoBuffer()\n                if (!buffer.hasRemaining()) {\n                    return -1\n                }\n            }\n            val length = destination.remaining().coerceAtMost(buffer.remaining())\n            val bufferLimit = buffer.limit()\n            buffer.limit(buffer.position() + length)\n            destination.put(buffer)\n            buffer.limit(bufferLimit)\n            return length\n        }\n\n        @Throws(IOException::class)\n        private fun readIntoBuffer() {\n            val future = synchronized(pendingReadLock) {\n                pendingRead?.also { pendingRead = null }\n            } ?: readIntoBufferAsync()\n            val newBuffer = try {\n                future.get()\n            } catch (e: CancellationException) {\n                throw InterruptedIOException().apply { initCause(e) }\n            } catch (e: InterruptedException) {\n                throw InterruptedIOException().apply { initCause(e) }\n            } catch (e: ExecutionException) {\n                val exception = e.cause ?: e\n                if (exception is IOException) {\n                    throw exception\n                } else {\n                    throw IOException(exception)\n                }\n            }\n            buffer.clear()\n            buffer.put(newBuffer)\n            buffer.flip()\n            if (!buffer.hasRemaining()) {\n                return\n            }\n            bufferedPosition += buffer.remaining()\n            synchronized(pendingReadLock) {\n                pendingRead = readIntoBufferAsync()\n            }\n        }\n\n        private fun readIntoBufferAsync(): Future<ByteBuffer> =\n            onReadAsync(bufferedPosition, BUFFER_SIZE, TIMEOUT_MILLIS)\n\n        fun reposition(oldPosition: Long, newPosition: Long) {\n            if (newPosition == oldPosition) {\n                return\n            }\n            val newBufferPosition = buffer.position() + (newPosition - oldPosition)\n            if (newBufferPosition in 0..buffer.limit()) {\n                buffer.position(newBufferPosition.toInt())\n            } else {\n                cancelPendingRead()\n                buffer.limit(0)\n                bufferedPosition = newPosition\n            }\n        }\n\n        override fun close() {\n            cancelPendingRead()\n        }\n\n        private fun cancelPendingRead() {\n            synchronized(pendingReadLock) {\n                pendingRead?.let {\n                    if (shouldCancelRead) {\n                        it.cancel(true)\n                        if (joinCancelledRead) {\n                            try {\n                                it.get()\n                            } catch (e: Exception) {\n                                // Ignored\n                            }\n                        }\n                    }\n                    pendingRead = null\n                }\n            }\n        }\n    }\n\n    companion object {\n        private const val BUFFER_SIZE = 1024 * 1024\n        private const val TIMEOUT_MILLIS = 15_000L\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/AbstractFileStore.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.file.FileStore\nimport java8.nio.file.attribute.FileStoreAttributeView\nimport java.io.IOException\n\nabstract class AbstractFileStore : FileStore() {\n    override fun <V : FileStoreAttributeView?> getFileStoreAttributeView(type: Class<V>): V? = null\n\n    @Throws(IOException::class)\n    override fun getAttribute(attribute: String): Any {\n        throw UnsupportedOperationException()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/AbstractPath.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.file.Path\nimport java8.nio.file.WatchEvent\nimport java8.nio.file.WatchKey\nimport java8.nio.file.WatchService\nimport java.io.IOException\n\nabstract class AbstractPath<T : AbstractPath<T>> : CovariantPath<T> {\n    override fun getFileName(): T? {\n        val nameCount = nameCount\n        return if (nameCount != 0) getName(nameCount - 1) else null\n    }\n\n    override fun startsWith(other: String): Boolean = startsWith(fileSystem.getPath(other))\n\n    override fun endsWith(other: String): Boolean = endsWith(fileSystem.getPath(other))\n\n    override fun resolve(other: String): T = resolve(fileSystem.getPath(other))\n\n    @Suppress(\"UNCHECKED_CAST\")\n    override fun resolveSibling(other: Path): T =\n        parent?.resolve(other) ?: other as T\n\n    override fun resolveSibling(other: String): T = resolveSibling(fileSystem.getPath(other))\n\n    override val names: Iterable<T>\n        get() = object : Iterable<T> {\n            override fun iterator(): Iterator<T> = object : Iterator<T> {\n                private var index = 0\n\n                override fun hasNext(): Boolean = index < nameCount\n\n                override fun next(): T {\n                    if (index >= nameCount) {\n                        throw NoSuchElementException()\n                    }\n                    return getName(index).also { ++index }\n                }\n            }\n        }\n\n    @Throws(IOException::class)\n    override fun register(watcher: WatchService, vararg events: WatchEvent.Kind<*>): WatchKey =\n        register(watcher, events)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/AbstractPathObservable.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport android.os.Handler\nimport android.os.HandlerThread\nimport me.zhanghai.android.files.util.ThrottledRunnable\nimport java.io.IOException\n\nabstract class AbstractPathObservable(private val intervalMillis: Long) : PathObservable {\n    private val observers = mutableMapOf<() -> Unit, ThrottledRunnable>()\n\n    private var isClosed = false\n\n    private val lock = Any()\n\n    override fun addObserver(observer: () -> Unit) {\n        synchronized(lock) {\n            ensureOpenLocked()\n            observers[observer] = ThrottledRunnable(handler, intervalMillis, observer)\n        }\n    }\n\n    override fun removeObserver(observer: () -> Unit) {\n        synchronized(lock) {\n            ensureOpenLocked()\n            observers.remove(observer)?.cancel()\n        }\n    }\n\n    protected fun notifyObservers() {\n        synchronized(lock) {\n            for (observer in observers.values) {\n                observer()\n            }\n        }\n    }\n\n    private fun ensureOpenLocked() {\n        if (isClosed) {\n            throw ClosedDirectoryObserverException()\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun close() {\n        synchronized(lock) {\n            if (isClosed) {\n                return\n            }\n            observers.values.forEach { it.cancel() }\n            observers.clear()\n            onCloseLocked()\n            isClosed = true\n        }\n    }\n\n    @Throws(IOException::class)\n    protected abstract fun onCloseLocked()\n\n    companion object {\n        private var notifier = Notifier()\n\n        internal val handler: Handler\n            get() = notifier.handler\n\n        init {\n            notifier.start()\n        }\n    }\n\n    private class Notifier : HandlerThread(\"AbstractPathObservable.Notifier\") {\n        val handler by lazy { Handler(looper) }\n\n        init {\n            isDaemon = true\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/AbstractPosixFileAttributes.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport android.os.Parcelable\nimport java8.nio.file.attribute.FileTime\n\nabstract class AbstractPosixFileAttributes : Parcelable, PosixFileAttributes {\n    protected abstract val lastModifiedTime: FileTime\n    protected abstract val lastAccessTime: FileTime\n    protected abstract val creationTime: FileTime\n    protected abstract val type: PosixFileType\n    protected abstract val size: Long\n    protected abstract val fileKey: Parcelable\n    protected abstract val owner: PosixUser?\n    protected abstract val group: PosixGroup?\n    protected abstract val mode: Set<PosixFileModeBit>?\n    protected abstract val seLinuxContext: ByteString?\n\n    override fun lastModifiedTime(): FileTime = lastModifiedTime\n\n    override fun lastAccessTime(): FileTime = lastAccessTime\n\n    override fun creationTime(): FileTime = creationTime\n\n    override fun type(): PosixFileType = type\n\n    override fun size(): Long = size\n\n    override fun fileKey(): Parcelable = fileKey\n\n    override fun owner(): PosixUser? = owner\n\n    override fun group(): PosixGroup? = group\n\n    override fun mode(): Set<PosixFileModeBit>? = mode\n\n    override fun seLinuxContext(): ByteString? = seLinuxContext\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/AbstractWatchKey.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.file.Path\nimport java8.nio.file.StandardWatchEventKinds\nimport java8.nio.file.WatchEvent\nimport java8.nio.file.WatchKey\n\nabstract class AbstractWatchKey<K : AbstractWatchKey<K, P>, P : Path>(\n    private val watchService: AbstractWatchService<K>,\n    private val path: P\n) : WatchKey {\n    private var isValid = true\n\n    private var isSignaled = false\n\n    private var events = mutableListOf<Event<*>>()\n\n    protected val lock = Any()\n\n    override fun isValid(): Boolean {\n        synchronized(lock) { return isValid }\n    }\n\n    fun setInvalid() {\n        synchronized(lock) { isValid = false }\n    }\n\n    fun <T> addEvent(kind: WatchEvent.Kind<T>, context: T?) {\n        synchronized(lock) {\n            if (events.isNotEmpty()) {\n                val lastEvent = events.last()\n                if (lastEvent.kind() == StandardWatchEventKinds.OVERFLOW\n                    || (lastEvent.kind() == kind && lastEvent.context() == context)) {\n                    lastEvent.repeat()\n                    return\n                }\n            }\n            if (kind === StandardWatchEventKinds.OVERFLOW || events.size >= MAX_PENDING_EVENTS) {\n                events.clear()\n                events.add(Event(StandardWatchEventKinds.OVERFLOW, null))\n                signal()\n                return\n            }\n            events.add(Event(kind, context))\n            signal()\n        }\n    }\n\n    fun signal() {\n        synchronized(lock) {\n            if (!isSignaled) {\n                isSignaled = true\n                @Suppress(\"UNCHECKED_CAST\")\n                watchService.enqueue(this as K)\n            }\n        }\n    }\n\n    override fun pollEvents(): List<WatchEvent<*>> {\n        synchronized(lock) {\n            val events = events\n            this.events = mutableListOf()\n            return events\n        }\n    }\n\n    override fun reset(): Boolean {\n        synchronized(lock) {\n            val isValid = isValid\n            if (isValid && isSignaled) {\n                if (events.isEmpty()) {\n                    isSignaled = false\n                } else {\n                    @Suppress(\"UNCHECKED_CAST\")\n                    watchService.enqueue(this as K)\n                }\n            }\n            return isValid\n        }\n    }\n\n    override fun cancel() {\n        synchronized(lock) {\n            if (isValid) {\n                @Suppress(\"UNCHECKED_CAST\")\n                watchService.cancel(this as K)\n            }\n        }\n    }\n\n    override fun watchable(): P = path\n\n    private class Event<T> constructor(\n        private val kind: WatchEvent.Kind<T>,\n        private val context: T?\n    ) : WatchEvent<T> {\n        private var count = 1\n\n        private val lock = Any()\n\n        override fun kind(): WatchEvent.Kind<T> = kind\n\n        override fun context(): T? = context\n\n        override fun count(): Int = count\n\n        fun repeat() {\n            synchronized(lock) { ++count }\n        }\n    }\n\n    companion object {\n        private const val MAX_PENDING_EVENTS = 256\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/AbstractWatchService.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.file.ClosedWatchServiceException\nimport java8.nio.file.WatchEvent\nimport java8.nio.file.WatchKey\nimport java8.nio.file.WatchService\nimport java8.nio.file.Watchable\nimport java.io.IOException\nimport java.util.concurrent.LinkedBlockingQueue\nimport java.util.concurrent.TimeUnit\n\nabstract class AbstractWatchService<K : AbstractWatchKey<K, *>> : WatchService {\n    private val queue = LinkedBlockingQueue<WatchKey>()\n\n    @Volatile\n    private var isClosed = false\n\n    private val lock = Any()\n\n    fun enqueue(key: K) {\n        queue.offer(key)\n    }\n\n    abstract fun cancel(key: K)\n\n    override fun poll(): WatchKey? {\n        ensureOpen()\n        return checkClosedKey(queue.poll())\n    }\n\n    @Throws(InterruptedException::class)\n    override fun poll(timeout: Long, unit: TimeUnit): WatchKey? {\n        ensureOpen()\n        return checkClosedKey(queue.poll(timeout, unit))\n    }\n\n    @Throws(InterruptedException::class)\n    override fun take(): WatchKey {\n        ensureOpen()\n        return checkClosedKey(queue.take())\n    }\n\n    private fun <T : WatchKey?> checkClosedKey(key: T): T {\n        if (key == KEY_CLOSED) {\n            // There may be other threads still waiting for a key.\n            queue.offer(key)\n        }\n        ensureOpen()\n        return key\n    }\n\n    private fun ensureOpen() {\n        if (isClosed) {\n            throw ClosedWatchServiceException()\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun close() {\n        synchronized(lock) {\n            if (isClosed) {\n                return\n            }\n            onClose()\n            isClosed = true\n            queue.clear()\n            queue.offer(KEY_CLOSED)\n        }\n    }\n\n    @Throws(IOException::class)\n    protected abstract fun onClose()\n\n    companion object {\n        private val KEY_CLOSED: WatchKey = DummyKey()\n    }\n\n    private class DummyKey : WatchKey {\n        override fun isValid(): Boolean {\n            throw AssertionError()\n        }\n\n        override fun pollEvents(): List<WatchEvent<*>> {\n            throw AssertionError()\n        }\n\n        override fun reset(): Boolean {\n            throw AssertionError()\n        }\n\n        override fun cancel() {\n            throw AssertionError()\n        }\n\n        override fun watchable(): Watchable {\n            throw AssertionError()\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/AccessModes.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.file.AccessMode\n\nclass AccessModes(val read: Boolean, val write: Boolean, val execute: Boolean)\n\nfun Array<out AccessMode>.toAccessModes(): AccessModes {\n    var read = false\n    var write = false\n    var execute = false\n    for (mode in this) {\n        when (mode) {\n            AccessMode.READ -> read = true\n            AccessMode.WRITE -> write = true\n            AccessMode.EXECUTE -> execute = true\n            else -> throw UnsupportedOperationException(mode.toString())\n        }\n    }\n    return AccessModes(read, write, execute)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/AndroidFileTypeDetector.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.file.Path\nimport java8.nio.file.attribute.BasicFileAttributes\nimport java8.nio.file.spi.FileTypeDetector\nimport me.zhanghai.android.files.file.MimeType\nimport me.zhanghai.android.files.file.forSpecialPosixFileType\nimport me.zhanghai.android.files.file.guessFromPath\nimport java.io.IOException\n\nobject AndroidFileTypeDetector : FileTypeDetector() {\n    @Throws(IOException::class)\n    override fun probeContentType(path: Path): String {\n        val attributes = path.readAttributes(BasicFileAttributes::class.java)\n        return getMimeType(path, attributes)\n    }\n\n    fun getMimeType(path: Path, attributes: BasicFileAttributes): String {\n        MimeType.forSpecialPosixFileType(attributes.posixFileType)?.let { return it.value }\n        if (attributes.isDirectory) {\n            return MimeType.DIRECTORY.value\n        }\n        if (attributes is ContentProviderFileAttributes) {\n            attributes.mimeType()?.let { return it }\n        }\n        return MimeType.guessFromPath(path.toString()).value\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/ByteBufferExtensions.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java.nio.ByteBuffer\nimport kotlin.reflect.KClass\n\nprivate val EMPTY_BYTE_BUFFER = ByteBuffer.allocate(0)\n\nval KClass<ByteBuffer>.EMPTY: ByteBuffer\n    get() = EMPTY_BYTE_BUFFER\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/ByteBufferInputStream.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java.io.IOException\nimport java.io.InputStream\nimport java.nio.ByteBuffer\n\nclass ByteBufferInputStream(buffer: ByteBuffer) : InputStream() {\n    private var buffer: ByteBuffer? = buffer\n\n    override fun read(): Int {\n        val buffer = ensureOpen()\n        return if (buffer.hasRemaining()) buffer.get().toInt() and 0xFF else -1\n    }\n\n    override fun read(bytes: ByteArray, offset: Int, length: Int): Int {\n        val buffer = ensureOpen()\n        if (length == 0) {\n            return 0\n        }\n        val remaining = buffer.remaining()\n        if (remaining == 0) {\n            return -1\n        }\n        val readLength = length.coerceAtMost(remaining)\n        buffer.get(bytes, offset, readLength)\n        return readLength\n    }\n\n    override fun skip(length: Long): Long {\n        val buffer = ensureOpen()\n        if (length <= 0) {\n            return 0\n        }\n        val skippedLength = length.toInt().coerceAtMost(buffer.remaining())\n        buffer.position(buffer.position() + skippedLength)\n        return skippedLength.toLong()\n    }\n\n    override fun available(): Int {\n        val buffer = ensureOpen()\n        return buffer.remaining()\n    }\n\n    override fun markSupported(): Boolean = true\n\n    override fun mark(readlimit: Int) {\n        val buffer = ensureOpen()\n        buffer.mark()\n    }\n\n    override fun reset() {\n        val buffer = ensureOpen()\n        buffer.reset()\n    }\n\n    override fun close() {\n        buffer = null\n    }\n\n    private fun ensureOpen(): ByteBuffer = buffer ?: throw IOException(\"Stream closed\")\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/ByteString.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport android.os.Parcelable\nimport kotlinx.parcelize.IgnoredOnParcel\nimport kotlinx.parcelize.Parcelize\nimport kotlin.contracts.ExperimentalContracts\nimport kotlin.contracts.contract\nimport kotlin.math.min\n\n@Parcelize\n// @see https://youtrack.jetbrains.com/issue/KT-24842\n// @Parcelize throws IllegalAccessError if the primary constructor is private.\nclass ByteString internal constructor(\n    private val bytes: ByteArray\n) : Comparable<ByteString>, Parcelable {\n    val length: Int\n        get() = bytes.size\n\n    operator fun get(index: Int): Byte = bytes[index]\n\n    operator fun iterator(): ByteIterator = bytes.iterator()\n\n    val indices: IntRange\n        get() = bytes.indices\n\n    val lastIndex: Int\n        get() = bytes.lastIndex\n\n    fun isEmpty(): Boolean = bytes.isEmpty()\n\n    fun isNotEmpty(): Boolean = bytes.isNotEmpty()\n\n    fun borrowBytes(): ByteArray = bytes\n\n    fun toBytes(): ByteArray = bytes.copyOf()\n\n    fun startsWith(prefix: ByteString, startIndex: Int = 0): Boolean {\n        if (startIndex !in 0..length - prefix.length) {\n            return false\n        }\n        for (index in prefix.indices) {\n            if (this[startIndex + index] != prefix[index]) {\n                return false\n            }\n        }\n        return true\n    }\n\n    fun endsWith(suffix: ByteString): Boolean = startsWith(suffix, length - suffix.length)\n\n    fun indexOf(byte: Byte, fromIndex: Int = 0): Int {\n        for (index in fromIndex.coerceAtLeast(0)..<length) {\n            if (this[index] == byte) {\n                return index\n            }\n        }\n        return -1\n    }\n\n    fun lastIndexOf(byte: Byte, fromIndex: Int = length - 1): Int {\n        for (index in fromIndex.coerceAtMost(length - 1) downTo 0) {\n            if (this[index] == byte) {\n                return index\n            }\n        }\n        return -1\n    }\n\n    fun contains(byte: Byte): Boolean = indexOf(byte) != -1\n\n    fun indexOf(substring: ByteString, fromIndex: Int = 0): Int {\n        for (index in fromIndex.coerceAtLeast(0)..<length - substring.length) {\n            if (startsWith(substring, index)) {\n                return index\n            }\n        }\n        return -1\n    }\n\n    fun lastIndexOf(substring: ByteString): Int = lastIndexOf(substring, length - substring.length)\n\n    fun lastIndexOf(substring: ByteString, fromIndex: Int): Int {\n        for (index in fromIndex.coerceAtMost(length - substring.length) downTo 0) {\n            if (startsWith(substring, index)) {\n                return index\n            }\n        }\n        return -1\n    }\n\n    fun contains(substring: ByteString): Boolean = indexOf(substring) != -1\n\n    fun substring(start: Int, end: Int = length): ByteString {\n        val length = length\n        if (start < 0 || end > length || start > end) {\n            throw IndexOutOfBoundsException()\n        }\n        if (start == 0 && end == length) {\n            return this\n        }\n        return ByteString(bytes.copyOfRange(start, end))\n    }\n\n    fun substring(range: IntRange): ByteString = substring(range.first, range.last + 1)\n\n    operator fun plus(other: ByteString): ByteString {\n        if (other.isEmpty()) {\n            return this\n        }\n        return ByteString(bytes + other.bytes)\n    }\n\n    fun split(delimiter: ByteString): List<ByteString> {\n        require(delimiter.isNotEmpty())\n        val result = mutableListOf<ByteString>()\n        var start = 0\n        while (true) {\n            val end = indexOf(delimiter, start)\n            if (end == -1) {\n                break\n            }\n            result.add(substring(start, end))\n            start = end + delimiter.length\n        }\n        result.add(substring(start))\n        return result\n    }\n\n    @IgnoredOnParcel\n    private var stringCache: String? = null\n\n    override fun toString(): String {\n        // We are okay with the potential race condition here.\n        var string = stringCache\n        if (string == null) {\n            // String() uses replacement char instead of throwing exception.\n            string = String(bytes)\n            stringCache = string\n        }\n        return string\n    }\n\n    val cstr: ByteArray\n        get() = bytes + '\\u0000'.code.toByte()\n\n    override fun equals(other: Any?): Boolean {\n        if (this === other) {\n            return true\n        }\n        if (javaClass != other?.javaClass) {\n            return false\n        }\n        other as ByteString\n        return bytes contentEquals other.bytes\n    }\n\n    override fun hashCode(): Int = bytes.contentHashCode()\n\n    override fun compareTo(other: ByteString): Int = bytes.compareTo(other.bytes)\n\n    private fun ByteArray.compareTo(other: ByteArray): Int {\n        val size = size\n        val otherSize = other.size\n        for (index in 0..<min(size, otherSize)) {\n            val byte = this[index]\n            val otherByte = other[index]\n            val result = byte - otherByte\n            if (result != 0) {\n                return result\n            }\n        }\n        return size - otherSize\n    }\n\n    companion object {\n        val EMPTY = ByteString(ByteArray(0))\n\n        fun fromBytes(bytes: ByteArray, start: Int = 0, end: Int = bytes.size): ByteString =\n            ByteString(bytes.copyOfRange(start, end))\n\n        fun takeBytes(bytes: ByteArray): ByteString = ByteString(bytes)\n\n        fun fromString(string: String): ByteString =\n            ByteString(string.toByteArray()).apply { stringCache = string }\n    }\n}\n\nfun Byte.toByteString(): ByteString = ByteString.takeBytes(byteArrayOf(this))\n\nfun ByteArray.toByteString(start: Int = 0, end: Int = size): ByteString =\n    ByteString.fromBytes(this, start, end)\n\nfun ByteArray.moveToByteString(): ByteString = ByteString.takeBytes(this)\n\nfun String.toByteString(): ByteString = ByteString.fromString(this)\n\n@OptIn(ExperimentalContracts::class)\nfun ByteString?.isNullOrEmpty(): Boolean {\n    contract { returns(false) implies (this@isNullOrEmpty != null) }\n    return this == null || this.isEmpty()\n}\n\nfun ByteString.takeIfNotEmpty(): ByteString? = if (isNotEmpty()) this else null\n\nfun ByteString.drop(n: Int): ByteString {\n    require(n >= 0)\n    return substring(n.coerceAtMost(length))\n}\n\nfun ByteString.dropLast(n: Int): ByteString {\n    require(n >= 0)\n    return take((length - n).coerceAtLeast(0))\n}\n\ninline fun ByteString.dropLastWhile(predicate: (Byte) -> Boolean): ByteString {\n    for (index in lastIndex downTo 0) {\n        if (!predicate(this[index]))\n            return substring(0, index + 1)\n    }\n    return ByteString.EMPTY\n}\n\ninline fun ByteString.dropWhile(predicate: (Byte) -> Boolean): ByteString {\n    for (index in indices) {\n        if (!predicate(this[index]))\n            return substring(index)\n    }\n    return ByteString.EMPTY\n}\n\nfun ByteString.take(n: Int): ByteString {\n    require(n >= 0)\n    return substring(0, n.coerceAtMost(length))\n}\n\nfun ByteString.takeLast(n: Int): ByteString {\n    require(n >= 0)\n    val length = length\n    return substring(length - n.coerceAtMost(length))\n}\n\ninline fun ByteString.takeLastWhile(predicate: (Byte) -> Boolean): ByteString {\n    for (index in lastIndex downTo 0) {\n        if (!predicate(this[index])) {\n            return substring(index + 1)\n        }\n    }\n    return this\n}\n\ninline fun ByteString.takeWhile(predicate: (Byte) -> Boolean): ByteString {\n    for (index in indices) {\n        if (!predicate(get(index))) {\n            return substring(0, index)\n        }\n    }\n    return this\n}\n\nfun ByteString.substringBefore(\n    delimiter: Byte,\n    missingDelimiterValue: ByteString = this\n): ByteString {\n    val index = indexOf(delimiter)\n    return if (index != -1) substring(0, index) else missingDelimiterValue\n}\n\nfun ByteString.substringBefore(\n    delimiter: ByteString,\n    missingDelimiterValue: ByteString = this\n): ByteString {\n    val index = indexOf(delimiter)\n    return if (index != -1) substring(0, index) else missingDelimiterValue\n}\n\nfun ByteString.substringAfter(\n    delimiter: Byte,\n    missingDelimiterValue: ByteString = this\n): ByteString {\n    val index = indexOf(delimiter)\n    return if (index != -1) substring(index + 1, length) else missingDelimiterValue\n}\n\nfun ByteString.substringAfter(\n    delimiter: ByteString,\n    missingDelimiterValue: ByteString = this\n): ByteString {\n    val index = indexOf(delimiter)\n    return if (index != -1) substring(index + delimiter.length, length) else missingDelimiterValue\n}\n\nfun ByteString.substringBeforeLast(\n    delimiter: Byte,\n    missingDelimiterValue: ByteString = this\n): ByteString {\n    val index = lastIndexOf(delimiter)\n    return if (index != -1) substring(0, index) else missingDelimiterValue\n}\n\nfun ByteString.substringBeforeLast(\n    delimiter: ByteString,\n    missingDelimiterValue: ByteString = this\n): ByteString {\n    val index = lastIndexOf(delimiter)\n    return if (index != -1) substring(0, index) else missingDelimiterValue\n}\n\nfun ByteString.substringAfterLast(\n    delimiter: Byte,\n    missingDelimiterValue: ByteString = this\n): ByteString {\n    val index = lastIndexOf(delimiter)\n    return if (index != -1) substring(index + 1, length) else missingDelimiterValue\n}\n\nfun ByteString.substringAfterLast(\n    delimiter: ByteString,\n    missingDelimiterValue: ByteString = this\n): ByteString {\n    val index = lastIndexOf(delimiter)\n    return if (index != -1) substring(index + delimiter.length, length) else missingDelimiterValue\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/ByteStringBuilder.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nclass ByteStringBuilder(capacity: Int = 16) {\n    private var bytes = ByteArray(capacity)\n\n    var length = 0\n        private set\n\n    constructor(byteString: ByteString) : this(byteString.length + 16) {\n        append(byteString)\n    }\n\n    operator fun get(index: Int): Byte {\n        if (index !in 0..<length) {\n            throw IndexOutOfBoundsException()\n        }\n        return bytes[index]\n    }\n\n    val isEmpty: Boolean\n        get() = length == 0\n\n    fun capacity(): Int = bytes.size\n\n    fun append(byte: Byte): ByteStringBuilder {\n        ensureCapacity(length + 1)\n        bytes[length] = byte\n        ++length\n        return this\n    }\n\n    fun append(bytes: ByteArray, start: Int = 0, end: Int = bytes.size): ByteStringBuilder {\n        val newLength = length + (end - start)\n        ensureCapacity(newLength)\n        bytes.copyInto(this.bytes, length, start, end)\n        length = newLength\n        return this\n    }\n\n    fun append(byteString: ByteString): ByteStringBuilder = append(byteString.borrowBytes())\n\n    private fun ensureCapacity(minimumCapacity: Int) {\n        val capacity = bytes.size\n        if (minimumCapacity > capacity) {\n            var newCapacity = (capacity shl 1) + 2\n            if (newCapacity < minimumCapacity) {\n                newCapacity = minimumCapacity\n            }\n            bytes = bytes.copyOf(newCapacity)\n        }\n    }\n\n    fun toByteString(): ByteString = bytes.toByteString(0, length)\n\n    override fun toString(): String = String(bytes, 0, length)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/ByteStringListPath.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport java.net.URI\nimport java8.nio.file.InvalidPathException\nimport java8.nio.file.Path\nimport java8.nio.file.ProviderMismatchException\nimport kotlin.math.min\nimport me.zhanghai.android.files.compat.readBooleanCompat\nimport me.zhanghai.android.files.compat.removeLastCompat\nimport me.zhanghai.android.files.compat.writeBooleanCompat\nimport me.zhanghai.android.files.compat.writeParcelableListCompat\nimport me.zhanghai.android.files.util.endsWith\nimport me.zhanghai.android.files.util.hash\nimport me.zhanghai.android.files.util.readParcelableListCompat\nimport me.zhanghai.android.files.util.startsWith\n\nabstract class ByteStringListPath<T : ByteStringListPath<T>> : AbstractPath<T>, Parcelable {\n    protected val separator: Byte\n    private val isAbsolute: Boolean\n    private val segments: List<ByteString>\n\n    @Volatile\n    private var byteStringCache: ByteString? = null\n\n    constructor(separator: Byte, path: ByteString) {\n        require(separator != '\\u0000'.code.toByte()) { \"Separator cannot be the nul character\" }\n        this.separator = separator\n        if (path.contains('\\u0000'.code.toByte())) {\n            throw InvalidPathException(path.toString(), \"Path cannot contain nul characters\")\n        }\n        isAbsolute = isPathAbsolute(path)\n        val segments = mutableListOf<ByteString>()\n        if (path.isEmpty()) {\n            segments += ByteString.EMPTY\n        } else {\n            var start = 0\n            val length = path.length\n            while (start < length) {\n                while (start < length && path[start] == separator) {\n                    ++start\n                }\n                if (start == length) {\n                    break\n                }\n                var end = start + 1\n                while (end < length && path[end] != separator) {\n                    ++end\n                }\n                segments += path.substring(start, end)\n                start = end\n            }\n        }\n        this.segments = segments\n        checkIsAbsoluteOrNotEmpty()\n    }\n\n    protected constructor(separator: Byte, isAbsolute: Boolean, segments: List<ByteString>) {\n        this.separator = separator\n        this.isAbsolute = isAbsolute\n        this.segments = segments\n        checkIsAbsoluteOrNotEmpty()\n    }\n\n    private fun checkIsAbsoluteOrNotEmpty() {\n        check(isAbsolute || segments.isNotEmpty()) { \"Non-absolute path must not be empty\" }\n    }\n\n    override fun isAbsolute(): Boolean = isAbsolute\n\n    val fileNameByteString: ByteString?\n        get() = segments.lastOrNull()\n\n    override fun getParent(): T? =\n        if (segments.isNotEmpty()) createPath(isAbsolute, segments.dropLast(1)) else null\n\n    override fun getNameCount(): Int = segments.size\n\n    override fun getName(index: Int): T = createPath(false, listOf(getNameByteString(index)))\n\n    fun getNameByteString(index: Int): ByteString = segments[index]\n\n    override fun subpath(beginIndex: Int, endIndex: Int): T {\n        val subSegments = segments.subList(beginIndex, endIndex).toList()\n        return createPath(false, subSegments)\n    }\n\n    override fun startsWith(other: Path): Boolean {\n        if (this === other) {\n            return true\n        }\n        if (javaClass != other.javaClass || provider != other.provider\n            || fileSystem != other.fileSystem) {\n            return false\n        }\n        other as ByteStringListPath<*>\n        return segments.startsWith(other.segments)\n    }\n\n    fun startsWith(other: ByteString): Boolean = startsWith(createPath(other))\n\n    override fun endsWith(other: Path): Boolean {\n        if (this === other) {\n            return true\n        }\n        if (javaClass != other.javaClass || provider != other.provider\n            || fileSystem != other.fileSystem) {\n            return false\n        }\n        other as ByteStringListPath<*>\n        return segments.endsWith(other.segments)\n    }\n\n    fun endsWith(other: ByteString): Boolean = endsWith(createPath(other))\n\n    override fun normalize(): T {\n        val normalizedSegments = mutableListOf<ByteString>()\n        for (segment in segments) {\n            if (segment == BYTE_STRING_DOT) {\n                // Ignored.\n            } else if (segment == BYTE_STRING_DOT_DOT) {\n                if (normalizedSegments.isEmpty()) {\n                    if (!isAbsolute) {\n                        normalizedSegments += segment\n                    }\n                } else {\n                    if (normalizedSegments.last() == BYTE_STRING_DOT_DOT) {\n                        normalizedSegments += segment\n                    } else {\n                        normalizedSegments.removeLastCompat()\n                    }\n                }\n            } else {\n                normalizedSegments += segment\n            }\n        }\n        if (!isAbsolute && normalizedSegments.isEmpty()) {\n            return createEmptyPath()\n        }\n        return createPath(isAbsolute, normalizedSegments)\n    }\n\n    override fun resolve(other: Path): T {\n        if (javaClass != other.javaClass || provider != other.provider) {\n            throw ProviderMismatchException(other.toString())\n        }\n        @Suppress(\"UNCHECKED_CAST\")\n        other as T\n        require(fileSystem == other.fileSystem) {\n            \"The other path must have the same file system as this path\"\n        }\n        if (other.isAbsolute) {\n            return other\n        }\n        if (other.isEmpty) {\n            @Suppress(\"UNCHECKED_CAST\")\n            return this as T\n        }\n        if (isEmpty) {\n            return other\n        }\n        val resolvedSegments = segments + other.segments\n        return createPath(isAbsolute, resolvedSegments)\n    }\n\n    fun resolve(other: ByteString): T = resolve(createPath(other))\n\n    fun resolveSibling(other: ByteString): T = resolveSibling(createPath(other))\n\n    override fun relativize(other: Path): T {\n        if (javaClass != other.javaClass || provider != other.provider) {\n            throw ProviderMismatchException(other.toString())\n        }\n        @Suppress(\"UNCHECKED_CAST\")\n        other as T\n        require(fileSystem == other.fileSystem) {\n            \"The other path must have the same file system as this path\"\n        }\n        require(isAbsolute == other.isAbsolute) {\n            \"The other path must be as absolute as this path\"\n        }\n        if (isEmpty) {\n            return other\n        }\n        if (this == other) {\n            return createEmptyPath()\n        }\n        val segmentsSize = segments.size\n        val otherSegmentsSize = other.segments.size\n        val minSegmentsSize = min(segmentsSize, otherSegmentsSize)\n        var commonSegmentsSize = 0\n        while (commonSegmentsSize < minSegmentsSize\n            && segments[commonSegmentsSize] == other.segments[commonSegmentsSize]) {\n            ++commonSegmentsSize\n        }\n        val relativeSegments = mutableListOf<ByteString>()\n        val dotDotCount = segmentsSize - commonSegmentsSize\n        if (dotDotCount > 0) {\n            repeat(dotDotCount) { relativeSegments += BYTE_STRING_DOT_DOT }\n        }\n        if (commonSegmentsSize < otherSegmentsSize) {\n            relativeSegments += other.segments.subList(commonSegmentsSize, otherSegmentsSize)\n        }\n        return createPath(false, relativeSegments)\n    }\n\n    override fun toUri(): URI = URI::class.create(uriScheme, uriAuthority, uriPath, uriQuery)\n\n    override fun toAbsolutePath(): T {\n        if (isAbsolute) {\n            @Suppress(\"UNCHECKED_CAST\")\n            return this as T\n        }\n        return defaultDirectory.resolve(this)\n    }\n\n    open fun toByteString(): ByteString {\n        // We are okay with the potential race condition here.\n        var byteString = byteStringCache\n        if (byteString == null) {\n            val builder = ByteStringBuilder()\n            if (isAbsolute && root != null) {\n                builder.append(separator)\n            }\n            var first = true\n            for (segment in segments) {\n                if (first) {\n                    first = false\n                } else {\n                    builder.append(separator)\n                }\n                builder.append(segment)\n            }\n            byteString = builder.toByteString()\n            byteStringCache = byteString\n        }\n        return byteString\n    }\n\n    override fun toString(): String = toByteString().toString()\n\n    override fun equals(other: Any?): Boolean {\n        if (this === other) {\n            return true\n        }\n        if (javaClass != other?.javaClass) {\n            return false\n        }\n        other as ByteStringListPath<*>\n        return separator == other.separator\n            && segments == other.segments\n            && isAbsolute == other.isAbsolute\n            && fileSystem == other.fileSystem\n    }\n\n    override fun hashCode(): Int = hash(separator, segments, isAbsolute, fileSystem)\n\n    override fun compareTo(other: Path): Int {\n        javaClass.cast(other)\n        @Suppress(\"UNCHECKED_CAST\")\n        other as T\n        if (provider != other.provider) {\n            throw ClassCastException(other.toString())\n        }\n        return toByteString().compareTo(other.toByteString())\n    }\n\n    val nameByteStrings: Iterable<ByteString>\n        get() = object : Iterable<ByteString> {\n            override fun iterator(): Iterator<ByteString> = object : Iterator<ByteString> {\n                private var index = 0\n\n                override fun hasNext(): Boolean = index < nameCount\n\n                override fun next(): ByteString {\n                    if (index >= nameCount) {\n                        throw NoSuchElementException()\n                    }\n                    val name = getNameByteString(index)\n                    ++index\n                    return name\n                }\n            }\n        }\n\n    val isEmpty: Boolean\n        get() = !isAbsolute && segments.size == 1 && segments[0] == ByteString.EMPTY\n\n    protected abstract fun isPathAbsolute(path: ByteString): Boolean\n\n    protected abstract fun createPath(path: ByteString): T\n\n    protected abstract fun createPath(absolute: Boolean, segments: List<ByteString>): T\n\n    private fun createEmptyPath(): T = createPath(false, listOf(ByteString.EMPTY))\n\n    protected open val uriScheme: String\n        get() = fileSystem.provider().scheme\n\n    protected open val uriAuthority: UriAuthority\n        get() = UriAuthority.EMPTY\n\n    protected open val uriPath: ByteString\n        get() = toAbsolutePath().toByteString()\n\n    protected open val uriQuery: ByteString?\n        get() = null\n\n    protected abstract val defaultDirectory: T\n\n    protected constructor(source: Parcel) {\n        separator = source.readByte()\n        isAbsolute = source.readBooleanCompat()\n        segments = source.readParcelableListCompat()\n    }\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        dest.writeByte(separator)\n        dest.writeBooleanCompat(isAbsolute)\n        dest.writeParcelableListCompat(segments, flags)\n    }\n\n    companion object {\n        private val BYTE_STRING_DOT = \".\".toByteString()\n        private val BYTE_STRING_DOT_DOT = \"..\".toByteString()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/ByteStringListPathCreator.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.file.FileSystem\n\ninterface ByteStringListPathCreator {\n    fun getPath(first: ByteString, vararg more: ByteString): ByteStringListPath<*>\n}\n\nfun FileSystem.getPath(first: ByteString, vararg more: ByteString): ByteStringListPath<*> =\n    (this as ByteStringListPathCreator).getPath(first, *more)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/ByteStringPath.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport java8.nio.file.FileSystem\nimport java8.nio.file.LinkOption\nimport java8.nio.file.Path\nimport java8.nio.file.WatchEvent\nimport java8.nio.file.WatchKey\nimport java8.nio.file.WatchService\nimport me.zhanghai.android.files.util.readParcelable\nimport java.io.File\nimport java.net.URI\n\nclass ByteStringPath(private val byteString: ByteString) : Parcelable, Path {\n    fun toByteString(): ByteString = byteString\n\n    override fun toString(): String = byteString.toString()\n\n    override fun getFileSystem(): FileSystem {\n        throw UnsupportedOperationException()\n    }\n\n    override fun isAbsolute(): Boolean {\n        throw UnsupportedOperationException()\n    }\n\n    override fun getRoot(): Path {\n        throw UnsupportedOperationException()\n    }\n\n    override fun getFileName(): Path {\n        throw UnsupportedOperationException()\n    }\n\n    override fun getParent(): Path {\n        throw UnsupportedOperationException()\n    }\n\n    override fun getNameCount(): Int {\n        throw UnsupportedOperationException()\n    }\n\n    override fun getName(index: Int): Path {\n        throw UnsupportedOperationException()\n    }\n\n    override fun subpath(beginIndex: Int, endIndex: Int): Path {\n        throw UnsupportedOperationException()\n    }\n\n    override fun startsWith(other: Path): Boolean {\n        throw UnsupportedOperationException()\n    }\n\n    override fun startsWith(other: String): Boolean {\n        throw UnsupportedOperationException()\n    }\n\n    override fun endsWith(other: Path): Boolean {\n        throw UnsupportedOperationException()\n    }\n\n    override fun endsWith(other: String): Boolean {\n        throw UnsupportedOperationException()\n    }\n\n    override fun normalize(): Path {\n        throw UnsupportedOperationException()\n    }\n\n    override fun resolve(other: Path): Path {\n        throw UnsupportedOperationException()\n    }\n\n    override fun resolve(other: String): Path {\n        throw UnsupportedOperationException()\n    }\n\n    override fun resolveSibling(other: Path): Path {\n        throw UnsupportedOperationException()\n    }\n\n    override fun resolveSibling(other: String): Path {\n        throw UnsupportedOperationException()\n    }\n\n    override fun relativize(other: Path): Path {\n        throw UnsupportedOperationException()\n    }\n\n    override fun toUri(): URI {\n        throw UnsupportedOperationException()\n    }\n\n    override fun toAbsolutePath(): Path {\n        throw UnsupportedOperationException()\n    }\n\n    override fun toRealPath(vararg options: LinkOption): Path {\n        throw UnsupportedOperationException()\n    }\n\n    override fun toFile(): File {\n        throw UnsupportedOperationException()\n    }\n\n    override fun register(\n        watcher: WatchService,\n        events: Array<WatchEvent.Kind<*>>,\n        vararg modifiers: WatchEvent.Modifier\n    ): WatchKey {\n        throw UnsupportedOperationException()\n    }\n\n    override fun register(watcher: WatchService, vararg events: WatchEvent.Kind<*>): WatchKey {\n        throw UnsupportedOperationException()\n    }\n\n    override fun iterator(): MutableIterator<Path> {\n        throw UnsupportedOperationException()\n    }\n\n    override fun compareTo(other: Path): Int {\n        throw UnsupportedOperationException()\n    }\n\n    private constructor(source: Parcel) : this(source.readParcelable<ByteString>()!!)\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        dest.writeParcelable(byteString, flags)\n    }\n\n    companion object {\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<ByteStringPath> {\n            override fun createFromParcel(source: Parcel): ByteStringPath = ByteStringPath(source)\n\n            override fun newArray(size: Int): Array<ByteStringPath?> = arrayOfNulls(size)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/ChannelExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java.io.InputStream\nimport java.io.OutputStream\nimport java.nio.channels.Channels\nimport java.nio.channels.ReadableByteChannel\nimport java.nio.channels.WritableByteChannel\n\nfun ReadableByteChannel.newInputStream(): InputStream = Channels.newInputStream(this)\n\nfun WritableByteChannel.newOutputStream(): OutputStream = Channels.newOutputStream(this)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/CloseableIterator.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java.io.Closeable\n\ninterface CloseableIterator<T> : Iterator<T>, Closeable\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/ClosedDirectoryObserverException.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nclass ClosedDirectoryObserverException : IllegalStateException()\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/ContentProviderFileAttributes.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport android.os.Parcelable\nimport java8.nio.file.attribute.BasicFileAttributes\nimport java8.nio.file.attribute.FileTime\nimport me.zhanghai.android.files.file.MimeType\n\ninterface ContentProviderFileAttributes : BasicFileAttributes {\n    override fun lastAccessTime(): FileTime = lastModifiedTime()\n\n    override fun creationTime(): FileTime = lastModifiedTime()\n\n    fun mimeType(): String?\n\n    override fun isRegularFile(): Boolean = !isDirectory\n\n    override fun isDirectory(): Boolean = mimeType() == MimeType.DIRECTORY.value\n\n    override fun isSymbolicLink(): Boolean = false\n\n    override fun isOther(): Boolean = false\n\n    override fun fileKey(): Parcelable\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/CopyOptions.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.file.CopyOption\nimport java8.nio.file.LinkOption\nimport java8.nio.file.StandardCopyOption\n\nclass CopyOptions(\n    val replaceExisting: Boolean,\n    val copyAttributes: Boolean,\n    val atomicMove: Boolean,\n    val noFollowLinks: Boolean,\n    val progressIntervalMillis: Long,\n    val progressListener: ((Long) -> Unit)?\n) {\n    fun toArray(): Array<CopyOption> {\n        val options = mutableListOf<CopyOption>()\n        if (replaceExisting) {\n            options += StandardCopyOption.REPLACE_EXISTING\n        }\n        if (copyAttributes) {\n            options += StandardCopyOption.COPY_ATTRIBUTES\n        }\n        if (atomicMove) {\n            options += StandardCopyOption.ATOMIC_MOVE\n        }\n        if (noFollowLinks) {\n            options += LinkOption.NOFOLLOW_LINKS\n        }\n        if (progressListener != null) {\n            options += ProgressCopyOption(progressIntervalMillis, progressListener)\n        }\n        return options.toTypedArray()\n    }\n}\n\nfun Array<out CopyOption>.toCopyOptions(): CopyOptions {\n    var replaceExisting = false\n    var copyAttributes = false\n    var atomicMove = false\n    var noFollowLinks = false\n    var progressIntervalMillis = 0L\n    var progressListener: ((Long) -> Unit)? = null\n    for (option in this) {\n        when {\n            option is StandardCopyOption ->\n                when (option) {\n                    StandardCopyOption.REPLACE_EXISTING -> replaceExisting = true\n                    StandardCopyOption.COPY_ATTRIBUTES -> copyAttributes = true\n                    StandardCopyOption.ATOMIC_MOVE -> atomicMove = true\n                    else -> throw UnsupportedOperationException(option.toString())\n                }\n            option === LinkOption.NOFOLLOW_LINKS -> noFollowLinks = true\n            option is ProgressCopyOption -> {\n                progressIntervalMillis = option.intervalMillis\n                progressListener = option.listener\n            }\n            else -> {\n                throw UnsupportedOperationException(option.toString())\n            }\n        }\n    }\n    return CopyOptions(\n        replaceExisting, copyAttributes, atomicMove, noFollowLinks, progressIntervalMillis,\n        progressListener\n    )\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/CovariantPath.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.file.LinkOption\nimport java8.nio.file.Path\nimport java.io.IOException\n\ninterface CovariantPath<T : CovariantPath<T>> : Path {\n    override fun getRoot(): T?\n\n    override fun getFileName(): T?\n\n    override fun getParent(): T?\n\n    override fun getName(index: Int): T\n\n    override fun subpath(beginIndex: Int, endIndex: Int): T\n\n    override fun normalize(): T\n\n    override fun resolve(other: Path): T\n\n    override fun resolve(other: String): T\n\n    override fun resolveSibling(other: Path): T\n\n    override fun resolveSibling(other: String): T\n\n    override fun relativize(other: Path): T\n\n    override fun toAbsolutePath(): T\n\n    @Throws(IOException::class)\n    override fun toRealPath(vararg options: LinkOption): T\n\n    override fun iterator(): MutableIterator<Path> {\n        throw UnsupportedOperationException()\n    }\n\n    val names: Iterable<T>\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/DelegateFileChannel.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.channels.FileChannel\nimport java.io.IOException\nimport java.nio.ByteBuffer\nimport java.nio.MappedByteBuffer\nimport java.nio.channels.FileLock\nimport java.nio.channels.ReadableByteChannel\nimport java.nio.channels.WritableByteChannel\n\nopen class DelegateFileChannel(private val channel: FileChannel) : FileChannel() {\n    @Throws(IOException::class)\n    override fun read(dst: ByteBuffer): Int = channel.read(dst)\n\n    @Throws(IOException::class)\n    override fun read(dsts: Array<ByteBuffer>, offset: Int, length: Int): Long =\n        channel.read(dsts, offset, length)\n\n    @Throws(IOException::class)\n    override fun write(src: ByteBuffer): Int = channel.write(src)\n\n    @Throws(IOException::class)\n    override fun write(srcs: Array<ByteBuffer>, offset: Int, length: Int): Long =\n        channel.write(srcs, offset, length)\n\n    @Throws(IOException::class)\n    override fun position(): Long = channel.position()\n\n    @Throws(IOException::class)\n    override fun position(newPosition: Long): FileChannel {\n        channel.position(newPosition)\n        return this\n    }\n\n    @Throws(IOException::class)\n    override fun size(): Long = channel.size()\n\n    @Throws(IOException::class)\n    override fun truncate(size: Long): FileChannel {\n        channel.truncate(size)\n        return this\n    }\n\n    @Throws(IOException::class)\n    override fun force(metaData: Boolean) {\n        channel.force(metaData)\n    }\n\n    @Throws(IOException::class)\n    override fun transferTo(position: Long, count: Long, target: WritableByteChannel): Long =\n        channel.transferTo(position, count, target)\n\n    @Throws(IOException::class)\n    override fun transferFrom(src: ReadableByteChannel, position: Long, count: Long): Long =\n        channel.transferFrom(src, position, count)\n\n    @Throws(IOException::class)\n    override fun read(dst: ByteBuffer, position: Long): Int = channel.read(dst, position)\n\n    @Throws(IOException::class)\n    override fun write(src: ByteBuffer, position: Long): Int = channel.write(src, position)\n\n    @Throws(IOException::class)\n    override fun map(mode: MapMode, position: Long, size: Long): MappedByteBuffer =\n        channel.map(mode, position, size)\n\n    @Throws(IOException::class)\n    override fun lock(position: Long, size: Long, shared: Boolean): FileLock =\n        channel.lock(position, size, shared)\n\n    @Throws(IOException::class)\n    override fun tryLock(position: Long, size: Long, shared: Boolean): FileLock? =\n        channel.tryLock(position, size, shared)\n\n    @Throws(IOException::class)\n    override fun implCloseChannel() {\n        channel.close()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/DelegateInputStream.kt",
    "content": "package me.zhanghai.android.files.provider.common\n\nimport java.io.IOException\nimport java.io.InputStream\n\nopen class DelegateInputStream(private val inputStream: InputStream) : InputStream() {\n    @Throws(IOException::class)\n    override fun read(): Int = inputStream.read()\n\n    @Throws(IOException::class)\n    override fun read(b: ByteArray): Int = inputStream.read(b)\n\n    @Throws(IOException::class)\n    override fun read(b: ByteArray, off: Int, len: Int): Int = inputStream.read(b, off, len)\n\n    @Throws(IOException::class)\n    override fun skip(n: Long): Long = inputStream.skip(n)\n\n    @Throws(IOException::class)\n    override fun available(): Int = inputStream.available()\n\n    @Throws(IOException::class)\n    override fun close() {\n        inputStream.close()\n    }\n\n    override fun mark(readlimit: Int) {\n        inputStream.mark(readlimit)\n    }\n\n    @Throws(IOException::class)\n    override fun reset() {\n        inputStream.reset()\n    }\n\n    override fun markSupported(): Boolean = inputStream.markSupported()\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/DelegateOutputStream.kt",
    "content": "package me.zhanghai.android.files.provider.common\n\nimport java.io.IOException\nimport java.io.OutputStream\n\nopen class DelegateOutputStream(private val outputStream: OutputStream) : OutputStream() {\n    @Throws(IOException::class)\n    override fun write(b: Int) {\n        outputStream.write(b)\n    }\n\n    @Throws(IOException::class)\n    override fun write(b: ByteArray) {\n        outputStream.write(b)\n    }\n\n    @Throws(IOException::class)\n    override fun write(b: ByteArray, off: Int, len: Int) {\n        outputStream.write(b, off, len)\n    }\n\n    @Throws(IOException::class)\n    override fun flush() {\n        outputStream.flush()\n    }\n\n    @Throws(IOException::class)\n    override fun close() {\n        outputStream.close()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/DelegateSchemeFileSystemProvider.kt",
    "content": "package me.zhanghai.android.files.provider.common\n\nimport java8.nio.channels.SeekableByteChannel\nimport java8.nio.file.AccessMode\nimport java8.nio.file.CopyOption\nimport java8.nio.file.DirectoryStream\nimport java8.nio.file.FileStore\nimport java8.nio.file.FileSystem\nimport java8.nio.file.LinkOption\nimport java8.nio.file.OpenOption\nimport java8.nio.file.Path\nimport java8.nio.file.attribute.BasicFileAttributes\nimport java8.nio.file.attribute.FileAttribute\nimport java8.nio.file.attribute.FileAttributeView\nimport java8.nio.file.spi.FileSystemProvider\nimport java.net.URI\n\nclass DelegateSchemeFileSystemProvider(\n    private val scheme: String,\n    private val fileSystemProvider: FileSystemProvider\n) : FileSystemProvider() {\n    override fun getScheme(): String = scheme\n\n    override fun newFileSystem(uri: URI, env: Map<String, *>): FileSystem =\n        fileSystemProvider.newFileSystem(uri, env)\n\n    override fun getFileSystem(uri: URI): FileSystem = fileSystemProvider.getFileSystem(uri)\n\n    override fun getPath(uri: URI): Path = fileSystemProvider.getPath(uri)\n\n    override fun newByteChannel(\n        file: Path,\n        options: Set<OpenOption>,\n        vararg attributes: FileAttribute<*>\n    ): SeekableByteChannel = throw NotImplementedError()\n\n    override fun newDirectoryStream(\n        directory: Path,\n        filter: DirectoryStream.Filter<in Path>\n    ): DirectoryStream<Path> = throw NotImplementedError()\n\n    override fun createDirectory(directory: Path, vararg attributes: FileAttribute<*>) =\n        throw NotImplementedError()\n\n    override fun delete(path: Path) = throw NotImplementedError()\n\n    override fun copy(source: Path, target: Path, vararg options: CopyOption) =\n        throw NotImplementedError()\n\n    override fun move(source: Path, target: Path, vararg options: CopyOption) =\n        throw NotImplementedError()\n\n    override fun isSameFile(path: Path, path2: Path): Boolean = throw NotImplementedError()\n\n    override fun isHidden(path: Path): Boolean = throw NotImplementedError()\n\n    override fun getFileStore(path: Path): FileStore = throw NotImplementedError()\n\n    override fun checkAccess(path: Path, vararg modes: AccessMode) = throw NotImplementedError()\n\n    override fun <V : FileAttributeView> getFileAttributeView(\n        path: Path,\n        type: Class<V>,\n        vararg options: LinkOption\n    ): V? = throw NotImplementedError()\n\n    override fun <A : BasicFileAttributes> readAttributes(\n        path: Path,\n        type: Class<A>,\n        vararg options: LinkOption\n    ): A = throw NotImplementedError()\n\n    override fun readAttributes(\n        path: Path,\n        attributes: String,\n        vararg options: LinkOption\n    ): Map<String, Any> = throw NotImplementedError()\n\n    override fun setAttribute(\n        path: Path,\n        attribute: String,\n        value: Any,\n        vararg options: LinkOption\n    ) = throw NotImplementedError()\n}"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/DelegateSeekableByteChannel.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.channels.SeekableByteChannel\nimport java.io.IOException\nimport java.nio.ByteBuffer\n\nfun DelegateSeekableByteChannel(channel: SeekableByteChannel): SeekableByteChannel =\n    if (channel is ForceableChannel) {\n        DelegateForceableSeekableByteChannel(channel)\n    } else {\n        DelegateNonForceableSeekableByteChannel(channel)\n    }\n\nopen class DelegateNonForceableSeekableByteChannel(\n    channel: SeekableByteChannel\n) : BaseDelegateSeekableByteChannel(channel) {\n    init {\n        require(channel !is ForceableChannel) {\n            \"Use DelegateForceableSeekableByteChannel for channels that are ForceableChannel\"\n        }\n    }\n}\n\nopen class DelegateForceableSeekableByteChannel(\n    private val channel: SeekableByteChannel\n) : BaseDelegateSeekableByteChannel(channel), ForceableChannel {\n    init {\n        require(channel is ForceableChannel) {\n            \"Use DelegateNonForceableSeekableByteChannel for channels that aren't ForceableChannel\"\n        }\n    }\n\n    override fun force(metaData: Boolean) {\n        (channel as ForceableChannel).force(metaData)\n    }\n}\n\nabstract class BaseDelegateSeekableByteChannel internal constructor(\n    private val channel: SeekableByteChannel\n) : SeekableByteChannel {\n    @Throws(IOException::class)\n    override fun read(dst: ByteBuffer): Int = channel.read(dst)\n\n    @Throws(IOException::class)\n    override fun write(src: ByteBuffer): Int = channel.write(src)\n\n    @Throws(IOException::class)\n    override fun position(): Long = channel.position()\n\n    @Throws(IOException::class)\n    override fun position(newPosition: Long): SeekableByteChannel {\n        channel.position(newPosition)\n        return this\n    }\n\n    @Throws(IOException::class)\n    override fun size(): Long = channel.size()\n\n    @Throws(IOException::class)\n    override fun truncate(size: Long): SeekableByteChannel {\n        channel.truncate(size)\n        return this\n    }\n\n    override fun isOpen(): Boolean = channel.isOpen\n\n    @Throws(IOException::class)\n    override fun close() {\n        channel.close()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/EncryptedFileAttributes.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.file.attribute.BasicFileAttributes\n\ninterface EncryptedFileAttributes {\n    fun isEncrypted(): Boolean\n}\n\nfun BasicFileAttributes.isEncrypted(): Boolean =\n    if (this is EncryptedFileAttributes) isEncrypted() else false\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/FileChannelExtensions.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport android.os.ParcelFileDescriptor\nimport java8.nio.channels.FileChannel\nimport java8.nio.channels.FileChannels\nimport me.zhanghai.android.files.compat.NioUtilsCompat\nimport me.zhanghai.android.files.provider.linux.syscall.Syscall\nimport me.zhanghai.android.files.provider.linux.syscall.SyscallException\nimport java.io.Closeable\nimport java.io.FileDescriptor\nimport java.io.IOException\nimport kotlin.reflect.KClass\n\nfun KClass<FileChannel>.open(fd: FileDescriptor, flags: Int): FileChannel {\n    val closeable = Closeable {\n        try {\n            Syscall.close(fd)\n        } catch (e: SyscallException) {\n            throw IOException(e)\n        }\n    }\n    return FileChannels.from(NioUtilsCompat.newFileChannel(closeable, fd, flags))\n}\n\nfun KClass<FileChannel>.open(pfd: ParcelFileDescriptor, mode: String): FileChannel =\n    FileChannels.from(\n        NioUtilsCompat.newFileChannel(\n            pfd, pfd.fileDescriptor,\n            ParcelFileDescriptor::class.modeToFlags(ParcelFileDescriptor.parseMode(mode))\n        )\n    )\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/FileStoreNotFoundException.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.file.FileSystemException\n\nclass FileStoreNotFoundException(file: String?) : FileSystemException(file)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/FileSystemCache.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.file.FileSystem\nimport java8.nio.file.FileSystemAlreadyExistsException\nimport java8.nio.file.FileSystemNotFoundException\nimport java.lang.ref.WeakReference\n\nclass FileSystemCache<K : Any, FS : FileSystem?> {\n    private val fileSystems: MutableMap<K, WeakReference<FS>> = HashMap()\n\n    private val lock = Any()\n\n    fun create(key: K, fileSystemCreator: () -> FS): FS {\n        synchronized(lock) {\n            var fileSystem = fileSystems[key]?.get()\n            if (fileSystem != null) {\n                throw FileSystemAlreadyExistsException(key.toString())\n            }\n            fileSystem = fileSystemCreator()\n            fileSystems[key] = WeakReference(fileSystem)\n            return fileSystem\n        }\n    }\n\n    fun getOrCreate(key: K, fileSystemCreator: () -> FS): FS {\n        synchronized(lock) {\n            var fileSystem = fileSystems[key]?.get()\n            if (fileSystem != null) {\n                return fileSystem\n            }\n            fileSystem = fileSystemCreator()\n            fileSystems[key] = WeakReference(fileSystem)\n            return fileSystem\n        }\n    }\n\n    operator fun get(key: K): FS {\n        synchronized(lock) {\n            val fileSystem = fileSystems[key]?.get()\n            if (fileSystem == null) {\n                fileSystems.remove(key)\n                throw FileSystemNotFoundException(key.toString())\n            }\n            return fileSystem\n        }\n    }\n\n    fun remove(key: K, fileSystem: FS) {\n        synchronized(lock) {\n            val fileSystemReference = fileSystems[key] ?: return\n            val currentFileSystem = fileSystemReference.get()\n            if (currentFileSystem == null || currentFileSystem == fileSystem) {\n                fileSystems.remove(key)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/FileTimeExtensions.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java.time.Instant\nimport java8.nio.file.attribute.FileTime\nimport kotlin.reflect.KClass\n\nval KClass<FileTime>.EPOCH: FileTime\n    get() = FileTime.from(Instant.EPOCH)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/FileTimeParceler.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport android.os.Parcel\nimport java.time.Instant\nimport java8.nio.file.attribute.FileTime\nimport kotlinx.parcelize.Parceler\nimport me.zhanghai.android.files.compat.readSerializableCompat\n\nobject FileTimeParceler : Parceler<FileTime?> {\n    override fun create(parcel: Parcel): FileTime? =\n        parcel.readSerializableCompat<Instant>()?.let { FileTime.from(it) }\n\n    override fun FileTime?.write(parcel: Parcel, flags: Int) {\n        parcel.writeSerializable(this?.toInstant())\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/ForceableChannel.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java.io.IOException\nimport java.nio.channels.Channel\nimport java.nio.channels.FileChannel\n\ninterface ForceableChannel {\n    @Throws(IOException::class)\n    fun force(metaData: Boolean)\n}\n\nval Channel.isForceable: Boolean\n    get() = this is FileChannel || this is ForceableChannel\n\n@Throws(IOException::class)\nfun Channel.force(metaData: Boolean) {\n    when (this) {\n        is FileChannel -> force(metaData)\n        is ForceableChannel -> force(metaData)\n        else -> throw UnsupportedOperationException()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/ForeignCopyMove.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.file.AtomicMoveNotSupportedException\nimport java8.nio.file.CopyOption\nimport java8.nio.file.FileAlreadyExistsException\nimport java8.nio.file.LinkOption\nimport java8.nio.file.NoSuchFileException\nimport java8.nio.file.Path\nimport java8.nio.file.StandardCopyOption\nimport java8.nio.file.StandardOpenOption\nimport java8.nio.file.attribute.BasicFileAttributeView\nimport java8.nio.file.attribute.BasicFileAttributes\nimport java8.nio.file.attribute.FileTime\nimport java.io.IOException\n\ninternal object ForeignCopyMove {\n    @Throws(IOException::class)\n    fun copy(source: Path, target: Path, vararg options: CopyOption) {\n        val copyOptions = options.toCopyOptions()\n        if (copyOptions.atomicMove) {\n            throw UnsupportedOperationException(StandardCopyOption.ATOMIC_MOVE.toString())\n        }\n        val linkOptions = if (copyOptions.noFollowLinks) {\n            arrayOf(LinkOption.NOFOLLOW_LINKS)\n        } else {\n            emptyArray()\n        }\n        val sourceAttributes = source.readAttributes(BasicFileAttributes::class.java, *linkOptions)\n        if (!(sourceAttributes.isRegularFile || sourceAttributes.isDirectory\n                || sourceAttributes.isSymbolicLink)) {\n            throw IOException(\"Cannot copy special file to foreign provider\")\n        }\n        if (!copyOptions.replaceExisting && target.exists(LinkOption.NOFOLLOW_LINKS)) {\n            throw FileAlreadyExistsException(source.toString(), target.toString(), null)\n        }\n        when {\n            sourceAttributes.isRegularFile -> {\n                if (copyOptions.replaceExisting) {\n                    target.deleteIfExists()\n                }\n                val openOptions = if (copyOptions.noFollowLinks) {\n                    arrayOf(LinkOption.NOFOLLOW_LINKS)\n                } else {\n                    emptyArray()\n                }\n                source.newInputStream(*openOptions).use { inputStream ->\n                    val outputStream = target.newOutputStream(\n                        StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE\n                    )\n                    var successful = false\n                    try {\n                        inputStream.copyTo(\n                            outputStream, copyOptions.progressIntervalMillis,\n                            copyOptions.progressListener\n                        )\n                        successful = true\n                    } finally {\n                        try {\n                            outputStream.close()\n                        } finally {\n                            if (!successful) {\n                                try {\n                                    target.deleteIfExists()\n                                } catch (e: IOException) {\n                                    e.printStackTrace()\n                                } catch (e: UnsupportedOperationException) {\n                                    e.printStackTrace()\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n            sourceAttributes.isDirectory -> {\n                if (copyOptions.replaceExisting) {\n                    target.deleteIfExists()\n                }\n                target.createDirectory()\n                copyOptions.progressListener?.invoke(sourceAttributes.size())\n            }\n            sourceAttributes.isSymbolicLink -> {\n                val sourceTarget = source.readSymbolicLink()\n                try {\n                    // Might throw UnsupportedOperationException, so we cannot delete beforehand.\n                    target.createSymbolicLink(sourceTarget)\n                } catch (e: FileAlreadyExistsException) {\n                    if (!copyOptions.replaceExisting) {\n                        throw e\n                    }\n                    target.deleteIfExists()\n                    target.createSymbolicLink(sourceTarget)\n                }\n                copyOptions.progressListener?.invoke(sourceAttributes.size())\n            }\n            else -> throw AssertionError()\n        }\n        // We don't take error when copying attribute fatal, so errors will only be logged from\n        // now on.\n        val targetAttributeView = target.getFileAttributeView(BasicFileAttributeView::class.java)!!\n        val lastModifiedTime = sourceAttributes.lastModifiedTime()\n            .takeIf { it != FileTime::class.EPOCH }\n        val lastAccessTime = if (copyOptions.copyAttributes) {\n            sourceAttributes.lastAccessTime().takeIf { it != FileTime::class.EPOCH }\n        } else {\n            null\n        }\n        val creationTime = if (copyOptions.copyAttributes) {\n            sourceAttributes.creationTime().takeIf { it != FileTime::class.EPOCH }\n        } else {\n            null\n        }\n        try {\n            targetAttributeView.setTimes(lastModifiedTime, lastAccessTime, creationTime)\n        } catch (e: IOException) {\n            e.printStackTrace()\n        } catch (e: UnsupportedOperationException) {\n            e.printStackTrace()\n        }\n    }\n\n    @Throws(IOException::class)\n    fun move(source: Path, target: Path, vararg options: CopyOption) {\n        val copyOptions = options.toCopyOptions()\n        if (copyOptions.atomicMove) {\n            throw AtomicMoveNotSupportedException(\n                source.toString(), target.toString(),\n                \"Cannot move file atomically to foreign provider\"\n            )\n        }\n        val optionsForCopy = if (copyOptions.copyAttributes && copyOptions.noFollowLinks) {\n            options\n        } else {\n            CopyOptions(\n                copyOptions.replaceExisting, true, false, true, copyOptions.progressIntervalMillis,\n                copyOptions.progressListener\n            ).toArray()\n        }\n        copy(source, target, *optionsForCopy)\n        try {\n            source.delete()\n        } catch (e: IOException) {\n            if (e !is NoSuchFileException) {\n                try {\n                    target.delete()\n                } catch (e2: IOException) {\n                    e.addSuppressed(e2)\n                } catch (e2: UnsupportedOperationException) {\n                    e.addSuppressed(e2)\n                }\n            }\n            throw e\n        } catch (e: UnsupportedOperationException) {\n            try {\n                target.delete()\n            } catch (e2: IOException) {\n                e.addSuppressed(e2)\n            } catch (e2: UnsupportedOperationException) {\n                e.addSuppressed(e2)\n            }\n            throw e\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/FutureExtensions.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport kotlinx.coroutines.Deferred\nimport kotlinx.coroutines.ExperimentalCoroutinesApi\nimport net.schmizz.concurrent.Promise\nimport java.util.concurrent.CancellationException\nimport java.util.concurrent.CountDownLatch\nimport java.util.concurrent.ExecutionException\nimport java.util.concurrent.Future\nimport java.util.concurrent.TimeUnit\nimport java.util.concurrent.TimeoutException\n\n// The following causes a NoSuchFieldError during runtime, similar to\n// https://youtrack.jetbrains.com/issue/KT-20245/\n//inline fun <T, R> Future<T>.map(\n//    crossinline transform: (T) -> R,\n//    crossinline transformException: (Exception) -> Exception = { it }\nfun <T, R> Future<T>.map(\n    transform: (T) -> R,\n    transformException: (Exception) -> Exception = { it }\n): Future<R> =\n    object : Future<R> {\n        override fun cancel(mayInterruptIfRunning: Boolean): Boolean =\n            this@map.cancel(mayInterruptIfRunning)\n\n        override fun isCancelled(): Boolean = this@map.isCancelled\n\n        override fun isDone(): Boolean = this@map.isDone\n\n        @Throws(ExecutionException::class, InterruptedException::class)\n        override fun get(): R = transformGet { this@map.get() }\n\n        @Throws(ExecutionException::class, InterruptedException::class, TimeoutException::class)\n        override fun get(timeout: Long, unit: TimeUnit): R =\n            transformGet { this@map.get(timeout, unit) }\n\n        @Throws(ExecutionException::class, InterruptedException::class, TimeoutException::class)\n        private inline fun transformGet(get: () -> T): R {\n            val result = try {\n                get()\n            } catch (e: Exception) {\n                val exception = try {\n                    transformException(e)\n                } catch (e2: Exception) {\n                    e2.addSuppressed(e)\n                    throw ExecutionException(e2)\n                }\n                check(\n                    exception is ExecutionException || exception is InterruptedException ||\n                        exception is TimeoutException\n                )\n                throw exception\n            }\n            try {\n                return transform(result)\n            } catch (e: Exception) {\n                throw ExecutionException(e)\n            }\n        }\n    }\n\nfun <T> Deferred<T>.asFuture(): Future<T> =\n    object : Future<T> {\n        private val latch = CountDownLatch(1)\n\n        init {\n            invokeOnCompletion { latch.countDown() }\n        }\n\n        override fun cancel(mayInterruptIfRunning: Boolean): Boolean {\n            cancel()\n            return this@asFuture.isCancelled\n        }\n\n        override fun isCancelled(): Boolean = this@asFuture.isCancelled\n\n        override fun isDone(): Boolean = isCompleted\n\n        @Throws(ExecutionException::class, InterruptedException::class)\n        override fun get(): T {\n            latch.await()\n            return getCompleted()\n        }\n\n        @Throws(ExecutionException::class, InterruptedException::class, TimeoutException::class)\n        override fun get(timeout: Long, unit: TimeUnit): T {\n            latch.await(timeout, unit)\n            return getCompleted()\n        }\n\n        @OptIn(ExperimentalCoroutinesApi::class)\n        @Throws(ExecutionException::class)\n        private fun getCompleted(): T =\n            try {\n                this@asFuture.getCompleted()\n            } catch (e: CancellationException) {\n                throw e\n            } catch (e: Exception) {\n                throw ExecutionException(e)\n            }\n    }\n\nfun <T> Promise<T, *>.asFuture(): Future<T> =\n    object : Future<T> {\n        override fun cancel(mayInterruptIfRunning: Boolean): Boolean = false\n\n        override fun isCancelled(): Boolean = false\n\n        override fun isDone(): Boolean = isFulfilled\n\n        @Throws(ExecutionException::class, InterruptedException::class)\n        override fun get(): T = tryRetrieve { retrieve() }\n\n        @Throws(ExecutionException::class, InterruptedException::class, TimeoutException::class)\n        override fun get(timeout: Long, unit: TimeUnit?): T =\n            tryRetrieve { retrieve(timeout, unit) }\n\n        @Throws(ExecutionException::class, InterruptedException::class, TimeoutException::class)\n        private inline fun tryRetrieve(retrieve: () -> T): T =\n            try {\n                retrieve()\n            } catch (e: Exception) {\n                when (val cause = e.cause) {\n                    is InterruptedException -> {\n                        Thread.interrupted()\n                        throw cause\n                    }\n                    is TimeoutException -> throw cause\n                    else -> throw ExecutionException(e)\n                }\n            }\n    }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/InputStreamExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java.io.IOException\nimport java.io.InputStream\nimport java.io.InterruptedIOException\nimport java.io.OutputStream\n\n// Can handle ProgressCopyOption.\n@Throws(IOException::class)\nfun InputStream.copyTo(\n    outputStream: OutputStream,\n    intervalMillis: Long,\n    listener: ((Long) -> Unit)?\n) {\n    val buffer = ByteArray(DEFAULT_BUFFER_SIZE)\n    var lastProgressMillis = System.currentTimeMillis()\n    var copiedSize = 0L\n    while (true) {\n        val readSize = read(buffer)\n        if (readSize == -1) {\n            break\n        }\n        outputStream.write(buffer, 0, readSize)\n        copiedSize += readSize.toLong()\n        throwIfInterrupted()\n        val currentTimeMillis = System.currentTimeMillis()\n        if (listener != null && currentTimeMillis >= lastProgressMillis + intervalMillis) {\n            listener(copiedSize)\n            lastProgressMillis = currentTimeMillis\n            copiedSize = 0\n        }\n    }\n    listener?.invoke(copiedSize)\n}\n\n@Throws(IOException::class)\nfun InputStream.readFully(buffer: ByteArray, offset: Int, length: Int): Int {\n    var totalReadSize = 0\n    while (totalReadSize < length) {\n        val readSize = read(buffer, offset + totalReadSize, length - totalReadSize)\n        if (readSize == -1) {\n            break\n        }\n        totalReadSize += readSize\n    }\n    return totalReadSize\n}\n\n@Throws(InterruptedIOException::class)\nprivate fun throwIfInterrupted() {\n    if (Thread.interrupted()) {\n        throw InterruptedIOException()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/InvalidFileNameException.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.file.FileSystemException\n\nclass InvalidFileNameException : FileSystemException {\n    constructor(file: String?) : super(file)\n\n    constructor(file: String?, other: String?, reason: String?) : super(file, other, reason)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/IsDirectoryException.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.file.FileSystemException\n\nclass IsDirectoryException : FileSystemException {\n    constructor(file: String?) : super(file)\n\n    constructor(file: String?, other: String?, reason: String?) : super(file, other, reason)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/LinkOptions.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.file.LinkOption\n\nclass LinkOptions(val noFollowLinks: Boolean) {\n    fun toArray(): Array<LinkOption> {\n        val options = mutableListOf<LinkOption>()\n        if (noFollowLinks) {\n            options += LinkOption.NOFOLLOW_LINKS\n        }\n        return options.toTypedArray()\n    }\n}\n\nfun Array<out LinkOption>.toLinkOptions(): LinkOptions {\n    var noFollowLinks = false\n    for (option in this) {\n        when (option) {\n            LinkOption.NOFOLLOW_LINKS -> noFollowLinks = true\n            else -> throw UnsupportedOperationException(option.toString())\n        }\n    }\n    return LinkOptions(noFollowLinks)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/LocalWatchKey.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.file.Path\nimport java8.nio.file.WatchEvent\n\nclass LocalWatchKey(\n    watchService: LocalWatchService,\n    path: Path,\n    @Volatile\n    internal var kinds: Set<WatchEvent.Kind<*>>\n) : AbstractWatchKey<LocalWatchKey, Path>(watchService, path)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/LocalWatchService.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.channels.SeekableByteChannel\nimport java8.nio.file.Path\nimport java8.nio.file.StandardWatchEventKinds\nimport java8.nio.file.WatchEvent\nimport me.zhanghai.android.files.provider.FileSystemProviders\nimport java.io.IOException\nimport java.io.OutputStream\nimport java.nio.ByteBuffer\n\nclass LocalWatchService : AbstractWatchService<LocalWatchKey>() {\n    private val keys = mutableMapOf<Path, LocalWatchKey>()\n\n    init {\n        synchronized(services) { services.add(this) }\n    }\n\n    @Throws(IOException::class)\n    fun register(\n        path: Path,\n        kinds: Array<WatchEvent.Kind<*>>,\n        vararg modifiers: WatchEvent.Modifier\n    ): LocalWatchKey {\n        val kindSet = mutableSetOf<WatchEvent.Kind<*>>()\n        for (kind in kinds) {\n            when (kind) {\n                StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE,\n                StandardWatchEventKinds.ENTRY_MODIFY -> kindSet += kind\n                // Ignored.\n                StandardWatchEventKinds.OVERFLOW -> {}\n                else -> throw UnsupportedOperationException(kind.name())\n            }\n        }\n        for (modifier in modifiers) {\n            throw UnsupportedOperationException(modifier.name())\n        }\n        synchronized(keys) {\n            var key = keys[path]\n            if (key != null) {\n                key.kinds = kindSet\n            } else {\n                key = LocalWatchKey(this, path, kindSet)\n                keys[path] = key\n            }\n            return key\n        }\n    }\n\n    override fun cancel(key: LocalWatchKey) {\n        synchronized(keys) { keys.remove(key.watchable())!! }\n    }\n\n    @Throws(IOException::class)\n    override fun onClose() {\n        synchronized(keys) { keys.clear() }\n        synchronized(services) { services.remove(this) }\n    }\n\n    private fun onWatchEvent(path: Path, kind: WatchEvent.Kind<Path>) {\n        val pathParent = path.parent\n        synchronized(keys) {\n            for ((keyPath, key) in keys) {\n                if (keyPath == path || keyPath == pathParent) {\n                    if (kind !in key.kinds) {\n                        continue\n                    }\n                    if (FileSystemProviders.overflowWatchEvents) {\n                        key.addEvent(StandardWatchEventKinds.OVERFLOW, null)\n                    } else {\n                        key.addEvent(kind, path)\n                    }\n                }\n            }\n        }\n    }\n\n    companion object {\n        private val services = mutableSetOf<LocalWatchService>()\n\n        fun onEntryCreated(path: Path) {\n            onWatchEvent(path, StandardWatchEventKinds.ENTRY_CREATE)\n        }\n\n        fun onEntryDeleted(path: Path) {\n            onWatchEvent(path, StandardWatchEventKinds.ENTRY_DELETE)\n        }\n\n        fun onEntryModified(path: Path) {\n            onWatchEvent(path, StandardWatchEventKinds.ENTRY_MODIFY)\n        }\n\n        private fun onWatchEvent(path: Path, kind: WatchEvent.Kind<Path>) {\n            synchronized(services) {\n                services.forEach { it.onWatchEvent(path, kind) }\n            }\n        }\n    }\n}\n\nfun NotifyEntryModifiedSeekableByteChannel(\n    channel: SeekableByteChannel,\n    path: Path\n) : SeekableByteChannel =\n    if (channel is ForceableChannel) {\n        NotifyEntryModifiedForceableSeekableByteChannel(channel, path)\n    } else {\n        NotifyEntryModifiedNonForceableSeekableByteChannel(channel, path)\n    }\n\nprivate class NotifyEntryModifiedNonForceableSeekableByteChannel(\n    channel: SeekableByteChannel,\n    private val path: Path\n) : DelegateNonForceableSeekableByteChannel(channel) {\n    override fun write(src: ByteBuffer): Int {\n        return super.write(src).also {\n            LocalWatchService.onEntryModified(path)\n        }\n    }\n\n    override fun truncate(size: Long): SeekableByteChannel {\n        return super.truncate(size).also {\n            LocalWatchService.onEntryModified(path)\n        }\n    }\n\n    override fun close() {\n        super.close()\n\n        LocalWatchService.onEntryModified(path)\n    }\n}\n\nprivate class NotifyEntryModifiedForceableSeekableByteChannel(\n    channel: SeekableByteChannel,\n    private val path: Path\n) : DelegateForceableSeekableByteChannel(channel) {\n    override fun write(src: ByteBuffer): Int {\n        return super.write(src).also {\n            LocalWatchService.onEntryModified(path)\n        }\n    }\n\n    override fun truncate(size: Long): SeekableByteChannel {\n        return super.truncate(size).also {\n            LocalWatchService.onEntryModified(path)\n        }\n    }\n\n    override fun close() {\n        super.close()\n\n        LocalWatchService.onEntryModified(path)\n    }\n}\n\nclass NotifyEntryModifiedOutputStream(\n    outputStream: OutputStream,\n    private val path: Path\n) : DelegateOutputStream(outputStream) {\n    override fun write(b: Int) {\n        super.write(b)\n\n        LocalWatchService.onEntryModified(path)\n    }\n\n    override fun write(b: ByteArray) {\n        super.write(b)\n\n        LocalWatchService.onEntryModified(path)\n    }\n\n    override fun write(b: ByteArray, off: Int, len: Int) {\n        super.write(b, off, len)\n\n        LocalWatchService.onEntryModified(path)\n    }\n\n    override fun flush() {\n        super.flush()\n\n        LocalWatchService.onEntryModified(path)\n    }\n\n    override fun close() {\n        super.close()\n\n        LocalWatchService.onEntryModified(path)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/OpenOptions.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.file.LinkOption\nimport java8.nio.file.OpenOption\nimport java8.nio.file.StandardOpenOption\n\nclass OpenOptions(\n    val read: Boolean,\n    val write: Boolean,\n    val append: Boolean,\n    val truncateExisting: Boolean,\n    val create: Boolean,\n    val createNew: Boolean,\n    val deleteOnClose: Boolean,\n    val sparse: Boolean,\n    val sync: Boolean,\n    val dsync: Boolean,\n    val noFollowLinks: Boolean\n)\n\nfun Array<out OpenOption>.toOpenOptions(): OpenOptions = setOf(*this).toOpenOptions()\n\nfun Set<OpenOption>.toOpenOptions(): OpenOptions {\n    var read = false\n    var write = false\n    var append = false\n    var truncateExisting = false\n    var create = false\n    var createNew = false\n    var deleteOnClose = false\n    var sparse = false\n    var sync = false\n    var dsync = false\n    var noFollowLinks = false\n    for (option in this) {\n        when (option) {\n            is StandardOpenOption -> when (option) {\n                StandardOpenOption.READ -> read = true\n                StandardOpenOption.WRITE -> write = true\n                StandardOpenOption.APPEND -> append = true\n                StandardOpenOption.TRUNCATE_EXISTING -> truncateExisting = true\n                StandardOpenOption.CREATE -> create = true\n                StandardOpenOption.CREATE_NEW -> createNew = true\n                StandardOpenOption.DELETE_ON_CLOSE -> deleteOnClose = true\n                StandardOpenOption.SPARSE -> sparse = true\n                StandardOpenOption.SYNC -> sync = true\n                StandardOpenOption.DSYNC -> dsync = true\n                else -> throw UnsupportedOperationException(option.toString())\n            }\n            LinkOption.NOFOLLOW_LINKS -> noFollowLinks = true\n            else -> throw UnsupportedOperationException(option.toString())\n        }\n    }\n    if (!read && !write) {\n        if (append) {\n            write = true\n        } else {\n            read = true\n        }\n    }\n    if (deleteOnClose) {\n        noFollowLinks = true\n    }\n    check(!(read && append)) { \"${StandardOpenOption.READ} + ${StandardOpenOption.APPEND}\" }\n    check(!(append && truncateExisting)) {\n        \"${StandardOpenOption.APPEND} + ${StandardOpenOption.TRUNCATE_EXISTING}\"\n    }\n    if (!write) {\n        append = false\n        truncateExisting = false\n        create = false\n        createNew = false\n    }\n    return OpenOptions(\n        read, write, append, truncateExisting, create, createNew, deleteOnClose, sparse,\n        sync, dsync, noFollowLinks\n    )\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/ParcelFileDescriptorExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights flagserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport android.os.ParcelFileDescriptor\nimport android.system.OsConstants\nimport me.zhanghai.android.files.util.hasBits\nimport kotlin.reflect.KClass\n\n// @see android.os.FileUtils#translateModePfdToPosix\nfun KClass<ParcelFileDescriptor>.modeToFlags(mode: Int): Int {\n    var flags = when {\n        mode.hasBits(ParcelFileDescriptor.MODE_READ_WRITE) -> OsConstants.O_RDWR\n        mode.hasBits(ParcelFileDescriptor.MODE_WRITE_ONLY) -> OsConstants.O_WRONLY\n        mode.hasBits(ParcelFileDescriptor.MODE_READ_ONLY) -> OsConstants.O_RDONLY\n        else -> throw IllegalArgumentException(mode.toString())\n    }\n    if (mode.hasBits(ParcelFileDescriptor.MODE_CREATE)) {\n        flags = flags or OsConstants.O_CREAT\n    }\n    if (mode.hasBits(ParcelFileDescriptor.MODE_TRUNCATE)) {\n        flags = flags or OsConstants.O_TRUNC\n    }\n    if (mode.hasBits(ParcelFileDescriptor.MODE_APPEND)) {\n        flags = flags or OsConstants.O_APPEND\n    }\n    return flags\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/ParcelableFileTime.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport java8.nio.file.attribute.FileTime\nimport me.zhanghai.android.files.compat.readSerializableCompat\n\nclass ParcelableFileTime(val value: FileTime) : Parcelable {\n    private constructor(source: Parcel) : this(FileTime.from(source.readSerializableCompat()))\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        dest.writeSerializable(value.toInstant())\n    }\n\n    companion object {\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<ParcelableFileTime> {\n            override fun createFromParcel(source: Parcel): ParcelableFileTime =\n                ParcelableFileTime(source)\n\n            override fun newArray(size: Int): Array<ParcelableFileTime?> = arrayOfNulls(size)\n        }\n    }\n}\n\nfun FileTime.toParcelable(): ParcelableFileTime = ParcelableFileTime(this)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/ParcelablePosixFileMode.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport me.zhanghai.android.files.compat.readSerializableCompat\nimport me.zhanghai.android.files.util.toEnumSet\nimport java.io.Serializable\n\nclass ParcelablePosixFileMode(val value: Set<PosixFileModeBit>) : Parcelable {\n    private constructor(source: Parcel) : this(\n        source.readSerializableCompat<Set<PosixFileModeBit>>()!!\n    )\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        val serializable = when (value) {\n            is Serializable -> value\n            else -> value.toEnumSet()\n        }\n        dest.writeSerializable(serializable)\n    }\n\n    companion object {\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<ParcelablePosixFileMode> {\n            override fun createFromParcel(source: Parcel): ParcelablePosixFileMode =\n                ParcelablePosixFileMode(source)\n\n            override fun newArray(size: Int): Array<ParcelablePosixFileMode?> = arrayOfNulls(size)\n        }\n    }\n}\n\nfun Set<PosixFileModeBit>.toParcelable(): ParcelablePosixFileMode = ParcelablePosixFileMode(this)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/PathExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.channels.SeekableByteChannel\nimport java8.nio.file.AccessMode\nimport java8.nio.file.CopyOption\nimport java8.nio.file.DirectoryStream\nimport java8.nio.file.FileStore\nimport java8.nio.file.Files\nimport java8.nio.file.LinkOption\nimport java8.nio.file.OpenOption\nimport java8.nio.file.Path\nimport java8.nio.file.ProviderMismatchException\nimport java8.nio.file.StandardOpenOption\nimport java8.nio.file.attribute.BasicFileAttributes\nimport java8.nio.file.attribute.FileAttribute\nimport java8.nio.file.attribute.FileAttributeView\nimport java8.nio.file.attribute.FileOwnerAttributeView\nimport java8.nio.file.attribute.FileTime\nimport java8.nio.file.attribute.GroupPrincipal\nimport java8.nio.file.attribute.UserPrincipal\nimport java8.nio.file.spi.FileSystemProvider\nimport java.io.BufferedReader\nimport java.io.BufferedWriter\nimport java.io.IOException\nimport java.io.InputStream\nimport java.io.InputStreamReader\nimport java.io.InterruptedIOException\nimport java.io.OutputStream\nimport java.io.OutputStreamWriter\nimport java.nio.channels.ClosedByInterruptException\nimport java.nio.charset.Charset\nimport kotlin.contracts.ExperimentalContracts\nimport kotlin.contracts.contract\nimport java8.nio.file.attribute.PosixFileAttributeView as Java8PosixFileAttributeView\n\n@Throws(IOException::class)\nfun Path.checkAccess(vararg modes: AccessMode) {\n    provider.checkAccess(this, *modes)\n}\n\n// Can handle ProgressCopyOption.\n@Throws(IOException::class)\nfun Path.copyTo(target: Path, vararg options: CopyOption) {\n    val provider = provider\n    if (provider == target.provider) {\n        provider.copy(this, target, *options)\n    } else {\n        ForeignCopyMove.copy(this, target, *options)\n    }\n}\n\n@Throws(IOException::class)\nfun Path.createDirectory(vararg attributes: FileAttribute<*>): Path =\n    Files.createDirectory(this, *attributes)\n\n@Throws(IOException::class)\nfun Path.createDirectories(vararg attributes: FileAttribute<*>): Path =\n    Files.createDirectories(this, *attributes)\n\n@Throws(IOException::class)\nfun Path.createFile(vararg attributes: FileAttribute<*>): Path =\n    try {\n        // This uses newByteChannel() under the hood, which may not be supported.\n        Files.createFile(this, *attributes)\n    } catch (e: UnsupportedOperationException) {\n        Files.newOutputStream(this, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE).close()\n        this\n    }\n\n@Throws(IOException::class)\nfun Path.createSymbolicLink(target: Path, vararg attributes: FileAttribute<*>): Path =\n    Files.createSymbolicLink(this, target, *attributes)\n\n@Throws(IOException::class)\nfun Path.createSymbolicLink(target: ByteString, vararg attributes: FileAttribute<*>): Path =\n    createSymbolicLink(ByteStringPath(target), *attributes)\n\n@Throws(IOException::class)\nfun Path.delete() {\n    Files.delete(this)\n}\n\n@Throws(IOException::class)\nfun Path.deleteIfExists() {\n    Files.deleteIfExists(this)\n}\n\nfun Path.exists(vararg options: LinkOption): Boolean = Files.exists(this, *options)\n\nfun <V : FileAttributeView> Path.getFileAttributeView(\n    type: Class<V>,\n    vararg options: LinkOption\n): V? = Files.getFileAttributeView(this, type, *options)\n\n@Throws(IOException::class)\nfun Path.getFileStore(): FileStore = Files.getFileStore(this)\n\n@Throws(IOException::class)\nfun Path.getLastModifiedTime(vararg options: LinkOption): FileTime =\n    Files.getLastModifiedTime(this, *options)\n\n@Throws(IOException::class)\nfun Path.getMode(vararg options: LinkOption): Set<PosixFileModeBit>? =\n    Files.readAttributes(this, PosixFileAttributes::class.java, *options).mode()\n\n@Throws(IOException::class)\nfun Path.getOwner(vararg options: LinkOption): UserPrincipal = Files.getOwner(this, *options)\n\n@Throws(IOException::class)\nfun Path.moveTo(target: Path, vararg options: CopyOption) {\n    val provider = provider\n    if (provider == target.provider) {\n        provider.move(this, target, *options)\n    } else {\n        ForeignCopyMove.move(this, target, *options)\n    }\n}\n\n@Throws(IOException::class)\nfun Path.newBufferedReader(charset: Charset, vararg options: OpenOption): BufferedReader =\n    BufferedReader(InputStreamReader(newInputStream(*options), charset.newDecoder()))\n\n@Throws(IOException::class)\nfun Path.newBufferedWriter(charset: Charset, vararg options: OpenOption): BufferedWriter =\n    BufferedWriter(OutputStreamWriter(newOutputStream(*options), charset.newEncoder()))\n\n@Throws(IOException::class)\nfun Path.newByteChannel(\n    options: Set<OpenOption>,\n    vararg attributes: FileAttribute<*>\n): SeekableByteChannel =\n    try {\n        Files.newByteChannel(this, options, *attributes)\n    } catch (e: UnsupportedOperationException) {\n        throw IOException(e)\n    }\n\n@Throws(IOException::class)\nfun Path.newByteChannel(vararg options: OpenOption): SeekableByteChannel =\n    try {\n        Files.newByteChannel(this, *options)\n    } catch (e: UnsupportedOperationException) {\n        throw IOException(e)\n    }\n\n@Throws(IOException::class)\nfun Path.newDirectoryStream(): DirectoryStream<Path> = Files.newDirectoryStream(this)\n\n@Throws(IOException::class)\nfun Path.newInputStream(vararg options: OpenOption): InputStream =\n    InterruptedIOExceptionInputStream(Files.newInputStream(this, *options))\n\nprivate class InterruptedIOExceptionInputStream(\n    inputStream: InputStream\n) : DelegateInputStream(inputStream) {\n    @Throws(IOException::class)\n    override fun read(): Int =\n        try {\n            super.read()\n        } catch (e: ClosedByInterruptException) {\n            throw e.toInterruptedIOException()\n        }\n\n    @Throws(IOException::class)\n    override fun read(b: ByteArray): Int =\n        try {\n            super.read(b)\n        } catch (e: ClosedByInterruptException) {\n            throw e.toInterruptedIOException()\n        }\n\n    @Throws(IOException::class)\n    override fun read(b: ByteArray, off: Int, len: Int): Int =\n        try {\n            super.read(b, off, len)\n        } catch (e: ClosedByInterruptException) {\n            throw e.toInterruptedIOException()\n        }\n\n    @Throws(IOException::class)\n    override fun skip(n: Long): Long = try {\n            super.skip(n)\n        } catch (e: ClosedByInterruptException) {\n            throw e.toInterruptedIOException()\n        }\n\n    @Throws(IOException::class)\n    override fun available(): Int =\n        try {\n            super.available()\n        } catch (e: ClosedByInterruptException) {\n            throw e.toInterruptedIOException()\n        }\n\n    @Throws(IOException::class)\n    override fun close() {\n        try {\n            super.close()\n        } catch (e: ClosedByInterruptException) {\n            throw e.toInterruptedIOException()\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun reset() {\n        try {\n            super.reset()\n        } catch (e: ClosedByInterruptException) {\n            throw e.toInterruptedIOException()\n        }\n    }\n}\n\n@Throws(IOException::class)\nfun Path.newOutputStream(vararg options: OpenOption): OutputStream =\n    InterruptedIOExceptionOutputStream(Files.newOutputStream(this, *options))\n\nprivate class InterruptedIOExceptionOutputStream(\n    outputStream: OutputStream\n) : DelegateOutputStream(outputStream) {\n    @Throws(IOException::class)\n    override fun write(b: Int) {\n        try {\n            super.write(b)\n        } catch (e: ClosedByInterruptException) {\n            throw e.toInterruptedIOException()\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun write(b: ByteArray) {\n        try {\n            super.write(b)\n        } catch (e: ClosedByInterruptException) {\n            throw e.toInterruptedIOException()\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun write(b: ByteArray, off: Int, len: Int) {\n        try {\n            super.write(b, off, len)\n        } catch (e: ClosedByInterruptException) {\n            throw e.toInterruptedIOException()\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun flush() {\n        try {\n            super.flush()\n        } catch (e: ClosedByInterruptException) {\n            throw e.toInterruptedIOException()\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun close() {\n        try {\n            super.close()\n        } catch (e: ClosedByInterruptException) {\n            throw e.toInterruptedIOException()\n        }\n    }\n}\n\nprivate fun ClosedByInterruptException.toInterruptedIOException(): InterruptedIOException {\n    Thread.interrupted()\n    return InterruptedIOException().apply { initCause(this@toInterruptedIOException) }\n}\n\n@Throws(IOException::class)\nfun Path.observe(intervalMillis: Long): PathObservable =\n    (provider as PathObservableProvider).observe(this, intervalMillis)\n\nval Path.provider: FileSystemProvider\n    get() = fileSystem.provider()\n\n// TODO: Just use Files.readAllBytes(), if all our providers support\n//  newByteChannel()?\n// Uses newInputStream() instead of newByteChannel().\n@Throws(IOException::class)\nfun Path.readAllBytes(vararg options: OpenOption): ByteArray =\n    newInputStream(*options).use { it.readBytes() }\n\n@Throws(IOException::class)\nfun <A : BasicFileAttributes> Path.readAttributes(type: Class<A>, vararg options: LinkOption): A =\n    Files.readAttributes(this, type, *options)\n\nfun Path.isDirectory(vararg options: LinkOption): Boolean = Files.isDirectory(this, *options)\n\nval Path.isHidden: Boolean\n    @Throws(IOException::class)\n    get() = Files.isHidden(this)\n\nval Path.isReadable: Boolean\n    get() = Files.isReadable(this)\n\nfun Path.isRegularFile(vararg options: LinkOption): Boolean = Files.isRegularFile(this, *options)\n\n@Throws(IOException::class)\nfun Path.isSameFile(path2: Path): Boolean = Files.isSameFile(this, path2)\n\nval Path.isWritable: Boolean\n    get() = Files.isWritable(this)\n\nfun Path.readSymbolicLink(): Path = Files.readSymbolicLink(this)\n\nfun Path.readSymbolicLinkByteString(): ByteString {\n    val target = readSymbolicLink()\n    target as? ByteStringPath ?: throw ProviderMismatchException(target.toString())\n    return target.toByteString()\n}\n\n// Can resolve path in a foreign provider.\nfun Path.resolveForeign(other: Path): Path {\n    asByteStringListPath()\n    other.asByteStringListPath()\n    if (javaClass == other.javaClass && provider == other.provider\n        && fileSystem == other.fileSystem) {\n        return resolve(other)\n    }\n    if (other.isAbsolute) {\n        return other\n    }\n    if (other.isEmpty) {\n        return this\n    }\n    var result: ByteStringListPath<*> = this\n    for (name in other.nameByteStrings) {\n        result = result.resolve(name)\n    }\n    return result\n}\n\n@Throws(IOException::class)\nfun Path.search(query: String, intervalMillis: Long, listener: (List<Path>) -> Unit) {\n    (provider as Searchable).search(this, query, intervalMillis, listener)\n}\n\n@Throws(IOException::class)\nfun Path.setGroup(group: GroupPrincipal, vararg options: LinkOption) {\n    val view = getFileAttributeView(Java8PosixFileAttributeView::class.java, *options)\n        ?: throw UnsupportedOperationException()\n    view.setGroup(group)\n}\n\n@Throws(IOException::class)\nfun Path.setLastModifiedTime(time: FileTime) {\n    Files.setLastModifiedTime(this, time)\n}\n\n@Throws(IOException::class)\nfun Path.setMode(mode: Set<PosixFileModeBit>) {\n    val view = Files.getFileAttributeView(this, PosixFileAttributeView::class.java)\n        ?: throw UnsupportedOperationException()\n    view.setMode(mode)\n}\n\n@Throws(IOException::class)\nfun Path.setOwner(owner: UserPrincipal, vararg options: LinkOption) {\n    val view = getFileAttributeView(FileOwnerAttributeView::class.java, *options)\n        ?: throw UnsupportedOperationException()\n    view.owner = owner\n}\n\n@Throws(IOException::class)\nfun Path.setSeLinuxContext(seLinuxContext: ByteString, vararg options: LinkOption) {\n    val view = getFileAttributeView(PosixFileAttributeView::class.java, *options)\n        ?: throw UnsupportedOperationException()\n    view.setSeLinuxContext(seLinuxContext)\n}\n\n// Can accept link options.\n@Throws(IOException::class)\nfun Path.size(vararg options: LinkOption): Long =\n    readAttributes(BasicFileAttributes::class.java, *options).size()\n\n@Throws(IOException::class)\nfun Path.restoreSeLinuxContext(vararg options: LinkOption) {\n    val view = getFileAttributeView(PosixFileAttributeView::class.java, *options)\n        ?: throw UnsupportedOperationException()\n    view.restoreSeLinuxContext()\n}\n\n@OptIn(ExperimentalContracts::class)\nfun Path.asByteStringListPath(): ByteStringListPath<*> {\n    contract {\n        returns() implies (this@asByteStringListPath is ByteStringListPath<*>)\n    }\n    this as? ByteStringListPath<*> ?: throw ProviderMismatchException(toString())\n    return this\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/PathIteratorDirectoryStream.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.file.DirectoryIteratorException\nimport java8.nio.file.DirectoryStream\nimport java8.nio.file.Path\nimport java.io.Closeable\nimport java.io.IOException\n\nopen class PathIteratorDirectoryStream(\n    private val iterator: Iterator<Path>,\n    private val closeable: Closeable?,\n    private val filter: DirectoryStream.Filter<in Path>\n) : DirectoryStream<Path> {\n    private var isClosed = false\n\n    private var isIteratorReturned = false\n\n    private val lock = Any()\n\n    // TODO: Should return Iterator<Path>\n    override fun iterator(): MutableIterator<Path> {\n        synchronized(lock) {\n            check(!isClosed) { \"This directory stream is closed\" }\n            check(!isIteratorReturned) { \"The iterator has already been returned\" }\n            val filteredIterator = iterator.asSequence().filter {\n                !isClosed && try {\n                    filter.accept(it)\n                } catch (e: IOException) {\n                    throw DirectoryIteratorException(e)\n                }\n            }.iterator()\n            return object : MutableIterator<Path> {\n                override fun hasNext(): Boolean = synchronized(lock) { filteredIterator.hasNext() }\n\n                override fun next(): Path = synchronized(lock) { filteredIterator.next() }\n\n                override fun remove() {\n                    throw UnsupportedOperationException()\n                }\n            }\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun close() {\n        synchronized(lock) {\n            if (isClosed) {\n                return\n            }\n            closeable?.close()\n            isClosed = true\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/PathListDirectoryStream.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.file.DirectoryStream\nimport java8.nio.file.Path\n\nclass PathListDirectoryStream(\n    paths: List<Path>,\n    filter: DirectoryStream.Filter<in Path>\n) : PathIteratorDirectoryStream(paths.iterator(), null, filter)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/PathObservable.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java.io.Closeable\n\ninterface PathObservable : Closeable {\n    fun addObserver(observer: () -> Unit)\n\n    fun removeObserver(observer: () -> Unit)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/PathObservableProvider.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.file.Path\nimport java.io.IOException\n\ninterface PathObservableProvider {\n    @Throws(IOException::class)\n    fun observe(path: Path, intervalMillis: Long): PathObservable\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/PollingWatchKey.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.file.Path\n\nclass PollingWatchKey(\n    watchService: PollingWatchService,\n    path: Path\n) : AbstractWatchKey<PollingWatchKey, Path>(watchService, path)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/PollingWatchService.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.file.DirectoryIteratorException\nimport java8.nio.file.LinkOption\nimport java8.nio.file.Path\nimport java8.nio.file.StandardWatchEventKinds\nimport java8.nio.file.WatchEvent\nimport java8.nio.file.attribute.BasicFileAttributes\nimport me.zhanghai.android.files.BuildConfig\nimport me.zhanghai.android.files.provider.FileSystemProviders\nimport java.io.IOException\nimport java.io.InterruptedIOException\nimport java.util.concurrent.atomic.AtomicInteger\n\nclass PollingWatchService : AbstractWatchService<PollingWatchKey>() {\n    private val pollers = mutableMapOf<Path, Poller>()\n\n    @Throws(IOException::class)\n    fun register(\n        path: Path,\n        kinds: Array<WatchEvent.Kind<*>>,\n        vararg modifiers: WatchEvent.Modifier\n    ): PollingWatchKey {\n        val kindSet = mutableSetOf<WatchEvent.Kind<*>>()\n        for (kind in kinds) {\n            when (kind) {\n                StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE,\n                StandardWatchEventKinds.ENTRY_MODIFY -> kindSet += kind\n                // Ignored.\n                StandardWatchEventKinds.OVERFLOW -> {}\n                else -> throw UnsupportedOperationException(kind.name())\n            }\n        }\n        for (modifier in modifiers) {\n            throw UnsupportedOperationException(modifier.name())\n        }\n        synchronized(pollers) {\n            var poller = pollers[path]\n            if (poller != null) {\n                poller.kinds = kindSet\n            } else {\n                poller = Poller(this, path, kindSet)\n                pollers[path] = poller\n                poller.start()\n            }\n            return poller.key\n        }\n    }\n\n    private fun removePoller(poller: Poller) {\n        // TODO: kotlinc: Type mismatch.\n        //synchronized(pollers) { pollers -= poller.key.watchable() }\n        synchronized(pollers) { pollers.remove(poller.key.watchable()) }\n    }\n\n    override fun cancel(key: PollingWatchKey) {\n        val poller = synchronized(pollers) { pollers.remove(key.watchable())!! }\n        poller.interrupt()\n        try {\n            poller.join()\n        } catch (e: InterruptedException) {\n            e.printStackTrace()\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun onClose() {\n        // Don't keep synchronized on pollers, or we may get a deadlock when joining.\n        val pollers = synchronized(pollers) {\n            pollers.values.toList().also { pollers.clear() }\n        }\n        var exception: IOException? = null\n        for (poller in pollers) {\n            poller.interrupt()\n            try {\n                poller.join()\n            } catch (e: InterruptedException) {\n                val newException = InterruptedIOException().apply { initCause(e) }\n                if (exception == null) {\n                    exception = newException\n                } else {\n                    exception.addSuppressed(newException)\n                }\n            }\n        }\n        exception?.let { throw it }\n    }\n\n    private class Poller @Throws(IOException::class) constructor(\n        private val watchService: PollingWatchService,\n        private val path: Path,\n        @Volatile\n        var kinds: Set<WatchEvent.Kind<*>>\n    ) : Thread(\"AbstractPollingWatchService.Poller-${id.getAndIncrement()}\") {\n        val key = PollingWatchKey(watchService, path)\n\n        private var oldFiles: Map<Path, BasicFileAttributes>\n\n        init {\n            isDaemon = true\n            oldFiles = getFiles()\n        }\n\n        override fun run() {\n            try {\n                while (true) {\n                    sleep(POLL_INTERNAL_MILLIS)\n                    val newFiles = getFiles()\n                    if (FileSystemProviders.overflowWatchEvents) {\n                        if (newFiles != oldFiles) {\n                            key.addEvent(StandardWatchEventKinds.OVERFLOW, null)\n                        }\n                    } else {\n                        for ((path, oldAttributes) in oldFiles) {\n                            val newAttributes = newFiles[path]\n                            val kind = when {\n                                newAttributes == null -> StandardWatchEventKinds.ENTRY_DELETE\n                                newAttributes != oldAttributes ->\n                                    StandardWatchEventKinds.ENTRY_MODIFY\n                                else -> continue\n                            }\n                            if (kind !in kinds) {\n                                continue\n                            }\n                            key.addEvent(kind, path)\n                        }\n                        for (path in newFiles.keys) {\n                            if (path in oldFiles) {\n                                continue\n                            }\n                            val kind = StandardWatchEventKinds.ENTRY_CREATE\n                            if (kind !in kinds) {\n                                continue\n                            }\n                            key.addEvent(kind, path)\n                        }\n                    }\n                    oldFiles = newFiles\n                }\n            } catch (e: Exception) {\n                e.printStackTrace()\n                key.setInvalid()\n                if (!(e is InterruptedException || e is InterruptedIOException)) {\n                    key.signal()\n                }\n                watchService.removePoller(this)\n            }\n        }\n\n        @Throws(IOException::class)\n        private fun getFiles(): Map<Path, BasicFileAttributes> =\n            mutableMapOf<Path, BasicFileAttributes>().apply {\n                if (path.isDirectory(LinkOption.NOFOLLOW_LINKS)) {\n                    path.newDirectoryStream().use { directoryStream ->\n                        try {\n                            directoryStream.forEach {\n                                val attributes = try {\n                                    it.readAttributes(\n                                        BasicFileAttributes::class.java, LinkOption.NOFOLLOW_LINKS\n                                    )\n                                } catch (e: IOException) {\n                                    e.printStackTrace()\n                                    return@forEach\n                                }\n                                this[it] = attributes\n                            }\n                        } catch (e: DirectoryIteratorException) {\n                            throw e.cause!!\n                        }\n                    }\n                } else {\n                    this[path] = path.readAttributes(\n                        BasicFileAttributes::class.java, LinkOption.NOFOLLOW_LINKS\n                    )\n                }\n            }.also {\n                if (BuildConfig.DEBUG) {\n                    // Ensure that the attributes class has overridden equals().\n                    val attributes = it.values.firstOrNull() ?: return@also\n                    check(\n                        attributes::class.java.getMethod(\"equals\", Object::class.java)\n                            != Object::class.java.getMethod(\"equals\", Object::class.java)\n                    )\n                }\n            }\n\n        companion object {\n            private const val POLL_INTERNAL_MILLIS = 1000L\n\n            private val id = AtomicInteger()\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/PosixFileAttributeView.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.file.attribute.GroupPrincipal\nimport java8.nio.file.attribute.PosixFileAttributeView\nimport java8.nio.file.attribute.PosixFilePermission\nimport java8.nio.file.attribute.UserPrincipal\nimport java.io.IOException\n\ninterface PosixFileAttributeView : PosixFileAttributeView {\n    @Throws(IOException::class)\n    override fun readAttributes(): PosixFileAttributes\n\n    @Throws(IOException::class)\n    override fun getOwner(): PosixUser? = readAttributes().owner()\n\n    @Throws(IOException::class)\n    override fun setOwner(owner: UserPrincipal) {\n        if (owner !is PosixUser) {\n            throw UnsupportedOperationException(owner.toString())\n        }\n        setOwner(owner)\n    }\n\n    @Throws(IOException::class)\n    fun setOwner(owner: PosixUser)\n\n    @Throws(IOException::class)\n    override fun setGroup(group: GroupPrincipal) {\n        if (group !is PosixGroup) {\n            throw UnsupportedOperationException(group.toString())\n        }\n        setGroup(group)\n    }\n\n    @Throws(IOException::class)\n    fun setGroup(group: PosixGroup)\n\n    @Throws(IOException::class)\n    override fun setPermissions(permissions: Set<PosixFilePermission>) {\n        setMode(permissions.toMode())\n    }\n\n    @Throws(IOException::class)\n    fun setMode(mode: Set<PosixFileModeBit>)\n\n    @Throws(IOException::class)\n    fun setSeLinuxContext(context: ByteString)\n\n    @Throws(IOException::class)\n    fun restoreSeLinuxContext()\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/PosixFileAttributes.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport android.os.Parcelable\nimport java8.nio.file.attribute.PosixFileAttributes\nimport java8.nio.file.attribute.PosixFilePermission\n\ninterface PosixFileAttributes : PosixFileAttributes {\n    fun type(): PosixFileType\n\n    override fun isRegularFile(): Boolean = type() == PosixFileType.REGULAR_FILE\n\n    override fun isDirectory(): Boolean = type() == PosixFileType.DIRECTORY\n\n    override fun isSymbolicLink(): Boolean = type() == PosixFileType.SYMBOLIC_LINK\n\n    override fun isOther(): Boolean = !isRegularFile && !isDirectory && !isSymbolicLink\n\n    override fun fileKey(): Parcelable\n\n    override fun owner(): PosixUser?\n\n    override fun group(): PosixGroup?\n\n    fun mode(): Set<PosixFileModeBit>?\n\n    override fun permissions(): Set<PosixFilePermission>? = mode()?.toPermissions()\n\n    fun seLinuxContext(): ByteString?\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/PosixFileMode.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport android.system.OsConstants\nimport java8.nio.file.attribute.FileAttribute\nimport java8.nio.file.attribute.PosixFilePermission\nimport me.zhanghai.android.files.util.enumSetOf\nimport me.zhanghai.android.files.util.hasBits\n\nenum class PosixFileModeBit {\n    SET_USER_ID,\n    SET_GROUP_ID,\n    STICKY,\n    OWNER_READ,\n    OWNER_WRITE,\n    OWNER_EXECUTE,\n    GROUP_READ,\n    GROUP_WRITE,\n    GROUP_EXECUTE,\n    OTHERS_READ,\n    OTHERS_WRITE,\n    OTHERS_EXECUTE\n}\n\nobject PosixFileMode {\n    val CREATE_DIRECTORY_DEFAULT = fromInt(\n        OsConstants.S_IRWXU or OsConstants.S_IRWXG or OsConstants.S_IRWXO\n    )\n\n    val CREATE_FILE_DEFAULT = fromInt(\n        OsConstants.S_IRUSR or OsConstants.S_IWUSR or OsConstants.S_IRGRP or OsConstants.S_IWGRP or\n            OsConstants.S_IROTH or OsConstants.S_IWOTH\n    )\n\n    val DIRECTORY_DEFAULT = fromInt(\n        OsConstants.S_IRWXU or OsConstants.S_IRGRP or OsConstants.S_IXGRP or\n            OsConstants.S_IROTH or OsConstants.S_IXOTH\n    )\n\n    val FILE_DEFAULT = fromInt(\n        OsConstants.S_IRUSR or OsConstants.S_IWUSR or OsConstants.S_IRGRP or OsConstants.S_IROTH\n    )\n\n    val SYMBOLIC_LINK_DEFAULT = fromInt(\n        OsConstants.S_IRWXU or OsConstants.S_IRWXG or OsConstants.S_IRWXO\n    )\n\n    fun fromAttribute(attribute: FileAttribute<*>): Set<PosixFileModeBit> {\n        if (attribute.name() != PosixFileModeAttribute.NAME) {\n            throw UnsupportedOperationException(attribute.name())\n        }\n        val value = attribute.value()\n        @Suppress(\"UNCHECKED_CAST\")\n        return value as? Set<PosixFileModeBit>\n            ?: throw UnsupportedOperationException(value.toString())\n    }\n\n    fun fromAttributes(attributes: Array<out FileAttribute<*>>): Set<PosixFileModeBit>? {\n        var mode: Set<PosixFileModeBit>? = null\n        for (attribute in attributes) {\n            mode = fromAttribute(attribute)\n        }\n        return mode\n    }\n\n    fun fromInt(modeInt: Int): Set<PosixFileModeBit> =\n        enumSetOf<PosixFileModeBit>().apply {\n            if (modeInt.hasBits(OsConstants.S_ISUID)) {\n                this += PosixFileModeBit.SET_USER_ID\n            }\n            if (modeInt.hasBits(OsConstants.S_ISGID)) {\n                this += PosixFileModeBit.SET_GROUP_ID\n            }\n            if (modeInt.hasBits(OsConstants.S_ISVTX)) {\n                this += PosixFileModeBit.STICKY\n            }\n            if (modeInt.hasBits(OsConstants.S_IRUSR)) {\n                this += PosixFileModeBit.OWNER_READ\n            }\n            if (modeInt.hasBits(OsConstants.S_IWUSR)) {\n                this += PosixFileModeBit.OWNER_WRITE\n            }\n            if (modeInt.hasBits(OsConstants.S_IXUSR)) {\n                this += PosixFileModeBit.OWNER_EXECUTE\n            }\n            if (modeInt.hasBits(OsConstants.S_IRGRP)) {\n                this += PosixFileModeBit.GROUP_READ\n            }\n            if (modeInt.hasBits(OsConstants.S_IWGRP)) {\n                this += PosixFileModeBit.GROUP_WRITE\n            }\n            if (modeInt.hasBits(OsConstants.S_IXGRP)) {\n                this += PosixFileModeBit.GROUP_EXECUTE\n            }\n            if (modeInt.hasBits(OsConstants.S_IROTH)) {\n                this += PosixFileModeBit.OTHERS_READ\n            }\n            if (modeInt.hasBits(OsConstants.S_IWOTH)) {\n                this += PosixFileModeBit.OTHERS_WRITE\n            }\n            if (modeInt.hasBits(OsConstants.S_IXOTH)) {\n                this += PosixFileModeBit.OTHERS_EXECUTE\n            }\n        }\n}\n\nfun Set<PosixFilePermission>.toMode(): Set<PosixFileModeBit> =\n    enumSetOf<PosixFileModeBit>().apply {\n        for (permission in this@toMode) {\n            this += when (permission) {\n                PosixFilePermission.OWNER_READ -> PosixFileModeBit.OWNER_READ\n                PosixFilePermission.OWNER_WRITE -> PosixFileModeBit.OWNER_WRITE\n                PosixFilePermission.OWNER_EXECUTE -> PosixFileModeBit.OWNER_EXECUTE\n                PosixFilePermission.GROUP_READ -> PosixFileModeBit.GROUP_READ\n                PosixFilePermission.GROUP_WRITE -> PosixFileModeBit.GROUP_WRITE\n                PosixFilePermission.GROUP_EXECUTE -> PosixFileModeBit.GROUP_EXECUTE\n                PosixFilePermission.OTHERS_READ -> PosixFileModeBit.OTHERS_READ\n                PosixFilePermission.OTHERS_WRITE -> PosixFileModeBit.OTHERS_WRITE\n                PosixFilePermission.OTHERS_EXECUTE -> PosixFileModeBit.OTHERS_EXECUTE\n                else -> throw UnsupportedOperationException(permission.toString())\n            }\n        }\n    }\n\nfun Set<PosixFileModeBit>.toAttribute(): FileAttribute<Set<PosixFileModeBit>> =\n    PosixFileModeAttribute(this)\n\nprivate class PosixFileModeAttribute(\n    private val mode: Set<PosixFileModeBit>\n) : FileAttribute<Set<PosixFileModeBit>> {\n    override fun name(): String = NAME\n\n    override fun value(): Set<PosixFileModeBit> = mode\n\n    companion object {\n        const val NAME = \"posix:mode\"\n    }\n}\n\nfun Set<PosixFileModeBit>.toInt(): Int =\n    ((if (contains(PosixFileModeBit.SET_USER_ID)) OsConstants.S_ISUID else 0)\n        or (if (contains(PosixFileModeBit.SET_GROUP_ID)) OsConstants.S_ISGID else 0)\n        or (if (contains(PosixFileModeBit.STICKY)) OsConstants.S_ISVTX else 0)\n        or (if (contains(PosixFileModeBit.OWNER_READ)) OsConstants.S_IRUSR else 0)\n        or (if (contains(PosixFileModeBit.OWNER_WRITE)) OsConstants.S_IWUSR else 0)\n        or (if (contains(PosixFileModeBit.OWNER_EXECUTE)) OsConstants.S_IXUSR else 0)\n        or (if (contains(PosixFileModeBit.GROUP_READ)) OsConstants.S_IRGRP else 0)\n        or (if (contains(PosixFileModeBit.GROUP_WRITE)) OsConstants.S_IWGRP else 0)\n        or (if (contains(PosixFileModeBit.GROUP_EXECUTE)) OsConstants.S_IXGRP else 0)\n        or (if (contains(PosixFileModeBit.OTHERS_READ)) OsConstants.S_IROTH else 0)\n        or (if (contains(PosixFileModeBit.OTHERS_WRITE)) OsConstants.S_IWOTH else 0)\n        or (if (contains(PosixFileModeBit.OTHERS_EXECUTE)) OsConstants.S_IXOTH else 0))\n\nfun Set<PosixFileModeBit>.toPermissions(): Set<PosixFilePermission> =\n    enumSetOf<PosixFilePermission>().apply {\n        for (modeBit in this@toPermissions) {\n            this += when (modeBit) {\n                PosixFileModeBit.OWNER_READ -> PosixFilePermission.OWNER_READ\n                PosixFileModeBit.OWNER_WRITE -> PosixFilePermission.OWNER_WRITE\n                PosixFileModeBit.OWNER_EXECUTE -> PosixFilePermission.OWNER_EXECUTE\n                PosixFileModeBit.GROUP_READ -> PosixFilePermission.GROUP_READ\n                PosixFileModeBit.GROUP_WRITE -> PosixFilePermission.GROUP_WRITE\n                PosixFileModeBit.GROUP_EXECUTE -> PosixFilePermission.GROUP_EXECUTE\n                PosixFileModeBit.OTHERS_READ -> PosixFilePermission.OTHERS_READ\n                PosixFileModeBit.OTHERS_WRITE -> PosixFilePermission.OTHERS_WRITE\n                PosixFileModeBit.OTHERS_EXECUTE -> PosixFilePermission.OTHERS_EXECUTE\n                else -> throw UnsupportedOperationException(modeBit.toString())\n            }\n        }\n    }\n\nfun Set<PosixFileModeBit>.toModeString(): String =\n    StringBuilder()\n        .append(if (contains(PosixFileModeBit.OWNER_READ)) 'r' else '-')\n        .append(if (contains(PosixFileModeBit.OWNER_WRITE)) 'w' else '-')\n        .apply {\n            val hasSetUserIdBit = contains(PosixFileModeBit.SET_USER_ID)\n            append(\n                if (contains(PosixFileModeBit.OWNER_EXECUTE)) {\n                    if (hasSetUserIdBit) 's' else 'x'\n                } else {\n                    if (hasSetUserIdBit) 'S' else '-'\n                }\n            )\n        }\n        .append(if (contains(PosixFileModeBit.GROUP_READ)) 'r' else '-')\n        .append(if (contains(PosixFileModeBit.GROUP_WRITE)) 'w' else '-')\n        .apply {\n            val hasSetGroupIdBit = contains(PosixFileModeBit.SET_GROUP_ID)\n            append(\n                if (contains(PosixFileModeBit.GROUP_EXECUTE)) {\n                    if (hasSetGroupIdBit) 's' else 'x'\n                } else {\n                    if (hasSetGroupIdBit) 'S' else '-'\n                }\n            )\n        }\n        .append(if (contains(PosixFileModeBit.OTHERS_READ)) 'r' else '-')\n        .append(if (contains(PosixFileModeBit.OTHERS_WRITE)) 'w' else '-')\n        .apply {\n            val hasStickyBit = contains(PosixFileModeBit.STICKY)\n            append(\n                if (contains(PosixFileModeBit.OTHERS_EXECUTE)) {\n                    if (hasStickyBit) 't' else 'x'\n                } else {\n                    if (hasStickyBit) 'T' else '-'\n                }\n            )\n        }\n        .toString()\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/PosixFileStore.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java.io.IOException\n\nabstract class PosixFileStore : AbstractFileStore() {\n    @Throws(IOException::class)\n    abstract fun refresh()\n\n    @Throws(IOException::class)\n    abstract fun setReadOnly(readOnly: Boolean)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/PosixFileType.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport android.system.OsConstants\nimport java8.nio.file.attribute.BasicFileAttributes\n\n// https://www.gnu.org/software/libc/manual/html_node/Testing-File-Type.html\nenum class PosixFileType(val mode: Int) {\n    UNKNOWN(0),\n    DIRECTORY(OsConstants.S_IFDIR),\n    CHARACTER_DEVICE(OsConstants.S_IFCHR),\n    BLOCK_DEVICE(OsConstants.S_IFBLK),\n    REGULAR_FILE(OsConstants.S_IFREG),\n    FIFO(OsConstants.S_IFIFO),\n    SYMBOLIC_LINK(OsConstants.S_IFLNK),\n    SOCKET((OsConstants.S_IFSOCK));\n\n    companion object {\n        fun fromMode(mode: Int): PosixFileType =\n            when {\n                OsConstants.S_ISDIR(mode) -> DIRECTORY\n                OsConstants.S_ISCHR(mode) -> CHARACTER_DEVICE\n                OsConstants.S_ISBLK(mode) -> BLOCK_DEVICE\n                OsConstants.S_ISREG(mode) -> REGULAR_FILE\n                OsConstants.S_ISFIFO(mode) -> FIFO\n                OsConstants.S_ISLNK(mode) -> SYMBOLIC_LINK\n                OsConstants.S_ISSOCK(mode) -> SOCKET\n                else -> UNKNOWN\n            }\n    }\n}\n\nval BasicFileAttributes.posixFileType: PosixFileType\n    get() =\n        when (this) {\n            is PosixFileAttributes -> type()\n            else ->\n                when {\n                    isRegularFile -> PosixFileType.REGULAR_FILE\n                    isDirectory -> PosixFileType.DIRECTORY\n                    isSymbolicLink -> PosixFileType.SYMBOLIC_LINK\n                    else -> PosixFileType.UNKNOWN\n                }\n        }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/PosixGroup.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport java8.nio.file.attribute.GroupPrincipal\n\nclass PosixGroup : PosixPrincipal, GroupPrincipal {\n    constructor(id: Int, name: ByteString?) : super(id, name)\n\n    private constructor(source: Parcel) : super(source)\n\n    companion object {\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<PosixGroup> {\n            override fun createFromParcel(source: Parcel): PosixGroup = PosixGroup(source)\n\n            override fun newArray(size: Int): Array<PosixGroup?> = arrayOfNulls(size)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/PosixPrincipal.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport me.zhanghai.android.files.util.hash\nimport me.zhanghai.android.files.util.readParcelable\nimport java.security.Principal\n\nabstract class PosixPrincipal(val id: Int, name: ByteString?) : Parcelable, Principal {\n    private val nameByteString = name\n\n    override fun getName(): String? = nameByteString?.toString()\n\n    override fun equals(other: Any?): Boolean {\n        if (this === other) {\n            return true\n        }\n        if (javaClass != other?.javaClass) {\n            return false\n        }\n        other as PosixPrincipal\n        return id == other.id && nameByteString == other.nameByteString\n    }\n\n    override fun hashCode(): Int = hash(id, nameByteString)\n\n    protected constructor(source: Parcel) : this(source.readInt(), source.readParcelable())\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        dest.writeInt(id)\n        dest.writeParcelable(nameByteString, flags)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/PosixUser.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport java8.nio.file.attribute.UserPrincipal\n\nclass PosixUser : PosixPrincipal, UserPrincipal {\n    constructor(id: Int, name: ByteString?) : super(id, name)\n\n    private constructor(source: Parcel) : super(source)\n\n    companion object {\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<PosixUser> {\n            override fun createFromParcel(source: Parcel): PosixUser = PosixUser(source)\n\n            override fun newArray(size: Int): Array<PosixUser?> = arrayOfNulls(size)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/ProgressCopyOption.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport android.os.Bundle\nimport android.os.Parcel\nimport android.os.Parcelable\nimport java8.nio.file.CopyOption\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.RemoteCallback\nimport me.zhanghai.android.files.util.getArgs\nimport me.zhanghai.android.files.util.putArgs\nimport me.zhanghai.android.files.util.readParcelable\n\nclass ProgressCopyOption(\n    val intervalMillis: Long,\n    val listener: (Long) -> Unit\n) : CopyOption, Parcelable {\n    private constructor(source: Parcel) : this(\n        source.readLong(),\n        source.readParcelable<RemoteCallback>()!!.let {\n            { copiedSize -> it.sendResult(Bundle().putArgs(ListenerArgs(copiedSize))) }\n        }\n    )\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        dest.writeLong(intervalMillis)\n        dest.writeParcelable(\n            RemoteCallback { listener(it.getArgs<ListenerArgs>().copiedSize) }, flags\n        )\n    }\n\n    companion object {\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<ProgressCopyOption> {\n            override fun createFromParcel(source: Parcel): ProgressCopyOption =\n                ProgressCopyOption(source)\n\n            override fun newArray(size: Int): Array<ProgressCopyOption?> = arrayOfNulls(size)\n        }\n    }\n\n    @Parcelize\n    private class ListenerArgs(val copiedSize: Long) : ParcelableArgs\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/ReadOnlyFileSystemException.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.file.FileSystemException\n\nclass ReadOnlyFileSystemException : FileSystemException {\n    constructor(file: String?) : super(file)\n\n    constructor(file: String?, other: String?, reason: String?) : super(file, other, reason)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/Searchable.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.file.Path\nimport java.io.IOException\n\ninterface Searchable {\n    @Throws(IOException::class)\n    fun search(directory: Path, query: String, intervalMillis: Long, listener: (List<Path>) -> Unit)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/UriAuthority.kt",
    "content": "package me.zhanghai.android.files.provider.common\n\nimport java.net.URI\nimport java.net.URISyntaxException\n\ndata class UriAuthority(\n    val userInfo: String?,\n    val host: String,\n    val port: Int?\n) {\n    fun encode(): String {\n        val uri = try {\n            // HACK: An empty host/authority requires a path, so use \"/\" as path here.\n            URI(null, userInfo, host, port ?: -1, \"/\", null, null)\n        } catch (e: URISyntaxException) {\n            throw IllegalArgumentException(e)\n        }\n        // URI.getRawAuthority() returns null when authority is empty.\n        return uri.rawAuthority.orEmpty()\n    }\n\n    // toString() is called by UI when the URI may not be valid, so build the string manually.\n    override fun toString(): String = buildString {\n        if (userInfo != null) {\n            append(userInfo)\n            append('@')\n        }\n        append(host)\n        if (port != null) {\n            append(':')\n            append(port.toString())\n        }\n    }\n\n    companion object {\n        val EMPTY = UriAuthority(null, \"\", null)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/UriByteStringExtensions.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java.net.URI\nimport kotlin.experimental.and\nimport kotlin.experimental.or\nimport kotlin.reflect.KClass\n\n// Note: The URI must have an authority, otherwise the Java URI class recognizes the rest of the URI\n// as scheme specific part and refuses to parse our query. As a result, our path must also either be\n// empty or absolute (beginning with a slash character).\n// @see https://datatracker.ietf.org/doc/html/rfc3986\nfun KClass<URI>.create(\n    scheme: String,\n    authority: UriAuthority,\n    path: ByteString,\n    query: ByteString?\n): URI {\n    val builder = StringBuilder()\n    builder.append(scheme).append(':')\n    builder.append(\"//\").append(authority.encode())\n    require(path.isEmpty() || path.startsWith(\"/\".toByteString())) {\n        \"Path $path must either be empty or begin with a slash character\"\n    }\n    builder.append(encodePath(path))\n    if (query != null) {\n        builder.append('?').append(encodeQuery(query))\n    }\n    val uriString = builder.toString()\n    return URI.create(uriString)\n}\n\n// @see java.net.URI\nprivate const val CHARSET_ALPHA = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\"\nprivate const val CHARSET_DIGIT = \"0123456789\"\nprivate const val CHARSET_UNRESERVED = \"$CHARSET_ALPHA$CHARSET_DIGIT-._~\"\nprivate const val CHARSET_SUB_DELIMS = \"!$&'()*+,;=\"\nprivate const val CHARSET_PCHAR = \"$CHARSET_UNRESERVED$CHARSET_SUB_DELIMS:@\"\nprivate const val CHARSET_PATH = \"$CHARSET_PCHAR/\"\nprivate const val CHARSET_QUERY = \"$CHARSET_PCHAR/?\"\n\nprivate fun encodePath(decoded: ByteString): String = encode(decoded, CHARSET_PATH)\n\nprivate fun encodeQuery(decoded: ByteString): String = encode(decoded, CHARSET_QUERY)\n\nprivate fun encode(decoded: ByteString, charset: String): String {\n    val builder = StringBuilder()\n    for (byte in decoded) {\n        if (charset.indexOf(byte.toInt().toChar()) != -1) {\n            builder.append(byte.toInt().toChar())\n        } else {\n            builder\n                .append('%')\n                .append(encodeHexCharacter(((byte.toInt() ushr 4).toByte() and 0x0F)))\n                .append(encodeHexCharacter(byte and 0x0F))\n        }\n    }\n    return builder.toString()\n}\n\nprivate fun encodeHexCharacter(halfByte: Byte): Char =\n    when (halfByte) {\n        in 0..9 -> '0' + halfByte.toInt()\n        in 10..15 -> 'A' + (halfByte.toInt() - 10)\n        else ->\n            throw IllegalArgumentException(\"Non-half-byte $halfByte for percent-encoding in URI\")\n    }\n\nval URI.decodedPathByteString: ByteString?\n    get() = rawPath?.let { decode(it) }\n\nval URI.decodedQueryByteString: ByteString?\n    get() = rawQuery?.let { decode(it) }\n\nprivate fun decode(encoded: String): ByteString {\n    val builder = ByteStringBuilder()\n    var index = 0\n    val length = encoded.length\n    while (index < length) {\n        var byte = getAsciiCharacterAt(encoded, index)\n        when (byte) {\n            '%'.code.toByte() -> {\n                require(index + 3 <= length) { \"Incomplete percent-encoding in URI\" }\n                val halfByte1 = decodeHexCharacter(getAsciiCharacterAt(encoded, index + 1))\n                val halfByte2 = decodeHexCharacter(getAsciiCharacterAt(encoded, index + 2))\n                byte = (halfByte1.toInt() shl 4).toByte() or halfByte2\n                builder.append(byte)\n                index += 3\n            }\n            else -> {\n                builder.append(byte)\n                ++index\n            }\n        }\n    }\n    return builder.toByteString()\n}\n\nprivate fun getAsciiCharacterAt(string: String, index: Int): Byte {\n    val char = string[index]\n    require(char.code == char.code and 0x7F) { \"Non-ASCII character $char in URI\" }\n    return char.code.toByte()\n}\n\nprivate fun decodeHexCharacter(hexCharacter: Byte): Byte =\n    when (hexCharacter) {\n        in '0'.code.toByte()..'9'.code.toByte() -> (hexCharacter.toInt().toChar() - '0').toByte()\n        in 'A'.code.toByte()..'F'.code.toByte() ->\n            (10 + (hexCharacter.toInt().toChar() - 'A')).toByte()\n        in 'a'.code.toByte()..'f'.code.toByte() ->\n            (10 + (hexCharacter.toInt().toChar() - 'a')).toByte()\n        else ->\n            throw IllegalArgumentException(\n                \"Non-hex-character ${hexCharacter.toInt().toChar()} for percent-encoding in URI\"\n            )\n    }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/UserActionRequiredException.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport android.content.Context\nimport android.content.Intent\nimport java8.nio.file.FileSystemException\nimport kotlin.coroutines.Continuation\n\nabstract class UserActionRequiredException : FileSystemException {\n    constructor(file: String?) : super(file)\n\n    constructor(file: String?, other: String?, reason: String?) : super(file, other, reason)\n\n    abstract fun getUserAction(continuation: Continuation<Boolean>, context: Context): UserAction\n}\n\nclass UserAction(\n    val intent: Intent,\n    val title: String,\n    val message: String?\n)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/WalkFileTreeSearchable.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.file.DirectoryIteratorException\nimport java8.nio.file.FileVisitOption\nimport java8.nio.file.FileVisitResult\nimport java8.nio.file.FileVisitor\nimport java8.nio.file.Files\nimport java8.nio.file.LinkOption\nimport java8.nio.file.Path\nimport java8.nio.file.attribute.BasicFileAttributes\nimport java.io.IOException\nimport java.io.InterruptedIOException\n\nobject WalkFileTreeSearchable {\n    @Throws(IOException::class)\n    fun search(\n        directory: Path,\n        query: String,\n        intervalMillis: Long,\n        listener: (List<Path>) -> Unit\n    ) {\n        val paths = mutableListOf<Path>()\n        // We cannot use Files.find() or Files.walk() because it cannot ignore exceptions.\n        walkFileTreeForSearch(directory, object : FileVisitor<Path> {\n            private var lastProgressMillis = System.currentTimeMillis()\n\n            @Throws(InterruptedIOException::class)\n            override fun preVisitDirectory(\n                directory: Path,\n                attributes: BasicFileAttributes\n            ): FileVisitResult {\n                visit(directory)\n                throwIfInterrupted()\n                return FileVisitResult.CONTINUE\n            }\n\n            @Throws(InterruptedIOException::class)\n            override fun visitFile(file: Path, attributes: BasicFileAttributes): FileVisitResult {\n                visit(file)\n                throwIfInterrupted()\n                return FileVisitResult.CONTINUE\n            }\n\n            @Throws(InterruptedIOException::class)\n            override fun visitFileFailed(file: Path, exception: IOException): FileVisitResult {\n                if (exception is InterruptedIOException) {\n                    throw exception\n                }\n                exception.printStackTrace()\n                visit(file)\n                throwIfInterrupted()\n                return FileVisitResult.CONTINUE\n            }\n\n            @Throws(InterruptedIOException::class)\n            override fun postVisitDirectory(\n                directory: Path,\n                exception: IOException?\n            ): FileVisitResult {\n                if (exception is InterruptedIOException) {\n                    throw exception\n                }\n                exception?.printStackTrace()\n                throwIfInterrupted()\n                return FileVisitResult.CONTINUE\n            }\n\n            private fun visit(path: Path) {\n                // Exclude the directory being searched.\n                if (path == directory) {\n                    return\n                }\n                val fileName = path.fileName\n                if (fileName != null && fileName.toString().contains(query, true)) {\n                    paths.add(path)\n                }\n                if (paths.isNotEmpty()) {\n                    val currentTimeMillis = System.currentTimeMillis()\n                    if (currentTimeMillis >= lastProgressMillis + intervalMillis) {\n                        listener(paths)\n                        lastProgressMillis = currentTimeMillis\n                        paths.clear()\n                    }\n                }\n            }\n        })\n        if (paths.isNotEmpty()) {\n            listener(paths)\n        }\n    }\n\n    // This method traverses the first level first, before diving into child directories.\n    // FileVisitResult returned from visitor may be ignored and always considered CONTINUE.\n    @Throws(IOException::class)\n    private fun walkFileTreeForSearch(start: Path, visitor: FileVisitor<in Path>): Path {\n        val attributes = try {\n            start.readAttributes(BasicFileAttributes::class.java)\n        } catch (ignored: IOException) {\n            try {\n                start.readAttributes(BasicFileAttributes::class.java, LinkOption.NOFOLLOW_LINKS)\n            } catch (e: IOException) {\n                visitor.visitFileFailed(start, e)\n                return start\n            }\n        }\n        if (!attributes.isDirectory) {\n            visitor.visitFile(start, attributes)\n            return start\n        }\n        val directoryStream = try {\n            start.newDirectoryStream()\n        } catch (e: IOException) {\n            visitor.visitFileFailed(start, e)\n            return start\n        }\n        val directories = mutableListOf<Path>()\n        directoryStream.use {\n            visitor.preVisitDirectory(start, attributes)\n            try {\n                for (path in directoryStream) {\n                    val attributes = try {\n                        path.readAttributes(BasicFileAttributes::class.java)\n                    } catch (ignored: IOException) {\n                        try {\n                            path.readAttributes(\n                                BasicFileAttributes::class.java, LinkOption.NOFOLLOW_LINKS\n                            )\n                        } catch (e: IOException) {\n                            visitor.visitFileFailed(path, e)\n                            continue\n                        }\n                    }\n                    visitor.visitFile(path, attributes)\n                    if (attributes.isDirectory) {\n                        directories.add(path)\n                    }\n                }\n            } catch (e: DirectoryIteratorException) {\n                visitor.postVisitDirectory(start, e.cause)\n                return start\n            }\n        }\n        for (path in directories) {\n            Files.walkFileTree(\n                path, setOf(FileVisitOption.FOLLOW_LINKS), Int.MAX_VALUE,\n                object : FileVisitor<Path> {\n                    @Throws(InterruptedIOException::class)\n                    override fun preVisitDirectory(\n                        directory: Path,\n                        attributes: BasicFileAttributes\n                    ): FileVisitResult {\n                        if (directory == path) {\n                            return FileVisitResult.CONTINUE\n                        }\n                        return visitor.preVisitDirectory(directory, attributes)\n                    }\n\n                    @Throws(InterruptedIOException::class)\n                    override fun visitFile(\n                        file: Path,\n                        attributes: BasicFileAttributes\n                    ): FileVisitResult {\n                        if (file == path) {\n                            return FileVisitResult.CONTINUE\n                        }\n                        return visitor.visitFile(file, attributes)\n                    }\n\n                    @Throws(InterruptedIOException::class)\n                    override fun visitFileFailed(\n                        file: Path,\n                        exception: IOException\n                    ): FileVisitResult {\n                        if (file == path) {\n                            // We are searching and ignoring errors, so just print it.\n                            exception.printStackTrace()\n                            return FileVisitResult.CONTINUE\n                        }\n                        return visitor.visitFileFailed(file, exception)\n                    }\n\n                    @Throws(InterruptedIOException::class)\n                    override fun postVisitDirectory(\n                        directory: Path,\n                        exception: IOException?\n                    ): FileVisitResult {\n                        if (directory == path) {\n                            // We are searching and ignoring errors, so just print it.\n                            exception?.printStackTrace()\n                            return FileVisitResult.CONTINUE\n                        }\n                        return visitor.postVisitDirectory(path, exception)\n                    }\n                }\n            )\n        }\n        visitor.postVisitDirectory(start, null)\n        return start\n    }\n\n    @Throws(InterruptedIOException::class)\n    private fun throwIfInterrupted() {\n        if (Thread.interrupted()) {\n            throw InterruptedIOException()\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/common/WatchServicePathObservable.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.common\n\nimport java8.nio.file.ClosedWatchServiceException\nimport java8.nio.file.Path\nimport java8.nio.file.StandardWatchEventKinds\nimport java8.nio.file.WatchService\nimport java.io.IOException\nimport java.util.concurrent.atomic.AtomicInteger\n\nclass WatchServicePathObservable(path: Path, intervalMillis: Long) : AbstractPathObservable(\n    intervalMillis\n) {\n    private val watchService: WatchService\n    private val poller: Poller\n\n    init {\n        var watchService: WatchService? = null\n        var poller: Poller? = null\n        var successful = false\n        try {\n            watchService = path.fileSystem.newWatchService()\n            this.watchService = watchService\n            path.register(\n                watchService, StandardWatchEventKinds.ENTRY_CREATE,\n                StandardWatchEventKinds.ENTRY_DELETE,\n                StandardWatchEventKinds.ENTRY_MODIFY\n            )\n            poller = Poller()\n            this.poller = poller\n            poller.start()\n            successful = true\n        } finally {\n            if (!successful) {\n                poller?.interrupt()\n                watchService?.close()\n            }\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun onCloseLocked() {\n        poller.interrupt()\n        watchService.close()\n    }\n\n    companion object {\n        private val pollerId = AtomicInteger()\n    }\n\n    private inner class Poller : Thread(\n        \"WatchServicePathObservable.Poller-${pollerId.getAndIncrement()}\"\n    ) {\n        init {\n            isDaemon = true\n        }\n\n        override fun run() {\n            while (true) {\n                val key = try {\n                    watchService.take()\n                } catch (e: ClosedWatchServiceException) {\n                    break\n                } catch (e: InterruptedException) {\n                    break\n                }\n                if (key.pollEvents().isNotEmpty()) {\n                    notifyObservers()\n                }\n                if (!key.reset()) {\n                    break\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/content/ContentFileAttributeView.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.content\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport java8.nio.file.attribute.BasicFileAttributeView\nimport java8.nio.file.attribute.FileTime\nimport me.zhanghai.android.files.provider.content.resolver.Resolver\nimport me.zhanghai.android.files.provider.content.resolver.ResolverException\nimport me.zhanghai.android.files.util.readParcelable\nimport java.io.IOException\n\ninternal class ContentFileAttributeView(\n    private val path: ContentPath\n) : BasicFileAttributeView, Parcelable {\n    override fun name(): String = NAME\n\n    @Throws(IOException::class)\n    override fun readAttributes(): ContentFileAttributes {\n        val uri = path.uri!!\n        val mimeType = try {\n            Resolver.getMimeType(uri)\n        } catch (e: ResolverException) {\n            throw e.toFileSystemException(path.toString())\n        }\n        val size = try {\n            Resolver.getSize(uri)\n        } catch (e: ResolverException) {\n            throw e.toFileSystemException(path.toString())\n        } ?: 0\n        return ContentFileAttributes.from(mimeType, size, uri)\n    }\n\n    override fun setTimes(\n        lastModifiedTime: FileTime?,\n        lastAccessTime: FileTime?,\n        createTime: FileTime?\n    ) {\n        throw UnsupportedOperationException()\n    }\n\n    private constructor(source: Parcel) : this(source.readParcelable<ContentPath>()!!)\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        dest.writeParcelable(path as Parcelable, flags)\n    }\n\n    companion object {\n        private val NAME = ContentFileSystemProvider.scheme\n\n        val SUPPORTED_NAMES = setOf(\"basic\", NAME)\n\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<ContentFileAttributeView> {\n            override fun createFromParcel(source: Parcel): ContentFileAttributeView =\n                ContentFileAttributeView(source)\n\n            override fun newArray(size: Int): Array<ContentFileAttributeView?> = arrayOfNulls(size)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/content/ContentFileAttributes.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.content\n\nimport android.net.Uri\nimport android.os.Parcelable\nimport java8.nio.file.attribute.FileTime\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.provider.common.AbstractContentProviderFileAttributes\nimport me.zhanghai.android.files.provider.common.EPOCH\nimport me.zhanghai.android.files.provider.common.FileTimeParceler\n\n@Parcelize\ninternal class ContentFileAttributes(\n    override val lastModifiedTime: @WriteWith<FileTimeParceler> FileTime,\n    override val mimeType: String?,\n    override val size: Long,\n    override val fileKey: Parcelable\n) : AbstractContentProviderFileAttributes() {\n    companion object {\n        fun from(mimeType: String?, size: Long, uri: Uri): ContentFileAttributes {\n            val lastModifiedTime = FileTime::class.EPOCH\n            val fileKey = uri\n            return ContentFileAttributes(lastModifiedTime, mimeType, size, fileKey)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/content/ContentFileSystem.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.content\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport androidx.core.net.toUri\nimport java8.nio.file.FileStore\nimport java8.nio.file.FileSystem\nimport java8.nio.file.Path\nimport java8.nio.file.PathMatcher\nimport java8.nio.file.WatchService\nimport java8.nio.file.attribute.UserPrincipalLookupService\nimport java8.nio.file.spi.FileSystemProvider\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.ByteStringListPathCreator\nimport java.io.IOException\n\ninternal class ContentFileSystem(private val provider: ContentFileSystemProvider) : FileSystem(),\n    ByteStringListPathCreator, Parcelable {\n    override fun provider(): FileSystemProvider = provider\n\n    override fun close() {\n        throw UnsupportedOperationException()\n    }\n\n    override fun isOpen(): Boolean = true\n\n    override fun isReadOnly(): Boolean = false\n\n    override fun getSeparator(): String = SEPARATOR_STRING\n\n    override fun getRootDirectories(): Iterable<Path> = emptyList()\n\n    override fun getFileStores(): Iterable<FileStore> {\n        // TODO\n        throw UnsupportedOperationException()\n    }\n\n    override fun supportedFileAttributeViews(): Set<String> =\n        ContentFileAttributeView.SUPPORTED_NAMES\n\n    override fun getPath(first: String, vararg more: String): ContentPath {\n        if (more.isNotEmpty()) {\n            throw UnsupportedOperationException()\n        }\n        val uri = first.toUri()\n        return ContentPath(this, uri)\n    }\n\n    override fun getPath(first: ByteString, vararg more: ByteString): ContentPath {\n        if (more.isNotEmpty()) {\n            throw UnsupportedOperationException()\n        }\n        val uri = first.toString().toUri()\n        return ContentPath(this, uri)\n    }\n\n    override fun getPathMatcher(syntaxAndPattern: String): PathMatcher {\n        throw UnsupportedOperationException()\n    }\n\n    override fun getUserPrincipalLookupService(): UserPrincipalLookupService {\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun newWatchService(): WatchService {\n        // TODO\n        throw UnsupportedOperationException()\n    }\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {}\n\n    companion object {\n        const val SEPARATOR = '/'.code.toByte()\n        private const val SEPARATOR_STRING = SEPARATOR.toInt().toChar().toString()\n\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<ContentFileSystem> {\n            override fun createFromParcel(source: Parcel): ContentFileSystem =\n                ContentFileSystemProvider.fileSystem\n\n            override fun newArray(size: Int): Array<ContentFileSystem?> = arrayOfNulls(size)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/content/ContentFileSystemProvider.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.content\n\nimport android.content.ContentResolver\nimport android.os.Build\nimport java8.nio.channels.FileChannel\nimport java8.nio.channels.SeekableByteChannel\nimport java8.nio.file.AccessDeniedException\nimport java8.nio.file.AccessMode\nimport java8.nio.file.CopyOption\nimport java8.nio.file.DirectoryStream\nimport java8.nio.file.FileStore\nimport java8.nio.file.FileSystem\nimport java8.nio.file.FileSystemAlreadyExistsException\nimport java8.nio.file.LinkOption\nimport java8.nio.file.OpenOption\nimport java8.nio.file.Path\nimport java8.nio.file.ProviderMismatchException\nimport java8.nio.file.StandardOpenOption\nimport java8.nio.file.attribute.BasicFileAttributes\nimport java8.nio.file.attribute.FileAttribute\nimport java8.nio.file.attribute.FileAttributeView\nimport java8.nio.file.spi.FileSystemProvider\nimport me.zhanghai.android.files.file.MimeType\nimport me.zhanghai.android.files.provider.common.ByteStringPath\nimport me.zhanghai.android.files.provider.common.PathObservable\nimport me.zhanghai.android.files.provider.common.PathObservableProvider\nimport me.zhanghai.android.files.provider.common.open\nimport me.zhanghai.android.files.provider.common.toAccessModes\nimport me.zhanghai.android.files.provider.common.toOpenOptions\nimport me.zhanghai.android.files.provider.content.resolver.Resolver\nimport me.zhanghai.android.files.provider.content.resolver.ResolverException\nimport java.io.IOException\nimport java.io.InputStream\nimport java.io.OutputStream\nimport java.net.URI\n\nobject ContentFileSystemProvider : FileSystemProvider(), PathObservableProvider {\n    private const val SCHEME = ContentResolver.SCHEME_CONTENT\n\n    internal val fileSystem = ContentFileSystem(this)\n\n    override fun getScheme(): String = SCHEME\n\n    override fun newFileSystem(uri: URI, env: Map<String, *>): FileSystem {\n        uri.requireSameScheme()\n        throw FileSystemAlreadyExistsException()\n    }\n\n    override fun getFileSystem(uri: URI): FileSystem {\n        uri.requireSameScheme()\n        return fileSystem\n    }\n\n    override fun getPath(uri: URI): Path {\n        uri.requireSameScheme()\n        return fileSystem.getPath(uri.toString())\n    }\n\n    private fun URI.requireSameScheme() {\n        val scheme = scheme\n        require(scheme == SCHEME) { \"URI scheme $scheme must be $SCHEME\" }\n    }\n\n    @Throws(IOException::class)\n    override fun newInputStream(file: Path, vararg options: OpenOption): InputStream {\n        file as? ContentPath ?: throw ProviderMismatchException(file.toString())\n        val uri = file.uri!!\n        val openOptions = options.toOpenOptions()\n        if (openOptions.write) {\n            throw UnsupportedOperationException(StandardOpenOption.WRITE.toString())\n        }\n        if (openOptions.append) {\n            throw UnsupportedOperationException(StandardOpenOption.APPEND.toString())\n        }\n        val mode = openOptions.toContentMode()\n        return try {\n            Resolver.openInputStream(uri, mode)\n        } catch (e: ResolverException) {\n            throw e.toFileSystemException(file.toString())\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun newOutputStream(file: Path, vararg options: OpenOption): OutputStream {\n        file as? ContentPath ?: throw ProviderMismatchException(file.toString())\n        val uri = file.uri!!\n        val optionsSet = mutableSetOf(*options)\n        if (optionsSet.isEmpty()) {\n            optionsSet += StandardOpenOption.CREATE\n            optionsSet += StandardOpenOption.TRUNCATE_EXISTING\n        }\n        optionsSet += StandardOpenOption.WRITE\n        val openOptions = optionsSet.toOpenOptions()\n        val mode = openOptions.toContentMode()\n        return try {\n            Resolver.openOutputStream(uri, mode)\n        } catch (e: ResolverException) {\n            throw e.toFileSystemException(file.toString())\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun newFileChannel(\n        file: Path,\n        options: Set<OpenOption>,\n        vararg attributes: FileAttribute<*>\n    ): FileChannel {\n        file as? ContentPath ?: throw ProviderMismatchException(file.toString())\n        val uri = file.uri!!\n        val openOptions = options.toOpenOptions()\n        val mode = openOptions.toContentMode()\n        if (attributes.isNotEmpty()) {\n            throw UnsupportedOperationException(attributes.contentToString())\n        }\n        val pfd = try {\n            Resolver.openParcelFileDescriptor(uri, mode)\n        } catch (e: ResolverException) {\n            throw e.toFileSystemException(file.toString())\n        }\n        return FileChannel::class.open(pfd, mode)\n    }\n\n    @Throws(IOException::class)\n    override fun newByteChannel(\n        file: Path,\n        options: Set<OpenOption>,\n        vararg attributes: FileAttribute<*>\n    ): SeekableByteChannel = newFileChannel(file, options, *attributes)\n\n    override fun newDirectoryStream(\n        directory: Path,\n        filter: DirectoryStream.Filter<in Path>\n    ): DirectoryStream<Path> {\n        directory as? ContentPath ?: throw ProviderMismatchException(directory.toString())\n        throw UnsupportedOperationException()\n    }\n\n    override fun createDirectory(directory: Path, vararg attributes: FileAttribute<*>) {\n        directory as? ContentPath ?: throw ProviderMismatchException(directory.toString())\n        throw UnsupportedOperationException()\n    }\n\n    override fun createSymbolicLink(link: Path, target: Path, vararg attributes: FileAttribute<*>) {\n        link as? ContentPath ?: throw ProviderMismatchException(link.toString())\n        when (target) {\n            is ContentPath, is ByteStringPath -> {}\n            else -> throw ProviderMismatchException(target.toString())\n        }\n        throw UnsupportedOperationException()\n    }\n\n    override fun createLink(link: Path, existing: Path) {\n        link as? ContentPath ?: throw ProviderMismatchException(link.toString())\n        existing as? ContentPath ?: throw ProviderMismatchException(existing.toString())\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun delete(path: Path) {\n        path as? ContentPath ?: throw ProviderMismatchException(path.toString())\n        val uri = path.uri!!\n        try {\n            Resolver.delete(uri)\n        } catch (e: ResolverException) {\n            throw e.toFileSystemException(path.toString())\n        }\n    }\n\n    override fun readSymbolicLink(link: Path): Path {\n        link as? ContentPath ?: throw ProviderMismatchException(link.toString())\n        throw UnsupportedOperationException()\n    }\n\n    override fun copy(source: Path, target: Path, vararg options: CopyOption) {\n        source as? ContentPath ?: throw ProviderMismatchException(source.toString())\n        target as? ContentPath ?: throw ProviderMismatchException(target.toString())\n        throw UnsupportedOperationException()\n    }\n\n    override fun move(source: Path, target: Path, vararg options: CopyOption) {\n        source as? ContentPath ?: throw ProviderMismatchException(source.toString())\n        target as? ContentPath ?: throw ProviderMismatchException(target.toString())\n        throw UnsupportedOperationException()\n    }\n\n    override fun isSameFile(path: Path, path2: Path): Boolean {\n        path as? ContentPath ?: throw ProviderMismatchException(path.toString())\n        return path == path2\n    }\n\n    override fun isHidden(path: Path): Boolean {\n        path as? ContentPath ?: throw ProviderMismatchException(path.toString())\n        return false\n    }\n\n    override fun getFileStore(path: Path): FileStore {\n        path as? ContentPath ?: throw ProviderMismatchException(path.toString())\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun checkAccess(path: Path, vararg modes: AccessMode) {\n        path as? ContentPath ?: throw ProviderMismatchException(path.toString())\n        val uri = path.uri!!\n        // This checks existence as well.\n        val mimeType = try {\n            Resolver.getMimeType(uri)\n        } catch (e: ResolverException) {\n            throw e.toFileSystemException(path.toString())\n        }\n        val isDirectory = mimeType == MimeType.DIRECTORY.value\n        if (isDirectory) {\n            // There's no elegant way to check access to a directory beyond its existence.\n            return\n        }\n        val accessModes = modes.toAccessModes()\n        if (accessModes.execute) {\n            throw AccessDeniedException(path.toString())\n        }\n        if (accessModes.write) {\n            try {\n                // Before Android 10, ParcelFileDescriptor.parseMode() parses \"w\" as \"wt\", and we would\n                // truncate the file to empty. So work around that with \"wa\" on older platforms.\n                val mode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) \"w\" else \"wa\"\n                Resolver.openOutputStream(uri, mode).use {}\n            } catch (e: ResolverException) {\n                throw e.toFileSystemException(path.toString())\n            }\n        }\n        if (accessModes.read) {\n            try {\n                Resolver.openInputStream(uri, \"r\").use {}\n            } catch (e: ResolverException) {\n                throw e.toFileSystemException(path.toString())\n            }\n        }\n    }\n\n    override fun <V : FileAttributeView> getFileAttributeView(\n        path: Path,\n        type: Class<V>,\n        vararg options: LinkOption\n    ): V? {\n        if (!supportsFileAttributeView(type)) {\n            return null\n        }\n        @Suppress(\"UNCHECKED_CAST\")\n        return getFileAttributeView(path) as V\n    }\n\n    internal fun supportsFileAttributeView(type: Class<out FileAttributeView>): Boolean =\n        type.isAssignableFrom(ContentFileAttributeView::class.java)\n\n    @Throws(IOException::class)\n    override fun <A : BasicFileAttributes> readAttributes(\n        path: Path,\n        type: Class<A>,\n        vararg options: LinkOption\n    ): A {\n        if (!type.isAssignableFrom(ContentFileAttributes::class.java)) {\n            throw UnsupportedOperationException(type.toString())\n        }\n        @Suppress(\"UNCHECKED_CAST\")\n        return getFileAttributeView(path).readAttributes() as A\n    }\n\n    private fun getFileAttributeView(path: Path): ContentFileAttributeView {\n        path as? ContentPath ?: throw ProviderMismatchException(path.toString())\n        return ContentFileAttributeView(path)\n    }\n\n    override fun readAttributes(\n        path: Path,\n        attributes: String,\n        vararg options: LinkOption\n    ): Map<String, Any> {\n        path as? ContentPath ?: throw ProviderMismatchException(path.toString())\n        throw UnsupportedOperationException()\n    }\n\n    override fun setAttribute(\n        path: Path,\n        attribute: String,\n        value: Any,\n        vararg options: LinkOption\n    ) {\n        path as? ContentPath ?: throw ProviderMismatchException(path.toString())\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun observe(path: Path, intervalMillis: Long): PathObservable {\n        path as? ContentPath ?: throw ProviderMismatchException(path.toString())\n        val uri = path.uri!!\n        return ContentPathObservable(uri, intervalMillis)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/content/ContentPath.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.content\n\nimport android.net.Uri\nimport android.os.Parcel\nimport android.os.Parcelable\nimport androidx.core.net.toUri\nimport java8.nio.file.FileSystem\nimport java8.nio.file.LinkOption\nimport java8.nio.file.Path\nimport java8.nio.file.WatchEvent\nimport java8.nio.file.WatchKey\nimport java8.nio.file.WatchService\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.ByteStringListPath\nimport me.zhanghai.android.files.provider.common.UriAuthority\nimport me.zhanghai.android.files.provider.common.toByteString\nimport me.zhanghai.android.files.provider.content.resolver.Resolver\nimport me.zhanghai.android.files.provider.content.resolver.ResolverException\nimport me.zhanghai.android.files.util.StableUriParceler\nimport me.zhanghai.android.files.util.readParcelable\nimport java.io.File\nimport java.net.URI\n\ninternal class ContentPath : ByteStringListPath<ContentPath> {\n    private val fileSystem: ContentFileSystem\n\n    val uri: Uri?\n\n    constructor(fileSystem: ContentFileSystem, uri: Uri) : super(\n        ContentFileSystem.SEPARATOR, true,\n        listOf(Uri.encode(uri.toString()).toByteString(), uri.bestFileName)\n    ) {\n        this.fileSystem = fileSystem\n        this.uri = uri\n    }\n\n    private constructor(fileSystem: ContentFileSystem, segments: List<ByteString>) : super(\n        ContentFileSystem.SEPARATOR, false, segments\n    ) {\n        this.fileSystem = fileSystem\n        uri = null\n    }\n\n    override fun isPathAbsolute(path: ByteString): Boolean {\n        throw AssertionError()\n    }\n\n    override fun createPath(path: ByteString): ContentPath =\n        ContentPath(fileSystem, path.toString().toUri())\n\n    override fun createPath(absolute: Boolean, segments: List<ByteString>): ContentPath {\n        if (absolute) {\n            require(segments.size == 2) {\n                \"Cannot create absolute ContentPath with segments $segments\"\n            }\n        }\n        return ContentPath(fileSystem, segments)\n    }\n\n    override val uriScheme: String\n        get() {\n            throw AssertionError()\n        }\n\n    override val uriAuthority: UriAuthority\n        get() {\n            throw AssertionError()\n        }\n\n    override val uriPath: ByteString\n        get() {\n            throw AssertionError()\n        }\n\n    override val uriQuery: ByteString?\n        get() {\n            throw AssertionError()\n        }\n\n    override val defaultDirectory: ContentPath\n        get() {\n            throw AssertionError()\n        }\n\n    override fun getFileSystem(): FileSystem = fileSystem\n\n    override fun getRoot(): ContentPath? = null\n\n    override fun getParent(): ContentPath? = null\n\n    override fun normalize(): ContentPath = this\n\n    override fun toUri(): URI = URI.create(uri!!.toString())\n\n    override fun toAbsolutePath(): ContentPath {\n        if (!isAbsolute) {\n            throw UnsupportedOperationException()\n        }\n        return this\n    }\n\n    override fun toRealPath(vararg options: LinkOption): ContentPath = this\n\n    override fun toFile(): File {\n        throw UnsupportedOperationException()\n    }\n\n    override fun register(\n        watcher: WatchService,\n        events: Array<WatchEvent.Kind<*>>,\n        vararg modifiers: WatchEvent.Modifier\n    ): WatchKey {\n        throw UnsupportedOperationException()\n    }\n\n    override fun toByteString(): ByteString =\n        uri?.toString()?.toByteString() ?: super.toByteString()\n\n    override fun toString(): String = uri?.toString() ?: super.toString()\n\n    override fun equals(other: Any?): Boolean {\n        if (this === other) {\n            return true\n        }\n        if (javaClass != other?.javaClass) {\n            return false\n        }\n        other as ContentPath\n        return if (uri != null || other.uri != null) uri == other.uri else super.equals(other)\n    }\n\n    override fun hashCode(): Int {\n        return uri?.hashCode() ?: super.hashCode()\n    }\n\n    private constructor(source: Parcel) : super(source) {\n        fileSystem = source.readParcelable()!!\n        //uri = source.readParcelable()\n        uri = StableUriParceler.create(source)\n    }\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        super.writeToParcel(dest, flags)\n\n        dest.writeParcelable(fileSystem, flags)\n        //dest.writeParcelable(uri, flags)\n        with(StableUriParceler) { uri.write(dest, flags) }\n    }\n\n    companion object {\n        private val Uri.bestFileName: ByteString\n            get() =\n                (try {\n                    Resolver.getDisplayName(this)\n                } catch (e: ResolverException) {\n                    e.printStackTrace()\n                    null\n                } ?: lastPathSegment ?: \"file\").toByteString()\n\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<ContentPath> {\n            override fun createFromParcel(source: Parcel): ContentPath = ContentPath(source)\n\n            override fun newArray(size: Int): Array<ContentPath?> = arrayOfNulls(size)\n        }\n    }\n}\n\nval Path.isContentPath: Boolean\n    get() = this is ContentPath\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/content/ContentPathObservable.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.content\n\nimport android.database.ContentObserver\nimport android.database.Cursor\nimport android.net.Uri\nimport me.zhanghai.android.files.provider.common.AbstractPathObservable\nimport me.zhanghai.android.files.provider.content.resolver.Resolver\nimport me.zhanghai.android.files.provider.content.resolver.ResolverException\n\ninternal class ContentPathObservable(\n    uri: Uri,\n    intervalMillis: Long\n) : AbstractPathObservable(intervalMillis) {\n    private val cursor: Cursor\n\n    private val contentObserver = object : ContentObserver(handler) {\n        override fun deliverSelfNotifications(): Boolean = true\n\n        override fun onChange(selfChange: Boolean) {\n            notifyObservers()\n        }\n    }\n\n    init {\n        cursor = try {\n            Resolver.query(uri, emptyArray(), null, null, null)\n        } catch (e: ResolverException) {\n            throw e.toFileSystemException(uri.toString())\n        }\n        cursor.registerContentObserver(contentObserver)\n    }\n\n    override fun onCloseLocked() {\n        cursor.unregisterContentObserver(contentObserver)\n        cursor.close()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/content/OpenOptionsContentExtensions.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.content\n\nimport java8.nio.file.StandardOpenOption\nimport me.zhanghai.android.files.provider.common.OpenOptions\n\ninternal fun OpenOptions.toContentMode(): String =\n    StringBuilder()\n        .apply {\n            if (read && write) {\n                append(\"rw\")\n            } else if (write) {\n                append('w')\n            } else {\n                append('r')\n            }\n            if (append) {\n                append('a')\n            }\n            if (truncateExisting) {\n                append('t')\n            }\n            if (createNew) {\n                throw UnsupportedOperationException(StandardOpenOption.CREATE_NEW.toString())\n            } else if (create) {\n                // Ignored.\n            }\n            if (deleteOnClose) {\n                throw UnsupportedOperationException(StandardOpenOption.DELETE_ON_CLOSE.toString())\n            }\n            if (sync) {\n                throw UnsupportedOperationException(StandardOpenOption.SYNC.toString())\n            }\n            if (dsync) {\n                throw UnsupportedOperationException(StandardOpenOption.DSYNC.toString())\n            }\n        }\n        .toString()\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/content/resolver/CursorExtensions.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.content.resolver\n\nimport android.database.Cursor\nimport androidx.core.database.getIntOrNull\nimport androidx.core.database.getLongOrNull\nimport androidx.core.database.getStringOrNull\n\n@Throws(ResolverException::class)\nfun Cursor.moveToFirstOrThrow() {\n    if (!moveToFirst()) {\n        throw ResolverException(\"Cursor.moveToFirst() returned false\")\n    }\n}\n\nfun Cursor.getColumnIndexOrNull(columnName: String): Int? =\n    getColumnIndex(columnName).takeIf { it != -1 }\n\nfun Cursor.getInt(columnName: String): Int? {\n    val columnIndex = getColumnIndexOrNull(columnName) ?: return null\n    return getIntOrNull(columnIndex)\n}\n\nfun Cursor.getLong(columnName: String): Long? {\n    val columnIndex = getColumnIndexOrNull(columnName) ?: return null\n    return getLongOrNull(columnIndex)\n}\n\nfun Cursor.getString(columnName: String): String? {\n    val columnIndex = getColumnIndexOrNull(columnName) ?: return null\n    return getStringOrNull(columnIndex)\n}\n\n@Throws(ResolverException::class)\nfun Cursor.requireColumnIndex(columnName: String): Int =\n    try {\n        getColumnIndexOrThrow(columnName)\n    } catch (e: IllegalArgumentException) {\n        throw ResolverException(e)\n    }\n\n@Throws(ResolverException::class)\nfun Cursor.requireString(columnName: String): String {\n    return getStringOrNull(requireColumnIndex(columnName))\n        ?: throw ResolverException(\"Cursor.getStringOrNull() for $columnName returned null\")\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/content/resolver/Resolver.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.content.resolver\n\nimport android.content.res.AssetFileDescriptor\nimport android.database.Cursor\nimport android.net.Uri\nimport android.os.ParcelFileDescriptor\nimport android.provider.OpenableColumns\nimport me.zhanghai.android.files.app.contentResolver\nimport me.zhanghai.android.files.file.MimeType\nimport me.zhanghai.android.files.util.closeSafe\nimport me.zhanghai.android.files.util.takeIfNotEmpty\nimport java.io.FileNotFoundException\nimport java.io.IOException\nimport java.io.InputStream\nimport java.io.OutputStream\n\nobject Resolver {\n    @Throws(ResolverException::class)\n    fun checkExistence(uri: Uri) {\n        val rowCount = query(uri, emptyArray(), null, null, null).use { cursor -> cursor.count }\n        if (rowCount < 1) {\n            throw ResolverException(FileNotFoundException(\"Row count $rowCount is less than 1\"))\n        }\n    }\n\n    @Throws(ResolverException::class)\n    fun delete(uri: Uri) {\n        val deletedRowCount = try {\n            contentResolver.delete(uri, null, null)\n        } catch (e: Exception) {\n            throw ResolverException(e)\n        }\n        if (deletedRowCount < 1) {\n            throw ResolverException(\n                FileNotFoundException(\"Deleted row count $deletedRowCount is less than 1\")\n            )\n        }\n    }\n\n    fun exists(uri: Uri): Boolean =\n        try {\n            checkExistence(uri)\n            true\n        } catch (e: ResolverException) {\n            false\n        }\n\n    @Throws(ResolverException::class)\n    fun getDisplayName(uri: Uri): String? =\n        query(uri, arrayOf(OpenableColumns.DISPLAY_NAME), null, null, null).use { cursor ->\n            cursor.moveToFirstOrThrow()\n            cursor.getString(OpenableColumns.DISPLAY_NAME)\n        }?.takeIfNotEmpty()\n\n    @Throws(ResolverException::class)\n    fun getSize(uri: Uri): Long? =\n        query(uri, arrayOf(OpenableColumns.SIZE), null, null, null).use { cursor ->\n            cursor.moveToFirstOrThrow()\n            cursor.getLong(OpenableColumns.SIZE)\n        }\n\n    @Throws(ResolverException::class)\n    fun getMimeType(uri: Uri): String? =\n        try {\n            contentResolver.getType(uri)\n        } catch (e: Exception) {\n            throw ResolverException(e)\n        }?.takeIf { it.isNotEmpty() && it != MimeType.GENERIC.value }\n\n    @Throws(ResolverException::class)\n    fun openAssetFileDescriptor(uri: Uri, mode: String): AssetFileDescriptor =\n        try {\n            contentResolver.openAssetFileDescriptor(uri, mode)\n        } catch (e: Exception) {\n            throw ResolverException(e)\n        } ?: throw ResolverException(\n            \"ContentResolver.openAssetFileDescriptor() with $uri returned null\"\n        )\n\n    @Throws(ResolverException::class)\n    fun openInputStream(uri: Uri, mode: String): InputStream {\n        val descriptor = openAssetFileDescriptor(uri, mode)\n        return try {\n            descriptor.createInputStream()\n        } catch (e: IOException) {\n            descriptor.closeSafe()\n            throw ResolverException(e)\n        }\n    }\n\n    @Throws(ResolverException::class)\n    fun openOutputStream(uri: Uri, mode: String): OutputStream {\n        val descriptor = openAssetFileDescriptor(uri, mode)\n        return try {\n            descriptor.createOutputStream()\n        } catch (e: IOException) {\n            descriptor.closeSafe()\n            throw ResolverException(e)\n        }\n    }\n\n    @Throws(ResolverException::class)\n    fun openParcelFileDescriptor(uri: Uri, mode: String): ParcelFileDescriptor =\n        try {\n            contentResolver.openFileDescriptor(\n                uri, mode\n            )\n        } catch (e: Exception) {\n            throw ResolverException(e)\n        } ?: throw ResolverException(\"ContentResolver.openFileDescriptor() with $uri returned null\")\n\n    @Throws(ResolverException::class)\n    fun query(\n        uri: Uri,\n        projection: Array<out String?>?,\n        selection: String?,\n        selectionArgs: Array<out String?>?,\n        sortOrder: String?\n    ): Cursor =\n        try {\n            contentResolver.query(uri, projection, selection, selectionArgs, sortOrder)\n        } catch (e: Exception) {\n            throw ResolverException(e)\n        } ?: throw ResolverException(\"ContentResolver.query() with $uri returned null\")\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/content/resolver/ResolverException.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.content.resolver\n\nimport java8.nio.file.AccessDeniedException\nimport java8.nio.file.FileSystemException\nimport java8.nio.file.NoSuchFileException\nimport java.io.FileNotFoundException\n\nclass ResolverException : Exception {\n    constructor(message: String?) : super(message)\n\n    constructor(cause: Throwable?) : super(cause)\n\n    fun toFileSystemException(file: String?, other: String? = null): FileSystemException =\n        when (cause) {\n            is FileNotFoundException -> NoSuchFileException(file, other, message)\n            is SecurityException -> AccessDeniedException(file, other, message)\n            else -> FileSystemException(file, other, message)\n        }.apply { initCause(this@ResolverException) }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/document/DocumentCopyMove.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.document\n\nimport android.net.Uri\nimport java8.nio.file.FileAlreadyExistsException\nimport java8.nio.file.StandardCopyOption\nimport me.zhanghai.android.files.provider.common.CopyOptions\nimport me.zhanghai.android.files.provider.content.resolver.ResolverException\nimport me.zhanghai.android.files.provider.document.resolver.DocumentResolver\nimport java.io.IOException\nimport java.io.InterruptedIOException\n\ninternal object DocumentCopyMove {\n    @Throws(IOException::class)\n    fun copy(source: DocumentPath, target: DocumentPath, copyOptions: CopyOptions): Uri {\n        if (copyOptions.atomicMove) {\n            throw UnsupportedOperationException(StandardCopyOption.ATOMIC_MOVE.toString())\n        }\n        if (source == target) {\n            val targetUri = try {\n                DocumentResolver.getDocumentUri(target)\n            } catch (e: ResolverException) {\n                throw e.toFileSystemException(target.toString())\n            }\n            copyOptions.progressListener?.invokeWithSize(targetUri)\n            return targetUri\n        }\n        val targetExists = DocumentResolver.exists(target)\n        if (targetExists) {\n            if (!copyOptions.replaceExisting) {\n                throw FileAlreadyExistsException(target.toString())\n            }\n            try {\n                DocumentResolver.remove(target)\n            } catch (e: ResolverException) {\n                throw e.toFileSystemException(target.toString())\n            }\n        }\n        return try {\n            DocumentResolver.copy(\n                source, target, copyOptions.progressIntervalMillis, copyOptions.progressListener\n            )\n        } catch (e: ResolverException) {\n            (e.cause as? InterruptedIOException)?.let { throw it }\n            throw e.toFileSystemException(source.toString(), target.toString())\n        }\n    }\n\n    @Throws(IOException::class)\n    fun move(source: DocumentPath, target: DocumentPath, copyOptions: CopyOptions): Uri {\n        if (source == target) {\n            val targetUri = try {\n                DocumentResolver.getDocumentUri(target)\n            } catch (e: ResolverException) {\n                throw e.toFileSystemException(target.toString())\n            }\n            copyOptions.progressListener?.invokeWithSize(targetUri)\n            return targetUri\n        }\n        val targetExists = DocumentResolver.exists(target)\n        if (targetExists) {\n            if (!copyOptions.replaceExisting) {\n                throw FileAlreadyExistsException(target.toString())\n            }\n            try {\n                DocumentResolver.remove(target)\n            } catch (e: ResolverException) {\n                throw e.toFileSystemException(target.toString())\n            }\n        }\n        return try {\n            DocumentResolver.move(\n                source, target, copyOptions.atomicMove, copyOptions.progressIntervalMillis,\n                copyOptions.progressListener\n            )\n        } catch (e: ResolverException) {\n            (e.cause as? InterruptedIOException)?.let { throw it }\n            throw e.toFileSystemException(source.toString(), target.toString())\n        }\n    }\n\n    private fun ((Long) -> Unit).invokeWithSize(uri: Uri) {\n        val size = try {\n            DocumentResolver.getSize(uri)\n        } catch (e: ResolverException) {\n            e.printStackTrace()\n            return\n        } ?: return\n        this(size)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/document/DocumentFileAttributeView.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.document\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport android.provider.DocumentsContract\nimport java8.nio.file.attribute.BasicFileAttributeView\nimport java8.nio.file.attribute.FileTime\nimport me.zhanghai.android.files.provider.content.resolver.ResolverException\nimport me.zhanghai.android.files.provider.content.resolver.getInt\nimport me.zhanghai.android.files.provider.content.resolver.getLong\nimport me.zhanghai.android.files.provider.content.resolver.getString\nimport me.zhanghai.android.files.provider.content.resolver.moveToFirstOrThrow\nimport me.zhanghai.android.files.provider.document.resolver.DocumentResolver\nimport me.zhanghai.android.files.util.readParcelable\nimport java.io.IOException\n\ninternal class DocumentFileAttributeView(\n    private val path: DocumentPath\n) : BasicFileAttributeView, Parcelable {\n    override fun name(): String = NAME\n\n    @Throws(IOException::class)\n    override fun readAttributes(): DocumentFileAttributes {\n        val uri = try {\n            DocumentResolver.getDocumentUri(path)\n        } catch (e: ResolverException) {\n            throw e.toFileSystemException(path.toString())\n        }\n        var lastModifiedTimeMillis: Long\n        var mimeType: String?\n        var size: Long\n        var flags: Int\n        try {\n            DocumentResolver.queryDocument(path, uri).use { cursor ->\n                cursor.moveToFirstOrThrow()\n                lastModifiedTimeMillis = cursor.getLong(\n                    DocumentsContract.Document.COLUMN_LAST_MODIFIED\n                ) ?: 0\n                mimeType = cursor.getString(DocumentsContract.Document.COLUMN_MIME_TYPE)\n                size = cursor.getLong(DocumentsContract.Document.COLUMN_SIZE) ?: 0\n                flags = cursor.getInt(DocumentsContract.Document.COLUMN_FLAGS) ?: 0\n            }\n        } catch (e: ResolverException) {\n            throw e.toFileSystemException(path.toString())\n        }\n        return DocumentFileAttributes.from(lastModifiedTimeMillis, mimeType, size, flags, uri)\n    }\n\n    override fun setTimes(\n        lastModifiedTime: FileTime?,\n        lastAccessTime: FileTime?,\n        createTime: FileTime?\n    ) {\n        throw UnsupportedOperationException()\n    }\n\n    private constructor(source: Parcel) : this(source.readParcelable<DocumentPath>()!!)\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        dest.writeParcelable(path, flags)\n    }\n\n    companion object {\n        private val NAME = DocumentFileSystemProvider.scheme\n\n        val SUPPORTED_NAMES = setOf(\"basic\", NAME)\n\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<DocumentFileAttributeView> {\n            override fun createFromParcel(source: Parcel): DocumentFileAttributeView =\n                DocumentFileAttributeView(source)\n\n            override fun newArray(size: Int): Array<DocumentFileAttributeView?> = arrayOfNulls(size)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/document/DocumentFileAttributes.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.document\n\nimport android.net.Uri\nimport android.os.Parcelable\nimport java.time.Instant\nimport java8.nio.file.attribute.FileTime\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.provider.common.AbstractContentProviderFileAttributes\nimport me.zhanghai.android.files.provider.common.FileTimeParceler\n\n@Parcelize\ninternal class DocumentFileAttributes(\n    override val lastModifiedTime: @WriteWith<FileTimeParceler> FileTime,\n    override val mimeType: String?,\n    override val size: Long,\n    override val fileKey: Parcelable,\n    private val flags: Int\n) : AbstractContentProviderFileAttributes() {\n    fun flags(): Int = flags\n\n    companion object {\n        fun from(\n            lastModifiedTimeMillis: Long,\n            mimeType: String?,\n            size: Long,\n            flags: Int,\n            uri: Uri\n        ): DocumentFileAttributes {\n            val lastModifiedTime = FileTime.from(Instant.ofEpochMilli(lastModifiedTimeMillis))\n            val fileKey = uri\n            return DocumentFileAttributes(lastModifiedTime, mimeType, size, fileKey, flags)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/document/DocumentFileSystem.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.document\n\nimport android.net.Uri\nimport android.os.Parcel\nimport android.os.Parcelable\nimport java8.nio.file.FileStore\nimport java8.nio.file.FileSystem\nimport java8.nio.file.Path\nimport java8.nio.file.PathMatcher\nimport java8.nio.file.WatchService\nimport java8.nio.file.attribute.UserPrincipalLookupService\nimport java8.nio.file.spi.FileSystemProvider\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.ByteStringBuilder\nimport me.zhanghai.android.files.provider.common.ByteStringListPathCreator\nimport me.zhanghai.android.files.provider.common.toByteString\nimport me.zhanghai.android.files.util.StableUriParceler\nimport java.io.IOException\n\ninternal class DocumentFileSystem(\n    private val provider: DocumentFileSystemProvider,\n    val treeUri: Uri\n) : FileSystem(), ByteStringListPathCreator, Parcelable {\n    val rootDirectory = DocumentPath(this, SEPARATOR_BYTE_STRING)\n\n    init {\n        if (!rootDirectory.isAbsolute) {\n            throw AssertionError(\"Root directory must be absolute\")\n        }\n        if (rootDirectory.nameCount != 0) {\n            throw AssertionError(\"Root directory must contain no names\")\n        }\n    }\n\n    private val lock = Any()\n\n    private var isOpen = true\n\n    val defaultDirectory: DocumentPath\n        get() = rootDirectory\n\n    override fun provider(): FileSystemProvider = provider\n\n    override fun close() {\n        synchronized(lock) {\n            if (!isOpen) {\n                return\n            }\n            provider.removeFileSystem(this)\n            isOpen = false\n        }\n    }\n\n    override fun isOpen(): Boolean = synchronized(lock) { isOpen }\n\n    override fun isReadOnly(): Boolean = false\n\n    override fun getSeparator(): String = SEPARATOR_STRING\n\n    override fun getRootDirectories(): Iterable<Path> = listOf(rootDirectory)\n\n    override fun getFileStores(): Iterable<FileStore> {\n        // TODO\n        throw UnsupportedOperationException()\n    }\n\n    override fun supportedFileAttributeViews(): Set<String> =\n        DocumentFileAttributeView.SUPPORTED_NAMES\n\n    override fun getPath(first: String, vararg more: String): DocumentPath {\n        val path = ByteStringBuilder(first.toByteString())\n            .apply { more.forEach { append(SEPARATOR).append(it.toByteString()) } }\n            .toByteString()\n        return DocumentPath(this, path)\n    }\n\n    override fun getPath(first: ByteString, vararg more: ByteString): DocumentPath {\n        val path = ByteStringBuilder(first)\n            .apply { more.forEach { append(SEPARATOR).append(it) } }\n            .toByteString()\n        return DocumentPath(this, path)\n    }\n\n    override fun getPathMatcher(syntaxAndPattern: String): PathMatcher {\n        throw UnsupportedOperationException()\n    }\n\n    override fun getUserPrincipalLookupService(): UserPrincipalLookupService {\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun newWatchService(): WatchService {\n        // TODO\n        throw UnsupportedOperationException()\n    }\n\n    override fun equals(other: Any?): Boolean {\n        if (this === other) {\n            return true\n        }\n        if (javaClass != other?.javaClass) {\n            return false\n        }\n        other as DocumentFileSystem\n        return treeUri == other.treeUri\n    }\n\n    override fun hashCode(): Int = treeUri.hashCode()\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        //dest.writeParcelable(treeUri, flags)\n        with(StableUriParceler) { treeUri.write(dest, flags) }\n    }\n\n    companion object {\n        const val SEPARATOR = '/'.code.toByte()\n        private val SEPARATOR_BYTE_STRING = SEPARATOR.toByteString()\n        private const val SEPARATOR_STRING = SEPARATOR.toInt().toChar().toString()\n\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<DocumentFileSystem> {\n            override fun createFromParcel(source: Parcel): DocumentFileSystem {\n                //val treeUri = source.readParcelable<Uri>()!!\n                val treeUri = StableUriParceler.create(source)!!\n                return DocumentFileSystemProvider.getOrNewFileSystem(treeUri)\n            }\n\n            override fun newArray(size: Int): Array<DocumentFileSystem?> = arrayOfNulls(size)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/document/DocumentFileSystemProvider.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.document\n\nimport android.net.Uri\nimport android.os.Build\nimport android.os.ParcelFileDescriptor\nimport androidx.core.net.toUri\nimport java8.nio.channels.FileChannel\nimport java8.nio.channels.SeekableByteChannel\nimport java8.nio.file.AccessDeniedException\nimport java8.nio.file.AccessMode\nimport java8.nio.file.CopyOption\nimport java8.nio.file.DirectoryStream\nimport java8.nio.file.FileAlreadyExistsException\nimport java8.nio.file.FileStore\nimport java8.nio.file.FileSystem\nimport java8.nio.file.FileSystemAlreadyExistsException\nimport java8.nio.file.FileSystemNotFoundException\nimport java8.nio.file.LinkOption\nimport java8.nio.file.OpenOption\nimport java8.nio.file.Path\nimport java8.nio.file.ProviderMismatchException\nimport java8.nio.file.StandardOpenOption\nimport java8.nio.file.attribute.BasicFileAttributes\nimport java8.nio.file.attribute.FileAttribute\nimport java8.nio.file.attribute.FileAttributeView\nimport java8.nio.file.spi.FileSystemProvider\nimport me.zhanghai.android.files.file.MimeType\nimport me.zhanghai.android.files.provider.common.ByteStringPath\nimport me.zhanghai.android.files.provider.common.PathListDirectoryStream\nimport me.zhanghai.android.files.provider.common.PathObservable\nimport me.zhanghai.android.files.provider.common.PathObservableProvider\nimport me.zhanghai.android.files.provider.common.Searchable\nimport me.zhanghai.android.files.provider.common.WalkFileTreeSearchable\nimport me.zhanghai.android.files.provider.common.decodedPathByteString\nimport me.zhanghai.android.files.provider.common.decodedQueryByteString\nimport me.zhanghai.android.files.provider.common.open\nimport me.zhanghai.android.files.provider.common.toAccessModes\nimport me.zhanghai.android.files.provider.common.toByteString\nimport me.zhanghai.android.files.provider.common.toCopyOptions\nimport me.zhanghai.android.files.provider.common.toOpenOptions\nimport me.zhanghai.android.files.provider.content.resolver.Resolver\nimport me.zhanghai.android.files.provider.content.resolver.ResolverException\nimport me.zhanghai.android.files.provider.document.resolver.DocumentResolver\nimport java.io.IOException\nimport java.io.InputStream\nimport java.io.OutputStream\nimport java.net.URI\n\nobject DocumentFileSystemProvider : FileSystemProvider(), PathObservableProvider, Searchable {\n    private const val SCHEME = \"document\"\n\n    private val HIDDEN_FILE_NAME_PREFIX = \".\".toByteString()\n\n    private val fileSystems = mutableMapOf<Uri, DocumentFileSystem>()\n\n    private val lock = Any()\n\n    override fun getScheme(): String = SCHEME\n\n    override fun newFileSystem(uri: URI, env: Map<String, *>): FileSystem {\n        uri.requireSameScheme()\n        val treeUri = uri.treeUri\n        synchronized(lock) {\n            if (fileSystems[treeUri] != null) {\n                throw FileSystemAlreadyExistsException(treeUri.toString())\n            }\n            return newFileSystemLocked(treeUri)\n        }\n    }\n\n    internal fun getOrNewFileSystem(treeUri: Uri): DocumentFileSystem =\n        synchronized(lock) { fileSystems[treeUri] ?: newFileSystemLocked(treeUri) }\n\n    private fun newFileSystemLocked(treeUri: Uri): DocumentFileSystem {\n        val fileSystem = DocumentFileSystem(this, treeUri)\n        fileSystems[treeUri] = fileSystem\n        return fileSystem\n    }\n\n    override fun getFileSystem(uri: URI): FileSystem {\n        uri.requireSameScheme()\n        val treeUri = uri.treeUri\n        return synchronized(lock) { fileSystems[treeUri] }\n            ?: throw FileSystemNotFoundException(treeUri.toString())\n    }\n\n    internal fun removeFileSystem(fileSystem: DocumentFileSystem) {\n        val treeUri = fileSystem.treeUri\n        synchronized(lock) { fileSystems.remove(treeUri) }\n    }\n\n    override fun getPath(uri: URI): Path {\n        uri.requireSameScheme()\n        val treeUri = uri.treeUri\n        val path = uri.decodedQueryByteString\n            ?: throw IllegalArgumentException(\"URI must have a query\")\n        return getOrNewFileSystem(treeUri).getPath(path)\n    }\n\n    private fun URI.requireSameScheme() {\n        val scheme = scheme\n        require(scheme == SCHEME) { \"URI scheme $scheme must be $SCHEME\" }\n    }\n\n    private val URI.treeUri: Uri\n        get() {\n            val path = decodedPathByteString\n                ?: throw IllegalArgumentException(\"URI must have a path\")\n            // Drop the first character which is always a slash.\n            return path.toString().drop(1).toUri()\n        }\n\n    @Throws(IOException::class)\n    override fun newInputStream(file: Path, vararg options: OpenOption): InputStream {\n        file as? DocumentPath ?: throw ProviderMismatchException(file.toString())\n        val optionsSet = mutableSetOf(*options)\n        val create = optionsSet.remove(StandardOpenOption.CREATE)\n        val createNew = optionsSet.remove(StandardOpenOption.CREATE_NEW)\n        val openOptions = optionsSet.toOpenOptions()\n        if (openOptions.write) {\n            throw UnsupportedOperationException(StandardOpenOption.WRITE.toString())\n        }\n        if (openOptions.append) {\n            throw UnsupportedOperationException(StandardOpenOption.APPEND.toString())\n        }\n        val mode = openOptions.toDocumentMode()\n        if (create || createNew) {\n            val exists = DocumentResolver.exists(file)\n            if (createNew && exists) {\n                throw FileAlreadyExistsException(file.toString())\n            }\n            if (!exists) {\n                val uri = try {\n                    // TODO: Allow passing in a mime type?\n                    DocumentResolver.create(file, MimeType.GENERIC.value)\n                } catch (e: ResolverException) {\n                    throw e.toFileSystemException(file.toString())\n                }\n                return try {\n                    Resolver.openInputStream(uri, mode)\n                } catch (e: ResolverException) {\n                    throw e.toFileSystemException(uri.toString())\n                }\n            }\n        }\n        return try {\n            DocumentResolver.openInputStream(file, mode)\n        } catch (e: ResolverException) {\n            throw e.toFileSystemException(file.toString())\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun newOutputStream(file: Path, vararg options: OpenOption): OutputStream {\n        file as? DocumentPath ?: throw ProviderMismatchException(file.toString())\n        val optionsSet = mutableSetOf(*options)\n        if (optionsSet.isEmpty()) {\n            optionsSet += StandardOpenOption.CREATE\n            optionsSet += StandardOpenOption.TRUNCATE_EXISTING\n        }\n        optionsSet += StandardOpenOption.WRITE\n        val create = optionsSet.remove(StandardOpenOption.CREATE)\n        val createNew = optionsSet.remove(StandardOpenOption.CREATE_NEW)\n        val openOptions = optionsSet.toOpenOptions()\n        val mode = openOptions.toDocumentMode()\n        if (create || createNew) {\n            val exists = DocumentResolver.exists(file)\n            if (createNew && exists) {\n                throw FileAlreadyExistsException(file.toString())\n            }\n            if (!exists) {\n                val uri = try {\n                    // TODO: Allow passing in a mime type?\n                    DocumentResolver.create(file, MimeType.GENERIC.value)\n                } catch (e: ResolverException) {\n                    throw e.toFileSystemException(file.toString())\n                }\n                return try {\n                    Resolver.openOutputStream(uri, mode)\n                } catch (e: ResolverException) {\n                    throw e.toFileSystemException(uri.toString())\n                }\n            }\n        }\n        return try {\n            DocumentResolver.openOutputStream(file, mode)\n        } catch (e: ResolverException) {\n            throw e.toFileSystemException(file.toString())\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun newFileChannel(\n        file: Path,\n        options: Set<OpenOption>,\n        vararg attributes: FileAttribute<*>\n    ): FileChannel {\n        file as? DocumentPath ?: throw ProviderMismatchException(file.toString())\n        val options = options.toMutableSet()\n        val hasCreate = options.remove(StandardOpenOption.CREATE)\n        val hasCreateNew = options.remove(StandardOpenOption.CREATE_NEW)\n        val openOptions = options.toOpenOptions()\n        val mode = openOptions.toDocumentMode()\n        if (attributes.isNotEmpty()) {\n            throw UnsupportedOperationException(attributes.contentToString())\n        }\n        var pfd: ParcelFileDescriptor? = null\n        if (hasCreate || hasCreateNew) {\n            val exists = DocumentResolver.exists(file)\n            if (hasCreateNew && exists) {\n                throw FileAlreadyExistsException(file.toString())\n            }\n            if (!exists) {\n                val uri = try {\n                    // TODO: Allow passing in a mime type?\n                    DocumentResolver.create(file, MimeType.GENERIC.value)\n                } catch (e: ResolverException) {\n                    throw e.toFileSystemException(file.toString())\n                }\n                pfd = try {\n                    Resolver.openParcelFileDescriptor(uri, mode)\n                } catch (e: ResolverException) {\n                    throw e.toFileSystemException(uri.toString())\n                }\n            }\n        }\n        if (pfd == null) {\n            pfd = try {\n                DocumentResolver.openParcelFileDescriptor(file, mode)\n            } catch (e: ResolverException) {\n                throw e.toFileSystemException(file.toString())\n            }\n        }\n        // TODO: kotlinc: Type mismatch: inferred type is ParcelFileDescriptor? but\n        //  ParcelFileDescriptor was expected\n        //return FileChannel::class.open(pfd, mode)\n        return FileChannel::class.open(pfd!!, mode)\n    }\n\n    @Throws(IOException::class)\n    override fun newByteChannel(\n        file: Path,\n        options: Set<OpenOption>,\n        vararg attributes: FileAttribute<*>\n    ): SeekableByteChannel {\n        file as? DocumentPath ?: throw ProviderMismatchException(file.toString())\n        return newFileChannel(file, options, *attributes)\n    }\n\n    @Throws(IOException::class)\n    override fun newDirectoryStream(\n        directory: Path,\n        filter: DirectoryStream.Filter<in Path>\n    ): DirectoryStream<Path> {\n        directory as? DocumentPath ?: throw ProviderMismatchException(directory.toString())\n        val children = try {\n            @Suppress(\"UNCHECKED_CAST\")\n            DocumentResolver.queryChildren(directory) as List<Path>\n        } catch (e: ResolverException) {\n            throw e.toFileSystemException(directory.toString())\n        }\n        // TODO: Handle DocumentsContract.EXTRA_LOADING, EXTRA_INFO and EXTRA_ERROR.\n        return PathListDirectoryStream(children, filter)\n    }\n\n    @Throws(IOException::class)\n    override fun createDirectory(directory: Path, vararg attributes: FileAttribute<*>) {\n        directory as? DocumentPath ?: throw ProviderMismatchException(directory.toString())\n        if (attributes.isNotEmpty()) {\n            throw UnsupportedOperationException(attributes.contentToString())\n        }\n        try {\n            DocumentResolver.create(directory, MimeType.DIRECTORY.value)\n        } catch (e: ResolverException) {\n            throw e.toFileSystemException(directory.toString())\n        }\n    }\n\n    override fun createSymbolicLink(link: Path, target: Path, vararg attributes: FileAttribute<*>) {\n        link as? DocumentPath ?: throw ProviderMismatchException(link.toString())\n        when (target) {\n            is DocumentPath, is ByteStringPath -> {}\n            else -> throw ProviderMismatchException(target.toString())\n        }\n        throw UnsupportedOperationException()\n    }\n\n    override fun createLink(link: Path, existing: Path) {\n        link as? DocumentPath ?: throw ProviderMismatchException(link.toString())\n        existing as? DocumentPath ?: throw ProviderMismatchException(existing.toString())\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun delete(path: Path) {\n        path as? DocumentPath ?: throw ProviderMismatchException(path.toString())\n        try {\n            DocumentResolver.remove(path)\n        } catch (e: ResolverException) {\n            throw e.toFileSystemException(path.toString())\n        }\n    }\n\n    override fun readSymbolicLink(link: Path): Path {\n        link as? DocumentPath ?: throw ProviderMismatchException(link.toString())\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun copy(source: Path, target: Path, vararg options: CopyOption) {\n        source as? DocumentPath ?: throw ProviderMismatchException(source.toString())\n        target as? DocumentPath ?: throw ProviderMismatchException(target.toString())\n        val copyOptions = options.toCopyOptions()\n        DocumentCopyMove.copy(source, target, copyOptions)\n    }\n\n    @Throws(IOException::class)\n    override fun move(source: Path, target: Path, vararg options: CopyOption) {\n        source as? DocumentPath ?: throw ProviderMismatchException(source.toString())\n        target as? DocumentPath ?: throw ProviderMismatchException(target.toString())\n        val copyOptions = options.toCopyOptions()\n        DocumentCopyMove.move(source, target, copyOptions)\n    }\n\n    override fun isSameFile(path: Path, path2: Path): Boolean {\n        path as? DocumentPath ?: throw ProviderMismatchException(path.toString())\n        // TODO: DocumentsContract.findDocumentPath()?\n        return path == path2\n    }\n\n    override fun isHidden(path: Path): Boolean {\n        path as? DocumentPath ?: throw ProviderMismatchException(path.toString())\n        val fileName = path.fileNameByteString ?: return false\n        return fileName.startsWith(HIDDEN_FILE_NAME_PREFIX)\n    }\n\n    override fun getFileStore(path: Path): FileStore {\n        path as? DocumentPath ?: throw ProviderMismatchException(path.toString())\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun checkAccess(path: Path, vararg modes: AccessMode) {\n        path as? DocumentPath ?: throw ProviderMismatchException(path.toString())\n        // This checks existence as well.\n        val mimeType = try {\n            DocumentResolver.getMimeType(path)\n        } catch (e: ResolverException) {\n            throw e.toFileSystemException(path.toString())\n        }\n        val isDirectory = mimeType == MimeType.DIRECTORY.value\n        if (isDirectory) {\n            // There's no elegant way to check access to a directory beyond its existence.\n            return\n        }\n        val accessModes = modes.toAccessModes()\n        if (accessModes.execute) {\n            throw AccessDeniedException(path.toString())\n        }\n        if (accessModes.write) {\n            // Before Android 10, ParcelFileDescriptor.parseMode() parses \"w\" as \"wt\", and we would\n            // truncate the file to empty. So work around that with \"wa\" on older platforms.\n            val mode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) \"w\" else \"wa\"\n            try {\n                DocumentResolver.openOutputStream(path, mode).use {}\n            } catch (e: ResolverException) {\n                throw e.toFileSystemException(path.toString())\n            }\n        }\n        if (accessModes.read) {\n            try {\n                DocumentResolver.openInputStream(path, \"r\").use {}\n            } catch (e: ResolverException) {\n                throw e.toFileSystemException(path.toString())\n            }\n        }\n    }\n\n    override fun <V : FileAttributeView> getFileAttributeView(\n        path: Path,\n        type: Class<V>,\n        vararg options: LinkOption\n    ): V? {\n        if (!supportsFileAttributeView(type)) {\n            return null\n        }\n        @Suppress(\"UNCHECKED_CAST\")\n        return getFileAttributeView(path) as V\n    }\n\n    internal fun supportsFileAttributeView(type: Class<out FileAttributeView>): Boolean =\n        type.isAssignableFrom(DocumentFileAttributeView::class.java)\n\n    @Throws(IOException::class)\n    override fun <A : BasicFileAttributes> readAttributes(\n        path: Path,\n        type: Class<A>,\n        vararg options: LinkOption\n    ): A {\n        if (!type.isAssignableFrom(DocumentFileAttributes::class.java)) {\n            throw UnsupportedOperationException(type.toString())\n        }\n        @Suppress(\"UNCHECKED_CAST\")\n        return getFileAttributeView(path).readAttributes() as A\n    }\n\n    private fun getFileAttributeView(path: Path): DocumentFileAttributeView {\n        path as? DocumentPath ?: throw ProviderMismatchException(path.toString())\n        return DocumentFileAttributeView(path)\n    }\n\n    override fun readAttributes(\n        path: Path,\n        attributes: String,\n        vararg options: LinkOption\n    ): Map<String, Any> {\n        path as? DocumentPath ?: throw ProviderMismatchException(path.toString())\n        throw UnsupportedOperationException()\n    }\n\n    override fun setAttribute(\n        path: Path,\n        attribute: String,\n        value: Any,\n        vararg options: LinkOption\n    ) {\n        path as? DocumentPath ?: throw ProviderMismatchException(path.toString())\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun observe(path: Path, intervalMillis: Long): PathObservable {\n        path as? DocumentPath ?: throw ProviderMismatchException(path.toString())\n        return DocumentPathObservable(path, intervalMillis)\n    }\n\n    @Throws(IOException::class)\n    override fun search(\n        directory: Path,\n        query: String,\n        intervalMillis: Long,\n        listener: (List<Path>) -> Unit\n    ) {\n        directory as? DocumentPath ?: throw ProviderMismatchException(directory.toString())\n        WalkFileTreeSearchable.search(directory, query, intervalMillis, listener)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/document/DocumentPath.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.document\n\nimport android.net.Uri\nimport android.os.Parcel\nimport android.os.Parcelable\nimport android.provider.DocumentsContract\nimport java8.nio.file.FileSystem\nimport java8.nio.file.LinkOption\nimport java8.nio.file.Path\nimport java8.nio.file.WatchEvent\nimport java8.nio.file.WatchKey\nimport java8.nio.file.WatchService\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.ByteStringListPath\nimport me.zhanghai.android.files.provider.common.toByteString\nimport me.zhanghai.android.files.provider.document.resolver.DocumentResolver\nimport me.zhanghai.android.files.util.readParcelable\nimport java.io.File\nimport java.io.IOException\n\n/** @see DocumentsContract.Path */\ninternal class DocumentPath : ByteStringListPath<DocumentPath>, DocumentResolver.Path {\n    private val fileSystem: DocumentFileSystem\n\n    constructor(fileSystem: DocumentFileSystem, path: ByteString) : super(\n        DocumentFileSystem.SEPARATOR, path\n    ) {\n        this.fileSystem = fileSystem\n    }\n\n    private constructor(\n        fileSystem: DocumentFileSystem,\n        absolute: Boolean,\n        segments: List<ByteString>\n    ) : super(DocumentFileSystem.SEPARATOR, absolute, segments) {\n        this.fileSystem = fileSystem\n    }\n\n    override fun isPathAbsolute(path: ByteString): Boolean =\n        path.isNotEmpty() && path[0] == DocumentFileSystem.SEPARATOR\n\n    override fun createPath(path: ByteString): DocumentPath = DocumentPath(fileSystem, path)\n\n    override fun createPath(absolute: Boolean, segments: List<ByteString>): DocumentPath =\n        DocumentPath(fileSystem, absolute, segments)\n\n    override val uriPath: ByteString\n        // Prepend a slash character to make it a valid URI path, since we always have an (empty)\n        // authority.\n        get() = (\"/\" + fileSystem.treeUri.toString()).toByteString()\n\n    override val uriQuery: ByteString?\n        get() = super.uriPath\n\n    override val defaultDirectory: DocumentPath\n        get() = fileSystem.defaultDirectory\n\n    override fun getFileSystem(): FileSystem = fileSystem\n\n    override fun getRoot(): DocumentPath? = if (isAbsolute) fileSystem.rootDirectory else null\n\n    @Throws(IOException::class)\n    override fun toRealPath(vararg options: LinkOption): DocumentPath {\n        throw UnsupportedOperationException()\n    }\n\n    override fun toFile(): File {\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun register(\n        watcher: WatchService,\n        events: Array<WatchEvent.Kind<*>>,\n        vararg modifiers: WatchEvent.Modifier\n    ): WatchKey {\n        throw UnsupportedOperationException()\n    }\n\n    override val treeUri: Uri\n        get() = fileSystem.treeUri\n\n    override val displayName: String?\n        get() = fileNameByteString?.toString()\n\n    override val parent: DocumentPath?\n        get() = getParent()\n\n    private constructor(source: Parcel) : super(source) {\n        fileSystem = source.readParcelable()!!\n    }\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        super.writeToParcel(dest, flags)\n\n        dest.writeParcelable(fileSystem, flags)\n    }\n\n    companion object {\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<DocumentPath> {\n            override fun createFromParcel(source: Parcel): DocumentPath = DocumentPath(source)\n\n            override fun newArray(size: Int): Array<DocumentPath?> = arrayOfNulls(size)\n        }\n    }\n}\n\nval Path.isDocumentPath: Boolean\n    get() = this is DocumentPath\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/document/DocumentPathObservable.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.document\n\nimport android.database.ContentObserver\nimport android.database.Cursor\nimport android.net.Uri\nimport android.os.Build\nimport me.zhanghai.android.files.file.MimeType\nimport me.zhanghai.android.files.provider.common.AbstractPathObservable\nimport me.zhanghai.android.files.provider.content.resolver.ResolverException\nimport me.zhanghai.android.files.provider.document.resolver.DocumentResolver\n\ninternal class DocumentPathObservable(\n    path: DocumentPath,\n    intervalMillis: Long\n) : AbstractPathObservable(intervalMillis) {\n    private val cursor: Cursor\n\n    private val contentObserver = object : ContentObserver(handler) {\n        override fun deliverSelfNotifications(): Boolean = true\n\n        override fun onChange(selfChange: Boolean) {\n            notifyObservers()\n        }\n    }\n\n    init {\n        val uri = try {\n            path.observableUri\n        } catch (e: ResolverException) {\n            throw e.toFileSystemException(path.toString())\n        }\n        cursor = try {\n            DocumentResolver.query(uri, emptyArray(), null)\n        } catch (e: ResolverException) {\n            throw e.toFileSystemException(path.toString())\n        }\n        cursor.registerContentObserver(contentObserver)\n    }\n\n    override fun onCloseLocked() {\n        cursor.unregisterContentObserver(contentObserver)\n        cursor.close()\n    }\n\n    private val DocumentPath.observableUri: Uri\n        @Throws(ResolverException::class)\n        get() {\n            // Querying children for a regular file is fine for non-directory since API 29, but for\n            // older APIs we'll have to work around by observing all children of its parent.\n            // @see com.android.internal.content.FileSystemProvider#queryChildDocuments(String,\n            //      String[], String)\n            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {\n                val mimeType = DocumentResolver.getMimeType(this)\n                if (mimeType != MimeType.DIRECTORY.value) {\n                    parent?.let { return DocumentResolver.getDocumentChildrenUri(it) }\n                }\n            }\n            return DocumentResolver.getDocumentChildrenUri(this)\n        }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/document/FileAttributesExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.document\n\nimport android.provider.DocumentsContract\nimport java8.nio.file.ProviderMismatchException\nimport java8.nio.file.attribute.BasicFileAttributes\nimport me.zhanghai.android.files.util.hasBits\n\nval BasicFileAttributes.documentSupportsThumbnail: Boolean\n    get() {\n        this as? DocumentFileAttributes ?: throw ProviderMismatchException(toString())\n        return flags().hasBits(DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL)\n    }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/document/OpenOptionsDocumentExtensions.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.document\n\nimport java8.nio.file.StandardOpenOption\nimport me.zhanghai.android.files.provider.common.OpenOptions\n\ninternal fun OpenOptions.toDocumentMode(): String =\n    StringBuilder().apply {\n        if (read && write) {\n            append(\"rw\")\n        } else if (write) {\n            append('w')\n        } else {\n            append('r')\n        }\n        if (append) {\n            append('a')\n        }\n        if (truncateExisting) {\n            append('t')\n        }\n        if (create || createNew) {\n            throw AssertionError(\n                \"${StandardOpenOption.CREATE} and ${StandardOpenOption.CREATE_NEW\n                } should have been handled before calling OpenOptions.toDocumentMode()\"\n            )\n        }\n        if (deleteOnClose) {\n            throw UnsupportedOperationException(StandardOpenOption.DELETE_ON_CLOSE.toString())\n        }\n        if (sync) {\n            throw UnsupportedOperationException(StandardOpenOption.SYNC.toString())\n        }\n        if (dsync) {\n            throw UnsupportedOperationException(StandardOpenOption.DSYNC.toString())\n        }\n    }.toString()\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/document/PathDocumentExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.document\n\nimport android.net.Uri\nimport java8.nio.file.Path\nimport java8.nio.file.ProviderMismatchException\nimport me.zhanghai.android.files.provider.content.resolver.ResolverException\nimport me.zhanghai.android.files.provider.document.resolver.DocumentResolver\nimport java.io.IOException\n\nval Path.documentUri: Uri\n    @Throws(IOException::class)\n    get() {\n        this as? DocumentPath ?: throw ProviderMismatchException(toString())\n        return try {\n            DocumentResolver.getDocumentUri(this)\n        } catch (e: ResolverException) {\n            throw e.toFileSystemException(toString())\n        }\n    }\n\nval Path.documentTreeUri: Uri\n    get() {\n        this as? DocumentPath ?: throw ProviderMismatchException(toString())\n        return treeUri\n    }\n\nfun Uri.createDocumentTreeRootPath(): Path =\n    DocumentFileSystemProvider.getOrNewFileSystem(this).rootDirectory\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/document/resolver/DocumentResolver.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.document.resolver\n\nimport android.database.ContentObserver\nimport android.database.Cursor\nimport android.graphics.Bitmap\nimport android.graphics.Point\nimport android.net.Uri\nimport android.os.Build\nimport android.os.CancellationSignal\nimport android.os.ParcelFileDescriptor\nimport android.provider.DocumentsContract\nimport androidx.annotation.RequiresApi\nimport java8.nio.file.NoSuchFileException\nimport kotlinx.coroutines.runBlocking\nimport kotlinx.coroutines.suspendCancellableCoroutine\nimport me.zhanghai.android.files.app.contentResolver\nimport me.zhanghai.android.files.compat.DocumentsContractCompat\nimport me.zhanghai.android.files.file.MimeType\nimport me.zhanghai.android.files.provider.common.copyTo\nimport me.zhanghai.android.files.provider.content.resolver.Resolver\nimport me.zhanghai.android.files.provider.content.resolver.ResolverException\nimport me.zhanghai.android.files.provider.content.resolver.getLong\nimport me.zhanghai.android.files.provider.content.resolver.getString\nimport me.zhanghai.android.files.provider.content.resolver.moveToFirstOrThrow\nimport me.zhanghai.android.files.provider.content.resolver.requireString\nimport me.zhanghai.android.files.util.AbstractLocalCursor\nimport java.io.FileNotFoundException\nimport java.io.IOException\nimport java.io.InputStream\nimport java.io.OutputStream\nimport java.util.Collections\nimport java.util.WeakHashMap\nimport kotlin.coroutines.resume\n\nobject DocumentResolver {\n    // @see com.android.shell.BugreportStorageProvider#AUTHORITY\n    private const val BUGREPORT_STORAGE_PROVIDER_AUTHORITY = \"com.android.shell.documents\"\n    // @see com.android.mtp.MtpDocumentsProvider#AUTHORITY\n    private const val MTP_DOCUMENTS_PROVIDER_AUTHORITY = \"com.android.mtp.documents\"\n\n    private val LOCAL_AUTHORITIES = setOf(\n        BUGREPORT_STORAGE_PROVIDER_AUTHORITY,\n        DocumentsContractCompat.EXTERNAL_STORAGE_PROVIDER_AUTHORITY,\n        MTP_DOCUMENTS_PROVIDER_AUTHORITY\n    )\n    private val COPY_UNSUPPORTED_AUTHORITIES = setOf(\n        BUGREPORT_STORAGE_PROVIDER_AUTHORITY,\n        DocumentsContractCompat.EXTERNAL_STORAGE_PROVIDER_AUTHORITY,\n        MTP_DOCUMENTS_PROVIDER_AUTHORITY\n    )\n    private val MOVE_UNSUPPORTED_AUTHORITIES = setOf(\n        MTP_DOCUMENTS_PROVIDER_AUTHORITY\n    )\n    private val REMOVE_UNSUPPORTED_AUTHORITIES = setOf(\n        BUGREPORT_STORAGE_PROVIDER_AUTHORITY,\n        DocumentsContractCompat.EXTERNAL_STORAGE_PROVIDER_AUTHORITY,\n        MTP_DOCUMENTS_PROVIDER_AUTHORITY\n    )\n\n    private val pathDocumentIdCache = Collections.synchronizedMap(WeakHashMap<Path, String>())\n\n    private val directoryCursorCache = Collections.synchronizedMap(WeakHashMap<Path, Cursor>())\n\n    @Throws(ResolverException::class)\n    fun checkExistence(path: Path) {\n        // Prevent cache from interfering with our check. Cache will be added again if\n        // queryDocumentId() succeeds.\n        pathDocumentIdCache -= path\n        queryDocumentId(path)\n    }\n\n    @Throws(ResolverException::class)\n    fun copy(\n        sourcePath: Path,\n        targetPath: Path,\n        intervalMillis: Long,\n        listener: ((Long) -> Unit)?\n    ): Uri {\n        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N\n            && sourcePath.hasSameAuthority(targetPath) && !sourcePath.isCopyUnsupported) {\n            copyApi24(sourcePath, targetPath, intervalMillis, listener)\n        } else {\n            copyManually(sourcePath, targetPath, intervalMillis, listener)\n        }\n    }\n\n    private val Path.isCopyUnsupported: Boolean\n        get() = treeUri.authority in COPY_UNSUPPORTED_AUTHORITIES\n\n    @RequiresApi(Build.VERSION_CODES.N)\n    @Throws(ResolverException::class)\n    private fun copyApi24(\n        sourcePath: Path,\n        targetPath: Path,\n        intervalMillis: Long,\n        listener: ((Long) -> Unit)?\n    ): Uri {\n        val sourceUri = getDocumentUri(sourcePath)\n        val targetParentUri = getDocumentUri(targetPath.requireParent())\n        val copiedTargetUri = try {\n            // This doesn't support progress interval millis and interruption.\n            DocumentsContract.copyDocument(contentResolver, sourceUri, targetParentUri)\n        } catch (e: UnsupportedOperationException) {\n            // Ignored.\n            return copyManually(sourcePath, targetPath, intervalMillis, listener)\n        } catch (e: Exception) {\n            throw ResolverException(e)\n        } ?: throw ResolverException(\n            \"DocumentsContract.copyDocument() with $sourceUri and $targetParentUri returned null\"\n        )\n        val sourceDisplayName = sourcePath.displayName\n        val targetDisplayName = targetPath.displayName\n        if (sourceDisplayName == targetDisplayName) {\n            listener?.invokeWithSize(copiedTargetUri)\n            return copiedTargetUri\n        }\n        val renamedTargetUri = try {\n            rename(copiedTargetUri, targetDisplayName!!)\n        } catch (e: ResolverException) {\n            try {\n                remove(copiedTargetUri, targetParentUri)\n            } catch (e2: ResolverException) {\n                e.addSuppressed(e2)\n            }\n            throw e\n        }\n        listener?.invokeWithSize(renamedTargetUri)\n        return renamedTargetUri\n    }\n\n    @Throws(ResolverException::class)\n    private fun copyManually(\n        sourcePath: Path,\n        targetPath: Path,\n        intervalMillis: Long,\n        listener: ((Long) -> Unit)?\n    ): Uri {\n        val sourceUri = getDocumentUri(sourcePath)\n        val mimeType = try {\n            getMimeType(sourceUri)\n        } catch (e: ResolverException) {\n            e.printStackTrace()\n            null\n        } ?: MimeType.GENERIC.value\n        if (mimeType == MimeType.DIRECTORY.value) {\n            return create(targetPath, MimeType.DIRECTORY.value)\n        }\n        val targetUri = create(targetPath, mimeType)\n        try {\n            Resolver.openInputStream(sourceUri, \"r\").use { inputStream ->\n                Resolver.openOutputStream(targetUri, \"wt\").use { outputStream ->\n                    inputStream.copyTo(outputStream, intervalMillis, listener)\n                }\n            }\n        } catch (e: IOException) {\n            val targetParentPath = targetPath.parent\n            if (targetParentPath != null) {\n                try {\n                    val targetParentUri = getDocumentUri(targetParentPath)\n                    remove(targetUri, targetParentUri)\n                } catch (e2: ResolverException) {\n                    e.addSuppressed(e2)\n                }\n            }\n            throw ResolverException(e)\n        }\n        return targetUri\n    }\n\n    @Throws(ResolverException::class)\n    fun create(path: Path, mimeType: String): Uri {\n        val parentUri = getDocumentUri(path.requireParent())\n        // The display name might have been changed so we cannot add the new URI to cache.\n        return try {\n            DocumentsContract.createDocument(\n                contentResolver, parentUri, mimeType, path.displayName!!\n            )\n        } catch (e: Exception) {\n            throw ResolverException(e)\n        } ?: throw ResolverException(\n            \"DocumentsContract.createDocument() with $parentUri returned null\"\n        )\n    }\n\n    @Deprecated(\"\", ReplaceWith(\"remove(path)\"))\n    @Throws(ResolverException::class)\n    fun delete(path: Path) {\n        val uri = getDocumentUri(path)\n        // Always remove the path from cache, in case a deletion actually succeeded despite\n        // exception being thrown.\n        pathDocumentIdCache -= path\n        directoryCursorCache -= path\n        @Suppress(\"DEPRECATION\")\n        delete(uri)\n    }\n\n    @Deprecated(\"\", ReplaceWith(\"remove(uri)\"))\n    @Throws(ResolverException::class)\n    fun delete(uri: Uri) {\n        val deleted = try {\n            DocumentsContract.deleteDocument(contentResolver, uri)\n        } catch (e: Exception) {\n            throw ResolverException(e)\n        }\n        if (!deleted) {\n            throw ResolverException(\"DocumentsContract.deleteDocument() with $uri returned false\")\n        }\n    }\n\n    fun exists(path: Path): Boolean =\n        try {\n            checkExistence(path)\n            true\n        } catch (e: ResolverException) {\n            false\n        }\n\n    @Throws(ResolverException::class)\n    fun getMimeType(path: Path): String? {\n        val uri = getDocumentUri(path)\n        return getMimeType(uri)\n    }\n\n    @Throws(ResolverException::class)\n    fun getMimeType(uri: Uri): String? =\n        query(uri, arrayOf(DocumentsContract.Document.COLUMN_MIME_TYPE), null).use { cursor ->\n            cursor.moveToFirstOrThrow()\n            cursor.getString(DocumentsContract.Document.COLUMN_MIME_TYPE)\n        }?.takeIf { it.isNotEmpty() && it != MimeType.GENERIC.value }\n\n    @Throws(ResolverException::class)\n    fun getSize(path: Path): Long? {\n        val uri = getDocumentUri(path)\n        return getSize(uri)\n    }\n\n    @Throws(ResolverException::class)\n    fun getSize(uri: Uri): Long? =\n        query(uri, arrayOf(DocumentsContract.Document.COLUMN_SIZE), null).use { cursor ->\n            cursor.moveToFirstOrThrow()\n            cursor.getLong(DocumentsContract.Document.COLUMN_SIZE)\n        }\n\n    @Throws(ResolverException::class)\n    fun getThumbnail(path: Path, width: Int, height: Int, signal: CancellationSignal): Bitmap? {\n        val uri = getDocumentUri(path)\n        return try {\n            DocumentsContract.getDocumentThumbnail(\n                contentResolver, uri, Point(width, height), signal\n            )\n        } catch (e: Exception) {\n            throw ResolverException(e)\n        }\n    }\n\n    fun isLocal(path: Path): Boolean {\n        val authority = path.treeUri.authority\n        return authority in LOCAL_AUTHORITIES\n    }\n\n    @Throws(ResolverException::class)\n    fun move(\n        sourcePath: Path,\n        targetPath: Path,\n        moveOnly: Boolean,\n        intervalMillis: Long,\n        listener: ((Long) -> Unit)?\n    ): Uri {\n        val sourceParentPath = sourcePath.requireParent()\n        val targetParentPath = targetPath.requireParent()\n        if (sourceParentPath == targetParentPath) {\n            return rename(sourcePath, targetPath.displayName!!)\n        }\n        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N\n            && sourcePath.hasSameAuthority(targetPath) && !sourcePath.isMoveUnsupported) {\n            moveApi24(sourcePath, targetPath, moveOnly, intervalMillis, listener)\n        } else {\n            if (moveOnly) {\n                // @see DocumentsProvider.moveDocument(String, String, String)\n                throw ResolverException(UnsupportedOperationException(\"Move not supported\"))\n            }\n            moveByCopy(sourcePath, targetPath, intervalMillis, listener)\n        }\n    }\n\n    private fun Path.hasSameAuthority(other: Path): Boolean =\n        treeUri.authority == other.treeUri.authority\n\n    private val Path.isMoveUnsupported: Boolean\n        get() = treeUri.authority in MOVE_UNSUPPORTED_AUTHORITIES\n\n    @RequiresApi(Build.VERSION_CODES.N)\n    @Throws(ResolverException::class)\n    private fun moveApi24(\n        sourcePath: Path,\n        targetPath: Path,\n        moveOnly: Boolean,\n        intervalMillis: Long,\n        listener: ((Long) -> Unit)?\n    ): Uri {\n        val sourceParentUri = getDocumentUri(sourcePath.requireParent())\n        val sourceUri = getDocumentUri(sourcePath)\n        val targetParentUri = getDocumentUri(targetPath.requireParent())\n        val movedTargetUri = try {\n            // This doesn't support progress interval millis and interruption.\n            DocumentsContract.moveDocument(\n                contentResolver, sourceUri, sourceParentUri, targetParentUri\n            )\n        } catch (e: UnsupportedOperationException) {\n            if (moveOnly) {\n                throw ResolverException(e)\n            }\n            return moveByCopy(sourcePath, targetPath, intervalMillis, listener)\n        } catch (e: Exception) {\n            throw ResolverException(e)\n        } ?: throw ResolverException(\n            \"DocumentsContract.moveDocument() with $sourceUri and $targetParentUri returned null\"\n        )\n        val sourceDisplayName = sourcePath.displayName\n        val targetDisplayName = targetPath.displayName\n        if (sourceDisplayName == targetDisplayName) {\n            listener?.invokeWithSize(movedTargetUri)\n            return movedTargetUri\n        }\n        val renamedTargetUri = rename(movedTargetUri, targetDisplayName!!)\n        listener?.invokeWithSize(renamedTargetUri)\n        return renamedTargetUri\n    }\n\n    private fun ((Long) -> Unit).invokeWithSize(uri: Uri) {\n        val size = try {\n            getSize(uri)\n        } catch (e: ResolverException) {\n            e.printStackTrace()\n            return\n        } ?: return\n        this(size)\n    }\n\n    @Throws(ResolverException::class)\n    private fun moveByCopy(\n        sourcePath: Path,\n        targetPath: Path,\n        intervalMillis: Long,\n        listener: ((Long) -> Unit)?\n    ): Uri {\n        val targetUri = copy(sourcePath, targetPath, intervalMillis, listener)\n        try {\n            val sourceUri = getDocumentUri(sourcePath)\n            val sourceParentUri = getDocumentUri(sourcePath.requireParent())\n            remove(sourceUri, sourceParentUri)\n        } catch (e: ResolverException) {\n            if (e.toFileSystemException(sourcePath.toString()) !is NoSuchFileException) {\n                try {\n                    val targetParentUri = getDocumentUri(targetPath.requireParent())\n                    remove(targetUri, targetParentUri)\n                } catch (e2: ResolverException) {\n                    e.addSuppressed(e2)\n                }\n            }\n            throw e\n        }\n        return targetUri\n    }\n\n    @Throws(ResolverException::class)\n    fun openInputStream(path: Path, mode: String): InputStream {\n        val uri = getDocumentUri(path)\n        return Resolver.openInputStream(uri, mode)\n    }\n\n    @Throws(ResolverException::class)\n    fun openOutputStream(path: Path, mode: String): OutputStream {\n        val uri = getDocumentUri(path)\n        return Resolver.openOutputStream(uri, mode)\n    }\n\n    @Throws(ResolverException::class)\n    fun openParcelFileDescriptor(\n        path: Path,\n        mode: String\n    ): ParcelFileDescriptor {\n        val uri = getDocumentUri(path)\n        return Resolver.openParcelFileDescriptor(uri, mode)\n    }\n\n    @Throws(ResolverException::class)\n    fun queryChildren(parentPath: Path): List<Path> {\n        val parentDocumentId = queryDocumentId(parentPath)\n        val childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(\n            parentPath.treeUri, parentDocumentId\n        )\n        while (true) {\n            // A null projection means all supported columns should be included according to\n            // [DocumentsProvider.queryChildDocuments]. This is fine for functionality and\n            // performance as DocumentsProviderHelper in DocumentsUI is doing the same thing.\n            query(childrenUri, null, null).use { cursor ->\n                if (cursor.extras.getBoolean(DocumentsContract.EXTRA_LOADING)) {\n                    cursor.waitUntilChanged()\n                    return@use\n                }\n                val childrenPaths = mutableListOf<Path>()\n                while (cursor.moveToNext()) {\n                    val childDocumentId = cursor.requireString(\n                        DocumentsContract.Document.COLUMN_DOCUMENT_ID\n                    )\n                    val childDisplayName = cursor.requireString(\n                        DocumentsContract.Document.COLUMN_DISPLAY_NAME\n                    )\n                    val childPath = parentPath.resolve(childDisplayName)\n                    pathDocumentIdCache[childPath] = childDocumentId\n                    directoryCursorCache[childPath] = cursor.toRowCursor()\n                    childrenPaths += childPath\n                }\n                return childrenPaths\n            }\n        }\n    }\n\n    @Throws(ResolverException::class)\n    fun queryDocument(path: Path, uri: Uri): Cursor {\n        directoryCursorCache.remove(path)?.let { return it }\n        // A null projection means all supported columns should be included according to\n        // [DocumentsProvider.queryDocument]. This is fine for functionality and performance as\n        // DocumentsProviderHelper in DocumentsUI is doing the same thing.\n        return query(uri, null, null)\n    }\n\n    @Throws(ResolverException::class)\n    fun remove(path: Path) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && !isRemoveUnsupported(path)) {\n            removeApi24(path)\n        } else {\n            @Suppress(\"DEPRECATION\")\n            delete(path)\n        }\n    }\n\n    @Throws(ResolverException::class)\n    fun remove(uri: Uri, parentUri: Uri) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && !isRemoveUnsupported(uri)) {\n            removeApi24(uri, parentUri)\n        } else {\n            delete(uri)\n        }\n    }\n\n    private fun isRemoveUnsupported(path: Path): Boolean = isRemoveUnsupported(path.treeUri)\n\n    private fun isRemoveUnsupported(uri: Uri): Boolean =\n        uri.authority in REMOVE_UNSUPPORTED_AUTHORITIES\n\n    @RequiresApi(Build.VERSION_CODES.N)\n    @Throws(ResolverException::class)\n    private fun removeApi24(path: Path) {\n        val uri = getDocumentUri(path)\n        val parentUri = getDocumentUri(path.requireParent())\n        // Always remove the path from cache, in case a removal actually succeeded despite exception\n        // being thrown.\n        pathDocumentIdCache -= path\n        directoryCursorCache -= path\n        removeApi24(uri, parentUri)\n    }\n\n    @RequiresApi(Build.VERSION_CODES.N)\n    @Throws(ResolverException::class)\n    private fun removeApi24(uri: Uri, parentUri: Uri) {\n        val removed = try {\n            DocumentsContract.removeDocument(contentResolver, uri, parentUri)\n        } catch (e: UnsupportedOperationException) {\n            // Ignored.\n            @Suppress(\"DEPRECATION\")\n            delete(uri)\n            return\n        } catch (e: Exception) {\n            throw ResolverException(e)\n        }\n        if (!removed) {\n            throw ResolverException(\"DocumentsContract.removeDocument() $uri returned false\")\n        }\n    }\n\n    @Throws(ResolverException::class)\n    fun rename(path: Path, displayName: String): Uri {\n        val uri = getDocumentUri(path)\n        // Always remove the path from cache, in case a rename actually succeeded despite exception\n        // being thrown.\n        pathDocumentIdCache -= path\n        directoryCursorCache -= path\n        return rename(uri, displayName)\n    }\n\n    @Throws(ResolverException::class)\n    fun rename(uri: Uri, displayName: String): Uri =\n        try {\n            DocumentsContract.renameDocument(contentResolver, uri, displayName)\n        } catch (e: Exception) {\n            throw ResolverException(e)\n        } ?: throw ResolverException(\n            \"DocumentsContract.renameDocument() with $uri and $displayName returned null\"\n        )\n\n    @Throws(ResolverException::class)\n    fun getDocumentUri(path: Path): Uri {\n        val documentId = queryDocumentId(path)\n        return DocumentsContract.buildDocumentUriUsingTree(path.treeUri, documentId)\n    }\n\n    @Throws(ResolverException::class)\n    fun getDocumentChildrenUri(path: Path): Uri {\n        val documentId = queryDocumentId(path)\n        return DocumentsContract.buildChildDocumentsUriUsingTree(path.treeUri, documentId)\n    }\n\n    @Throws(ResolverException::class)\n    private fun queryDocumentId(path: Path): String {\n        pathDocumentIdCache[path]?.let { return it }\n        val parentPath = path.parent\n        val treeUri = path.treeUri\n        val documentId = if (parentPath != null) {\n            queryChildDocumentId(parentPath, path.displayName!!, treeUri)\n        } else {\n            // TODO: kotlinc: Type mismatch: inferred type is String? but String was expected\n            //DocumentsContract.getTreeDocumentId(treeUri)\n            DocumentsContract.getTreeDocumentId(treeUri)!!\n        }\n        pathDocumentIdCache[path] = documentId\n        return documentId\n    }\n\n    @Throws(ResolverException::class)\n    private fun queryChildDocumentId(parentPath: Path, displayName: String, treeUri: Uri): String {\n        val parentDocumentId = queryDocumentId(parentPath)\n        val childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(\n            treeUri, parentDocumentId\n        )\n        query(\n            childrenUri, arrayOf(\n                DocumentsContract.Document.COLUMN_DOCUMENT_ID,\n                DocumentsContract.Document.COLUMN_DISPLAY_NAME\n            ), null\n        ).use { cursor ->\n            while (cursor.moveToNext()) {\n                val childDocumentId = cursor.requireString(\n                    DocumentsContract.Document.COLUMN_DOCUMENT_ID\n                )\n                val childDisplayName = cursor.requireString(\n                    DocumentsContract.Document.COLUMN_DISPLAY_NAME\n                )\n                val childPath = parentPath.resolve(childDisplayName)\n                pathDocumentIdCache[childPath] = childDocumentId\n                if (childDisplayName == displayName) {\n                    return childDocumentId\n                }\n            }\n        }\n        throw ResolverException(\n            FileNotFoundException(\"Cannot find document ID for ${parentPath.resolve(displayName)}\")\n        )\n    }\n\n    @Throws(ResolverException::class)\n    fun query(uri: Uri, projection: Array<out String?>?, sortOrder: String?): Cursor {\n        // DocumentsProvider doesn't support selection and selectionArgs.\n        var cursor = Resolver.query(uri, projection, null, null, sortOrder)\n        cursor = ExternalStorageProviderHacks.transformQueryResult(uri, cursor)\n        return cursor\n    }\n\n    @Throws(ResolverException::class)\n    private fun Path.requireParent(): Path =\n        parent ?: throw ResolverException(\"Path.getParent() with $this returned null\")\n\n    interface Path {\n        val treeUri: Uri\n        val displayName: String?\n        val parent: Path?\n        fun resolve(other: String): Path\n    }\n\n    @Throws(ResolverException::class)\n    private fun Cursor.waitUntilChanged() {\n        try {\n            runBlocking {\n                suspendCancellableCoroutine<Unit> { continuation ->\n                    val observer = object : ContentObserver(null) {\n                        override fun onChange(selfChange: Boolean) {\n                            unregisterContentObserver(this)\n                            continuation.resume(Unit)\n                        }\n                    }\n                    registerContentObserver(observer)\n                    continuation.invokeOnCancellation {\n                        try {\n                            unregisterContentObserver(observer)\n                        // This may be invoked when continuation is resumed but still cancelled\n                        // while waiting to be dispatched.\n                        } catch (ignored: IllegalStateException) {}\n                    }\n                }\n            }\n        } catch (e: InterruptedException) {\n            throw ResolverException(e)\n        }\n    }\n\n    private fun Cursor.toRowCursor(): Cursor {\n        val columnNames = columnNames\n        val rowValues = Array<Any?>(columnNames.size) {\n            when (val type = getType(it)) {\n                Cursor.FIELD_TYPE_NULL -> null\n                Cursor.FIELD_TYPE_INTEGER -> getLong(it)\n                Cursor.FIELD_TYPE_FLOAT -> getDouble(it)\n                Cursor.FIELD_TYPE_STRING -> getString(it)\n                Cursor.FIELD_TYPE_BLOB -> getBlob(it)\n                else -> throw ResolverException(\"Unknown cursor column type $type\")\n            }\n        }\n        return RowCursor(columnNames, rowValues)\n    }\n\n    private class RowCursor(\n        private val columnNames: Array<String>,\n        private val rowValues: Array<Any?>\n    ) : AbstractLocalCursor() {\n        override fun getCount(): Int = 1\n\n        override fun getColumnNames(): Array<String> = columnNames\n\n        override fun getObject(columnIndex: Int): Any? = rowValues[columnIndex]\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/document/resolver/ExternalStorageProviderHacks.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.document.resolver\n\nimport android.database.Cursor\nimport android.database.MergeCursor\nimport android.net.Uri\nimport android.provider.DocumentsContract\nimport me.zhanghai.android.files.compat.DocumentsContractCompat\nimport me.zhanghai.android.files.provider.content.resolver.requireString\n\n// On Android 11, ExternalStorageProvider no longer returns Android/data and Android/obb as children\n// of the Android directory on primary storage. However, the two child directories are actually\n// still accessible.\nobject ExternalStorageProviderHacks {\n    private const val DOCUMENT_ID_PRIMARY = \"primary\"\n    private const val DOCUMENT_ID_PRIMARY_ANDROID = \"primary:Android\"\n    private const val DOCUMENT_ID_PRIMARY_ANDROID_DATA = \"primary:Android/data\"\n    private const val DOCUMENT_ID_PRIMARY_ANDROID_OBB = \"primary:Android/obb\"\n\n    private val TREE_URI_PRIMARY_ANDROID = DocumentsContract.buildTreeDocumentUri(\n        DocumentsContractCompat.EXTERNAL_STORAGE_PROVIDER_AUTHORITY, DOCUMENT_ID_PRIMARY\n    )\n    val DOCUMENT_URI_ANDROID_DATA = DocumentsContract.buildDocumentUriUsingTree(\n        TREE_URI_PRIMARY_ANDROID, DOCUMENT_ID_PRIMARY_ANDROID_DATA\n    )\n    val DOCUMENT_URI_ANDROID_OBB = DocumentsContract.buildDocumentUriUsingTree(\n        TREE_URI_PRIMARY_ANDROID, DOCUMENT_ID_PRIMARY_ANDROID_OBB\n    )\n\n    fun transformQueryResult(uri: Uri, cursor: Cursor): Cursor {\n        if (uri.authority == DocumentsContractCompat.EXTERNAL_STORAGE_PROVIDER_AUTHORITY\n            && DocumentsContractCompat.isChildDocumentsUri(uri)\n            && DocumentsContract.getDocumentId(uri) == DOCUMENT_ID_PRIMARY_ANDROID) {\n            var hasDataRow = false\n            var hasObbRow = false\n            try {\n                while (cursor.moveToNext()) {\n                    when (cursor.requireString(DocumentsContract.Document.COLUMN_DOCUMENT_ID)) {\n                        DOCUMENT_ID_PRIMARY_ANDROID_DATA -> hasDataRow = true\n                        DOCUMENT_ID_PRIMARY_ANDROID_OBB -> hasObbRow = true\n                    }\n                    if (hasDataRow && hasObbRow) {\n                        break\n                    }\n                }\n            } finally {\n                cursor.moveToPosition(-1)\n            }\n            if (hasDataRow && hasObbRow) {\n                return cursor\n            }\n            val cursors = mutableListOf(cursor)\n            if (!hasDataRow) {\n                val androidDataUri = DocumentsContract.buildDocumentUriUsingTree(\n                    uri, DOCUMENT_ID_PRIMARY_ANDROID_DATA\n                )\n                cursors += DocumentResolver.query(androidDataUri, null, null)\n            }\n            if (!hasObbRow) {\n                val androidObbUri = DocumentsContract.buildDocumentUriUsingTree(\n                    uri, DOCUMENT_ID_PRIMARY_ANDROID_OBB\n                )\n                cursors += DocumentResolver.query(androidObbUri, null, null)\n            }\n            return MergeCursor(cursors.toTypedArray())\n        } else {\n            return cursor\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/ftp/FtpCopyMove.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.ftp\n\nimport java8.nio.file.FileAlreadyExistsException\nimport java8.nio.file.NoSuchFileException\nimport java8.nio.file.StandardCopyOption\nimport me.zhanghai.android.files.provider.common.CopyOptions\nimport me.zhanghai.android.files.provider.common.copyTo\nimport me.zhanghai.android.files.provider.ftp.client.Client\nimport java.io.IOException\n\ninternal object FtpCopyMove {\n    @Throws(IOException::class)\n    fun copy(source: FtpPath, target: FtpPath, copyOptions: CopyOptions) {\n        if (copyOptions.atomicMove) {\n            throw UnsupportedOperationException(StandardCopyOption.ATOMIC_MOVE.toString())\n        }\n        val sourceFile = try {\n            Client.listFile(source, copyOptions.noFollowLinks)\n        } catch (e: IOException) {\n            throw e.toFileSystemExceptionForFtp(source.toString())\n        }\n        val targetFile = try {\n            Client.listFileOrNull(target, true)\n        } catch (e: IOException) {\n            throw e.toFileSystemExceptionForFtp(target.toString())\n        }\n        val sourceSize = sourceFile.size\n        if (targetFile != null) {\n            if (source == target) {\n                copyOptions.progressListener?.invoke(sourceSize)\n                return\n            }\n            if (!copyOptions.replaceExisting) {\n                throw FileAlreadyExistsException(source.toString(), target.toString(), null)\n            }\n            try {\n                Client.delete(target, targetFile.isDirectory)\n            } catch (e: IOException) {\n                throw e.toFileSystemExceptionForFtp(target.toString())\n            }\n        }\n        when {\n            sourceFile.isDirectory -> {\n                try {\n                    Client.createDirectory(target)\n                } catch (e: IOException) {\n                    throw e.toFileSystemExceptionForFtp(target.toString())\n                }\n                copyOptions.progressListener?.invoke(sourceSize)\n            }\n            sourceFile.isSymbolicLink ->\n                throw UnsupportedOperationException(\"Cannot copy symbolic links\")\n            else -> {\n                val sourceInputStream = try {\n                    Client.retrieveFile(source)\n                } catch (e: IOException) {\n                    throw e.toFileSystemExceptionForFtp(source.toString())\n                }\n                try {\n                    val targetOutputStream = try {\n                        Client.storeFile(target)\n                    } catch (e: IOException) {\n                        throw e.toFileSystemExceptionForFtp(target.toString())\n                    }\n                    var successful = false\n                    try {\n                        sourceInputStream.copyTo(\n                            targetOutputStream, copyOptions.progressIntervalMillis,\n                            copyOptions.progressListener\n                        )\n                        successful = true\n                    } finally {\n                        try {\n                            targetOutputStream.close()\n                        } catch (e: IOException) {\n                            throw e.toFileSystemExceptionForFtp(target.toString())\n                        } finally {\n                            if (!successful) {\n                                try {\n                                    Client.delete(target, sourceFile.isDirectory)\n                                } catch (e: IOException) {\n                                    e.printStackTrace()\n                                }\n                            }\n                        }\n                    }\n                } finally {\n                    try {\n                        sourceInputStream.close()\n                    } catch (e: IOException) {\n                        throw e.toFileSystemExceptionForFtp(source.toString())\n                    }\n                }\n            }\n        }\n        // We don't take error when copying attribute fatal, so errors will only be logged from now\n        // on.\n        if (!sourceFile.isSymbolicLink) {\n            val timestamp = sourceFile.timestamp\n            if (timestamp != null) {\n                try {\n                    Client.setLastModifiedTime(target, timestamp.toInstant())\n                } catch (e: IOException) {\n                    e.printStackTrace()\n                }\n            }\n        }\n    }\n\n    @Throws(IOException::class)\n    fun move(source: FtpPath, target: FtpPath, copyOptions: CopyOptions) {\n        val sourceFile = try {\n            Client.listFile(source, copyOptions.noFollowLinks)\n        } catch (e: IOException) {\n            throw e.toFileSystemExceptionForFtp(source.toString())\n        }\n        val targetFile = try {\n            Client.listFileOrNull(target, true)\n        } catch (e: IOException) {\n            throw e.toFileSystemExceptionForFtp(target.toString())\n        }\n        val sourceSize = sourceFile.size\n        if (targetFile != null) {\n            if (source == target) {\n                copyOptions.progressListener?.invoke(sourceSize)\n                return\n            }\n            if (!copyOptions.replaceExisting) {\n                throw FileAlreadyExistsException(source.toString(), target.toString(), null)\n            }\n            try {\n                Client.delete(target, targetFile.isDirectory)\n            } catch (e: IOException) {\n                throw e.toFileSystemExceptionForFtp(target.toString())\n            }\n        }\n        var renameSuccessful = false\n        try {\n            Client.renameFile(source, target)\n            renameSuccessful = true\n        } catch (e: IOException) {\n            if (copyOptions.atomicMove) {\n                throw e.toFileSystemExceptionForFtp(source.toString(), target.toString())\n            }\n            // Ignored.\n        }\n        if (renameSuccessful) {\n            copyOptions.progressListener?.invoke(sourceSize)\n            return\n        }\n        if (copyOptions.atomicMove) {\n            throw AssertionError()\n        }\n        var copyOptions = copyOptions\n        if (!copyOptions.copyAttributes || !copyOptions.noFollowLinks) {\n            copyOptions = CopyOptions(\n                copyOptions.replaceExisting, true, false, true, copyOptions.progressIntervalMillis,\n                copyOptions.progressListener\n            )\n        }\n        copy(source, target, copyOptions)\n        try {\n            Client.delete(source, sourceFile.isDirectory)\n        } catch (e: IOException) {\n            if (e.toFileSystemExceptionForFtp(source.toString()) !is NoSuchFileException) {\n                try {\n                    Client.delete(target, sourceFile.isDirectory)\n                } catch (e2: IOException) {\n                    e.addSuppressed(e2.toFileSystemExceptionForFtp(target.toString()))\n                }\n            }\n            throw e.toFileSystemExceptionForFtp(source.toString())\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/ftp/FtpFileAttributeView.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.ftp\n\nimport java8.nio.file.LinkOption\nimport java8.nio.file.attribute.BasicFileAttributeView\nimport java8.nio.file.attribute.FileTime\nimport me.zhanghai.android.files.provider.ftp.client.Client\nimport java.io.IOException\n\ninternal class FtpFileAttributeView(\n    private val path: FtpPath,\n    private val noFollowLinks: Boolean\n) : BasicFileAttributeView {\n    override fun name(): String = NAME\n\n    @Throws(IOException::class)\n    override fun readAttributes(): FtpFileAttributes {\n        val file = try {\n            Client.listFile(path, noFollowLinks)\n        } catch (e: IOException) {\n            throw e.toFileSystemExceptionForFtp(path.toString())\n        }\n        return FtpFileAttributes.from(file, path)\n    }\n\n    override fun setTimes(\n        lastModifiedTime: FileTime?,\n        lastAccessTime: FileTime?,\n        createTime: FileTime?\n    ) {\n        if (lastModifiedTime == null) {\n            // Only throw if caller is trying to set only last access time and/or create time, so\n            // that foreign copy move can still set last modified time.\n            if (lastAccessTime != null) {\n                throw UnsupportedOperationException(\"lastAccessTime\")\n            }\n            if (createTime != null) {\n                throw UnsupportedOperationException(\"createTime\")\n            }\n            return\n        }\n        if (noFollowLinks) {\n            throw UnsupportedOperationException(LinkOption.NOFOLLOW_LINKS.toString())\n        }\n        try {\n            Client.setLastModifiedTime(path, lastModifiedTime.toInstant())\n        } catch (e: IOException) {\n            throw e.toFileSystemExceptionForFtp(path.toString())\n        }\n    }\n\n    companion object {\n        private val NAME = FtpFileSystemProvider.scheme\n\n        val SUPPORTED_NAMES = setOf(\"basic\", NAME)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/ftp/FtpFileAttributes.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.ftp\n\nimport android.os.Parcelable\nimport java.time.Instant\nimport java8.nio.file.attribute.FileTime\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.provider.common.AbstractBasicFileAttributes\nimport me.zhanghai.android.files.provider.common.BasicFileType\nimport me.zhanghai.android.files.provider.common.FileTimeParceler\nimport org.apache.commons.net.ftp.FTPFile\n\n@Parcelize\ninternal data class FtpFileAttributes(\n    override val lastModifiedTime: @WriteWith<FileTimeParceler> FileTime,\n    override val lastAccessTime: @WriteWith<FileTimeParceler> FileTime,\n    override val creationTime: @WriteWith<FileTimeParceler> FileTime,\n    override val type: BasicFileType,\n    override val size: Long,\n    override val fileKey: Parcelable,\n) : AbstractBasicFileAttributes() {\n    companion object {\n        fun from(file: FTPFile, path: FtpPath): FtpFileAttributes {\n            val lastModifiedTime = FileTime.from(file.timestamp?.toInstant() ?: Instant.EPOCH)\n            val lastAccessTime = lastModifiedTime\n            val creationTime = lastModifiedTime\n            val type = when {\n                file.isDirectory -> BasicFileType.DIRECTORY\n                file.isFile -> BasicFileType.REGULAR_FILE\n                file.isSymbolicLink -> BasicFileType.SYMBOLIC_LINK\n                else -> BasicFileType.OTHER\n            }\n            val size = file.size.let { if (it != -1L) it else 0 }\n            val fileKey = path\n            return FtpFileAttributes(\n                lastModifiedTime, lastAccessTime, creationTime, type, size, fileKey\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/ftp/FtpFileSystem.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.ftp\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport java8.nio.file.FileStore\nimport java8.nio.file.FileSystem\nimport java8.nio.file.Path\nimport java8.nio.file.PathMatcher\nimport java8.nio.file.WatchService\nimport java8.nio.file.attribute.UserPrincipalLookupService\nimport java8.nio.file.spi.FileSystemProvider\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.ByteStringBuilder\nimport me.zhanghai.android.files.provider.common.ByteStringListPathCreator\nimport me.zhanghai.android.files.provider.common.LocalWatchService\nimport me.zhanghai.android.files.provider.common.toByteString\nimport me.zhanghai.android.files.provider.ftp.client.Authority\nimport me.zhanghai.android.files.util.readParcelable\nimport java.io.IOException\n\ninternal class FtpFileSystem(\n    private val provider: FtpFileSystemProvider,\n    val authority: Authority\n) : FileSystem(), ByteStringListPathCreator, Parcelable {\n    val rootDirectory = FtpPath(this, SEPARATOR_BYTE_STRING)\n\n    init {\n        if (!rootDirectory.isAbsolute) {\n            throw AssertionError(\"Root directory must be absolute\")\n        }\n        if (rootDirectory.nameCount != 0) {\n            throw AssertionError(\"Root directory must contain no names\")\n        }\n    }\n\n    private val lock = Any()\n\n    private var isOpen = true\n\n    val defaultDirectory: FtpPath\n        get() = rootDirectory\n\n    override fun provider(): FileSystemProvider = provider\n\n    override fun close() {\n        synchronized(lock) {\n            if (!isOpen) {\n                return\n            }\n            provider.removeFileSystem(this)\n            isOpen = false\n        }\n    }\n\n    override fun isOpen(): Boolean = synchronized(lock) { isOpen }\n\n    override fun isReadOnly(): Boolean = false\n\n    override fun getSeparator(): String = SEPARATOR_STRING\n\n    override fun getRootDirectories(): Iterable<Path> = listOf(rootDirectory)\n\n    override fun getFileStores(): Iterable<FileStore> {\n        // TODO\n        throw UnsupportedOperationException()\n    }\n\n    override fun supportedFileAttributeViews(): Set<String> =\n        FtpFileAttributeView.SUPPORTED_NAMES\n\n    override fun getPath(first: String, vararg more: String): FtpPath {\n        val path = ByteStringBuilder(first.toByteString())\n            .apply { more.forEach { append(SEPARATOR).append(it.toByteString()) } }\n            .toByteString()\n        return FtpPath(this, path)\n    }\n\n    override fun getPath(first: ByteString, vararg more: ByteString): FtpPath {\n        val path = ByteStringBuilder(first)\n            .apply { more.forEach { append(SEPARATOR).append(it) } }\n            .toByteString()\n        return FtpPath(this, path)\n    }\n\n    override fun getPathMatcher(syntaxAndPattern: String): PathMatcher {\n        throw UnsupportedOperationException()\n    }\n\n    override fun getUserPrincipalLookupService(): UserPrincipalLookupService {\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun newWatchService(): WatchService = LocalWatchService()\n\n    override fun equals(other: Any?): Boolean {\n        if (this === other) {\n            return true\n        }\n        if (javaClass != other?.javaClass) {\n            return false\n        }\n        other as FtpFileSystem\n        return authority == other.authority\n    }\n\n    override fun hashCode(): Int = authority.hashCode()\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        dest.writeParcelable(authority, flags)\n    }\n\n    companion object {\n        const val SEPARATOR = '/'.code.toByte()\n        private val SEPARATOR_BYTE_STRING = SEPARATOR.toByteString()\n        private const val SEPARATOR_STRING = SEPARATOR.toInt().toChar().toString()\n\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<FtpFileSystem> {\n            override fun createFromParcel(source: Parcel): FtpFileSystem {\n                val authority = source.readParcelable<Authority>()!!\n                return FtpFileSystemProvider.getOrNewFileSystem(authority)\n            }\n\n            override fun newArray(size: Int): Array<FtpFileSystem?> = arrayOfNulls(size)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/ftp/FtpFileSystemProvider.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.ftp\n\nimport android.net.Uri\nimport java8.nio.channels.FileChannel\nimport java8.nio.channels.SeekableByteChannel\nimport java8.nio.file.AccessMode\nimport java8.nio.file.CopyOption\nimport java8.nio.file.DirectoryStream\nimport java8.nio.file.FileAlreadyExistsException\nimport java8.nio.file.FileStore\nimport java8.nio.file.FileSystem\nimport java8.nio.file.FileSystemAlreadyExistsException\nimport java8.nio.file.FileSystemException\nimport java8.nio.file.FileSystemNotFoundException\nimport java8.nio.file.LinkOption\nimport java8.nio.file.NoSuchFileException\nimport java8.nio.file.NotLinkException\nimport java8.nio.file.OpenOption\nimport java8.nio.file.Path\nimport java8.nio.file.ProviderMismatchException\nimport java8.nio.file.StandardOpenOption\nimport java8.nio.file.attribute.BasicFileAttributes\nimport java8.nio.file.attribute.FileAttribute\nimport java8.nio.file.attribute.FileAttributeView\nimport java8.nio.file.spi.FileSystemProvider\nimport me.zhanghai.android.files.provider.common.ByteStringPath\nimport me.zhanghai.android.files.provider.common.DelegateSchemeFileSystemProvider\nimport me.zhanghai.android.files.provider.common.PathListDirectoryStream\nimport me.zhanghai.android.files.provider.common.PathObservable\nimport me.zhanghai.android.files.provider.common.PathObservableProvider\nimport me.zhanghai.android.files.provider.common.Searchable\nimport me.zhanghai.android.files.provider.common.WalkFileTreeSearchable\nimport me.zhanghai.android.files.provider.common.WatchServicePathObservable\nimport me.zhanghai.android.files.provider.common.decodedPathByteString\nimport me.zhanghai.android.files.provider.common.decodedQueryByteString\nimport me.zhanghai.android.files.provider.common.toAccessModes\nimport me.zhanghai.android.files.provider.common.toByteString\nimport me.zhanghai.android.files.provider.common.toCopyOptions\nimport me.zhanghai.android.files.provider.common.toLinkOptions\nimport me.zhanghai.android.files.provider.common.toOpenOptions\nimport me.zhanghai.android.files.provider.ftp.client.Authority\nimport me.zhanghai.android.files.provider.ftp.client.Client\nimport me.zhanghai.android.files.provider.ftp.client.Mode\nimport me.zhanghai.android.files.provider.ftp.client.Protocol\nimport java.io.IOException\nimport java.io.InputStream\nimport java.io.OutputStream\nimport java.net.URI\n\nobject FtpFileSystemProvider : FileSystemProvider(), PathObservableProvider, Searchable {\n    private val HIDDEN_FILE_NAME_PREFIX = \".\".toByteString()\n\n    private val fileSystems = mutableMapOf<Authority, FtpFileSystem>()\n\n    private val lock = Any()\n\n    override fun getScheme(): String = Protocol.FTP.scheme\n\n    override fun newFileSystem(uri: URI, env: Map<String, *>): FileSystem {\n        uri.requireSameScheme()\n        val authority = uri.ftpAuthority\n        synchronized(lock) {\n            if (fileSystems[authority] != null) {\n                throw FileSystemAlreadyExistsException(authority.toString())\n            }\n            return newFileSystemLocked(authority)\n        }\n    }\n\n    internal fun getOrNewFileSystem(authority: Authority): FtpFileSystem =\n        synchronized(lock) { fileSystems[authority] ?: newFileSystemLocked(authority) }\n\n    private fun newFileSystemLocked(authority: Authority): FtpFileSystem {\n        val fileSystem = FtpFileSystem(this, authority)\n        fileSystems[authority] = fileSystem\n        return fileSystem\n    }\n\n    override fun getFileSystem(uri: URI): FileSystem {\n        uri.requireSameScheme()\n        val authority = uri.ftpAuthority\n        return synchronized(lock) { fileSystems[authority] }\n            ?: throw FileSystemNotFoundException(authority.toString())\n    }\n\n    internal fun removeFileSystem(fileSystem: FtpFileSystem) {\n        val authority = fileSystem.authority\n        synchronized(lock) { fileSystems.remove(authority) }\n    }\n\n    override fun getPath(uri: URI): Path {\n        uri.requireSameScheme()\n        val authority = uri.ftpAuthority\n        val path = uri.decodedPathByteString\n            ?: throw IllegalArgumentException(\"URI must have a path\")\n        return getOrNewFileSystem(authority).getPath(path)\n    }\n\n    private fun URI.requireSameScheme() {\n        val scheme = scheme\n        require(scheme in Protocol.SCHEMES) { \"URI scheme $scheme must be in ${Protocol.SCHEMES}\" }\n    }\n\n    private val URI.ftpAuthority: Authority\n        get() {\n            val protocol = Protocol.fromScheme(scheme)\n            val port = if (port != -1) port else protocol.defaultPort\n            val username = userInfo.orEmpty()\n            val queryUri = decodedQueryByteString?.toString()?.let { Uri.parse(it) }\n            val mode = queryUri?.getQueryParameter(FtpPath.QUERY_PARAMETER_MODE)\n                ?.let { mode -> Mode.entries.first { it.name.equals(mode, true) } }\n                ?: Authority.DEFAULT_MODE\n            val encoding = queryUri?.getQueryParameter(FtpPath.QUERY_PARAMETER_ENCODING)\n                ?: Authority.DEFAULT_ENCODING\n            return Authority(protocol, host, port, username, mode, encoding)\n        }\n\n    @Throws(IOException::class)\n    override fun newInputStream(file: Path, vararg options: OpenOption): InputStream {\n        file as? FtpPath ?: throw ProviderMismatchException(file.toString())\n        val openOptions = options.toOpenOptions()\n        openOptions.checkForFtp()\n        if (openOptions.write) {\n            throw UnsupportedOperationException(StandardOpenOption.WRITE.toString())\n        }\n        if (openOptions.append) {\n            throw UnsupportedOperationException(StandardOpenOption.APPEND.toString())\n        }\n        if (openOptions.truncateExisting) {\n            throw UnsupportedOperationException(StandardOpenOption.TRUNCATE_EXISTING.toString())\n        }\n        if (openOptions.create || openOptions.createNew || openOptions.noFollowLinks) {\n            val fileFile = try {\n                Client.listFileOrNull(file, true)\n            } catch (e: IOException) {\n                throw e.toFileSystemExceptionForFtp(file.toString())\n            }\n            if (openOptions.createNew && fileFile != null) {\n                throw FileAlreadyExistsException(file.toString())\n            }\n            if (openOptions.noFollowLinks && fileFile != null && fileFile.isSymbolicLink) {\n                throw FileSystemException(\n                    file.toString(), null, \"File is a symbolic link: $fileFile\"\n                )\n            }\n            if ((openOptions.create || openOptions.createNew) && fileFile == null) {\n                try {\n                    Client.createFile(file)\n                } catch (e: IOException) {\n                    throw e.toFileSystemExceptionForFtp(file.toString())\n                }\n            }\n        }\n        try {\n            return Client.retrieveFile(file)\n        } catch (e: IOException) {\n            throw e.toFileSystemExceptionForFtp(file.toString())\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun newOutputStream(file: Path, vararg options: OpenOption): OutputStream {\n        file as? FtpPath ?: throw ProviderMismatchException(file.toString())\n        val optionsSet = mutableSetOf(*options)\n        if (optionsSet.isEmpty()) {\n            optionsSet += StandardOpenOption.CREATE\n            optionsSet += StandardOpenOption.TRUNCATE_EXISTING\n        }\n        optionsSet += StandardOpenOption.WRITE\n        val openOptions = optionsSet.toOpenOptions()\n        openOptions.checkForFtp()\n        if (!openOptions.truncateExisting && !openOptions.createNew) {\n            throw UnsupportedOperationException(\"Missing ${StandardOpenOption.TRUNCATE_EXISTING}\")\n        }\n        val fileFile = try {\n            Client.listFileOrNull(file, true)\n        } catch (e: IOException) {\n            throw e.toFileSystemExceptionForFtp(file.toString())\n        }\n        if (openOptions.createNew && fileFile != null) {\n            throw FileAlreadyExistsException(file.toString())\n        }\n        if (!(openOptions.create || openOptions.createNew) && fileFile == null) {\n            throw NoSuchFileException(file.toString())\n        }\n        try {\n            return Client.storeFile(file)\n        } catch (e: IOException) {\n            throw e.toFileSystemExceptionForFtp(file.toString())\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun newFileChannel(\n        file: Path,\n        options: Set<OpenOption>,\n        vararg attributes: FileAttribute<*>\n    ): FileChannel {\n        file as? FtpPath ?: throw ProviderMismatchException(file.toString())\n        options.toOpenOptions().checkForFtp()\n        if (attributes.isNotEmpty()) {\n            throw UnsupportedOperationException(attributes.contentToString())\n        }\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun newByteChannel(\n        file: Path,\n        options: Set<OpenOption>,\n        vararg attributes: FileAttribute<*>\n    ): SeekableByteChannel {\n        file as? FtpPath ?: throw ProviderMismatchException(file.toString())\n        val openOptions = options.toOpenOptions()\n        openOptions.checkForFtp()\n        if (openOptions.write && !openOptions.truncateExisting) {\n            throw UnsupportedOperationException(\"Missing ${StandardOpenOption.TRUNCATE_EXISTING}\")\n        }\n        if (openOptions.write || openOptions.create || openOptions.createNew ||\n            openOptions.noFollowLinks) {\n            val fileFile = try {\n                Client.listFileOrNull(file, true)\n            } catch (e: IOException) {\n                throw e.toFileSystemExceptionForFtp(file.toString())\n            }\n            if (openOptions.createNew && fileFile != null) {\n                throw FileAlreadyExistsException(file.toString())\n            }\n            if (openOptions.noFollowLinks && fileFile != null && fileFile.isSymbolicLink) {\n                throw FileSystemException(\n                    file.toString(), null, \"File is a symbolic link: $fileFile\"\n                )\n            }\n            if (fileFile == null) {\n                if (!(openOptions.create || openOptions.createNew)) {\n                    throw NoSuchFileException(file.toString())\n                }\n                try {\n                    Client.createFile(file)\n                } catch (e: IOException) {\n                    throw e.toFileSystemExceptionForFtp(file.toString())\n                }\n            }\n        }\n        if (attributes.isNotEmpty()) {\n            throw UnsupportedOperationException(attributes.contentToString())\n        }\n        try {\n            return Client.openByteChannel(file, openOptions.append)\n        } catch (e: IOException) {\n            throw e.toFileSystemExceptionForFtp(file.toString())\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun newDirectoryStream(\n        directory: Path,\n        filter: DirectoryStream.Filter<in Path>\n    ): DirectoryStream<Path> {\n        directory as? FtpPath ?: throw ProviderMismatchException(directory.toString())\n        val paths = try {\n            @Suppress(\"UNCHECKED_CAST\")\n            Client.listDirectory(directory) as List<Path>\n        } catch (e: IOException) {\n            throw e.toFileSystemExceptionForFtp(directory.toString())\n        }\n        return PathListDirectoryStream(paths, filter)\n    }\n\n    @Throws(IOException::class)\n    override fun createDirectory(directory: Path, vararg attributes: FileAttribute<*>) {\n        directory as? FtpPath ?: throw ProviderMismatchException(directory.toString())\n        if (attributes.isNotEmpty()) {\n            throw UnsupportedOperationException(attributes.contentToString())\n        }\n        try {\n            Client.createDirectory(directory)\n        } catch (e: IOException) {\n            throw e.toFileSystemExceptionForFtp(directory.toString())\n        }\n    }\n\n    override fun createSymbolicLink(link: Path, target: Path, vararg attributes: FileAttribute<*>) {\n        link as? FtpPath ?: throw ProviderMismatchException(link.toString())\n        when (target) {\n            is FtpPath, is ByteStringPath -> {}\n            else -> throw ProviderMismatchException(target.toString())\n        }\n        if (attributes.isNotEmpty()) {\n            throw UnsupportedOperationException(attributes.contentToString())\n        }\n        throw UnsupportedOperationException()\n    }\n\n    override fun createLink(link: Path, existing: Path) {\n        link as? FtpPath ?: throw ProviderMismatchException(link.toString())\n        existing as? FtpPath ?: throw ProviderMismatchException(existing.toString())\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun delete(path: Path) {\n        path as? FtpPath ?: throw ProviderMismatchException(path.toString())\n        try {\n            Client.delete(path)\n        } catch (e: IOException) {\n            throw e.toFileSystemExceptionForFtp(path.toString())\n        }\n    }\n\n    override fun readSymbolicLink(link: Path): Path {\n        link as? FtpPath ?: throw ProviderMismatchException(link.toString())\n        val linkFile = try {\n            Client.listFile(link, true)\n        } catch (e: IOException) {\n            throw e.toFileSystemExceptionForFtp(link.toString())\n        }\n        if (!linkFile.isSymbolicLink) {\n            throw NotLinkException(link.toString(), null, linkFile.toString())\n        }\n        val target = linkFile.link ?: throw FileSystemException(\n            link.toString(), null, \"FTPFile.getLink() returned null: $linkFile\"\n        )\n        return ByteStringPath(target.toByteString())\n    }\n\n    @Throws(IOException::class)\n    override fun copy(source: Path, target: Path, vararg options: CopyOption) {\n        source as? FtpPath ?: throw ProviderMismatchException(source.toString())\n        target as? FtpPath ?: throw ProviderMismatchException(target.toString())\n        val copyOptions = options.toCopyOptions()\n        FtpCopyMove.copy(source, target, copyOptions)\n    }\n\n    @Throws(IOException::class)\n    override fun move(source: Path, target: Path, vararg options: CopyOption) {\n        source as? FtpPath ?: throw ProviderMismatchException(source.toString())\n        target as? FtpPath ?: throw ProviderMismatchException(target.toString())\n        val copyOptions = options.toCopyOptions()\n        FtpCopyMove.move(source, target, copyOptions)\n    }\n\n    override fun isSameFile(path: Path, path2: Path): Boolean {\n        path as? FtpPath ?: throw ProviderMismatchException(path.toString())\n        return path == path2\n    }\n\n    override fun isHidden(path: Path): Boolean {\n        path as? FtpPath ?: throw ProviderMismatchException(path.toString())\n        val fileName = path.fileNameByteString ?: return false\n        return fileName.startsWith(HIDDEN_FILE_NAME_PREFIX)\n    }\n\n    override fun getFileStore(path: Path): FileStore {\n        path as? FtpPath ?: throw ProviderMismatchException(path.toString())\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun checkAccess(path: Path, vararg modes: AccessMode) {\n        path as? FtpPath ?: throw ProviderMismatchException(path.toString())\n        val accessModes = modes.toAccessModes()\n        if (accessModes.write) {\n            throw UnsupportedOperationException(AccessMode.WRITE.toString())\n        }\n        if (accessModes.execute) {\n            throw UnsupportedOperationException(AccessMode.EXECUTE.toString())\n        }\n        // Assume the file can be read if it can be listed.\n        try {\n            Client.listFile(path, false)\n        } catch (e: IOException) {\n            throw e.toFileSystemExceptionForFtp(path.toString())\n        }\n    }\n\n    override fun <V : FileAttributeView> getFileAttributeView(\n        path: Path,\n        type: Class<V>,\n        vararg options: LinkOption\n    ): V? {\n        if (!supportsFileAttributeView(type)) {\n            return null\n        }\n        @Suppress(\"UNCHECKED_CAST\")\n        return getFileAttributeView(path, *options) as V\n    }\n\n    internal fun supportsFileAttributeView(type: Class<out FileAttributeView>): Boolean =\n        type.isAssignableFrom(FtpFileAttributeView::class.java)\n\n    @Throws(IOException::class)\n    override fun <A : BasicFileAttributes> readAttributes(\n        path: Path,\n        type: Class<A>,\n        vararg options: LinkOption\n    ): A {\n        if (!type.isAssignableFrom(BasicFileAttributes::class.java)) {\n            throw UnsupportedOperationException(type.toString())\n        }\n        @Suppress(\"UNCHECKED_CAST\")\n        return getFileAttributeView(path, *options).readAttributes() as A\n    }\n\n    private fun getFileAttributeView(path: Path, vararg options: LinkOption): FtpFileAttributeView {\n        path as? FtpPath ?: throw ProviderMismatchException(path.toString())\n        val linkOptions = options.toLinkOptions()\n        return FtpFileAttributeView(path, linkOptions.noFollowLinks)\n    }\n\n    override fun readAttributes(\n        path: Path,\n        attributes: String,\n        vararg options: LinkOption\n    ): Map<String, Any> {\n        path as? FtpPath ?: throw ProviderMismatchException(path.toString())\n        throw UnsupportedOperationException()\n    }\n\n    override fun setAttribute(\n        path: Path,\n        attribute: String,\n        value: Any,\n        vararg options: LinkOption\n    ) {\n        path as? FtpPath ?: throw ProviderMismatchException(path.toString())\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun observe(path: Path, intervalMillis: Long): PathObservable {\n        path as? FtpPath ?: throw ProviderMismatchException(path.toString())\n        return WatchServicePathObservable(path, intervalMillis)\n    }\n\n    @Throws(IOException::class)\n    override fun search(\n        directory: Path,\n        query: String,\n        intervalMillis: Long,\n        listener: (List<Path>) -> Unit\n    ) {\n        directory as? FtpPath ?: throw ProviderMismatchException(directory.toString())\n        WalkFileTreeSearchable.search(directory, query, intervalMillis, listener)\n    }\n}\n\nval FtpsFileSystemProvider =\n    DelegateSchemeFileSystemProvider(Protocol.FTPS.scheme, FtpFileSystemProvider)\n\nval FtpesFileSystemProvider =\n    DelegateSchemeFileSystemProvider(Protocol.FTPES.scheme, FtpFileSystemProvider)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/ftp/FtpPath.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.ftp\n\nimport android.net.Uri\nimport android.os.Parcel\nimport android.os.Parcelable\nimport java8.nio.file.FileSystem\nimport java8.nio.file.LinkOption\nimport java8.nio.file.Path\nimport java8.nio.file.ProviderMismatchException\nimport java8.nio.file.WatchEvent\nimport java8.nio.file.WatchKey\nimport java8.nio.file.WatchService\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.ByteStringListPath\nimport me.zhanghai.android.files.provider.common.LocalWatchService\nimport me.zhanghai.android.files.provider.common.UriAuthority\nimport me.zhanghai.android.files.provider.common.toByteString\nimport me.zhanghai.android.files.provider.ftp.client.Authority\nimport me.zhanghai.android.files.provider.ftp.client.Client\nimport me.zhanghai.android.files.util.readParcelable\nimport java.io.File\nimport java.io.IOException\n\ninternal class FtpPath : ByteStringListPath<FtpPath>, Client.Path {\n    private val fileSystem: FtpFileSystem\n\n    constructor(\n        fileSystem: FtpFileSystem,\n        path: ByteString\n    ) : super(FtpFileSystem.SEPARATOR, path) {\n        this.fileSystem = fileSystem\n    }\n\n    private constructor(\n        fileSystem: FtpFileSystem,\n        absolute: Boolean,\n        segments: List<ByteString>\n    ) : super(FtpFileSystem.SEPARATOR, absolute, segments) {\n        this.fileSystem = fileSystem\n    }\n\n    override fun isPathAbsolute(path: ByteString): Boolean =\n        path.isNotEmpty() && path[0] == FtpFileSystem.SEPARATOR\n\n    override fun createPath(path: ByteString): FtpPath = FtpPath(fileSystem, path)\n\n    override fun createPath(absolute: Boolean, segments: List<ByteString>): FtpPath =\n        FtpPath(fileSystem, absolute, segments)\n\n    override val uriScheme: String\n        get() = fileSystem.authority.protocol.scheme\n\n    override val uriAuthority: UriAuthority\n        get() = fileSystem.authority.toUriAuthority()\n\n    override val uriQuery: ByteString?\n        get() =\n            Uri.Builder().apply {\n                val authority = fileSystem.authority\n                if (authority.mode != Authority.DEFAULT_MODE) {\n                    appendQueryParameter(QUERY_PARAMETER_MODE, authority.mode.name.lowercase())\n                }\n                if (authority.encoding != Authority.DEFAULT_ENCODING) {\n                    appendQueryParameter(QUERY_PARAMETER_ENCODING, authority.encoding)\n                }\n            }.build().query?.toByteString()\n\n    override val defaultDirectory: FtpPath\n        get() = fileSystem.defaultDirectory\n\n    override fun getFileSystem(): FileSystem = fileSystem\n\n    override fun getRoot(): FtpPath? = if (isAbsolute) fileSystem.rootDirectory else null\n\n    @Throws(IOException::class)\n    override fun toRealPath(vararg options: LinkOption): FtpPath {\n        throw UnsupportedOperationException()\n    }\n\n    override fun toFile(): File {\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun register(\n        watcher: WatchService,\n        events: Array<WatchEvent.Kind<*>>,\n        vararg modifiers: WatchEvent.Modifier\n    ): WatchKey {\n        if (watcher !is LocalWatchService) {\n            throw ProviderMismatchException(watcher.toString())\n        }\n        return watcher.register(this, events, *modifiers)\n    }\n\n    override val authority: Authority\n        get() = fileSystem.authority\n\n    override val remotePath: String\n        get() = toString()\n\n    private constructor(source: Parcel) : super(source) {\n        fileSystem = source.readParcelable()!!\n    }\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        super.writeToParcel(dest, flags)\n\n        dest.writeParcelable(fileSystem, flags)\n    }\n\n    companion object {\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<FtpPath> {\n            override fun createFromParcel(source: Parcel): FtpPath = FtpPath(source)\n\n            override fun newArray(size: Int): Array<FtpPath?> = arrayOfNulls(size)\n        }\n\n        const val QUERY_PARAMETER_MODE = \"mode\"\n        const val QUERY_PARAMETER_ENCODING = \"encoding\"\n    }\n}\n\nval Path.isFtpPath: Boolean\n    get() = this is FtpPath\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/ftp/IOExceptionFtpExtensions.kt",
    "content": "package me.zhanghai.android.files.provider.ftp\n\nimport java8.nio.file.FileSystemException\nimport me.zhanghai.android.files.provider.ftp.client.NegativeReplyCodeException\nimport java.io.IOException\n\nfun IOException.toFileSystemExceptionForFtp(\n    file: String?,\n    other: String? = null\n): FileSystemException =\n    when (this) {\n        is NegativeReplyCodeException -> toFileSystemException(file, other)\n        else ->\n            FileSystemException(file, other, message)\n                .apply { initCause(this@toFileSystemExceptionForFtp) }\n    }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/ftp/OpenOptionsFtpExtensions.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.ftp\n\nimport java8.nio.file.StandardOpenOption\nimport me.zhanghai.android.files.provider.common.OpenOptions\n\ninternal fun OpenOptions.checkForFtp() {\n    if (deleteOnClose) {\n        throw UnsupportedOperationException(StandardOpenOption.DELETE_ON_CLOSE.toString())\n    }\n    if (sync) {\n        throw UnsupportedOperationException(StandardOpenOption.SYNC.toString())\n    }\n    if (dsync) {\n        throw UnsupportedOperationException(StandardOpenOption.DSYNC.toString())\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/ftp/PathFtpExtensions.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.ftp\n\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.provider.ftp.client.Authority\n\nfun Authority.createFtpRootPath(): Path =\n    FtpFileSystemProvider.getOrNewFileSystem(this).rootDirectory\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/ftp/client/Authenticator.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.ftp.client\n\ninterface Authenticator {\n    fun getPassword(authority: Authority): String?\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/ftp/client/Authority.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.ftp.client\n\nimport android.os.Parcelable\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.provider.common.UriAuthority\nimport me.zhanghai.android.files.util.takeIfNotEmpty\nimport java.nio.charset.StandardCharsets\n\n@Parcelize\ndata class Authority(\n    val protocol: Protocol,\n    val host: String,\n    val port: Int,\n    val username: String,\n    val mode: Mode,\n    val encoding: String\n) : Parcelable {\n    fun toUriAuthority(): UriAuthority {\n        val userInfo = username.takeIfNotEmpty()\n        val uriPort = port.takeIf { it != protocol.defaultPort }\n        return UriAuthority(userInfo, host, uriPort)\n    }\n\n    override fun toString(): String = toUriAuthority().toString()\n\n    companion object {\n        // @see https://www.rfc-editor.org/rfc/rfc1635\n        const val ANONYMOUS_USERNAME = \"anonymous\"\n        const val ANONYMOUS_PASSWORD = \"guest\"\n        val DEFAULT_MODE = Mode.PASSIVE\n        val DEFAULT_ENCODING = StandardCharsets.UTF_8.name()!!\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/ftp/client/Client.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.ftp.client\n\nimport java8.nio.file.Path as Java8Path\nimport java.io.IOException\nimport java.io.InputStream\nimport java.io.OutputStream\nimport java.time.Instant\nimport java.time.ZoneOffset\nimport java.time.chrono.IsoChronology\nimport java.time.format.DateTimeFormatter\nimport java.util.Collections\nimport java.util.Locale\nimport java.util.WeakHashMap\nimport java8.nio.channels.SeekableByteChannel\nimport me.zhanghai.android.files.provider.common.DelegateInputStream\nimport me.zhanghai.android.files.provider.common.DelegateOutputStream\nimport me.zhanghai.android.files.provider.common.LocalWatchService\nimport me.zhanghai.android.files.provider.common.NotifyEntryModifiedOutputStream\nimport me.zhanghai.android.files.provider.common.NotifyEntryModifiedSeekableByteChannel\nimport org.apache.commons.net.ftp.FTPClient\nimport org.apache.commons.net.ftp.FTPClientConfig\nimport org.apache.commons.net.ftp.FTPCmd\nimport org.apache.commons.net.ftp.FTPFile\nimport org.apache.commons.net.ftp.FTPReply\nimport org.apache.commons.net.ftp.FTPSClient\n\nobject Client {\n    private val TIMESTAMP_FORMATTER =\n        DateTimeFormatter.ofPattern(\"yyyyMMddHHmmss\", Locale.ROOT)\n            .withChronology(IsoChronology.INSTANCE)\n            .withZone(ZoneOffset.UTC)\n\n    @Volatile\n    lateinit var authenticator: Authenticator\n\n    private val clientPool = mutableMapOf<Authority, MutableList<FTPClient>>()\n\n    private val directoryFilesCache = Collections.synchronizedMap(WeakHashMap<Path, FTPFile>())\n\n    @Throws(IOException::class)\n    private fun acquireClient(authority: Authority): FTPClient {\n        while (true) {\n            val client = acquireClientUnchecked(authority) ?: break\n            if (!client.isConnected) {\n                client.disconnect()\n                continue\n            }\n            val isAlive = try {\n                client.sendNoOp()\n            } catch (e: IOException) {\n                e.printStackTrace()\n                false\n            }\n            if (!isAlive) {\n                closeClient(client)\n                continue\n            }\n            return client\n        }\n        return createClient(authority)\n    }\n\n    private fun acquireClientUnchecked(authority: Authority): FTPClient? =\n        synchronized(clientPool) {\n            val pooledClients = clientPool[authority] ?: return null\n            pooledClients.removeLastOrNull().also {\n                if (pooledClients.isEmpty()) {\n                    clientPool -= authority\n                }\n            }\n        }\n\n    @Throws(IOException::class)\n    private fun createClient(authority: Authority): FTPClient {\n        val password = authenticator.getPassword(authority)\n            ?: throw IOException(\"No password found for $authority\")\n        return authority.protocol.createClient().apply {\n            configure(FTPClientConfig(\"\"))\n            // This has to be set before connect().\n            controlEncoding = authority.encoding\n            listHiddenFiles = true\n            connect(authority.host, authority.port)\n            try {\n                if (!FTPReply.isPositiveCompletion(replyCode)) {\n                    throwNegativeReplyCodeException()\n                }\n                if (!login(authority.username, password)) {\n                    throwNegativeReplyCodeException()\n                }\n            } catch (t: Throwable) {\n                disconnect()\n                throw t\n            }\n            // This has to be called after connect() despite being entirely local.\n            if (authority.mode == Mode.PASSIVE) {\n                enterLocalPassiveMode()\n            }\n            try {\n                if (this is FTPSClient) {\n                    // @see https://datatracker.ietf.org/doc/html/rfc4217#section-9\n                    execPBSZ(0)\n                    execPROT(\"P\")\n                }\n                if (!setFileType(FTPClient.BINARY_FILE_TYPE)) {\n                    throwNegativeReplyCodeException()\n                }\n            } catch (t: Throwable) {\n                closeClient(this)\n                throw t\n            }\n        }\n    }\n\n    private fun releaseClient(authority: Authority, client: FTPClient) {\n        if (!client.isConnected) {\n            client.disconnect()\n            return\n        }\n        // FIXME: Disconnect clients based on time.\n        if (false) {\n            closeClient(client)\n            return\n        }\n        synchronized(clientPool) {\n            clientPool.getOrPut(authority) { mutableListOf() } += client\n        }\n    }\n\n    private fun closeClient(client: FTPClient) {\n        try {\n            client.logout()\n        } catch (e: IOException) {\n            e.printStackTrace()\n        }\n        client.disconnect()\n    }\n\n    private inline fun <R> useClient(authority: Authority, block: (FTPClient) -> R): R {\n        val client = acquireClient(authority)\n        try {\n            return block(client)\n        } finally {\n            releaseClient(authority, client)\n        }\n    }\n\n    @Throws(IOException::class)\n    fun createDirectory(path: Path) {\n        useClient(path.authority) { client ->\n            if (!client.makeDirectory(path.remotePath)) {\n                client.throwNegativeReplyCodeException()\n            }\n        }\n        LocalWatchService.onEntryCreated(path as Java8Path)\n    }\n\n    @Throws(IOException::class)\n    fun createFile(path: Path) {\n        storeFile(path).close()\n        LocalWatchService.onEntryCreated(path as Java8Path)\n    }\n\n    @Throws(IOException::class)\n    fun delete(path: Path) {\n        val file = listFile(path, true)\n        delete(path, file.isDirectory)\n    }\n\n    @Throws(IOException::class)\n    fun delete(path: Path, isDirectory: Boolean) {\n        if (isDirectory) {\n            deleteDirectory(path)\n        } else {\n            deleteFile(path)\n        }\n    }\n\n    @Throws(IOException::class)\n    fun deleteFile(path: Path) {\n        useClient(path.authority) { client ->\n            if (!client.deleteFile(path.remotePath)) {\n                client.throwNegativeReplyCodeException()\n            }\n        }\n        directoryFilesCache -= path\n        LocalWatchService.onEntryDeleted(path as Java8Path)\n    }\n\n    @Throws(IOException::class)\n    fun deleteDirectory(path: Path) {\n        useClient(path.authority) { client ->\n            if (!client.removeDirectory(path.remotePath)) {\n                client.throwNegativeReplyCodeException()\n            }\n        }\n        directoryFilesCache -= path\n        LocalWatchService.onEntryDeleted(path as Java8Path)\n    }\n\n    @Throws(IOException::class)\n    fun renameFile(source: Path, target: Path) {\n        if (source.authority != target.authority) {\n            throw IOException(\"Paths aren't on the same authority\")\n        }\n        useClient(source.authority) { client ->\n            if (!client.rename(source.remotePath, target.remotePath)) {\n                client.throwNegativeReplyCodeException()\n            }\n        }\n        directoryFilesCache -= source\n        directoryFilesCache -= target\n        LocalWatchService.onEntryDeleted(source as Java8Path)\n        LocalWatchService.onEntryCreated(target as Java8Path)\n    }\n\n    @Throws(IOException::class)\n    fun retrieveFile(path: Path): InputStream {\n        val authority = path.authority\n        val client = acquireClient(authority)\n        val inputStream = try {\n            client.retrieveFileStream(path.remotePath) ?: client.throwNegativeReplyCodeException()\n        } catch (t: Throwable) {\n            releaseClient(authority, client)\n            throw t\n        }\n        return CompletePendingCommandInputStream(inputStream, authority, client)\n    }\n\n    @Throws(IOException::class)\n    fun listDirectory(path: Path): List<Path> {\n        useClient(path.authority) { client ->\n            val files = client.mlistDirCompat(path.remotePath)\n                ?: client.throwNegativeReplyCodeException()\n            return files.mapNotNull { file ->\n                if (file.name == \".\" || file.name == \"..\") {\n                    return@mapNotNull null\n                }\n                path.resolve(file.name).also { directoryFilesCache[it] = file }\n            }\n        }\n    }\n\n    @Throws(IOException::class)\n    fun listFileOrNull(path: Path, noFollowLinks: Boolean): FTPFile? =\n        try {\n            listFile(path, noFollowLinks)\n        } catch (e: NegativeReplyCodeException) {\n            null\n        }\n\n    @Throws(IOException::class)\n    fun listFile(path: Path, noFollowLinks: Boolean): FTPFile {\n        val file = listFileNoFollowLinks(path, noFollowLinks)\n        if (!file.isSymbolicLink || noFollowLinks) {\n            return file\n        }\n        val targetString = file.link ?: throw IOException(\"FTPFile.getLink() returned null: $file\")\n        val target = path.resolve(targetString)\n        return listFileNoFollowLinks(target, false)\n    }\n\n    @Throws(IOException::class)\n    private fun listFileNoFollowLinks(path: Path, preserveCacheForSymbolicLink: Boolean): FTPFile {\n        synchronized(directoryFilesCache) {\n            directoryFilesCache[path]?.let {\n                if (!(it.isSymbolicLink && preserveCacheForSymbolicLink)) {\n                    directoryFilesCache -= path\n                }\n                return it\n            }\n        }\n        useClient(path.authority) { client ->\n            return client.mlistFileCompat(path.remotePath)\n                ?: client.throwNegativeReplyCodeException()\n        }\n    }\n\n    @Throws(IOException::class)\n    fun openByteChannel(path: Path, isAppend: Boolean): SeekableByteChannel {\n        val authority = path.authority\n        val client = acquireClient(authority)\n        if (!client.hasFeature(FTPCmd.REST)) {\n            throw IOException(\"Missing feature ${FTPCmd.REST.command}\")\n        }\n        return NotifyEntryModifiedSeekableByteChannel(\n            FileByteChannel(\n                client, { releaseClient(authority, client) }, path.remotePath, isAppend\n            ), path as Java8Path\n        )\n    }\n\n    @Throws(IOException::class)\n    fun setLastModifiedTime(path: Path, lastModifiedTime: Instant) {\n        val lastModifiedTimeString = TIMESTAMP_FORMATTER.format(lastModifiedTime)\n        useClient(path.authority) { client ->\n            if (!client.setModificationTimeCompat(path.remotePath, lastModifiedTimeString)) {\n                client.throwNegativeReplyCodeException()\n            }\n        }\n        LocalWatchService.onEntryModified(path as Java8Path)\n    }\n\n    @Throws(IOException::class)\n    fun storeFile(path: Path): OutputStream {\n        val authority = path.authority\n        val client = acquireClient(authority)\n        val outputStream = try {\n            client.storeFileStream(path.remotePath) ?: client.throwNegativeReplyCodeException()\n        } catch (t: Throwable) {\n            releaseClient(authority, client)\n            throw t\n        }\n        return NotifyEntryModifiedOutputStream(\n            CompletePendingCommandOutputStream(outputStream, authority, client), path as Java8Path\n        )\n    }\n\n    interface Path {\n        val authority: Authority\n        val remotePath: String\n        fun resolve(other: String): Path\n    }\n\n    private class CompletePendingCommandInputStream(\n        inputStream: InputStream,\n        private val authority: Authority,\n        private val client: FTPClient\n    ) : DelegateInputStream(inputStream) {\n        @Throws(IOException::class)\n        override fun close() {\n            try {\n                super.close()\n                if (!client.completePendingCommand()) {\n                    // We may close the input stream before the file is fully read (may happen when\n                    // decoding images) and it will result in an error reported here, but that's\n                    // totally fine.\n                    client.createNegativeReplyCodeException().printStackTrace()\n                }\n            } finally {\n                releaseClient(authority, client)\n            }\n        }\n    }\n\n    private class CompletePendingCommandOutputStream(\n        outputStream: OutputStream,\n        private val authority: Authority,\n        private val client: FTPClient\n    ) : DelegateOutputStream(outputStream) {\n        @Throws(IOException::class)\n        override fun close() {\n            try {\n                super.close()\n                if (!client.completePendingCommand()) {\n                    client.throwNegativeReplyCodeException()\n                }\n            } finally {\n                releaseClient(authority, client)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/ftp/client/FTPClientCompat.kt",
    "content": "package me.zhanghai.android.files.provider.ftp.client\n\nimport org.apache.commons.net.ftp.FTPClient\nimport org.apache.commons.net.ftp.FTPCmd\nimport org.apache.commons.net.ftp.FTPFile\nimport java.io.File\nimport java.io.IOException\nimport java.util.Calendar\n\nprivate val DUMMY_ROOT_FTP_FILE = FTPFile().apply {\n    rawListing = \"Type=dir;Size=4096;Modify=19700101000000;Perm=cdeflmp; /\"\n    type = FTPFile.DIRECTORY_TYPE\n    size = 4096\n    timestamp = Calendar.getInstance().apply { timeInMillis = 0 }\n    setPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION, true)\n    setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION, true)\n    setPermission(FTPFile.USER_ACCESS, FTPFile.EXECUTE_PERMISSION, true)\n    name = \"/\"\n}\n\n@Throws(IOException::class)\nfun FTPClient.mlistFileCompat(pathname: String): FTPFile? {\n    if (hasFeature(FTPCmd.MLST)) {\n        return mlistFile(pathname)\n    } else {\n        val path = File(pathname)\n        val parent = path.parent ?: return DUMMY_ROOT_FTP_FILE\n        return listFiles(parent)?.firstOrNull { it != null && it.name == path.name }\n    }\n}\n\n@Throws(IOException::class)\nfun FTPClient.mlistDirCompat(pathname: String): Array<FTPFile>? =\n    // Note that there is no distinct FEAT output for MLSD. The presence of the MLST feature\n    // indicates that both MLST and MLSD are supported.\n    // @see https://datatracker.ietf.org/doc/html/rfc3659#section-7.8\n    // FTPClient silently returns an empty array even when server returns an error for unknown\n    // command, so we have to rely on checking the feature.\n    if (hasFeature(FTPCmd.MLST)) mlistDir(pathname) else listFiles(pathname)\n\n@Throws(IOException::class)\nfun FTPClient.setModificationTimeCompat(pathname: String, timeval: String): Boolean =\n    // @see https://www.ietf.org/archive/id/draft-somers-ftp-mfxx-04.txt\n    // This is frequently called during file operations, so in order to avoid wasting network\n    // requests, we check the feature first which is cached locally.\n    if (hasFeature(FTPCmd.MFMT)) {\n        setModificationTime(pathname, timeval)\n    } else {\n        throw IOException(\"Missing feature ${FTPCmd.MFMT.command}\")\n    }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/ftp/client/FileByteChannel.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.ftp.client\n\nimport me.zhanghai.android.files.compat.nullInputStream\nimport me.zhanghai.android.files.provider.common.AbstractFileByteChannel\nimport me.zhanghai.android.files.provider.common.ByteBufferInputStream\nimport me.zhanghai.android.files.provider.common.readFully\nimport org.apache.commons.net.ftp.FTPClient\nimport java.io.IOException\nimport java.io.InputStream\nimport java.nio.ByteBuffer\n\nclass FileByteChannel(\n    private val client: FTPClient,\n    private val releaseClient: (FTPClient) -> Unit,\n    private val path: String,\n    isAppend: Boolean\n) : AbstractFileByteChannel(isAppend, joinCancelledRead = true) {\n    private val clientLock = Any()\n\n    @Throws(IOException::class)\n    override fun onRead(position: Long, size: Int): ByteBuffer {\n        val destination = ByteBuffer.allocate(size)\n        synchronized(clientLock) {\n            client.restartOffset = position\n            val inputStream = client.retrieveFileStream(path)\n                ?: client.throwNegativeReplyCodeException()\n            try {\n                val limit = inputStream.use {\n                    it.readFully(destination.array(), destination.arrayOffset(), size)\n                }\n                destination.limit(limit)\n            } finally {\n                // We will likely close the input stream before the file is fully\n                // read and it will result in a false return value here, but that's\n                // totally fine.\n                client.completePendingCommand()\n            }\n        }\n        return destination\n    }\n\n    @Throws(IOException::class)\n    override fun onWrite(position: Long, source: ByteBuffer) {\n        synchronized(clientLock) {\n            client.restartOffset = position\n            ByteBufferInputStream(source).use {\n                if (!client.storeFile(path, it)) {\n                    client.throwNegativeReplyCodeException()\n                }\n            }\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun onAppend(source: ByteBuffer) {\n        synchronized(clientLock) {\n            ByteBufferInputStream(source).use {\n                if (!client.appendFile(path, it)) {\n                    client.throwNegativeReplyCodeException()\n                }\n            }\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun onTruncate(size: Long) {\n        synchronized(clientLock) {\n            client.restartOffset = size\n            InputStream::class.nullInputStream().use {\n                if (!client.storeFile(path, it)) {\n                    client.throwNegativeReplyCodeException()\n                }\n            }\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun onSize(): Long {\n        val sizeString = synchronized(clientLock) {\n            client.getSize(path) ?: client.throwNegativeReplyCodeException()\n        }\n        return sizeString.toLongOrNull() ?: throw IOException(\"Invalid size $sizeString\")\n    }\n\n    @Throws(IOException::class)\n    override fun onClose() {\n        synchronized(clientLock) { releaseClient(client) }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/ftp/client/Mode.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.ftp.client\n\nenum class Mode {\n    ACTIVE,\n    PASSIVE\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/ftp/client/NegativeReplyCodeException.kt",
    "content": "package me.zhanghai.android.files.provider.ftp.client\n\nimport java8.nio.file.AccessDeniedException\nimport java8.nio.file.FileSystemException\nimport java8.nio.file.NoSuchFileException\nimport me.zhanghai.android.files.provider.common.InvalidFileNameException\nimport org.apache.commons.net.ftp.FTPClient\nimport org.apache.commons.net.ftp.FTPReply\nimport java.io.IOException\n\nclass NegativeReplyCodeException(\n    private val replyCode: Int,\n    replyString: String\n) : IOException(replyString) {\n    fun toFileSystemException(file: String?, other: String? = null): FileSystemException =\n        when (replyCode) {\n            FTPReply.NOT_LOGGED_IN, FTPReply.NEED_ACCOUNT_FOR_STORING_FILES ->\n                AccessDeniedException(file, other, message)\n            FTPReply.FILE_UNAVAILABLE -> NoSuchFileException(file, other, message)\n            FTPReply.FILE_NAME_NOT_ALLOWED -> InvalidFileNameException(file, other, message)\n            else -> FileSystemException(file, other, message)\n        }.apply { initCause(this@NegativeReplyCodeException) }\n}\n\ninternal fun FTPClient.createNegativeReplyCodeException() =\n    NegativeReplyCodeException(replyCode, replyString)\n\ninternal fun FTPClient.throwNegativeReplyCodeException(): Nothing {\n    throw createNegativeReplyCodeException()\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/ftp/client/Protocol.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.ftp.client\n\nimport org.apache.commons.net.ftp.FTPClient\nimport org.apache.commons.net.ftp.FTPSClient\n\nenum class Protocol(val scheme: String, val defaultPort: Int, val createClient: () -> FTPClient) {\n    FTP(\"ftp\", FTPClient.DEFAULT_PORT, ::FTPClient),\n    FTPS(\"ftps\", FTPSClient.DEFAULT_FTPS_PORT, { FTPSClient(true) }),\n    FTPES(\"ftpes\", FTPClient.DEFAULT_PORT, { FTPSClient(false) });\n\n    companion object {\n        val SCHEMES = entries.map { it.scheme }\n\n        fun fromScheme(scheme: String): Protocol =\n            entries.firstOrNull { it.scheme == scheme } ?: throw IllegalArgumentException(scheme)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/linux/LinuxCopyMove.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.linux\n\nimport android.system.OsConstants\nimport java8.nio.file.FileAlreadyExistsException\nimport java8.nio.file.FileSystemException\nimport java8.nio.file.StandardCopyOption\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.CopyOptions\nimport me.zhanghai.android.files.provider.common.toByteString\nimport me.zhanghai.android.files.provider.linux.syscall.Constants\nimport me.zhanghai.android.files.provider.linux.syscall.StructTimespec\nimport me.zhanghai.android.files.provider.linux.syscall.Syscall\nimport me.zhanghai.android.files.provider.linux.syscall.SyscallException\nimport java.io.IOException\nimport java.io.InterruptedIOException\n\ninternal object LinuxCopyMove {\n    private const val SEND_FILE_COUNT = 8 * 1024\n\n    private val XATTR_NAME_PREFIX_USER = \"user.\".toByteString()\n\n    @Throws(IOException::class)\n    fun copy(source: ByteString, target: ByteString, copyOptions: CopyOptions) {\n        if (copyOptions.atomicMove) {\n            throw UnsupportedOperationException(StandardCopyOption.ATOMIC_MOVE.toString())\n        }\n        val sourceStat = try {\n            if (copyOptions.noFollowLinks) Syscall.lstat(source) else Syscall.stat(source)\n        } catch (e: SyscallException) {\n            throw e.toFileSystemException(source.toString())\n        }\n        val targetStat = try {\n            Syscall.lstat(target)\n        } catch (e: SyscallException) {\n            if (e.errno != OsConstants.ENOENT) {\n                throw e.toFileSystemException(target.toString())\n            }\n            // Ignored.\n            null\n        }\n        if (targetStat != null) {\n            if (sourceStat.st_dev == targetStat.st_dev && sourceStat.st_ino == targetStat.st_ino) {\n                copyOptions.progressListener?.invoke(sourceStat.st_size)\n                return\n            }\n            if (!copyOptions.replaceExisting) {\n                throw FileAlreadyExistsException(source.toString(), target.toString(), null)\n            }\n            // Symbolic links may not be supported so we cannot simply remove the target here.\n        }\n        if (OsConstants.S_ISREG(sourceStat.st_mode)) {\n            if (targetStat != null) {\n                try {\n                    Syscall.remove(target)\n                } catch (e: SyscallException) {\n                    if (e.errno != OsConstants.ENOENT) {\n                        throw e.toFileSystemException(target.toString())\n                    }\n                }\n            }\n            val sourceFd = try {\n                Syscall.open(source, OsConstants.O_RDONLY, 0)\n            } catch (e: SyscallException) {\n                throw e.toFileSystemException(source.toString())\n            }\n            try {\n                var targetFlags = (OsConstants.O_WRONLY or OsConstants.O_TRUNC\n                    or OsConstants.O_CREAT)\n                if (!copyOptions.replaceExisting) {\n                    targetFlags = targetFlags or OsConstants.O_EXCL\n                }\n                val targetFd = try {\n                    Syscall.open(target, targetFlags, sourceStat.st_mode)\n                } catch (e: SyscallException) {\n                    e.maybeThrowInvalidFileNameException(target.toString())\n                    throw e.toFileSystemException(target.toString())\n                }\n                var successful = false\n                try {\n                    val progressIntervalMillis = copyOptions.progressIntervalMillis\n                    val progressListener = copyOptions.progressListener\n                    var lastProgressMillis = System.currentTimeMillis()\n                    var copiedSize = 0L\n                    while (true) {\n                        val sentSize = try {\n                            Syscall.sendfile(targetFd, sourceFd, null, SEND_FILE_COUNT.toLong())\n                        } catch (e: SyscallException) {\n                            throw e.toFileSystemException(source.toString(), target.toString())\n                        }\n                        if (sentSize == 0L) {\n                            break\n                        }\n                        copiedSize += sentSize\n                        throwIfInterrupted()\n                        val currentTimeMillis = System.currentTimeMillis()\n                        if (progressListener != null\n                            && currentTimeMillis >= lastProgressMillis + progressIntervalMillis) {\n                            progressListener(copiedSize)\n                            lastProgressMillis = currentTimeMillis\n                            copiedSize = 0\n                        }\n                    }\n                    progressListener?.invoke(copiedSize)\n                    successful = true\n                } finally {\n                    try {\n                        Syscall.close(targetFd)\n                    } catch (e: SyscallException) {\n                        throw e.toFileSystemException(target.toString())\n                    } finally {\n                        if (!successful) {\n                            try {\n                                Syscall.remove(target)\n                            } catch (e: SyscallException) {\n                                e.printStackTrace()\n                            }\n                        }\n                    }\n                }\n            } finally {\n                try {\n                    Syscall.close(sourceFd)\n                } catch (e: SyscallException) {\n                    throw e.toFileSystemException(source.toString())\n                }\n            }\n        } else if (OsConstants.S_ISDIR(sourceStat.st_mode)) {\n            if (targetStat != null) {\n                try {\n                    Syscall.remove(target)\n                } catch (e: SyscallException) {\n                    if (e.errno != OsConstants.ENOENT) {\n                        throw e.toFileSystemException(target.toString())\n                    }\n                }\n            }\n            try {\n                Syscall.mkdir(target, sourceStat.st_mode)\n            } catch (e: SyscallException) {\n                e.maybeThrowInvalidFileNameException(target.toString())\n                throw e.toFileSystemException(target.toString())\n            }\n            copyOptions.progressListener?.invoke(sourceStat.st_size)\n        } else if (OsConstants.S_ISLNK(sourceStat.st_mode)) {\n            val sourceTarget = try {\n                Syscall.readlink(source)\n            } catch (e: SyscallException) {\n                throw e.toFileSystemException(source.toString())\n            }\n            try {\n                Syscall.symlink(sourceTarget, target)\n            } catch (e: SyscallException) {\n                if (e.errno == OsConstants.EEXIST && copyOptions.replaceExisting) {\n                    try {\n                        Syscall.remove(target)\n                    } catch (e2: SyscallException) {\n                        if (e2.errno != OsConstants.ENOENT) {\n                            e2.addSuppressed(e.toFileSystemException(target.toString()))\n                            throw e2.toFileSystemException(target.toString())\n                        }\n                    }\n                    try {\n                        Syscall.symlink(sourceTarget, target)\n                    } catch (e2: SyscallException) {\n                        e2.addSuppressed(e.toFileSystemException(target.toString()))\n                        throw e2.toFileSystemException(target.toString())\n                    }\n                }\n                e.maybeThrowInvalidFileNameException(target.toString())\n                throw e.toFileSystemException(target.toString())\n            }\n            copyOptions.progressListener?.invoke(sourceStat.st_size)\n        } else {\n            throw FileSystemException(source.toString(), null, \"st_mode ${sourceStat.st_mode}\")\n        }\n        // We don't take error when copying attribute fatal, so errors will only be logged from now\n        // on.\n        // Ownership should be copied before permissions so that special permission bits like\n        // setuid work properly.\n        try {\n            if (copyOptions.copyAttributes) {\n                Syscall.lchown(target, sourceStat.st_uid, sourceStat.st_gid)\n            }\n        } catch (e: SyscallException) {\n            e.printStackTrace()\n        }\n        try {\n            if (!OsConstants.S_ISLNK(sourceStat.st_mode)) {\n                Syscall.chmod(target, sourceStat.st_mode)\n            }\n        } catch (e: SyscallException) {\n            e.printStackTrace()\n        }\n        // TODO: Change modified time last?\n        try {\n            val times = arrayOf(\n                if (copyOptions.copyAttributes) {\n                    sourceStat.st_atim\n                } else {\n                    StructTimespec(0, Constants.UTIME_OMIT)\n                }, sourceStat.st_mtim\n            )\n            Syscall.lutimens(target, times)\n        } catch (e: SyscallException) {\n            e.printStackTrace()\n        }\n        try {\n            // TODO: Allow u+rw temporarily if we are to copy xattrs.\n            val xattrNames = Syscall.llistxattr(source)\n            for (xattrName in xattrNames) {\n                if (!(copyOptions.copyAttributes || xattrName.startsWith(XATTR_NAME_PREFIX_USER))) {\n                    continue\n                }\n                val xattrValue = Syscall.lgetxattr(target, xattrName)\n                Syscall.lsetxattr(target, xattrName, xattrValue, 0)\n            }\n        } catch (e: SyscallException) {\n            e.printStackTrace()\n        }\n    }\n\n    @Throws(InterruptedIOException::class)\n    private fun throwIfInterrupted() {\n        if (Thread.interrupted()) {\n            throw InterruptedIOException()\n        }\n    }\n\n    @Throws(IOException::class)\n    fun move(source: ByteString, target: ByteString, copyOptions: CopyOptions) {\n        val sourceStat = try {\n            Syscall.lstat(source)\n        } catch (e: SyscallException) {\n            throw e.toFileSystemException(source.toString())\n        }\n        val targetStat = try {\n            Syscall.lstat(target)\n        } catch (e: SyscallException) {\n            if (e.errno != OsConstants.ENOENT) {\n                throw e.toFileSystemException(target.toString())\n            }\n            // Ignored.\n            null\n        }\n        if (targetStat != null) {\n            if (sourceStat.st_dev == targetStat.st_dev && sourceStat.st_ino == targetStat.st_ino) {\n                copyOptions.progressListener?.invoke(sourceStat.st_size)\n                return\n            }\n            if (!copyOptions.replaceExisting) {\n                throw FileAlreadyExistsException(source.toString(), target.toString(), null)\n            }\n            try {\n                Syscall.remove(target)\n            } catch (e: SyscallException) {\n                throw e.toFileSystemException(target.toString())\n            }\n        }\n        var renameSuccessful = false\n        try {\n            Syscall.rename(source, target)\n            renameSuccessful = true\n        } catch (e: SyscallException) {\n            if (copyOptions.atomicMove) {\n                e.maybeThrowAtomicMoveNotSupportedException(source.toString(), target.toString())\n                e.maybeThrowInvalidFileNameException(target.toString())\n                throw e.toFileSystemException(source.toString(), target.toString())\n            }\n            // Ignored.\n        }\n        if (renameSuccessful) {\n            copyOptions.progressListener?.invoke(sourceStat.st_size)\n            return\n        }\n        if (copyOptions.atomicMove) {\n            throw AssertionError()\n        }\n        var copyOptions = copyOptions\n        if (!copyOptions.copyAttributes || !copyOptions.noFollowLinks) {\n            copyOptions = CopyOptions(\n                copyOptions.replaceExisting, true, false, true, copyOptions.progressIntervalMillis,\n                copyOptions.progressListener\n            )\n        }\n        copy(source, target, copyOptions)\n        try {\n            Syscall.remove(source)\n        } catch (e: SyscallException) {\n            if (e.errno != OsConstants.ENOENT) {\n                try {\n                    Syscall.remove(target)\n                } catch (e2: SyscallException) {\n                    e.addSuppressed(e2.toFileSystemException(target.toString()))\n                }\n            }\n            throw e.toFileSystemException(source.toString())\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/linux/LinuxDirectoryStream.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.linux\n\nimport java8.nio.file.DirectoryIteratorException\nimport java8.nio.file.DirectoryStream\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.provider.common.toByteString\nimport me.zhanghai.android.files.provider.linux.syscall.Syscall\nimport me.zhanghai.android.files.provider.linux.syscall.SyscallException\nimport java.io.IOException\n\ninternal class LinuxDirectoryStream(\n    private val directory: LinuxPath,\n    private val dir: Long,\n    private val filter: DirectoryStream.Filter<in Path>\n) : DirectoryStream<Path> {\n    private var iterator: PathIterator? = null\n\n    private var isClosed = false\n\n    private val lock = Any()\n\n    // TODO: Should return Iterator<Path>\n    override fun iterator(): MutableIterator<Path> {\n        synchronized(lock) {\n            check(!isClosed) { \"This directory stream is closed\" }\n            check(iterator == null) { \"The iterator has already been returned\" }\n            val iterator = PathIterator()\n            this.iterator = iterator\n            return iterator\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun close() {\n        synchronized(lock) {\n            if (isClosed) {\n                return\n            }\n            try {\n                Syscall.closedir(dir)\n            } catch (e: SyscallException) {\n                throw e.toFileSystemException(directory.toString())\n            }\n            isClosed = true\n        }\n    }\n\n    companion object {\n        private val BYTE_STRING_DOT = \".\".toByteString()\n        private val BYTE_STRING_DOT_DOT = \"..\".toByteString()\n    }\n\n    // TODO: Try kotlin.sequences.iterator()?\n    private inner class PathIterator : MutableIterator<Path> {\n        private var nextPath: Path? = null\n\n        private var isEndOfStreamReached = false\n\n        override fun hasNext(): Boolean {\n            synchronized(lock) {\n                if (nextPath != null) {\n                    return true\n                }\n                if (isEndOfStreamReached) {\n                    return false\n                }\n                nextPath = getNextPathLocked()\n                isEndOfStreamReached = nextPath == null\n                return !isEndOfStreamReached\n            }\n        }\n\n        private fun getNextPathLocked(): Path? {\n                while (true) {\n                    if (isClosed) {\n                        return null\n                    }\n                    val dirent = try {\n                        Syscall.readdir(dir)\n                    } catch (e: SyscallException) {\n                        throw DirectoryIteratorException(\n                            e.toFileSystemException(directory.toString())\n                        )\n                    } ?: return null\n                    val name = dirent.d_name\n                    if (name == BYTE_STRING_DOT || name == BYTE_STRING_DOT_DOT) {\n                        continue\n                    }\n                    val path = directory.resolve(dirent.d_name)\n                    val accepted = try {\n                        filter.accept(path)\n                    } catch (e: IOException) {\n                        throw DirectoryIteratorException(e)\n                    }\n                    if (!accepted) {\n                        continue\n                    }\n                    return path\n                }\n            }\n\n        override fun next(): Path {\n            synchronized(lock) {\n                if (!hasNext()) {\n                    throw NoSuchElementException()\n                }\n                val path = nextPath!!\n                nextPath = null\n                return path\n            }\n        }\n\n        override fun remove() {\n            throw UnsupportedOperationException()\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/linux/LinuxFileAttributeView.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.linux\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport me.zhanghai.android.files.compat.readBooleanCompat\nimport me.zhanghai.android.files.compat.writeBooleanCompat\nimport me.zhanghai.android.files.provider.root.RootPosixFileAttributeView\nimport me.zhanghai.android.files.provider.root.RootablePosixFileAttributeView\nimport me.zhanghai.android.files.util.readParcelable\n\ninternal class LinuxFileAttributeView constructor(\n    private val path: LinuxPath,\n    private val noFollowLinks: Boolean\n) : RootablePosixFileAttributeView(\n    path, LocalLinuxFileAttributeView(path.toByteString(), noFollowLinks),\n    { RootPosixFileAttributeView(it) }\n) {\n    private constructor(source: Parcel) : this(\n        source.readParcelable()!!, source.readBooleanCompat()\n    )\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        dest.writeParcelable(path, flags)\n        dest.writeBooleanCompat(noFollowLinks)\n    }\n\n    companion object {\n        val SUPPORTED_NAMES = LocalLinuxFileAttributeView.SUPPORTED_NAMES\n\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<LinuxFileAttributeView> {\n            override fun createFromParcel(source: Parcel): LinuxFileAttributeView =\n                LinuxFileAttributeView(source)\n\n            override fun newArray(size: Int): Array<LinuxFileAttributeView?> = arrayOfNulls(size)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/linux/LinuxFileAttributes.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.linux\n\nimport android.os.Parcelable\nimport java.time.Instant\nimport java8.nio.file.attribute.FileTime\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.provider.common.AbstractPosixFileAttributes\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.FileTimeParceler\nimport me.zhanghai.android.files.provider.common.PosixFileMode\nimport me.zhanghai.android.files.provider.common.PosixFileModeBit\nimport me.zhanghai.android.files.provider.common.PosixFileType\nimport me.zhanghai.android.files.provider.common.PosixGroup\nimport me.zhanghai.android.files.provider.common.PosixUser\nimport me.zhanghai.android.files.provider.linux.syscall.StructStat\n\n@Parcelize\ninternal class LinuxFileAttributes(\n    override val lastModifiedTime: @WriteWith<FileTimeParceler> FileTime,\n    override val lastAccessTime: @WriteWith<FileTimeParceler> FileTime,\n    override val creationTime: @WriteWith<FileTimeParceler> FileTime,\n    override val type: PosixFileType,\n    override val size: Long,\n    override val fileKey: Parcelable,\n    override val owner: PosixUser?,\n    override val group: PosixGroup?,\n    override val mode: Set<PosixFileModeBit>?,\n    override val seLinuxContext: ByteString?\n) : AbstractPosixFileAttributes() {\n    companion object {\n        fun from(\n            stat: StructStat,\n            owner: PosixUser,\n            group: PosixGroup,\n            seLinuxContext: ByteString?\n        ): LinuxFileAttributes {\n            val lastModifiedTime =\n                FileTime.from(Instant.ofEpochSecond(stat.st_mtim.tv_sec, stat.st_mtim.tv_nsec))\n            val lastAccessTime =\n                FileTime.from(Instant.ofEpochSecond(stat.st_atim.tv_sec, stat.st_atim.tv_nsec))\n            val creationTime = lastModifiedTime\n            val type = PosixFileType.fromMode(stat.st_mode)\n            val size = stat.st_size\n            val fileKey = LinuxFileKey(stat.st_dev, stat.st_ino)\n            val mode = PosixFileMode.fromInt(stat.st_mode)\n            return LinuxFileAttributes(\n                lastModifiedTime, lastAccessTime, creationTime, type, size, fileKey, owner, group,\n                mode, seLinuxContext\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/linux/LinuxFileKey.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.linux\n\nimport android.os.Parcelable\nimport kotlinx.parcelize.Parcelize\n\n@Parcelize\ninternal data class LinuxFileKey(\n    private val deviceId: Long,\n    private val inodeNumber: Long\n) : Parcelable\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/linux/LinuxFileStore.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.linux\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport me.zhanghai.android.files.provider.root.RootPosixFileStore\nimport me.zhanghai.android.files.provider.root.RootablePosixFileStore\nimport me.zhanghai.android.files.util.readParcelable\n\ninternal class LinuxFileStore private constructor(\n    private val path: LinuxPath,\n    private val localFileStore: LocalLinuxFileStore\n) : RootablePosixFileStore(path, localFileStore, { RootPosixFileStore(it) }) {\n    constructor(path: LinuxPath) : this(path, LocalLinuxFileStore(path))\n\n    private constructor(source: Parcel) : this(source.readParcelable()!!, source.readParcelable()!!)\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        dest.writeParcelable(path, flags)\n        dest.writeParcelable(localFileStore, flags)\n    }\n\n    companion object {\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<LinuxFileStore> {\n            override fun createFromParcel(source: Parcel): LinuxFileStore = LinuxFileStore(source)\n\n            override fun newArray(size: Int): Array<LinuxFileStore?> = arrayOfNulls(size)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/linux/LinuxFileSystem.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.linux\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.ByteStringListPathCreator\nimport me.zhanghai.android.files.provider.root.RootFileSystem\nimport me.zhanghai.android.files.provider.root.RootableFileSystem\n\ninternal class LinuxFileSystem(provider: LinuxFileSystemProvider) : RootableFileSystem(\n    { LocalLinuxFileSystem(it as LinuxFileSystem, provider) }, { RootFileSystem(it) }\n), ByteStringListPathCreator {\n    override val localFileSystem: LocalLinuxFileSystem\n        get() = super.localFileSystem as LocalLinuxFileSystem\n\n    val rootDirectory: LinuxPath\n        get() = localFileSystem.rootDirectory\n\n    val defaultDirectory: LinuxPath\n        get() = localFileSystem.defaultDirectory\n\n    override fun getPath(first: ByteString, vararg more: ByteString): LinuxPath =\n        localFileSystem.getPath(first, *more)\n\n    override fun close() {\n        throw UnsupportedOperationException()\n    }\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {}\n\n    companion object {\n        const val SEPARATOR = LocalLinuxFileSystem.SEPARATOR\n\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<LinuxFileSystem> {\n            override fun createFromParcel(source: Parcel): LinuxFileSystem =\n                LinuxFileSystemProvider.fileSystem\n\n            override fun newArray(size: Int): Array<LinuxFileSystem?> = arrayOfNulls(size)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/linux/LinuxFileSystemProvider.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.linux\n\nimport java8.nio.file.attribute.FileAttributeView\nimport me.zhanghai.android.files.provider.root.RootFileSystemProvider\nimport me.zhanghai.android.files.provider.root.RootableFileSystemProvider\n\nobject LinuxFileSystemProvider : RootableFileSystemProvider(\n    { LocalLinuxFileSystemProvider(it as LinuxFileSystemProvider) },\n    { RootFileSystemProvider(LocalLinuxFileSystemProvider.SCHEME) }\n) {\n    override val localProvider: LocalLinuxFileSystemProvider\n        get() = super.localProvider as LocalLinuxFileSystemProvider\n\n    override val rootProvider: RootFileSystemProvider\n        get() = super.rootProvider as RootFileSystemProvider\n\n    internal val fileSystem: LinuxFileSystem\n        get() = localProvider.fileSystem\n\n    internal fun supportsFileAttributeView(type: Class<out FileAttributeView>): Boolean =\n        LocalLinuxFileSystemProvider.supportsFileAttributeView(type)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/linux/LinuxPath.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.linux\n\nimport android.os.Build\nimport android.os.Parcel\nimport android.os.Parcelable\nimport java8.nio.file.LinkOption\nimport java8.nio.file.Path\nimport java8.nio.file.ProviderMismatchException\nimport java8.nio.file.WatchEvent\nimport java8.nio.file.WatchKey\nimport java8.nio.file.WatchService\nimport me.zhanghai.android.files.app.application\nimport me.zhanghai.android.files.compat.isPrimaryCompat\nimport me.zhanghai.android.files.compat.pathFileCompat\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.ByteStringListPath\nimport me.zhanghai.android.files.provider.root.RootablePath\nimport me.zhanghai.android.files.storage.StorageVolumeListLiveData\nimport me.zhanghai.android.files.util.readParcelable\nimport me.zhanghai.android.files.util.valueCompat\nimport java.io.File\nimport java.io.IOException\n\ninternal class LinuxPath : ByteStringListPath<LinuxPath>, RootablePath {\n    private val fileSystem: LinuxFileSystem\n\n    constructor(fileSystem: LinuxFileSystem, path: ByteString) : super(\n        LinuxFileSystem.SEPARATOR, path\n    ) {\n        this.fileSystem = fileSystem\n    }\n\n    private constructor(\n        fileSystem: LinuxFileSystem,\n        absolute: Boolean,\n        segments: List<ByteString>\n    ) : super(LinuxFileSystem.SEPARATOR, absolute, segments) {\n        this.fileSystem = fileSystem\n    }\n\n    override fun isPathAbsolute(path: ByteString): Boolean =\n        path.isNotEmpty() && path[0] == LinuxFileSystem.SEPARATOR\n\n    override fun createPath(path: ByteString): LinuxPath = LinuxPath(fileSystem, path)\n\n    override fun createPath(absolute: Boolean, segments: List<ByteString>): LinuxPath =\n        LinuxPath(fileSystem, absolute, segments)\n\n    override val defaultDirectory: LinuxPath\n        get() = fileSystem.defaultDirectory\n\n    override fun getFileSystem(): LinuxFileSystem = fileSystem\n\n    override fun getRoot(): LinuxPath? = if (isAbsolute) fileSystem.rootDirectory else null\n\n    @Throws(IOException::class)\n    override fun toRealPath(vararg options: LinkOption): LinuxPath {\n        throw UnsupportedOperationException()\n    }\n\n    override fun toFile(): File = File(toString())\n\n    @Throws(IOException::class)\n    override fun register(\n        watcher: WatchService,\n        events: Array<WatchEvent.Kind<*>>,\n        vararg modifiers: WatchEvent.Modifier\n    ): WatchKey {\n        if (watcher !is LocalLinuxWatchService) {\n            throw ProviderMismatchException(watcher.toString())\n        }\n        return watcher.register(this, events, *modifiers)\n    }\n\n    override fun isRootRequired(isAttributeAccess: Boolean): Boolean {\n        val file = toFile()\n        return StorageVolumeListLiveData.valueCompat.none {\n            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R && !it.isPrimaryCompat) {\n                return@none false\n            }\n            val storageVolumeDirectory = it.pathFileCompat\n            if (!file.startsWith(storageVolumeDirectory)) {\n                return@none false\n            }\n            return@none file.isAccessibleInStorageVolume(storageVolumeDirectory, isAttributeAccess)\n        }\n    }\n\n    private fun File.isAccessibleInStorageVolume(\n        storageVolumeDirectory: File,\n        isAttributeAccess: Boolean\n    ): Boolean {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {\n            val parentDirectory = parentFile\n            val androidDataDirectory = storageVolumeDirectory.resolve(FILE_ANDROID_DATA)\n            val isInAndroidDataDirectory = if (isAttributeAccess && parentDirectory != null) {\n                parentDirectory.startsWith(androidDataDirectory)\n            } else {\n                startsWith(androidDataDirectory)\n            }\n            val appPackageName = application.packageName\n            if (isInAndroidDataDirectory) {\n                val appDataDirectory = androidDataDirectory.resolve(appPackageName)\n                return startsWith(appDataDirectory)\n            }\n            val androidObbDirectory = storageVolumeDirectory.resolve(FILE_ANDROID_OBB)\n            val isInAndroidObbDirectory = if (isAttributeAccess && parentDirectory != null) {\n                parentDirectory.startsWith(androidObbDirectory)\n            } else {\n                startsWith(androidObbDirectory)\n            }\n            if (isInAndroidObbDirectory) {\n                val appObbDirectory = androidObbDirectory.resolve(appPackageName)\n                return startsWith(appObbDirectory)\n            }\n        }\n        return true\n    }\n\n    private constructor(source: Parcel) : super(source) {\n        fileSystem = source.readParcelable()!!\n    }\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        super.writeToParcel(dest, flags)\n\n        dest.writeParcelable(fileSystem, flags)\n    }\n\n    companion object {\n        private val FILE_ANDROID_DATA = File(\"Android/data\")\n        private val FILE_ANDROID_OBB = File(\"Android/obb\")\n\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<LinuxPath> {\n            override fun createFromParcel(source: Parcel): LinuxPath = LinuxPath(source)\n\n            override fun newArray(size: Int): Array<LinuxPath?> = arrayOfNulls(size)\n        }\n    }\n}\n\nval Path.isLinuxPath: Boolean\n    get() = this is LinuxPath\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/linux/LinuxUserPrincipalLookupService.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.linux\n\nimport java8.nio.file.attribute.UserPrincipalLookupService\nimport java8.nio.file.attribute.UserPrincipalNotFoundException\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.PosixGroup\nimport me.zhanghai.android.files.provider.common.PosixUser\nimport me.zhanghai.android.files.provider.common.toByteString\nimport me.zhanghai.android.files.provider.linux.syscall.Syscall\nimport me.zhanghai.android.files.provider.linux.syscall.SyscallException\nimport java.io.IOException\n\ninternal object LinuxUserPrincipalLookupService : UserPrincipalLookupService() {\n    @Throws(IOException::class)\n    override fun lookupPrincipalByName(name: String): PosixUser =\n        lookupPrincipalByName(name.toByteString())\n\n    @Throws(IOException::class)\n    fun lookupPrincipalByName(name: ByteString): PosixUser {\n        val passwd = try {\n            Syscall.getpwnam(name)\n        } catch (e: SyscallException) {\n            throw e.toFileSystemException(null)\n        } ?: throw UserPrincipalNotFoundException(name.toString())\n        return PosixUser(passwd.pw_uid, passwd.pw_name)\n    }\n\n    @Throws(IOException::class)\n    fun lookupPrincipalById(id: Int): PosixUser =\n        try {\n            getUserById(id)\n        } catch (e: SyscallException) {\n            throw e.toFileSystemException(null)\n        }\n\n    @Throws(SyscallException::class)\n    fun getUserById(uid: Int): PosixUser {\n        val passwd = Syscall.getpwuid(uid)\n        return PosixUser(uid, passwd?.pw_name)\n    }\n\n    @Throws(IOException::class)\n    override fun lookupPrincipalByGroupName(group: String): PosixGroup =\n        lookupPrincipalByGroupName(group.toByteString())\n\n    @Throws(IOException::class)\n    fun lookupPrincipalByGroupName(group: ByteString): PosixGroup {\n        val groupStruct = try {\n            Syscall.getgrnam(group)\n        } catch (e: SyscallException) {\n            throw e.toFileSystemException(null)\n        } ?: throw UserPrincipalNotFoundException(group.toString())\n        return PosixGroup(groupStruct.gr_gid, groupStruct.gr_name)\n    }\n\n    @Throws(IOException::class)\n    fun lookupPrincipalByGroupId(groupId: Int): PosixGroup =\n        try {\n            getGroupById(groupId)\n        } catch (e: SyscallException) {\n            throw e.toFileSystemException(null)\n        }\n\n    @Throws(SyscallException::class)\n    fun getGroupById(gid: Int): PosixGroup {\n        val group = Syscall.getgrgid(gid)\n        return PosixGroup(gid, group?.gr_name)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/linux/LocalLinuxFileAttributeView.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.linux\n\nimport android.system.OsConstants\nimport java8.nio.file.attribute.FileTime\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.PosixFileAttributeView\nimport me.zhanghai.android.files.provider.common.PosixFileModeBit\nimport me.zhanghai.android.files.provider.common.PosixGroup\nimport me.zhanghai.android.files.provider.common.PosixUser\nimport me.zhanghai.android.files.provider.common.toInt\nimport me.zhanghai.android.files.provider.linux.syscall.Constants\nimport me.zhanghai.android.files.provider.linux.syscall.StructTimespec\nimport me.zhanghai.android.files.provider.linux.syscall.Syscall\nimport me.zhanghai.android.files.provider.linux.syscall.SyscallException\nimport java.io.IOException\n\ninternal class LocalLinuxFileAttributeView(\n    private val path: ByteString,\n    private val noFollowLinks: Boolean\n) : PosixFileAttributeView {\n    override fun name(): String = NAME\n\n    @Throws(IOException::class)\n    override fun readAttributes(): LinuxFileAttributes {\n        val stat = try {\n            if (noFollowLinks) {\n                Syscall.lstat(path)\n            } else {\n                Syscall.stat(path)\n            }\n        } catch (e: SyscallException) {\n            throw e.toFileSystemException(path.toString())\n        }\n        val owner = try {\n            LinuxUserPrincipalLookupService.getUserById(stat.st_uid)\n        } catch (e: SyscallException) {\n            // It's okay to have a non-existent UID.\n            e.toFileSystemException(path.toString()).printStackTrace()\n            PosixUser(stat.st_uid, null)\n        }\n        val group = try {\n            LinuxUserPrincipalLookupService.getGroupById(stat.st_gid)\n        } catch (e: SyscallException) {\n            // It's okay to have a non-existent GID.\n            e.toFileSystemException(path.toString()).printStackTrace()\n            PosixGroup(stat.st_gid, null)\n        }\n        val seLinuxContext = try {\n            if (noFollowLinks) {\n                Syscall.lgetfilecon(path)\n            } else {\n                Syscall.getfilecon(path)\n            }\n        } catch (e: SyscallException) {\n            // SELinux calls may fail with ENODATA or ENOTSUP, and there may be other errors.\n            e.toFileSystemException(path.toString()).printStackTrace()\n            if (e.errno == OsConstants.ENODATA) ByteString.EMPTY else null\n        }\n        return LinuxFileAttributes.from(stat, owner, group, seLinuxContext)\n    }\n\n    @Throws(IOException::class)\n    override fun setTimes(\n        lastModifiedTime: FileTime?,\n        lastAccessTime: FileTime?,\n        createTime: FileTime?\n    ) {\n        if (lastAccessTime == null && lastModifiedTime == null) {\n            // Only throw if caller is trying to set only create time, so that foreign copy move can\n            // still set other times.\n            if (createTime != null) {\n                throw UnsupportedOperationException(\"createTime\")\n            }\n            return\n        }\n        val times = arrayOf(lastAccessTime.toTimespec(), lastModifiedTime.toTimespec())\n        try {\n            if (noFollowLinks) {\n                Syscall.lutimens(path, times)\n            } else {\n                Syscall.utimens(path, times)\n            }\n        } catch (e: SyscallException) {\n            throw e.toFileSystemException(path.toString())\n        }\n    }\n\n    private fun FileTime?.toTimespec(): StructTimespec {\n        if (this == null) {\n            return StructTimespec(0, Constants.UTIME_OMIT)\n        }\n        val instant = toInstant()\n        return StructTimespec(instant.epochSecond, instant.nano.toLong())\n    }\n\n    @Throws(IOException::class)\n    override fun setOwner(owner: PosixUser) {\n        val uid = owner.id\n        try {\n            if (noFollowLinks) {\n                Syscall.lchown(path, uid, -1)\n            } else {\n                Syscall.chown(path, uid, -1)\n            }\n        } catch (e: SyscallException) {\n            throw e.toFileSystemException(path.toString())\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun setGroup(group: PosixGroup) {\n        val gid = group.id\n        try {\n            if (noFollowLinks) {\n                Syscall.lchown(path, -1, gid)\n            } else {\n                Syscall.chown(path, -1, gid)\n            }\n        } catch (e: SyscallException) {\n            throw e.toFileSystemException(path.toString())\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun setMode(mode: Set<PosixFileModeBit>) {\n        if (noFollowLinks) {\n            throw UnsupportedOperationException(\"Cannot set mode for symbolic links\")\n        }\n        val modeInt = mode.toInt()\n        try {\n            Syscall.chmod(path, modeInt)\n        } catch (e: SyscallException) {\n            throw e.toFileSystemException(path.toString())\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun setSeLinuxContext(context: ByteString) {\n        try {\n            if (noFollowLinks) {\n                Syscall.lsetfilecon(path, context)\n            } else {\n                Syscall.setfilecon(path, context)\n            }\n        } catch (e: SyscallException) {\n            throw e.toFileSystemException(path.toString())\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun restoreSeLinuxContext() {\n        val path = if (noFollowLinks) {\n            path\n        } else {\n            try {\n                Syscall.realpath(path)\n            } catch (e: SyscallException) {\n                throw e.toFileSystemException(path.toString())\n            }\n        }\n        try {\n            Syscall.selinux_android_restorecon(path, 0)\n        } catch (e: SyscallException) {\n            throw e.toFileSystemException(path.toString())\n        }\n    }\n\n    companion object {\n        private val NAME = LinuxFileSystemProvider.scheme\n\n        val SUPPORTED_NAMES = setOf(\"basic\", \"posix\", NAME)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/linux/LocalLinuxFileStore.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.linux\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport android.system.OsConstants\nimport android.system.StructStatVfs\nimport java8.nio.file.attribute.FileAttributeView\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.ByteStringBuilder\nimport me.zhanghai.android.files.provider.common.FileStoreNotFoundException\nimport me.zhanghai.android.files.provider.common.PosixFileStore\nimport me.zhanghai.android.files.provider.common.toByteString\nimport me.zhanghai.android.files.provider.linux.syscall.Constants\nimport me.zhanghai.android.files.provider.linux.syscall.Int32Ref\nimport me.zhanghai.android.files.provider.linux.syscall.StructMntent\nimport me.zhanghai.android.files.provider.linux.syscall.Syscall\nimport me.zhanghai.android.files.provider.linux.syscall.SyscallException\nimport me.zhanghai.android.files.util.andInv\nimport me.zhanghai.android.files.util.hasBits\nimport me.zhanghai.android.files.util.readParcelable\nimport java.io.IOException\n\ninternal class LocalLinuxFileStore : PosixFileStore, Parcelable {\n    private val path: LinuxPath\n    private lateinit var mntent: StructMntent\n\n    @Throws(IOException::class)\n    constructor(path: LinuxPath) {\n        this.path = path\n        refresh()\n    }\n\n    private constructor(fileSystem: LocalLinuxFileSystem, mntent: StructMntent) {\n        path = fileSystem.getPath(mntent.mnt_dir)\n        this.mntent = mntent\n    }\n\n    @Throws(IOException::class)\n    override fun refresh() {\n        this.mntent = try {\n            findMountEntry(path)\n        } catch (e: SyscallException) {\n            throw e.toFileSystemException(path.toString())\n        } ?: throw FileStoreNotFoundException(path.toString())\n    }\n\n    @Throws(SyscallException::class)\n    private fun findMountEntry(path: LinuxPath): StructMntent? {\n        val entries = mutableMapOf<LinuxPath, StructMntent>()\n        // The last mount entry for the same path will win because we are putting them into a Map,\n        // so no need to traverse in reverse order like other implementations.\n        for (mntent in getMountEntries()) {\n            val entryPath = path.fileSystem.getPath(mntent.mnt_dir)\n            entries[entryPath] = mntent\n        }\n        var path = path\n        while (true) {\n            val mntent = entries[path]\n            if (mntent != null) {\n                return mntent\n            }\n            path = path.parent ?: break\n        }\n        return null\n    }\n\n    override fun name(): String = mntent.mnt_dir.toString()\n\n    override fun type(): String = mntent.mnt_type.toString()\n\n    override fun isReadOnly(): Boolean = Syscall.hasmntopt(mntent, OPTION_RO)\n\n    @Throws(IOException::class)\n    override fun setReadOnly(readOnly: Boolean) {\n        // Fetch the latest mount entry before we remount.\n        refresh()\n        if (isReadOnly == readOnly) {\n            return\n        }\n        var (flags, options) = getFlagsFromOptions(mntent.mnt_opts)\n        flags = if (readOnly) {\n            flags or Constants.MS_RDONLY\n        } else {\n            flags andInv Constants.MS_RDONLY\n        }\n        val data = options.cstr\n        try {\n            remount(mntent.mnt_fsname, mntent.mnt_dir, mntent.mnt_type, flags, data)\n        } catch (e: SyscallException) {\n            throw e.toFileSystemException(mntent.mnt_dir.toString())\n        }\n        refresh()\n    }\n\n    private fun getFlagsFromOptions(options: ByteString): Pair<Long, ByteString> {\n        var flags = 0L\n        val builder = ByteStringBuilder()\n        for (option in options.split(OPTIONS_DELIMITER)) {\n            val flag = OPTION_FLAG_MAP[option]\n            if (flag != null) {\n                flags = flags or flag\n            } else {\n                if (!builder.isEmpty) {\n                    builder.append(OPTIONS_DELIMITER)\n                }\n                builder.append(option)\n            }\n        }\n        return flags to builder.toByteString()\n    }\n\n    @Throws(SyscallException::class)\n    private fun remount(\n        source: ByteString?,\n        target: ByteString,\n        fileSystemType: ByteString?,\n        mountFlags: Long,\n        data: ByteArray?\n    ) {\n        val mountFlags = mountFlags or Constants.MS_REMOUNT\n        try {\n            Syscall.mount(source, target, fileSystemType, mountFlags, data)\n        } catch (e: SyscallException) {\n            val readOnly = mountFlags.hasBits(Constants.MS_RDONLY)\n            val isReadOnlyError = e.errno == OsConstants.EACCES || e.errno == OsConstants.EROFS\n            if (readOnly || !isReadOnlyError) {\n                throw e\n            }\n            try {\n                val fd = Syscall.open(source!!, OsConstants.O_RDONLY, 0)\n                try {\n                    Syscall.ioctl_int(fd, Constants.BLKROSET, Int32Ref(0))\n                } finally {\n                    Syscall.close(fd)\n                }\n                Syscall.mount(source, target, fileSystemType, mountFlags, data)\n            } catch (e2: SyscallException) {\n                e.addSuppressed(e2)\n                throw e\n            }\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun getTotalSpace(): Long {\n        val statVfs = getStatVfs()\n        return statVfs.f_blocks * statVfs.f_bsize\n    }\n\n    @Throws(IOException::class)\n    override fun getUsableSpace(): Long {\n        val statVfs = getStatVfs()\n        return statVfs.f_bavail * statVfs.f_bsize\n    }\n\n    @Throws(IOException::class)\n    override fun getUnallocatedSpace(): Long {\n        val statVfs = getStatVfs()\n        return statVfs.f_bfree * statVfs.f_bsize\n    }\n\n    @Throws(IOException::class)\n    private fun getStatVfs(): StructStatVfs =\n        try {\n            Syscall.statvfs(path.toByteString())\n        } catch (e: SyscallException) {\n            throw e.toFileSystemException(path.toString())\n        }\n\n    override fun supportsFileAttributeView(type: Class<out FileAttributeView>): Boolean =\n        LinuxFileSystemProvider.supportsFileAttributeView(type)\n\n    override fun supportsFileAttributeView(name: String): Boolean =\n        name in LinuxFileAttributeView.SUPPORTED_NAMES\n\n    private constructor(source: Parcel) {\n        path = source.readParcelable()!!\n        mntent = source.readParcelable()!!\n    }\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        dest.writeParcelable(path, flags)\n        dest.writeParcelable(mntent, flags)\n    }\n\n    companion object {\n        private val PATH_PROC_SELF_MOUNTS = \"/proc/self/mounts\".toByteString()\n\n        private val MODE_R = \"r\".toByteString()\n\n        private val OPTIONS_DELIMITER = \",\".toByteString()\n        private val OPTION_RO = \"ro\".toByteString()\n        // @see https://android.googlesource.com/platform/system/core/+/master/fs_mgr/fs_mgr_fstab.cpp\n        //      kMountFlagsList\n        // @see https://github.com/mmalecki/util-linux/blob/master/mount-deprecated/mount.c opt_map\n        // @see https://android.googlesource.com/platform/external/toybox/+/refs/heads/master/toys/lsb/mount.c\n        //      flag_opts()\n        // @see http://lists.landley.net/pipermail/toybox-landley.net/2012-August/000628.html\n        private val OPTION_FLAG_MAP = mapOf(\n            \"defaults\" to 0L,\n            \"ro\" to Constants.MS_RDONLY,\n            \"rw\" to 0L,\n            \"nosuid\" to Constants.MS_NOSUID,\n            \"suid\" to 0L,\n            \"nodev\" to Constants.MS_NODEV,\n            \"dev\" to 0L,\n            \"noexec\" to Constants.MS_NOEXEC,\n            \"exec\" to 0L,\n            \"sync\" to Constants.MS_SYNCHRONOUS,\n            \"async\" to 0L,\n            \"remount\" to Constants.MS_REMOUNT,\n            \"mand\" to Constants.MS_MANDLOCK,\n            \"nomand\" to 0L,\n            \"dirsync\" to Constants.MS_DIRSYNC,\n            \"noatime\" to Constants.MS_NOATIME,\n            \"atime\" to 0L,\n            \"nodiratime\" to Constants.MS_NODIRATIME,\n            \"diratime\" to 0L,\n            \"bind\" to Constants.MS_BIND,\n            \"rbind\" to (Constants.MS_BIND or Constants.MS_REC),\n            \"move\" to Constants.MS_MOVE,\n            \"rec\" to Constants.MS_REC,\n            \"verbose\" to Constants.MS_VERBOSE,\n            \"silent\" to Constants.MS_SILENT,\n            \"loud\" to 0L,\n            //\"posixacl\" to Constants.MS_POSIXACL,\n            //\"noposixacl\" to 0L,\n            \"unbindable\" to Constants.MS_UNBINDABLE,\n            \"runbindable\" to (Constants.MS_UNBINDABLE or Constants.MS_REC),\n            \"private\" to Constants.MS_PRIVATE,\n            \"rprivate\" to (Constants.MS_PRIVATE or Constants.MS_REC),\n            \"slave\" to Constants.MS_SLAVE,\n            \"rslave\" to (Constants.MS_SLAVE or Constants.MS_REC),\n            \"shared\" to Constants.MS_SHARED,\n            \"rshared\" to (Constants.MS_SHARED or Constants.MS_REC),\n            \"relatime\" to Constants.MS_RELATIME,\n            \"norelatime\" to 0L,\n            //\"kernmount\" to Constants.MS_KERNMOUNT,\n            \"iversion\" to Constants.MS_I_VERSION,\n            \"noiversion\" to 0L,\n            \"strictatime\" to Constants.MS_STRICTATIME,\n            \"nostrictatime\" to 0L,\n            \"lazytime\" to Constants.MS_LAZYTIME,\n            \"nolazytime\" to 0L,\n            //\"submount\" to Constants.MS_SUBMOUNT,\n            //\"noremotelock\" to Constants.MS_NOREMOTELOCK,\n            //\"remotelock\" to 0L,\n            //\"nosec\" to Constants.MS_NOSEC,\n            //\"sec\" to 0L,\n            //\"born\" to Constants.MS_BORN,\n            //\"active\" to Constants.MS_ACTIVE,\n            \"nouser\" to Constants.MS_NOUSER,\n            \"user\" to 0L\n        ).mapKeys { it.key.toByteString() }\n\n        fun getFileStores(fileSystem: LocalLinuxFileSystem): List<LocalLinuxFileStore> {\n            val entries = try {\n                getMountEntries()\n            } catch (e: SyscallException) {\n                e.printStackTrace()\n                return emptyList()\n            }\n            return entries.map { LocalLinuxFileStore(fileSystem, it) }\n        }\n\n        @Throws(SyscallException::class)\n        private fun getMountEntries(): List<StructMntent> {\n            val entries = mutableListOf<StructMntent>()\n            val file = Syscall.setmntent(PATH_PROC_SELF_MOUNTS, MODE_R)\n            try {\n                while (true) {\n                    val mntent = Syscall.getmntent(file) ?: break\n                    entries += mntent\n                }\n            } finally {\n                Syscall.endmntent(file)\n            }\n            return entries\n        }\n\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<LocalLinuxFileStore> {\n            override fun createFromParcel(source: Parcel): LocalLinuxFileStore =\n                LocalLinuxFileStore(source)\n\n            override fun newArray(size: Int): Array<LocalLinuxFileStore?> = arrayOfNulls(size)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/linux/LocalLinuxFileSystem.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.linux\n\nimport java8.nio.file.FileStore\nimport java8.nio.file.FileSystem\nimport java8.nio.file.Path\nimport java8.nio.file.PathMatcher\nimport java8.nio.file.WatchService\nimport java8.nio.file.spi.FileSystemProvider\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.ByteStringBuilder\nimport me.zhanghai.android.files.provider.common.ByteStringListPathCreator\nimport me.zhanghai.android.files.provider.common.toByteString\nimport me.zhanghai.android.files.util.takeIfNotEmpty\nimport java.io.IOException\n\ninternal class LocalLinuxFileSystem(\n    private val fileSystem: LinuxFileSystem,\n    private val provider: LinuxFileSystemProvider\n) : FileSystem(), ByteStringListPathCreator {\n    val rootDirectory = LinuxPath(fileSystem, SEPARATOR_BYTE_STRING)\n\n    init {\n        if (!rootDirectory.isAbsolute) {\n            throw AssertionError(\"Root directory must be absolute\")\n        }\n        if (rootDirectory.nameCount != 0) {\n            throw AssertionError(\"Root directory must contain no names\")\n        }\n    }\n\n    val defaultDirectory = LinuxPath(\n        fileSystem,\n        System.getenv(\"user.dir\")?.takeIfNotEmpty()?.toByteString() ?: SEPARATOR_BYTE_STRING\n    )\n\n    init {\n        if (!defaultDirectory.isAbsolute) {\n            throw AssertionError(\"Default directory must be absolute\")\n        }\n    }\n\n    override fun provider(): FileSystemProvider = provider\n\n    override fun close() {\n        throw UnsupportedOperationException()\n    }\n\n    override fun isOpen(): Boolean = true\n\n    override fun isReadOnly(): Boolean = false\n\n    override fun getSeparator(): String = SEPARATOR_STRING\n\n    override fun getRootDirectories(): Iterable<Path> = listOf(rootDirectory)\n\n    override fun getFileStores(): Iterable<FileStore> = LocalLinuxFileStore.getFileStores(this)\n\n    override fun supportedFileAttributeViews(): Set<String> = LinuxFileAttributeView.SUPPORTED_NAMES\n\n    override fun getPath(first: String, vararg more: String): LinuxPath {\n        val path = ByteStringBuilder(first.toByteString())\n            .apply { more.forEach { append(SEPARATOR).append(it.toByteString()) } }\n            .toByteString()\n        return LinuxPath(fileSystem, path)\n    }\n\n    override fun getPath(first: ByteString, vararg more: ByteString): LinuxPath {\n        val path = ByteStringBuilder(first)\n            .apply { more.forEach { append(SEPARATOR).append(it) } }\n            .toByteString()\n        return LinuxPath(fileSystem, path)\n    }\n\n    override fun getPathMatcher(syntaxAndPattern: String): PathMatcher {\n        throw UnsupportedOperationException()\n    }\n\n    override fun getUserPrincipalLookupService(): LinuxUserPrincipalLookupService =\n        LinuxUserPrincipalLookupService\n\n    @Throws(IOException::class)\n    override fun newWatchService(): WatchService = LocalLinuxWatchService()\n\n    companion object {\n        const val SEPARATOR = '/'.code.toByte()\n        private val SEPARATOR_BYTE_STRING = SEPARATOR.toByteString()\n        private const val SEPARATOR_STRING = SEPARATOR.toInt().toChar().toString()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/linux/LocalLinuxFileSystemProvider.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.linux\n\nimport android.system.OsConstants\nimport java8.nio.channels.FileChannel\nimport java8.nio.channels.SeekableByteChannel\nimport java8.nio.file.AccessDeniedException\nimport java8.nio.file.AccessMode\nimport java8.nio.file.CopyOption\nimport java8.nio.file.DirectoryStream\nimport java8.nio.file.FileStore\nimport java8.nio.file.FileSystem\nimport java8.nio.file.FileSystemAlreadyExistsException\nimport java8.nio.file.LinkOption\nimport java8.nio.file.OpenOption\nimport java8.nio.file.Path\nimport java8.nio.file.ProviderMismatchException\nimport java8.nio.file.attribute.BasicFileAttributes\nimport java8.nio.file.attribute.FileAttribute\nimport java8.nio.file.attribute.FileAttributeView\nimport java8.nio.file.spi.FileSystemProvider\nimport me.zhanghai.android.files.provider.common.ByteStringPath\nimport me.zhanghai.android.files.provider.common.PathObservable\nimport me.zhanghai.android.files.provider.common.PathObservableProvider\nimport me.zhanghai.android.files.provider.common.PosixFileMode\nimport me.zhanghai.android.files.provider.common.Searchable\nimport me.zhanghai.android.files.provider.common.WalkFileTreeSearchable\nimport me.zhanghai.android.files.provider.common.WatchServicePathObservable\nimport me.zhanghai.android.files.provider.common.decodedPathByteString\nimport me.zhanghai.android.files.provider.common.open\nimport me.zhanghai.android.files.provider.common.toAccessModes\nimport me.zhanghai.android.files.provider.common.toByteString\nimport me.zhanghai.android.files.provider.common.toCopyOptions\nimport me.zhanghai.android.files.provider.common.toInt\nimport me.zhanghai.android.files.provider.common.toLinkOptions\nimport me.zhanghai.android.files.provider.common.toOpenOptions\nimport me.zhanghai.android.files.provider.linux.media.MediaScanner\nimport me.zhanghai.android.files.provider.linux.syscall.Syscall\nimport me.zhanghai.android.files.provider.linux.syscall.SyscallException\nimport me.zhanghai.android.files.util.hasBits\nimport java.io.IOException\nimport java.net.URI\n\nclass LocalLinuxFileSystemProvider(provider: LinuxFileSystemProvider) : FileSystemProvider(),\n    PathObservableProvider, Searchable {\n    internal val fileSystem: LinuxFileSystem = LinuxFileSystem(provider)\n\n    override fun getScheme(): String = SCHEME\n\n    override fun newFileSystem(uri: URI, env: Map<String, *>): FileSystem {\n        uri.requireSameScheme()\n        throw FileSystemAlreadyExistsException()\n    }\n\n    override fun getFileSystem(uri: URI): FileSystem {\n        uri.requireSameScheme()\n        return fileSystem\n    }\n\n    override fun getPath(uri: URI): Path {\n        uri.requireSameScheme()\n        val path = uri.decodedPathByteString\n            ?: throw IllegalArgumentException(\"URI must have a path\")\n        return fileSystem.getPath(path)\n    }\n\n    private fun URI.requireSameScheme() {\n        val scheme = scheme\n        require(scheme == SCHEME) { \"URI scheme $scheme must be $SCHEME\" }\n    }\n\n    @Throws(IOException::class)\n    override fun newFileChannel(\n        file: Path,\n        options: Set<OpenOption>,\n        vararg attributes: FileAttribute<*>\n    ): FileChannel {\n        file as? LinuxPath ?: throw ProviderMismatchException(file.toString())\n        val fileBytes = file.toByteString()\n        val openOptions = options.toOpenOptions()\n        val flags = openOptions.toLinuxFlags()\n        val mode = (PosixFileMode.fromAttributes(attributes) ?: PosixFileMode.CREATE_FILE_DEFAULT)\n            .toInt()\n        val fd = try {\n            Syscall.open(fileBytes, flags, mode)\n        } catch (e: SyscallException) {\n            if (flags.hasBits(OsConstants.O_CREAT)) {\n                e.maybeThrowInvalidFileNameException(fileBytes.toString())\n            }\n            throw e.toFileSystemException(fileBytes.toString())\n        }\n        val fileChannel = FileChannel::class.open(fd, flags)\n        if (openOptions.deleteOnClose) {\n            try {\n                Syscall.remove(fileBytes)\n            } catch (e: SyscallException) {\n                e.printStackTrace()\n            }\n        }\n        val javaFile = file.toFile()\n        MediaScanner.scan(javaFile)\n        return MediaScanner.createScanOnCloseFileChannel(fileChannel, javaFile)\n    }\n\n    @Throws(IOException::class)\n    override fun newByteChannel(\n        file: Path,\n        options: Set<OpenOption>,\n        vararg attributes: FileAttribute<*>\n    ): SeekableByteChannel = newFileChannel(file, options, *attributes)\n\n    @Throws(IOException::class)\n    override fun newDirectoryStream(\n        directory: Path,\n        filter: DirectoryStream.Filter<in Path>\n    ): DirectoryStream<Path> {\n        directory as? LinuxPath ?: throw ProviderMismatchException(directory.toString())\n        val directoryBytes = directory.toByteString()\n        val dir = try {\n            Syscall.opendir(directoryBytes)\n        } catch (e: SyscallException) {\n            throw e.toFileSystemException(directoryBytes.toString())\n        }\n        return LinuxDirectoryStream(directory, dir, filter)\n    }\n\n    @Throws(IOException::class)\n    override fun createDirectory(directory: Path, vararg attributes: FileAttribute<*>) {\n        directory as? LinuxPath ?: throw ProviderMismatchException(directory.toString())\n        val directoryBytes = directory.toByteString()\n        val mode = (PosixFileMode.fromAttributes(attributes)\n            ?: PosixFileMode.CREATE_DIRECTORY_DEFAULT).toInt()\n        try {\n            Syscall.mkdir(directoryBytes, mode)\n        } catch (e: SyscallException) {\n            e.maybeThrowInvalidFileNameException(directoryBytes.toString())\n            throw e.toFileSystemException(directoryBytes.toString())\n        }\n        MediaScanner.scan(directory.toFile())\n    }\n\n    @Throws(IOException::class)\n    override fun createSymbolicLink(link: Path, target: Path, vararg attributes: FileAttribute<*>) {\n        link as? LinuxPath ?: throw ProviderMismatchException(link.toString())\n        val targetBytes = when (target) {\n            is LinuxPath -> target.toByteString()\n            is ByteStringPath -> target.toByteString()\n            else -> throw ProviderMismatchException(target.toString())\n        }\n        if (attributes.isNotEmpty()) {\n            throw UnsupportedOperationException(attributes.contentToString())\n        }\n        val linkBytes = link.toByteString()\n        try {\n            Syscall.symlink(targetBytes, linkBytes)\n        } catch (e: SyscallException) {\n            e.maybeThrowInvalidFileNameException(linkBytes.toString())\n            throw e.toFileSystemException(linkBytes.toString(), targetBytes.toString())\n        }\n        MediaScanner.scan(link.toFile())\n    }\n\n    @Throws(IOException::class)\n    override fun createLink(link: Path, existing: Path) {\n        link as? LinuxPath ?: throw ProviderMismatchException(link.toString())\n        existing as? LinuxPath ?: throw ProviderMismatchException(existing.toString())\n        val oldPathBytes = existing.toByteString()\n        val newPathBytes = link.toByteString()\n        try {\n            Syscall.link(oldPathBytes, newPathBytes)\n        } catch (e: SyscallException) {\n            e.maybeThrowInvalidFileNameException(newPathBytes.toString())\n            throw e.toFileSystemException(newPathBytes.toString(), oldPathBytes.toString())\n        }\n        MediaScanner.scan(link.toFile())\n    }\n\n    @Throws(IOException::class)\n    override fun delete(path: Path) {\n        path as? LinuxPath ?: throw ProviderMismatchException(path.toString())\n        val pathBytes = path.toByteString()\n        try {\n            Syscall.remove(pathBytes)\n        } catch (e: SyscallException) {\n            throw e.toFileSystemException(pathBytes.toString())\n        }\n        MediaScanner.scan(path.toFile(), true)\n    }\n\n    @Throws(IOException::class)\n    override fun readSymbolicLink(link: Path): Path {\n        link as? LinuxPath ?: throw ProviderMismatchException(link.toString())\n        val linkBytes = link.toByteString()\n        val targetBytes = try {\n            Syscall.readlink(linkBytes)\n        } catch (e: SyscallException) {\n            e.maybeThrowNotLinkException(linkBytes.toString())\n            throw e.toFileSystemException(linkBytes.toString())\n        }\n        return ByteStringPath(targetBytes)\n    }\n\n    @Throws(IOException::class)\n    override fun copy(source: Path, target: Path, vararg options: CopyOption) {\n        source as? LinuxPath ?: throw ProviderMismatchException(source.toString())\n        target as? LinuxPath ?: throw ProviderMismatchException(target.toString())\n        val sourceBytes = source.toByteString()\n        val targetBytes = target.toByteString()\n        val copyOptions = options.toCopyOptions()\n        LinuxCopyMove.copy(sourceBytes, targetBytes, copyOptions)\n        MediaScanner.scan(target.toFile())\n    }\n\n    @Throws(IOException::class)\n    override fun move(source: Path, target: Path, vararg options: CopyOption) {\n        source as? LinuxPath ?: throw ProviderMismatchException(source.toString())\n        target as? LinuxPath ?: throw ProviderMismatchException(target.toString())\n        val sourceBytes = source.toByteString()\n        val targetBytes = target.toByteString()\n        val copyOptions = options.toCopyOptions()\n        LinuxCopyMove.move(sourceBytes, targetBytes, copyOptions)\n        MediaScanner.scan(source.toFile())\n        MediaScanner.scan(target.toFile())\n    }\n\n    @Throws(IOException::class)\n    override fun isSameFile(path: Path, path2: Path): Boolean {\n        path as? LinuxPath ?: throw ProviderMismatchException(path.toString())\n        if (path == path2) {\n            return true\n        }\n        if (path2 !is LinuxPath) {\n            return false\n        }\n        path2 as? LinuxPath ?: throw ProviderMismatchException(path2.toString())\n        val pathBytes = path.toByteString()\n        val path2Bytes = path2.toByteString()\n        val pathStat = try {\n            Syscall.lstat(pathBytes)\n        } catch (e: SyscallException) {\n            throw e.toFileSystemException(pathBytes.toString())\n        }\n        val path2Stat = try {\n            Syscall.lstat(path2Bytes)\n        } catch (e: SyscallException) {\n            throw e.toFileSystemException(path2Bytes.toString())\n        }\n        return pathStat.st_dev == path2Stat.st_dev && pathStat.st_ino == path2Stat.st_ino\n    }\n\n    override fun isHidden(path: Path): Boolean {\n        path as? LinuxPath ?: throw ProviderMismatchException(path.toString())\n        val fileName = path.fileName ?: return false\n        val fileNameBytes = fileName.toByteString()\n        return fileNameBytes.startsWith(HIDDEN_FILE_NAME_PREFIX)\n    }\n\n    @Throws(IOException::class)\n    override fun getFileStore(path: Path): FileStore {\n        path as? LinuxPath ?: throw ProviderMismatchException(path.toString())\n        return LinuxFileStore(path)\n    }\n\n    @Throws(IOException::class)\n    override fun checkAccess(path: Path, vararg modes: AccessMode) {\n        path as? LinuxPath ?: throw ProviderMismatchException(path.toString())\n        val pathBytes = path.toByteString()\n        val accessModes = modes.toAccessModes()\n        var mode: Int\n        if (!(accessModes.read || accessModes.write || accessModes.execute)) {\n            mode = OsConstants.F_OK\n        } else {\n            mode = 0\n            if (accessModes.read) {\n                mode = mode or OsConstants.R_OK\n            }\n            if (accessModes.write) {\n                mode = mode or OsConstants.W_OK\n            }\n            if (accessModes.execute) {\n                mode = mode or OsConstants.X_OK\n            }\n        }\n        val accessible = try {\n            // TODO: Should use euidaccess() but that's unavailable on Android.\n            Syscall.access(pathBytes, mode)\n        } catch (e: SyscallException) {\n            throw e.toFileSystemException(pathBytes.toString())\n        }\n        if (!accessible) {\n            throw AccessDeniedException(pathBytes.toString())\n        }\n    }\n\n    override fun <V : FileAttributeView> getFileAttributeView(\n        path: Path,\n        type: Class<V>,\n        vararg options: LinkOption\n    ): V? {\n        path as? LinuxPath ?: throw ProviderMismatchException(path.toString())\n        if (!supportsFileAttributeView(type)) {\n            return null\n        }\n        @Suppress(\"UNCHECKED_CAST\")\n        return getFileAttributeView(path, *options) as V\n    }\n\n    @Throws(IOException::class)\n    override fun <A : BasicFileAttributes> readAttributes(\n        path: Path,\n        type: Class<A>,\n        vararg options: LinkOption\n    ): A {\n        path as? LinuxPath ?: throw ProviderMismatchException(path.toString())\n        if (!type.isAssignableFrom(LinuxFileAttributes::class.java)) {\n            throw UnsupportedOperationException(type.toString())\n        }\n        @Suppress(\"UNCHECKED_CAST\")\n        return getFileAttributeView(path, *options).readAttributes() as A\n    }\n\n    private fun getFileAttributeView(\n        path: Path,\n        vararg options: LinkOption\n    ): LinuxFileAttributeView {\n        path as? LinuxPath ?: throw ProviderMismatchException(path.toString())\n        val linkOptions = options.toLinkOptions()\n        return LinuxFileAttributeView(path, linkOptions.noFollowLinks)\n    }\n\n    override fun readAttributes(\n        path: Path,\n        attributes: String,\n        vararg options: LinkOption\n    ): Map<String, Any> {\n        path as? LinuxPath ?: throw ProviderMismatchException(path.toString())\n        throw UnsupportedOperationException()\n    }\n\n    override fun setAttribute(\n        path: Path,\n        attribute: String,\n        value: Any,\n        vararg options: LinkOption\n    ) {\n        path as? LinuxPath ?: throw ProviderMismatchException(path.toString())\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun observe(path: Path, intervalMillis: Long): PathObservable {\n        path as? LinuxPath ?: throw ProviderMismatchException(path.toString())\n        return WatchServicePathObservable(path, intervalMillis)\n    }\n\n    @Throws(IOException::class)\n    override fun search(\n        directory: Path,\n        query: String,\n        intervalMillis: Long,\n        listener: (List<Path>) -> Unit\n    ) {\n        directory as? LinuxPath ?: throw ProviderMismatchException(directory.toString())\n        WalkFileTreeSearchable.search(directory, query, intervalMillis, listener)\n    }\n\n    companion object {\n        internal const val SCHEME = \"file\"\n\n        private val HIDDEN_FILE_NAME_PREFIX = \".\".toByteString()\n\n        internal fun supportsFileAttributeView(type: Class<out FileAttributeView>): Boolean =\n            type.isAssignableFrom(LinuxFileAttributeView::class.java)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/linux/LocalLinuxWatchKey.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.linux\n\nimport me.zhanghai.android.files.provider.common.AbstractWatchKey\n\ninternal class LocalLinuxWatchKey(\n    watchService: LocalLinuxWatchService,\n    path: LinuxPath,\n    val watchDescriptor: Int\n) : AbstractWatchKey<LocalLinuxWatchKey, LinuxPath>(watchService, path)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/linux/LocalLinuxWatchService.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.linux\n\nimport android.system.OsConstants\nimport android.system.StructPollfd\nimport java8.nio.file.ClosedWatchServiceException\nimport java8.nio.file.LinkOption\nimport java8.nio.file.Path\nimport java8.nio.file.StandardWatchEventKinds\nimport java8.nio.file.WatchEvent\nimport java8.nio.file.attribute.BasicFileAttributes\nimport kotlinx.coroutines.runBlocking\nimport me.zhanghai.android.files.provider.FileSystemProviders\nimport me.zhanghai.android.files.provider.common.AbstractWatchService\nimport me.zhanghai.android.files.provider.common.readAttributes\nimport me.zhanghai.android.files.provider.linux.syscall.Constants\nimport me.zhanghai.android.files.provider.linux.syscall.Syscall\nimport me.zhanghai.android.files.provider.linux.syscall.SyscallException\nimport me.zhanghai.android.files.util.hasBits\nimport java.io.Closeable\nimport java.io.FileDescriptor\nimport java.io.IOException\nimport java.io.InterruptedIOException\nimport java.util.LinkedList\nimport java.util.Queue\nimport java.util.concurrent.atomic.AtomicInteger\nimport kotlin.coroutines.Continuation\nimport kotlin.coroutines.resume\nimport kotlin.coroutines.resumeWithException\nimport kotlin.coroutines.suspendCoroutine\n\ninternal class LocalLinuxWatchService : AbstractWatchService<LocalLinuxWatchKey>() {\n    private val poller = Poller(this)\n\n    init {\n        poller.start()\n    }\n\n    @Throws(IOException::class)\n    fun register(\n        path: LinuxPath,\n        kinds: Array<WatchEvent.Kind<*>>,\n        vararg modifiers: WatchEvent.Modifier\n    ): LocalLinuxWatchKey {\n        val kindSet = mutableSetOf<WatchEvent.Kind<*>>()\n        for (kind in kinds) {\n            when (kind) {\n                StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE,\n                StandardWatchEventKinds.ENTRY_MODIFY -> kindSet += kind\n                // Ignored.\n                StandardWatchEventKinds.OVERFLOW -> {}\n                else -> throw UnsupportedOperationException(kind.name())\n            }\n        }\n        for (modifier in modifiers) {\n            throw UnsupportedOperationException(modifier.name())\n        }\n        return poller.register(path, kindSet)\n    }\n\n    override fun cancel(key: LocalLinuxWatchKey) {\n        poller.cancel(key)\n    }\n\n    @Throws(IOException::class)\n    override fun onClose() {\n        poller.close()\n    }\n\n    private class Poller(\n        private val watchService: LocalLinuxWatchService\n    ) : Thread(\"LocalLinuxWatchService.Poller-${id.getAndIncrement()}\"), Closeable {\n        private val socketFds: Array<FileDescriptor>\n\n        private var inotifyFd: FileDescriptor\n\n        private val keys = mutableMapOf<Int, LocalLinuxWatchKey>()\n\n        private val inotifyBuffer = ByteArray(4 * 1024)\n\n        private val runnables: Queue<() -> Unit> = LinkedList()\n\n        private var isClosed = false\n\n        private val lock = Any()\n\n        init {\n            isDaemon = true\n            try {\n                socketFds = Syscall.socketpair(OsConstants.AF_UNIX, OsConstants.SOCK_STREAM, 0)\n                val flags = Syscall.fcntl(socketFds[0], OsConstants.F_GETFL)\n                if (!flags.hasBits(OsConstants.O_NONBLOCK)) {\n                    Syscall.fcntl(\n                        socketFds[0], OsConstants.F_SETFL, flags or OsConstants.O_NONBLOCK\n                    )\n                }\n                inotifyFd = Syscall.inotify_init1(OsConstants.O_NONBLOCK)\n            } catch (e: SyscallException) {\n                throw e.toFileSystemException(null)\n            }\n        }\n\n        @Throws(IOException::class)\n        fun register(path: LinuxPath, kinds: Set<WatchEvent.Kind<*>>): LocalLinuxWatchKey =\n            try {\n                runBlocking<LocalLinuxWatchKey> {\n                    suspendCoroutine { continuation ->\n                        post(true, continuation) {\n                            try {\n                                val pathBytes = path.toByteString()\n                                var mask = eventKindsToMask(kinds)\n                                mask = maybeAddDontFollowMask(path, mask)\n                                val wd = try {\n                                    Syscall.inotify_add_watch(inotifyFd, pathBytes, mask)\n                                } catch (e: SyscallException) {\n                                    continuation.resumeWithException(\n                                        e.toFileSystemException(pathBytes.toString())\n                                    )\n                                    return@post\n                                }\n                                val key = LocalLinuxWatchKey(watchService, path, wd)\n                                keys[wd] = key\n                                continuation.resume(key)\n                            } catch (e: RuntimeException) {\n                                continuation.resumeWithException(e)\n                            }\n                        }\n                    }\n                }\n            } catch (e: InterruptedException) {\n                throw InterruptedIOException().apply { initCause(e) }\n            }\n\n        private fun maybeAddDontFollowMask(path: Path, mask: Int): Int {\n            val attributes = try {\n                path.readAttributes(BasicFileAttributes::class.java)\n            } catch (ignored: IOException) {\n                try {\n                    path.readAttributes(BasicFileAttributes::class.java, LinkOption.NOFOLLOW_LINKS)\n                } catch (ignored: IOException) {\n                    null\n                }\n            }\n            return if (attributes != null && attributes.isSymbolicLink) {\n                mask or Constants.IN_DONT_FOLLOW\n            } else {\n                mask\n            }\n        }\n\n        fun cancel(key: LocalLinuxWatchKey) {\n            try {\n                runBlocking<Unit> {\n                    suspendCoroutine { continuation ->\n                        post(true, continuation) {\n                            try {\n                                if (key.isValid) {\n                                    val wd = key.watchDescriptor\n                                    try {\n                                        Syscall.inotify_rm_watch(inotifyFd, wd)\n                                    } catch (e: SyscallException) {\n                                        e.toFileSystemException(key.watchable().toString())\n                                            .printStackTrace()\n                                    }\n                                    key.setInvalid()\n                                    keys.remove(wd)\n                                }\n                                continuation.resume(Unit)\n                            } catch (e: RuntimeException) {\n                                continuation.resumeWithException(e)\n                            }\n                        }\n                    }\n                }\n            } catch (e: Exception) {\n                e.printStackTrace()\n            }\n        }\n\n        @Throws(IOException::class)\n        override fun close() {\n            try {\n                runBlocking<Unit> {\n                    suspendCoroutine { continuation ->\n                        post(false, continuation) {\n                            try {\n                                for (key in keys.values) {\n                                    val wd = key.watchDescriptor\n                                    try {\n                                        Syscall.inotify_rm_watch(inotifyFd, wd)\n                                    } catch (e: SyscallException) {\n                                        continuation.resumeWithException(\n                                            e.toFileSystemException(key.watchable().toString())\n                                        )\n                                        return@post\n                                    }\n                                    key.setInvalid()\n                                }\n                                keys.clear()\n                                try {\n                                    Syscall.close(inotifyFd)\n                                    Syscall.close(socketFds[1])\n                                    Syscall.close(socketFds[0])\n                                } catch (e: SyscallException) {\n                                    e.printStackTrace()\n                                }\n                                isClosed = true\n                                continuation.resume(Unit)\n                            } catch (e: RuntimeException) {\n                                continuation.resumeWithException(e)\n                            }\n                        }\n                    }\n                }\n            } catch (e: InterruptedException) {\n                throw InterruptedIOException().apply { initCause(e) }\n            }\n        }\n\n        private fun post(ensureOpen: Boolean, continuation: Continuation<*>, runnable: () -> Unit) {\n            synchronized(lock) {\n                runnables.offer {\n                    if (isClosed) {\n                        if (ensureOpen) {\n                            continuation.resumeWithException(ClosedWatchServiceException())\n                        }\n                        return@offer\n                    }\n                    runnable()\n                }\n            }\n            try {\n                Syscall.write(socketFds[1], ONE_BYTE)\n            } catch (e: InterruptedIOException) {\n                continuation.resumeWithException(e)\n            } catch (e: SyscallException) {\n                continuation.resumeWithException(e.toFileSystemException(null))\n            }\n        }\n\n        override fun run() {\n            val fds = arrayOf(createStructPollFd(socketFds[0]), createStructPollFd(inotifyFd))\n            try {\n                while (true) {\n                    fds[0].revents = 0\n                    fds[1].revents = 0\n                    Syscall.poll(fds, -1)\n                    if (fds[0].revents.toInt().hasBits(OsConstants.POLLIN)) {\n                        val size = try {\n                            Syscall.read(socketFds[0], ONE_BYTE)\n                        } catch (e: SyscallException) {\n                            if (e.errno != OsConstants.EAGAIN) {\n                                throw e\n                            }\n                            0\n                        }\n                        if (size > 0) {\n                            synchronized(lock) {\n                                while (true) {\n                                    val runnable = runnables.poll() ?: break\n                                    runnable()\n                                }\n                            }\n                            if (isClosed) {\n                                break\n                            }\n                        }\n                    }\n                    if (fds[1].revents.toInt().hasBits(OsConstants.POLLIN)) {\n                        val size = try {\n                            Syscall.read(inotifyFd, inotifyBuffer)\n                        } catch (e: SyscallException) {\n                            if (e.errno != OsConstants.EAGAIN) {\n                                throw e\n                            }\n                            0\n                        }\n                        if (size > 0) {\n                            if (FileSystemProviders.overflowWatchEvents) {\n                                for (key in keys.values) {\n                                    key.addEvent(StandardWatchEventKinds.OVERFLOW, null)\n                                }\n                                continue\n                            }\n                            val events = Syscall.inotify_get_events(inotifyBuffer, 0, size)\n                            for (event in events) {\n                                if (event.mask.hasBits(Constants.IN_Q_OVERFLOW)) {\n                                    for (key in keys.values) {\n                                        key.addEvent(StandardWatchEventKinds.OVERFLOW, null)\n                                    }\n                                    break\n                                }\n                                val key = keys[event.wd]!!\n                                if (event.mask.hasBits(Constants.IN_IGNORED)) {\n                                    key.setInvalid()\n                                    key.signal()\n                                    keys.remove(event.wd)\n                                } else {\n                                    val kind = maskToEventKind(event.mask)\n                                    val name = event.name\n                                        ?.let { key.watchable().fileSystem.getPath(it) }\n                                    key.addEvent(kind, name)\n                                }\n                            }\n                        }\n                    }\n                }\n            } catch (e: InterruptedIOException) {\n                e.printStackTrace()\n            } catch (e: SyscallException) {\n                e.printStackTrace()\n            }\n        }\n\n        private fun createStructPollFd(fd: FileDescriptor): StructPollfd =\n            StructPollfd().apply {\n                this.fd = fd\n                events = OsConstants.POLLIN.toShort()\n            }\n\n        private fun eventKindsToMask(kinds: Set<WatchEvent.Kind<*>>): Int {\n            var mask = 0\n            for (kind in kinds) {\n                when (kind) {\n                    StandardWatchEventKinds.ENTRY_CREATE ->\n                        mask = mask or (Constants.IN_CREATE or Constants.IN_MOVED_TO)\n                    StandardWatchEventKinds.ENTRY_DELETE ->\n                        mask = mask or (Constants.IN_DELETE_SELF or Constants.IN_DELETE\n                            or Constants.IN_MOVED_FROM)\n                    StandardWatchEventKinds.ENTRY_MODIFY ->\n                        mask = mask or (Constants.IN_MOVE_SELF or Constants.IN_MODIFY\n                            or Constants.IN_ATTRIB)\n                }\n            }\n            return mask\n        }\n\n        private fun maskToEventKind(mask: Int): WatchEvent.Kind<Path> =\n            when {\n                mask.hasBits(Constants.IN_CREATE) || mask.hasBits(Constants.IN_MOVED_TO) ->\n                    StandardWatchEventKinds.ENTRY_CREATE\n                mask.hasBits(Constants.IN_DELETE_SELF) || mask.hasBits(Constants.IN_DELETE)\n                    || mask.hasBits(Constants.IN_MOVED_FROM) ->\n                    StandardWatchEventKinds.ENTRY_DELETE\n                mask.hasBits(Constants.IN_MOVE_SELF) || mask.hasBits(Constants.IN_MODIFY)\n                    || mask.hasBits(Constants.IN_ATTRIB) -> StandardWatchEventKinds.ENTRY_MODIFY\n                else -> throw AssertionError(mask)\n            }\n\n        companion object {\n            private val ONE_BYTE = ByteArray(1)\n\n            private val id = AtomicInteger()\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/linux/OpenOptionsLinuxExtensions.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.linux\n\nimport android.system.OsConstants\nimport me.zhanghai.android.files.provider.common.OpenOptions\nimport me.zhanghai.android.files.provider.linux.syscall.Constants\n\ninternal fun OpenOptions.toLinuxFlags(): Int {\n    var flags = if (read && write) {\n        OsConstants.O_RDWR\n    } else if (write) {\n        OsConstants.O_WRONLY\n    } else {\n        OsConstants.O_RDONLY\n    }\n    if (append) {\n        flags = flags or OsConstants.O_APPEND\n    }\n    if (truncateExisting) {\n        flags = flags or OsConstants.O_TRUNC\n    }\n    if (createNew) {\n        flags = flags or OsConstants.O_CREAT or OsConstants.O_EXCL\n    } else if (create) {\n        flags = flags or OsConstants.O_CREAT\n    }\n    if (sync) {\n        flags = flags or OsConstants.O_SYNC\n    }\n    if (dsync) {\n        flags = flags or Constants.O_DSYNC\n    }\n    if (noFollowLinks || (!createNew && deleteOnClose)) {\n        flags = flags or OsConstants.O_NOFOLLOW\n    }\n    return flags\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/linux/media/MediaScanner.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.linux.media\n\nimport android.media.MediaScannerConnection\nimport android.mtp.MtpConstants\nimport android.os.Build\nimport android.os.Handler\nimport android.os.HandlerThread\nimport android.provider.MediaStore\nimport androidx.annotation.RequiresApi\nimport java8.nio.channels.FileChannel\nimport me.zhanghai.android.files.app.application\nimport me.zhanghai.android.files.app.contentResolver\nimport me.zhanghai.android.files.hiddenapi.RestrictedHiddenApi\nimport me.zhanghai.android.files.provider.common.DelegateFileChannel\nimport me.zhanghai.android.files.provider.root.isRunningAsRoot\nimport me.zhanghai.android.files.util.lazyReflectedMethod\nimport java.io.File\nimport java.io.IOException\n\n/*\n * @see com.android.internal.content.FileSystemProvider\n * @see com.android.providers.media.scan.ModernMediaScanner.java\n */\nobject MediaScanner {\n    fun scan(file: File, isDeleted: Boolean = false) {\n        if (isRunningAsRoot) {\n            return\n        }\n        MediaScannerConnection.scanFile(application, arrayOf(file.path), null) { _, _ ->\n            if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q && isDeleted) {\n                // ModernMediaScanner has a bug on Android 10 that may prevent it from removing\n                // certain files after their deletion. This has been fixed on Android 11 by\n                // https://android.googlesource.com/platform/packages/providers/MediaProvider/+/637d133d90f49dd18bda5de219184bfa9d6c2deb\n                // , but we still have to work around it for Android 10 by always trying to delete\n                // the MediaStore entry ourselves.\n                deleteMediaStoreEntryAsync(file)\n            }\n        }\n    }\n\n    @get:RequiresApi(Build.VERSION_CODES.Q)\n    private val deleteMediaStoreEntryHandler by lazy {\n        val thread = HandlerThread(\"DeleteMediaStoreEntry\")\n        thread.start()\n        Handler(thread.looper)\n    }\n\n    @RequiresApi(Build.VERSION_CODES.Q)\n    private fun deleteMediaStoreEntryAsync(file: File) {\n        deleteMediaStoreEntryHandler.post {\n            try {\n                deleteMediaStoreEntrySync(file)\n            } catch (e: Exception) {\n                e.printStackTrace()\n            }\n        }\n    }\n\n    @RestrictedHiddenApi\n    @get:RequiresApi(Build.VERSION_CODES.Q)\n    private val mediaStoreGetVolumeName by lazyReflectedMethod(\n        MediaStore::class.java, \"getVolumeName\", File::class.java\n    )\n\n    // @see com.android.providers.media.scan.ModernMediaScanner.reconcileAndClean\n    // @see https://android.googlesource.com/platform/packages/providers/MediaProvider/+/android10-release/src/com/android/providers/media/scan/ModernMediaScanner.java\n    // @see https://android.googlesource.com/platform/packages/providers/MediaProvider/+/android11-release/src/com/android/providers/media/scan/ModernMediaScanner.java\n    @RequiresApi(Build.VERSION_CODES.Q)\n    private fun deleteMediaStoreEntrySync(file: File) {\n        val file = file.canonicalFile\n        val volumeName = mediaStoreGetVolumeName.invoke(null, file) as String\n        val uri = MediaStore.Files.getContentUri(volumeName)\n            .buildUpon()\n            .appendQueryParameter(\"includePending\", \"1\")\n            .appendQueryParameter(\"deletedata\", \"false\")\n            .build()\n        @Suppress(\"DEPRECATION\")\n        val where = \"ifnull(format, ${MtpConstants.FORMAT_UNDEFINED}) != ${\n            MtpConstants.FORMAT_ABSTRACT_AV_PLAYLIST} AND ${MediaStore.Files.FileColumns.DATA} = ?\"\n        val selectionArgs = arrayOf(file.absolutePath)\n        contentResolver.delete(uri, where, selectionArgs)\n    }\n\n    fun createScanOnCloseFileChannel(fileChannel: FileChannel, file: File): FileChannel =\n        if (isRunningAsRoot) {\n            fileChannel\n        } else {\n            object : DelegateFileChannel(fileChannel) {\n                @Throws(IOException::class)\n                override fun implCloseChannel() {\n                    super.implCloseChannel()\n\n                    scan(file)\n                }\n            }\n        }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/linux/syscall/Constants.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.linux.syscall\n\nimport android.os.Build\nimport android.system.OsConstants\n\nobject Constants {\n    // 0x0000125D\n    const val BLKROSET = (0x12 shl 8) or 93\n\n    const val IN_ACCESS = 0x00000001\n    const val IN_MODIFY = 0x00000002\n    const val IN_ATTRIB = 0x00000004\n    const val IN_CLOSE_WRITE = 0x00000008\n    const val IN_CLOSE_NOWRITE = 0x00000010\n    const val IN_OPEN = 0x00000020\n    const val IN_MOVED_FROM = 0x00000040\n    const val IN_MOVED_TO = 0x00000080\n    const val IN_CREATE = 0x00000100\n    const val IN_DELETE = 0x00000200\n    const val IN_DELETE_SELF = 0x00000400\n    const val IN_MOVE_SELF = 0x00000800\n    const val IN_UNMOUNT = 0x00002000\n    const val IN_Q_OVERFLOW = 0x00004000\n    const val IN_IGNORED = 0x00008000\n    const val IN_CLOSE = IN_CLOSE_WRITE or IN_CLOSE_NOWRITE\n    const val IN_MOVE = IN_MOVED_FROM or IN_MOVED_TO\n    const val IN_ONLYDIR = 0x01000000\n    const val IN_DONT_FOLLOW = 0x02000000\n    const val IN_EXCL_UNLINK = 0x04000000\n    const val IN_MASK_ADD = 0x20000000\n    const val IN_ISDIR = 0x40000000\n    const val IN_ONESHOT = 0x80000000.toInt()\n    const val IN_ALL_EVENTS = (IN_ACCESS or IN_MODIFY or IN_ATTRIB or IN_CLOSE_WRITE\n        or IN_CLOSE_NOWRITE or IN_OPEN or IN_MOVED_FROM or IN_MOVED_TO or IN_DELETE or IN_CREATE\n        or IN_DELETE_SELF or IN_MOVE_SELF)\n\n    const val MS_RDONLY = 1L\n    const val MS_NOSUID = 2L\n    const val MS_NODEV = 4L\n    const val MS_NOEXEC = 8L\n    const val MS_SYNCHRONOUS = 16L\n    const val MS_REMOUNT = 32L\n    const val MS_MANDLOCK = 64L\n    const val MS_DIRSYNC = 128L\n    const val MS_NOATIME = 1024L\n    const val MS_NODIRATIME = 2048L\n    const val MS_BIND = 4096L\n    const val MS_MOVE = 8192L\n    const val MS_REC = 16384L\n    const val MS_VERBOSE = 32768L\n    const val MS_SILENT = 32768L\n    const val MS_POSIXACL = 1L shl 16\n    const val MS_UNBINDABLE = 1L shl 17\n    const val MS_PRIVATE = 1L shl 18\n    const val MS_SLAVE = 1L shl 19\n    const val MS_SHARED = 1L shl 20\n    const val MS_RELATIME = 1L shl 21\n    const val MS_KERNMOUNT = 1L shl 22\n    const val MS_I_VERSION = 1L shl 23\n    const val MS_STRICTATIME = 1L shl 24\n    const val MS_LAZYTIME = 1L shl 25\n    const val MS_SUBMOUNT = 1L shl 26\n    const val MS_NOREMOTELOCK = 1L shl 27\n    const val MS_NOSEC = 1L shl 28\n    const val MS_BORN = 1L shl 29\n    const val MS_ACTIVE = 1L shl 30\n    const val MS_NOUSER = 1L shl 31\n    const val MS_RMT_MASK = (MS_RDONLY or MS_SYNCHRONOUS or MS_MANDLOCK or MS_I_VERSION\n        or MS_LAZYTIME)\n    const val MS_MGC_VAL = 0xC0ED0000L\n    const val MS_MGC_MSK = 0xffff0000L\n\n    val O_DSYNC =\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) OsConstants.O_DSYNC else 0x1000\n\n    /* @see android.os.SELinux#SELINUX_ANDROID_RESTORECON_NOCHANGE */\n    const val SELINUX_ANDROID_RESTORECON_NOCHANGE = 1\n    /* @see android.os.SELinux#SELINUX_ANDROID_RESTORECON_VERBOSE */\n    const val SELINUX_ANDROID_RESTORECON_VERBOSE = 2\n    /* @see android.os.SELinux#SELINUX_ANDROID_RESTORECON_RECURSE */\n    const val SELINUX_ANDROID_RESTORECON_RECURSE = 4\n    /* @see android.os.SELinux#SELINUX_ANDROID_RESTORECON_FORCE */\n    const val SELINUX_ANDROID_RESTORECON_FORCE = 8\n    /* @see android.os.SELinux#SELINUX_ANDROID_RESTORECON_DATADATA */\n    const val SELINUX_ANDROID_RESTORECON_DATADATA = 16\n\n    const val UTIME_NOW = (1L shl 30) - 1L\n    const val UTIME_OMIT = (1L shl 30) - 2L\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/linux/syscall/Int32Ref.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.linux.syscall\n\nclass Int32Ref(var value: Int)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/linux/syscall/StructDirent.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.linux.syscall\n\nimport me.zhanghai.android.files.provider.common.ByteString\n\nclass StructDirent(\n    val d_ino: Long, /*ino_t*/\n    val d_off: Long, /*off64_t*/\n    val d_reclen: Int, /*unsigned short*/\n    val d_type: Int, /*unsigned char*/\n    val d_name: ByteString\n)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/linux/syscall/StructGroup.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.linux.syscall\n\nimport me.zhanghai.android.files.provider.common.ByteString\n\nclass StructGroup(\n    val gr_name: ByteString?,\n    val gr_passwd: ByteString?,\n    val gr_gid: Int,\n    val gr_mem: Array<ByteString>?\n)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/linux/syscall/StructInotifyEvent.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.linux.syscall\n\nimport me.zhanghai.android.files.provider.common.ByteString\n\nclass StructInotifyEvent(\n    val wd: Int,\n    val mask: Int, /* uint32_t */\n    val cookie: Int, /* uint32_t */\n    val name: ByteString?\n)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/linux/syscall/StructMntent.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.linux.syscall\n\nimport android.os.Parcelable\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.provider.common.ByteString\n\n@Parcelize\nclass StructMntent(\n    val mnt_fsname: ByteString,\n    val mnt_dir: ByteString,\n    val mnt_type: ByteString,\n    val mnt_opts: ByteString,\n    val mnt_freq: Int,\n    val mnt_passno: Int\n) : Parcelable\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/linux/syscall/StructPasswd.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.linux.syscall\n\nimport me.zhanghai.android.files.provider.common.ByteString\n\nclass StructPasswd(\n    val pw_name: ByteString?,\n    val pw_uid: Int,\n    val pw_gid: Int,\n    val pw_gecos: ByteString?,\n    val pw_dir: ByteString?,\n    val pw_shell: ByteString?\n)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/linux/syscall/StructStat.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.linux.syscall\n\n/**\n * @see android.system.StructStat\n */\nclass StructStat(\n    val st_dev: Long, /*dev_t*/\n    val st_ino: Long, /*ino_t*/\n    val st_mode: Int, /*mode_t*/\n    val st_nlink: Long, /*nlink_t*/\n    val st_uid: Int, /*uid_t*/\n    val st_gid: Int, /*gid_t*/\n    val st_rdev: Long, /*dev_t*/\n    val st_size: Long, /*off_t*/\n    val st_blksize: Long, /*blksize_t*/\n    val st_blocks: Long, /*blkcnt_t*/\n    val st_atim: StructTimespec,\n    val st_mtim: StructTimespec,\n    val st_ctim: StructTimespec\n) {\n    val st_atime: Long /*time_t*/\n        get() = st_atim.tv_sec\n    val st_mtime: Long /*time_t*/\n        get() = st_mtim.tv_sec\n    val st_ctime: Long /*time_t*/\n        get() = st_ctim.tv_sec\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/linux/syscall/StructTimespec.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.linux.syscall\n\nimport android.os.Parcelable\nimport kotlinx.parcelize.Parcelize\n\n/**\n * @see android.system.StructTimespec\n */\n@Parcelize\nclass StructTimespec(\n    val tv_sec: Long, /*time_t*/\n    val tv_nsec: Long\n) : Parcelable\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/linux/syscall/Syscall.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.linux.syscall\n\nimport android.os.Build\nimport android.system.ErrnoException\nimport android.system.Int64Ref\nimport android.system.Os\nimport android.system.OsConstants\nimport android.system.StructPollfd\nimport android.system.StructStatVfs\nimport androidx.annotation.Size\nimport me.zhanghai.android.files.compat.SELinuxCompat\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.moveToByteString\nimport me.zhanghai.android.libselinux.SeLinux\nimport java.io.FileDescriptor\nimport java.io.InterruptedIOException\n\nobject Syscall {\n    init {\n        System.loadLibrary(\"syscall\")\n    }\n\n    @Throws(SyscallException::class)\n    external fun access(path: ByteString, mode: Int): Boolean\n\n    @Throws(SyscallException::class)\n    external fun chmod(path: ByteString, mode: Int)\n\n    @Throws(SyscallException::class)\n    external fun chown(path: ByteString, uid: Int, gid: Int)\n\n    @Throws(SyscallException::class)\n    fun close(fd: FileDescriptor) {\n        try {\n            Os.close(fd)\n        } catch (e: ErrnoException) {\n            throw SyscallException(e)\n        }\n    }\n\n    @Throws(SyscallException::class)\n    external fun closedir(dir: Long)\n\n    @kotlin.jvm.JvmStatic\n    @Throws(SyscallException::class)\n    external fun endgrent()\n\n    @Throws(SyscallException::class)\n    external fun endmntent(file: Long)\n\n    @kotlin.jvm.JvmStatic\n    @Throws(SyscallException::class)\n    external fun endpwent()\n\n    private external fun errno(): Int\n\n    @Throws(SyscallException::class)\n    fun fcntl(fd: FileDescriptor, cmd: Int): Int = fcntl_void(fd, cmd)\n\n    @Throws(SyscallException::class)\n    fun fcntl(fd: FileDescriptor, cmd: Int, arg: Int): Int = fcntl_int(fd, cmd, arg)\n\n    @Throws(SyscallException::class)\n    private external fun fcntl_int(fd: FileDescriptor, cmd: Int, arg: Int): Int\n\n    @Throws(SyscallException::class)\n    private external fun fcntl_void(fd: FileDescriptor, cmd: Int): Int\n\n    @Throws(SyscallException::class)\n    fun getfilecon(path: ByteString): ByteString =\n        try {\n            SeLinux.getfilecon(path.borrowBytes()).moveToByteString()\n        } catch (e: ErrnoException) {\n            throw SyscallException(e)\n        }\n\n    @kotlin.jvm.JvmStatic\n    @Throws(SyscallException::class)\n    external fun getgrent(): StructGroup?\n\n    @Throws(SyscallException::class)\n    external fun getgrgid(gid: Int): StructGroup?\n\n    @Throws(SyscallException::class)\n    external fun getgrnam(name: ByteString): StructGroup?\n\n    @Throws(SyscallException::class)\n    external fun getmntent(file: Long): StructMntent?\n\n    @kotlin.jvm.JvmStatic\n    @Throws(SyscallException::class)\n    external fun getpwent(): StructPasswd?\n\n    @Throws(SyscallException::class)\n    external fun getpwnam(name: ByteString): StructPasswd?\n\n    @Throws(SyscallException::class)\n    external fun getpwuid(uid: Int): StructPasswd?\n\n    external fun hasmntopt(mntent: StructMntent, option: ByteString): Boolean\n\n    @Throws(SyscallException::class)\n    external fun inotify_add_watch(fd: FileDescriptor, path: ByteString, mask: Int): Int\n\n    @Throws(SyscallException::class)\n    external fun inotify_init1(flags: Int): FileDescriptor\n\n    @Throws(SyscallException::class)\n    external fun inotify_get_events(\n        buffer: ByteArray,\n        offset: Int,\n        length: Int\n    ): Array<StructInotifyEvent>\n\n    @Throws(SyscallException::class)\n    external fun inotify_rm_watch(fd: FileDescriptor, wd: Int)\n\n    @Throws(SyscallException::class)\n    external fun ioctl_int(fd: FileDescriptor, request: Int, argument: Int32Ref?): Int\n\n    fun is_selinux_enabled(): Boolean = SeLinux.is_selinux_enabled()\n\n    @Throws(SyscallException::class)\n    external fun lchown(path: ByteString, uid: Int, gid: Int)\n\n    @Throws(SyscallException::class)\n    fun lgetfilecon(path: ByteString): ByteString =\n        try {\n            SeLinux.lgetfilecon(path.borrowBytes()).moveToByteString()\n        } catch (e: ErrnoException) {\n            throw SyscallException(e)\n        }\n\n    @Throws(SyscallException::class)\n    fun lsetfilecon(path: ByteString, context: ByteString) {\n        try {\n            SeLinux.lsetfilecon(path.borrowBytes(), context.borrowBytes())\n        } catch (e: ErrnoException) {\n            throw SyscallException(e)\n        }\n    }\n\n    @Throws(SyscallException::class)\n    external fun lgetxattr(path: ByteString, name: ByteString): ByteArray\n\n    @Throws(SyscallException::class)\n    external fun link(oldPath: ByteString, newPath: ByteString)\n\n    @Throws(SyscallException::class)\n    external fun llistxattr(path: ByteString): Array<ByteString>\n\n    @Throws(SyscallException::class)\n    external fun lsetxattr(path: ByteString, name: ByteString, value: ByteArray, flags: Int)\n\n    @Throws(SyscallException::class)\n    external fun lstat(path: ByteString): StructStat\n\n    @Throws(SyscallException::class)\n    external fun lutimens(path: ByteString, @Size(2) times: Array<StructTimespec>)\n\n    @Throws(SyscallException::class)\n    external fun mkdir(path: ByteString, mode: Int)\n\n    @Throws(SyscallException::class)\n    external fun mount(\n        source: ByteString?,\n        target: ByteString,\n        fileSystemType: ByteString?,\n        mountFlags: Long,\n        data: ByteArray?\n    ): Int\n\n    @Throws(SyscallException::class)\n    external fun open(path: ByteString, flags: Int, mode: Int): FileDescriptor\n\n    @Throws(SyscallException::class)\n    external fun opendir(path: ByteString): Long\n\n    @Throws(SyscallException::class)\n    fun poll(fds: Array<StructPollfd>, timeout: Int): Int =\n        try {\n            Os_poll(fds, timeout)\n        } catch (e: ErrnoException) {\n            throw SyscallException(e)\n        }\n\n    @Throws(ErrnoException::class)\n    private fun Os_poll(fds: Array<StructPollfd>, timeout: Int): Int {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M || timeout < 0) {\n            return Os.poll(fds, timeout)\n        } else {\n            val timeoutTime = System.currentTimeMillis() + timeout\n            var timeout = timeout\n            while (true) {\n                return try {\n                    Os.poll(fds, timeout)\n                } catch (e: ErrnoException) {\n                    if (e.errno == OsConstants.EINTR) {\n                        val newTimeout = timeoutTime - System.currentTimeMillis()\n                        if (newTimeout <= 0) {\n                            return 0\n                        }\n                        timeout = newTimeout.toInt()\n                        continue\n                    }\n                    throw e\n                }\n            }\n        }\n    }\n\n    @Throws(InterruptedIOException::class, SyscallException::class)\n    fun read(\n        fd: FileDescriptor,\n        buffer: ByteArray,\n        offset: Int = 0,\n        length: Int = buffer.size\n    ): Int =\n        try {\n            Os.read(fd, buffer, offset, length)\n        } catch (e: ErrnoException) {\n            throw SyscallException(e)\n        }\n\n    @Throws(SyscallException::class)\n    external fun readdir(dir: Long): StructDirent?\n\n    @Throws(SyscallException::class)\n    external fun readlink(path: ByteString): ByteString\n\n    @Throws(SyscallException::class)\n    external fun realpath(path: ByteString): ByteString\n\n    @Throws(SyscallException::class)\n    external fun remove(path: ByteString)\n\n    @Throws(SyscallException::class)\n    external fun rename(oldPath: ByteString, newPath: ByteString)\n\n    @Throws(SyscallException::class)\n    fun security_getenforce(): Boolean =\n        try {\n            SeLinux.security_getenforce()\n        } catch (e: ErrnoException) {\n            throw SyscallException(e)\n        }\n\n    @Throws(SyscallException::class)\n    fun selinux_android_restorecon(path: ByteString, flags: Int) {\n        // FIXME: Platform SELinux class cannot accept byte array, so we have to use a string.\n        val successful = SELinuxCompat.native_restorecon(path.toString(), flags)\n        if (!successful) {\n            var errno = errno()\n            if (errno == 0) {\n                // Just set some generic error.\n                errno = OsConstants.EIO\n            }\n            throw SyscallException(\"selinux_android_restorecon\", errno)\n        }\n    }\n\n    @Throws(SyscallException::class)\n    external fun sendfile(\n        outFd: FileDescriptor,\n        inFd: FileDescriptor,\n        offset: Int64Ref?,\n        count: Long\n    ): Long\n\n    @Throws(SyscallException::class)\n    fun setfilecon(path: ByteString, context: ByteString) {\n        try {\n            SeLinux.setfilecon(path.borrowBytes(), context.borrowBytes())\n        } catch (e: ErrnoException) {\n            throw SyscallException(e)\n        }\n    }\n\n    @kotlin.jvm.JvmStatic\n    @Throws(SyscallException::class)\n    external fun setgrent()\n\n    @Throws(SyscallException::class)\n    external fun setmntent(path: ByteString, mode: ByteString): Long\n\n    @kotlin.jvm.JvmStatic\n    @Throws(SyscallException::class)\n    external fun setpwent()\n\n    @Size(2)\n    @Throws(SyscallException::class)\n    fun socketpair(domain: Int, type: Int, protocol: Int): Array<FileDescriptor> {\n        val fds = arrayOf(FileDescriptor(), FileDescriptor())\n        try {\n            Os.socketpair(domain, type, protocol, fds[0], fds[1])\n        } catch (e: ErrnoException) {\n            throw SyscallException(e)\n        }\n        return fds\n    }\n\n    @Throws(SyscallException::class)\n    external fun stat(path: ByteString): StructStat\n\n    @Throws(SyscallException::class)\n    external fun statvfs(path: ByteString): StructStatVfs\n\n    fun strerror(errno: Int): String = Os.strerror(errno)\n\n    @Throws(SyscallException::class)\n    external fun symlink(target: ByteString, linkPath: ByteString)\n\n    @Throws(SyscallException::class)\n    external fun utimens(path: ByteString, @Size(2) times: Array<StructTimespec>)\n\n    @Throws(InterruptedIOException::class, SyscallException::class)\n    fun write(\n        fd: FileDescriptor,\n        buffer: ByteArray,\n        offset: Int = 0,\n        length: Int = buffer.size\n    ): Int =\n        try {\n            Os.write(fd, buffer, offset, length)\n        } catch (e: ErrnoException) {\n            throw SyscallException(e)\n        }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/linux/syscall/SyscallException.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.linux.syscall\n\nimport android.system.ErrnoException\nimport android.system.OsConstants\nimport java8.nio.file.AccessDeniedException\nimport java8.nio.file.AtomicMoveNotSupportedException\nimport java8.nio.file.DirectoryNotEmptyException\nimport java8.nio.file.FileAlreadyExistsException\nimport java8.nio.file.FileSystemException\nimport java8.nio.file.FileSystemLoopException\nimport java8.nio.file.NoSuchFileException\nimport java8.nio.file.NotDirectoryException\nimport java8.nio.file.NotLinkException\nimport me.zhanghai.android.files.compat.functionNameCompat\nimport me.zhanghai.android.files.provider.common.InvalidFileNameException\nimport me.zhanghai.android.files.provider.common.IsDirectoryException\nimport me.zhanghai.android.files.provider.common.ReadOnlyFileSystemException\n\nclass SyscallException @JvmOverloads constructor(\n    val functionName: String,\n    val errno: Int,\n    cause: Throwable? = null\n) : Exception(perror(errno, functionName), cause) {\n\n    constructor(errnoException: ErrnoException) : this(\n        errnoException.functionNameCompat, errnoException.errno, errnoException\n    )\n\n    @Throws(AtomicMoveNotSupportedException::class)\n    fun maybeThrowAtomicMoveNotSupportedException(file: String?, other: String?) {\n        if (errno == OsConstants.EXDEV) {\n            throw AtomicMoveNotSupportedException(file, other, message)\n                .apply { initCause(this@SyscallException) }\n        }\n    }\n\n    @Throws(InvalidFileNameException::class)\n    fun maybeThrowInvalidFileNameException(file: String?) {\n        if (errno == OsConstants.EINVAL) {\n            throw InvalidFileNameException(file, null, message)\n                .apply { initCause(this@SyscallException) }\n        }\n    }\n\n    @Throws(NotLinkException::class)\n    fun maybeThrowNotLinkException(file: String?) {\n        if (errno == OsConstants.EINVAL) {\n            throw InvalidFileNameException(file, null, message)\n                .apply { initCause(this@SyscallException) }\n        }\n    }\n\n    fun toFileSystemException(file: String?, other: String? = null): FileSystemException =\n        when (errno) {\n            OsConstants.EACCES, OsConstants.EPERM -> AccessDeniedException(file, other, message)\n            OsConstants.EEXIST -> FileAlreadyExistsException(file, other, message)\n            OsConstants.EISDIR -> IsDirectoryException(file, other, message)\n            OsConstants.ELOOP -> FileSystemLoopException(file)\n            OsConstants.ENOTDIR -> NotDirectoryException(file)\n            OsConstants.ENOTEMPTY -> DirectoryNotEmptyException(file)\n            OsConstants.ENOENT -> NoSuchFileException(file, other, message)\n            OsConstants.EROFS -> ReadOnlyFileSystemException(file, other, message)\n            else -> FileSystemException(file, other, message)\n        }.apply { initCause(this@SyscallException) }\n\n    companion object {\n        private fun perror(errno: Int, functionName: String): String =\n            \"$functionName: ${Syscall.strerror(errno)}\"\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/remote/FilesAcceptAllFilter.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.remote\n\nimport java8.nio.channels.SeekableByteChannel\nimport java8.nio.file.AccessMode\nimport java8.nio.file.CopyOption\nimport java8.nio.file.DirectoryStream\nimport java8.nio.file.FileStore\nimport java8.nio.file.FileSystem\nimport java8.nio.file.Files\nimport java8.nio.file.LinkOption\nimport java8.nio.file.OpenOption\nimport java8.nio.file.Path\nimport java8.nio.file.PathMatcher\nimport java8.nio.file.WatchEvent\nimport java8.nio.file.WatchKey\nimport java8.nio.file.WatchService\nimport java8.nio.file.attribute.BasicFileAttributes\nimport java8.nio.file.attribute.FileAttribute\nimport java8.nio.file.attribute.FileAttributeView\nimport java8.nio.file.attribute.UserPrincipalLookupService\nimport java8.nio.file.spi.FileSystemProvider\nimport java.io.File\nimport java.net.URI\n\nval filesAcceptAllFilter: DirectoryStream.Filter<in Path> = run {\n    var capturedFilter: DirectoryStream.Filter<in Path>? = null\n    val path = object : StubPath() {\n        override fun getFileSystem(): FileSystem =\n            object : StubFileSystem() {\n                override fun provider(): FileSystemProvider =\n                    object : StubFileSystemProvider() {\n                        override fun newDirectoryStream(\n                            dir: Path,\n                            filter: DirectoryStream.Filter<in Path>\n                        ): DirectoryStream<Path> {\n                            capturedFilter = filter\n                            return StubDirectoryStream()\n                        }\n                    }\n            }\n    }\n    Files.newDirectoryStream(path)\n    capturedFilter!!\n}\n\nprivate open class StubPath : Path {\n    override fun toFile(): File = throw AssertionError()\n\n    override fun isAbsolute(): Boolean = throw AssertionError()\n\n    override fun getFileName(): Path = throw AssertionError()\n\n    override fun getName(index: Int): Path = throw AssertionError()\n\n    override fun subpath(beginIndex: Int, endIndex: Int): Path = throw AssertionError()\n\n    override fun endsWith(other: Path): Boolean = throw AssertionError()\n\n    override fun endsWith(other: String): Boolean = throw AssertionError()\n\n    override fun register(\n        watcher: WatchService,\n        events: Array<out WatchEvent.Kind<*>>,\n        vararg modifiers: WatchEvent.Modifier\n    ): WatchKey = throw AssertionError()\n\n    override fun register(watcher: WatchService, vararg events: WatchEvent.Kind<*>): WatchKey =\n        throw AssertionError()\n\n    override fun iterator(): MutableIterator<Path> = throw AssertionError()\n\n    override fun relativize(other: Path): Path = throw AssertionError()\n\n    override fun toUri(): URI = throw AssertionError()\n\n    override fun toRealPath(vararg options: LinkOption): Path = throw AssertionError()\n\n    override fun normalize(): Path = throw AssertionError()\n\n    override fun getParent(): Path = throw AssertionError()\n\n    override fun compareTo(other: Path): Int = throw AssertionError()\n\n    override fun getNameCount(): Int = throw AssertionError()\n\n    override fun startsWith(other: Path): Boolean = throw AssertionError()\n\n    override fun startsWith(other: String): Boolean = throw AssertionError()\n\n    override fun getFileSystem(): FileSystem = throw AssertionError()\n\n    override fun getRoot(): Path = throw AssertionError()\n\n    override fun resolveSibling(other: Path): Path = throw AssertionError()\n\n    override fun resolveSibling(other: String): Path = throw AssertionError()\n\n    override fun resolve(other: Path): Path = throw AssertionError()\n\n    override fun resolve(other: String): Path = throw AssertionError()\n\n    override fun toAbsolutePath(): Path = throw AssertionError()\n}\n\nprivate open class StubFileSystem : FileSystem() {\n    override fun getSeparator(): String = throw AssertionError()\n\n    override fun newWatchService(): WatchService = throw AssertionError()\n\n    override fun supportedFileAttributeViews(): MutableSet<String> = throw AssertionError()\n\n    override fun isReadOnly(): Boolean = throw AssertionError()\n\n    override fun getFileStores(): MutableIterable<FileStore> = throw AssertionError()\n\n    override fun getPath(first: String, vararg more: String): Path = throw AssertionError()\n\n    override fun provider(): FileSystemProvider = throw AssertionError()\n\n    override fun isOpen(): Boolean = throw AssertionError()\n\n    override fun getUserPrincipalLookupService(): UserPrincipalLookupService =\n        throw AssertionError()\n\n    override fun close() = throw AssertionError()\n\n    override fun getPathMatcher(syntaxAndPattern: String): PathMatcher = throw AssertionError()\n\n    override fun getRootDirectories(): MutableIterable<Path> = throw AssertionError()\n}\n\nprivate open class StubFileSystemProvider : FileSystemProvider() {\n    override fun checkAccess(path: Path, vararg modes: AccessMode) = throw AssertionError()\n\n    override fun copy(source: Path, target: Path, vararg options: CopyOption) =\n        throw AssertionError()\n\n    override fun <V : FileAttributeView> getFileAttributeView(\n        path: Path,\n        type: Class<V>,\n        vararg options: LinkOption\n    ): V = throw AssertionError()\n\n    override fun isSameFile(path: Path, path2: Path): Boolean = throw AssertionError()\n\n    override fun newFileSystem(uri: URI, env: MutableMap<String, *>): FileSystem =\n        throw AssertionError()\n\n    override fun getScheme(): String = throw AssertionError()\n\n    override fun isHidden(path: Path): Boolean = throw AssertionError()\n\n    override fun newDirectoryStream(\n        dir: Path,\n        filter: DirectoryStream.Filter<in Path>\n    ): DirectoryStream<Path> = throw AssertionError()\n\n    override fun newByteChannel(\n        path: Path,\n        options: MutableSet<out OpenOption>,\n        vararg attrs: FileAttribute<*>\n    ): SeekableByteChannel = throw AssertionError()\n\n    override fun delete(path: Path) = throw AssertionError()\n\n    override fun <A : BasicFileAttributes> readAttributes(\n        path: Path,\n        type: Class<A>,\n        vararg options: LinkOption\n    ): A = throw AssertionError()\n\n    override fun readAttributes(\n        path: Path,\n        attributes: String,\n        vararg options: LinkOption\n    ): MutableMap<String, Any> = throw AssertionError()\n\n    override fun getFileSystem(uri: URI): FileSystem = throw AssertionError()\n\n    override fun getPath(uri: URI): Path = throw AssertionError()\n\n    override fun getFileStore(path: Path): FileStore = throw AssertionError()\n\n    override fun setAttribute(\n        path: Path,\n        attribute: String,\n        value: Any,\n        vararg options: LinkOption\n    ) = throw AssertionError()\n\n    override fun move(source: Path, target: Path, vararg options: CopyOption) =\n        throw AssertionError()\n\n    override fun createDirectory(dir: Path, vararg attrs: FileAttribute<*>) =\n        throw AssertionError()\n}\n\nprivate open class StubDirectoryStream<T> : DirectoryStream<T> {\n    override fun iterator(): MutableIterator<T> = throw AssertionError()\n\n    override fun close() = throw AssertionError()\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/remote/IInterfaceExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.remote\n\nimport android.os.Binder\nimport android.os.IInterface\nimport android.os.RemoteException\nimport java.io.IOException\nimport kotlin.contracts.ExperimentalContracts\nimport kotlin.contracts.InvocationKind\nimport kotlin.contracts.contract\n\n@OptIn(ExperimentalContracts::class)\n@Throws(IOException::class)\nfun <T : IInterface, R> T.call(block: T.(ParcelableException) -> R): R {\n    contract {\n        callsInPlace(block, InvocationKind.EXACTLY_ONCE)\n    }\n    val exception = ParcelableException()\n    return try {\n        block(exception)\n    } catch (e: RemoteException) {\n        throw RemoteFileSystemException(e)\n    }.also { exception.value?.let { throw it } }\n}\n\n@OptIn(ExperimentalContracts::class)\nfun <T, R> T.tryRun(exception: ParcelableException, block: T.() -> R): R?\n    where T : IInterface, T : Binder {\n    contract {\n        callsInPlace(block, InvocationKind.EXACTLY_ONCE)\n    }\n    return try {\n        block()\n    } catch (e: IOException) {\n        exception.value = e\n        null\n    } catch (e: RuntimeException) {\n        exception.value = e\n        null\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/remote/ParcelableCopyOptions.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.remote\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport java8.nio.file.CopyOption\nimport java8.nio.file.LinkOption\nimport me.zhanghai.android.files.compat.readSerializableCompat\nimport java.io.Serializable\n\nclass ParcelableCopyOptions(val value: Array<out CopyOption>) : Parcelable {\n    private constructor(source: Parcel) : this(\n        Array(source.readInt()) {\n            when (val type = source.readInt()) {\n                0 ->\n                    source.readParcelable<Parcelable>(CopyOption::class.java.classLoader)!!\n                        as CopyOption\n                1 -> source.readSerializableCompat()!!\n                else -> throw AssertionError(type)\n            }\n        }\n    )\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        dest.writeInt(value.size)\n        for (option in value) {\n            when (option) {\n                is Parcelable -> {\n                    dest.writeInt(0)\n                    dest.writeParcelable(option as Parcelable, flags)\n                }\n                is Serializable -> {\n                    dest.writeInt(1)\n                    dest.writeSerializable(option as Serializable)\n                }\n                else -> throw UnsupportedOperationException(option.toString())\n            }\n        }\n    }\n\n    companion object {\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<ParcelableCopyOptions> {\n            override fun createFromParcel(source: Parcel): ParcelableCopyOptions =\n                ParcelableCopyOptions(source)\n\n            override fun newArray(size: Int): Array<ParcelableCopyOptions?> = arrayOfNulls(size)\n        }\n    }\n}\n\nfun Array<out CopyOption>.toParcelable(): ParcelableCopyOptions = ParcelableCopyOptions(this)\n\nfun Array<out LinkOption>.toParcelable() = (this as Serializable).toParcelable()\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/remote/ParcelableDirectoryStream.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.remote\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport java.io.IOException\nimport java8.nio.file.DirectoryIteratorException\nimport java8.nio.file.DirectoryStream\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.provider.common.PathListDirectoryStream\nimport me.zhanghai.android.files.util.ParcelSlicedList\nimport me.zhanghai.android.files.util.readParcelable\n\nclass ParcelableDirectoryStream : Parcelable {\n    private val paths: List<Path>\n\n    val value: DirectoryStream<Path>\n        get() = PathListDirectoryStream(paths) { true }\n\n    @Throws(IOException::class)\n    constructor(value: DirectoryStream<Path>) {\n        paths = try {\n            value.toList()\n        } catch (e: DirectoryIteratorException) {\n            throw e.cause!!\n        }\n    }\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        @Suppress(\"UNCHECKED_CAST\")\n        dest.writeParcelable(ParcelSlicedList(paths as List<Parcelable>), flags)\n    }\n\n    private constructor(source: Parcel) {\n        @Suppress(\"UNCHECKED_CAST\")\n        paths = source.readParcelable<ParcelSlicedList<Parcelable>>()!!.list as List<Path>\n    }\n\n    companion object {\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<ParcelableDirectoryStream> {\n            override fun createFromParcel(source: Parcel): ParcelableDirectoryStream =\n                ParcelableDirectoryStream(source)\n\n            override fun newArray(size: Int): Array<ParcelableDirectoryStream?> = arrayOfNulls(size)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/remote/ParcelableException.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.remote\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport me.zhanghai.android.files.compat.readSerializableCompat\nimport java.io.IOException\n\nclass ParcelableException() : Parcelable {\n    var value: Exception? = null\n        set(value) {\n            check((value == null) or (value is IOException) or (value is RuntimeException)) {\n                \"$value is not an IOException or a RuntimeException\"\n            }\n            check(field == null) { \"Exception is already set\" }\n            field = value\n        }\n\n    private constructor(source: Parcel) : this() {\n        value = source.readSerializableCompat()\n    }\n\n    override fun describeContents(): Int = 0\n\n    fun readFromParcel(source: Parcel) {\n        value = source.readSerializableCompat()\n    }\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        dest.writeSerializable(value)\n    }\n\n    companion object {\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<ParcelableException> {\n            override fun createFromParcel(source: Parcel): ParcelableException =\n                ParcelableException(source)\n\n            override fun newArray(size: Int): Array<ParcelableException?> = arrayOfNulls(size)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/remote/ParcelableFileAttributes.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.remote\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport java8.nio.file.attribute.FileAttribute\nimport me.zhanghai.android.files.compat.readSerializableCompat\nimport me.zhanghai.android.files.provider.common.PosixFileMode\nimport me.zhanghai.android.files.provider.common.PosixFileModeBit\nimport me.zhanghai.android.files.provider.common.toAttribute\nimport me.zhanghai.android.files.util.toEnumSet\nimport java.io.Serializable\n\nclass ParcelableFileAttributes(val value: Array<out FileAttribute<*>>) : Parcelable {\n    private constructor(source: Parcel) : this(\n        source.readSerializableCompat<List<Set<PosixFileModeBit>>>()!!\n            .map { it.toAttribute() }\n            .toTypedArray()\n    )\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        //noinspection unchecked\n        val modes = value.map {\n            when (val mode = PosixFileMode.fromAttribute(it)) {\n                is Serializable -> mode\n                else -> mode.toEnumSet()\n            }\n        }\n        dest.writeSerializable(modes as Serializable)\n    }\n\n    companion object {\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<ParcelableFileAttributes> {\n            override fun createFromParcel(source: Parcel): ParcelableFileAttributes =\n                ParcelableFileAttributes(source)\n\n            override fun newArray(size: Int): Array<ParcelableFileAttributes?> = arrayOfNulls(size)\n        }\n    }\n}\n\nfun Array<out FileAttribute<*>>.toParcelable(): ParcelableFileAttributes =\n    ParcelableFileAttributes(this)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/remote/ParcelableObject.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.remote\n\nimport android.os.Parcel\nimport android.os.Parcelable\n\nclass ParcelableObject(val value: Any) : Parcelable {\n    @Suppress(\"UNCHECKED_CAST\")\n    fun <T : Any> value(): T = value as T\n\n    private constructor(source: Parcel) : this(\n        source.readParcelable<Parcelable>(ParcelableObject::class.java.classLoader) as Any\n    )\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        dest.writeParcelable(value as Parcelable, flags)\n    }\n\n    companion object {\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<ParcelableObject> {\n            override fun createFromParcel(source: Parcel): ParcelableObject =\n                ParcelableObject(source)\n\n            override fun newArray(size: Int): Array<ParcelableObject?> = arrayOfNulls(size)\n        }\n    }\n}\n\nfun Any.toParcelable(): ParcelableObject = ParcelableObject(this)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/remote/ParcelablePathListConsumer.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.remote\n\nimport android.os.Bundle\nimport android.os.Parcel\nimport android.os.Parcelable\nimport java8.nio.file.Path\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.ParcelableListParceler\nimport me.zhanghai.android.files.util.RemoteCallback\nimport me.zhanghai.android.files.util.getArgs\nimport me.zhanghai.android.files.util.putArgs\nimport me.zhanghai.android.files.util.readParcelable\n\nclass ParcelablePathListConsumer(val value: (List<Path>) -> Unit) : Parcelable {\n    private constructor(source: Parcel) : this(\n        source.readParcelable<RemoteCallback>()!!.let<RemoteCallback, (List<Path>) -> Unit> {\n            { paths -> it.sendResult(Bundle().putArgs(ListenerArgs(paths))) }\n        }\n    )\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        dest.writeParcelable(RemoteCallback { value(it.getArgs<ListenerArgs>().paths) }, flags)\n    }\n\n    companion object {\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<ParcelablePathListConsumer> {\n            override fun createFromParcel(source: Parcel): ParcelablePathListConsumer =\n                ParcelablePathListConsumer(source)\n\n            override fun newArray(size: Int): Array<ParcelablePathListConsumer?> =\n                arrayOfNulls(size)\n        }\n    }\n\n    @Parcelize\n    private class ListenerArgs(\n        val paths: @WriteWith<ParcelableListParceler> List<Path>\n    ) : ParcelableArgs\n}\n\nfun ((List<Path>) -> Unit).toParcelable(): ParcelablePathListConsumer =\n    ParcelablePathListConsumer(this)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/remote/ParcelableSerializable.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.remote\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport java.io.Serializable\n\nclass ParcelableSerializable(private val value: Serializable) : Parcelable {\n    @Suppress(\"UNCHECKED_CAST\")\n    fun <T> value(): T = value as T\n\n    private constructor(source: Parcel) : this(source.readSerializable()!!)\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        dest.writeSerializable(value)\n    }\n\n    companion object {\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<ParcelableSerializable> {\n            override fun createFromParcel(source: Parcel): ParcelableSerializable =\n                ParcelableSerializable(source)\n\n            override fun newArray(size: Int): Array<ParcelableSerializable?> = arrayOfNulls(size)\n        }\n    }\n}\n\nfun Serializable.toParcelable(): ParcelableSerializable = ParcelableSerializable(this)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/remote/RemoteFileService.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.remote\n\nimport java8.nio.file.FileSystem\nimport me.zhanghai.android.files.provider.common.PosixFileAttributeView\nimport me.zhanghai.android.files.provider.common.PosixFileStore\n\nabstract class RemoteFileService(private val remoteInterface: RemoteInterface<IRemoteFileService>) {\n    @Throws(RemoteFileSystemException::class)\n    fun getRemoteFileSystemProviderInterface(scheme: String): IRemoteFileSystemProvider =\n        remoteInterface.get().call { getRemoteFileSystemProviderInterface(scheme) }\n\n    @Throws(RemoteFileSystemException::class)\n    fun getRemoteFileSystemInterface(fileSystem: FileSystem): IRemoteFileSystem =\n        remoteInterface.get().call { getRemoteFileSystemInterface(fileSystem.toParcelable()) }\n\n    @Throws(RemoteFileSystemException::class)\n    fun getRemotePosixFileStoreInterface(fileStore: PosixFileStore): IRemotePosixFileStore =\n        remoteInterface.get().call { getRemotePosixFileStoreInterface(fileStore.toParcelable()) }\n\n    @Throws(RemoteFileSystemException::class)\n    fun getRemotePosixFileAttributeViewInterface(\n        attributeView: PosixFileAttributeView\n    ): IRemotePosixFileAttributeView =\n        remoteInterface.get().call {\n            getRemotePosixFileAttributeViewInterface(attributeView.toParcelable())\n        }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/remote/RemoteFileServiceInterface.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.remote\n\nimport me.zhanghai.android.files.provider.FileSystemProviders\n\nopen class RemoteFileServiceInterface : IRemoteFileService.Stub() {\n    override fun getRemoteFileSystemProviderInterface(scheme: String): IRemoteFileSystemProvider =\n        RemoteFileSystemProviderInterface(FileSystemProviders[scheme])\n\n    override fun getRemoteFileSystemInterface(fileSystem: ParcelableObject): IRemoteFileSystem =\n        RemoteFileSystemInterface(fileSystem.value())\n\n    override fun getRemotePosixFileStoreInterface(\n        fileStore: ParcelableObject\n    ): IRemotePosixFileStore = RemotePosixFileStoreInterface(fileStore.value())\n\n    override fun getRemotePosixFileAttributeViewInterface(\n        attributeView: ParcelableObject\n    ): IRemotePosixFileAttributeView =\n        RemotePosixFileAttributeViewInterface(attributeView.value())\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/remote/RemoteFileSystem.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.remote\n\nimport java8.nio.file.FileSystem\nimport java.io.IOException\n\nabstract class RemoteFileSystem(\n    private val remoteInterface: RemoteInterface<IRemoteFileSystem>\n) : FileSystem() {\n    @Throws(IOException::class)\n    override fun close() {\n        if (!remoteInterface.has()) {\n            return\n        }\n        remoteInterface.get().call { exception -> close(exception) }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/remote/RemoteFileSystemException.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.remote\n\nimport java.io.IOException\n\nclass RemoteFileSystemException : IOException {\n    constructor()\n\n    constructor(message: String?) : super(message)\n\n    constructor(message: String?, cause: Throwable?) : super(message, cause)\n\n    constructor(cause: Throwable?) : super(cause)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/remote/RemoteFileSystemInterface.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.remote\n\nimport java8.nio.file.FileSystem\n\nclass RemoteFileSystemInterface(private val fileSystem: FileSystem) : IRemoteFileSystem.Stub() {\n    override fun close(exception: ParcelableException) {\n        tryRun(exception) { fileSystem.close() }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/remote/RemoteFileSystemProvider.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.remote\n\nimport android.os.Bundle\nimport android.os.Parcel\nimport android.os.Parcelable\nimport java8.nio.channels.FileChannel\nimport java8.nio.channels.SeekableByteChannel\nimport java8.nio.file.AccessMode\nimport java8.nio.file.CopyOption\nimport java8.nio.file.DirectoryStream\nimport java8.nio.file.FileStore\nimport java8.nio.file.LinkOption\nimport java8.nio.file.OpenOption\nimport java8.nio.file.Path\nimport java8.nio.file.attribute.BasicFileAttributes\nimport java8.nio.file.attribute.FileAttribute\nimport java8.nio.file.spi.FileSystemProvider\nimport kotlinx.coroutines.runBlocking\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.provider.common.PathObservable\nimport me.zhanghai.android.files.provider.common.PathObservableProvider\nimport me.zhanghai.android.files.provider.common.Searchable\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.RemoteCallback\nimport me.zhanghai.android.files.util.getArgs\nimport java.io.IOException\nimport java.io.InputStream\nimport java.io.InterruptedIOException\nimport java.io.Serializable\nimport kotlin.coroutines.resume\nimport kotlin.coroutines.resumeWithException\nimport kotlin.coroutines.suspendCoroutine\n\nabstract class RemoteFileSystemProvider(\n    private val remoteInterface: RemoteInterface<IRemoteFileSystemProvider>\n) : FileSystemProvider(), PathObservableProvider, Searchable {\n    @Throws(IOException::class)\n    override fun newInputStream(file: Path, vararg options: OpenOption): InputStream =\n        remoteInterface.get().call { exception ->\n            newInputStream(file.toParcelable(), options.toParcelable(), exception)\n        }\n\n    @Throws(IOException::class)\n    override fun newFileChannel(\n        file: Path,\n        options: Set<OpenOption>,\n        vararg attributes: FileAttribute<*>\n    ): FileChannel {\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun newByteChannel(\n        file: Path,\n        options: Set<OpenOption>,\n        vararg attributes: FileAttribute<*>\n    ): SeekableByteChannel {\n        val options = when (options) {\n            is Serializable -> options\n            else -> options.toSet() as Serializable\n        }\n        return remoteInterface.get().call { exception ->\n            newByteChannel(\n                file.toParcelable(), options.toParcelable(), attributes.toParcelable(), exception\n            )\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun newDirectoryStream(\n        directory: Path,\n        filter: DirectoryStream.Filter<in Path>\n    ): DirectoryStream<Path> {\n        val filter = when (filter) {\n            is Parcelable -> filter\n            filesAcceptAllFilter -> ParcelableAcceptAllFilter.instance\n            else -> throw IllegalArgumentException(\"$filter is not Parcelable\")\n        }\n        return remoteInterface.get().call { exception ->\n            newDirectoryStream(directory.toParcelable(), filter.toParcelable(), exception)\n        }.value\n    }\n\n    @Throws(IOException::class)\n    override fun createDirectory(directory: Path, vararg attributes: FileAttribute<*>) {\n        remoteInterface.get().call { exception ->\n            createDirectory(directory.toParcelable(), attributes.toParcelable(), exception)\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun createSymbolicLink(link: Path, target: Path, vararg attributes: FileAttribute<*>) {\n        remoteInterface.get().call { exception ->\n            createSymbolicLink(\n                link.toParcelable(), target.toParcelable(), attributes.toParcelable(), exception\n            )\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun createLink(link: Path, existing: Path) {\n        remoteInterface.get().call { exception ->\n            createLink(link.toParcelable(), existing.toParcelable(), exception)\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun delete(path: Path) {\n        remoteInterface.get().call { exception -> delete(path.toParcelable(), exception) }\n    }\n\n    @Throws(IOException::class)\n    override fun readSymbolicLink(link: Path): Path =\n        remoteInterface.get().call { exception ->\n            readSymbolicLink(link.toParcelable(), exception)\n        }.value()\n\n    @Throws(IOException::class)\n    override fun copy(source: Path, target: Path, vararg options: CopyOption) {\n        var interruptible: RemoteCallback? = null\n        try {\n            runBlocking<Unit> {\n                suspendCoroutine { continuation ->\n                    val callback = RemoteCallback {\n                        val exception = it.getArgs<CallbackArgs>().exception.value\n                        if (exception != null) {\n                            continuation.resumeWithException(exception)\n                        } else {\n                            continuation.resume(Unit)\n                        }\n                    }\n                    interruptible = remoteInterface.get().call {\n                        copy(\n                            source.toParcelable(), target.toParcelable(), options.toParcelable(),\n                            callback\n                        )\n                    }\n                }\n            }\n        } catch (e: InterruptedException) {\n            interruptible?.sendResult(Bundle())\n            throw InterruptedIOException().apply { initCause(e) }\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun move(source: Path, target: Path, vararg options: CopyOption) {\n        var interruptible: RemoteCallback? = null\n        try {\n            runBlocking<Unit> {\n                suspendCoroutine { continuation ->\n                    val callback = RemoteCallback {\n                        val exception = it.getArgs<CallbackArgs>().exception.value\n                        if (exception != null) {\n                            continuation.resumeWithException(exception)\n                        } else {\n                            continuation.resume(Unit)\n                        }\n                    }\n                    interruptible = remoteInterface.get().call {\n                        move(\n                            source.toParcelable(), target.toParcelable(), options.toParcelable(),\n                            callback\n                        )\n                    }\n                }\n            }\n        } catch (e: InterruptedException) {\n            interruptible?.sendResult(Bundle())\n            throw InterruptedIOException().apply { initCause(e) }\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun isSameFile(path: Path, path2: Path): Boolean =\n        remoteInterface.get().call { exception ->\n            isSameFile(path.toParcelable(), path2.toParcelable(), exception)\n        }\n\n    @Throws(IOException::class)\n    override fun isHidden(path: Path): Boolean =\n        remoteInterface.get().call { exception -> isHidden(path.toParcelable(), exception) }\n\n    @Throws(IOException::class)\n    override fun getFileStore(path: Path): FileStore =\n        remoteInterface.get().call {\n            exception -> getFileStore(path.toParcelable(), exception)\n        }.value()\n\n    @Throws(IOException::class)\n    override fun checkAccess(path: Path, vararg modes: AccessMode) {\n        remoteInterface.get().call { exception ->\n            checkAccess(path.toParcelable(), modes.toParcelable(), exception)\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun <A : BasicFileAttributes> readAttributes(\n        path: Path,\n        type: Class<A>,\n        vararg options: LinkOption\n    ): A =\n        remoteInterface.get().call { exception ->\n            readAttributes(\n                path.toParcelable(), type.toParcelable(), options.toParcelable(), exception\n            )\n        }.value()\n\n    @Throws(IOException::class)\n    override fun readAttributes(\n        path: Path,\n        attributes: String,\n        vararg options: LinkOption\n    ): Map<String, Any> {\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun setAttribute(\n        path: Path,\n        attribute: String,\n        value: Any,\n        vararg options: LinkOption\n    ) {\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun observe(path: Path, intervalMillis: Long): PathObservable =\n        remoteInterface.get().call { exception ->\n            observe(path.toParcelable(), intervalMillis, exception)\n        }.also { it.initializeForRemote() }\n\n    @Throws(IOException::class)\n    override fun search(\n        directory: Path,\n        query: String,\n        intervalMillis: Long,\n        listener: (List<Path>) -> Unit\n    ) {\n        var interruptible: RemoteCallback? = null\n        try {\n            runBlocking<Unit> {\n                suspendCoroutine { continuation ->\n                    val callback = RemoteCallback {\n                        val exception = it.getArgs<CallbackArgs>().exception.value\n                        if (exception != null) {\n                            continuation.resumeWithException(exception)\n                        } else {\n                            continuation.resume(Unit)\n                        }\n                    }\n                    interruptible = remoteInterface.get().call {\n                        search(\n                            directory.toParcelable(), query, intervalMillis,\n                            listener.toParcelable(), callback\n                        )\n                    }\n                }\n            }\n        } catch (e: InterruptedException) {\n            interruptible?.sendResult(Bundle())\n            throw InterruptedIOException().apply { initCause(e) }\n        }\n    }\n\n    private class ParcelableAcceptAllFilter private constructor() : DirectoryStream.Filter<Path>,\n        Parcelable {\n        override fun accept(entry: Path): Boolean = true\n\n        override fun describeContents(): Int = 0\n\n        override fun writeToParcel(dest: Parcel, flags: Int) {}\n\n        companion object {\n            val instance = ParcelableAcceptAllFilter()\n\n            @JvmField\n            val CREATOR = object : Parcelable.Creator<ParcelableAcceptAllFilter> {\n                override fun createFromParcel(source: Parcel): ParcelableAcceptAllFilter = instance\n\n                override fun newArray(size: Int): Array<ParcelableAcceptAllFilter?> =\n                    arrayOfNulls(size)\n            }\n        }\n    }\n\n    @Parcelize\n    internal class CallbackArgs(val exception: ParcelableException) : ParcelableArgs\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/remote/RemoteFileSystemProviderInterface.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.remote\n\nimport java8.nio.file.attribute.BasicFileAttributes\nimport java8.nio.file.spi.FileSystemProvider\nimport me.zhanghai.android.files.provider.common.PathObservableProvider\nimport me.zhanghai.android.files.provider.common.Searchable\nimport me.zhanghai.android.files.util.RemoteCallback\nimport me.zhanghai.android.files.util.toBundle\nimport java.util.concurrent.Executors\n\nclass RemoteFileSystemProviderInterface(\n    private val provider: FileSystemProvider\n) : IRemoteFileSystemProvider.Stub() {\n    private val executorService = Executors.newCachedThreadPool()\n\n    override fun newInputStream(\n        file: ParcelableObject,\n        options: ParcelableSerializable,\n        exception: ParcelableException\n    ): RemoteInputStream? =\n        tryRun(exception) { provider.newInputStream(file.value(), *options.value()).toRemote() }\n\n    override fun newByteChannel(\n        file: ParcelableObject,\n        options: ParcelableSerializable,\n        attributes: ParcelableFileAttributes,\n        exception: ParcelableException\n    ): RemoteSeekableByteChannel? =\n        tryRun(exception) {\n            provider.newByteChannel(file.value(), options.value(), *attributes.value).toRemote()\n        }\n\n    override fun newDirectoryStream(\n        directory: ParcelableObject,\n        filter: ParcelableObject,\n        exception: ParcelableException\n    ): ParcelableDirectoryStream? =\n        tryRun(exception) {\n            provider.newDirectoryStream(directory.value(), filter.value())\n                .use { ParcelableDirectoryStream(it) }\n        }\n\n    override fun createDirectory(\n        directory: ParcelableObject,\n        attributes: ParcelableFileAttributes,\n        exception: ParcelableException\n    ) {\n        tryRun(exception) { provider.createDirectory(directory.value(), *attributes.value) }\n    }\n\n    override fun createSymbolicLink(\n        link: ParcelableObject,\n        target: ParcelableObject,\n        attributes: ParcelableFileAttributes,\n        exception: ParcelableException\n    ) {\n        tryRun(exception) {\n            provider.createSymbolicLink(link.value(), target.value(), *attributes.value)\n        }\n    }\n\n    override fun createLink(\n        link: ParcelableObject,\n        existing: ParcelableObject,\n        exception: ParcelableException\n    ) {\n        tryRun(exception) { provider.createLink(link.value(), existing.value()) }\n    }\n\n    override fun delete(path: ParcelableObject, exception: ParcelableException) {\n        tryRun(exception) { provider.delete(path.value()) }\n    }\n\n    override fun readSymbolicLink(\n        link: ParcelableObject,\n        exception: ParcelableException\n    ): ParcelableObject? =\n        tryRun(exception) { provider.readSymbolicLink(link.value()).toParcelable() }\n\n    override fun copy(\n        source: ParcelableObject,\n        target: ParcelableObject,\n        options: ParcelableCopyOptions,\n        callback: RemoteCallback\n    ): RemoteCallback {\n        val future = executorService.submit<Unit> {\n            val exception = ParcelableException()\n            tryRun(exception) {\n                provider.copy(source.value(), target.value(), *options.value)\n            }\n            callback.sendResult(RemoteFileSystemProvider.CallbackArgs(exception).toBundle())\n        }\n        return RemoteCallback { future.cancel(true) }\n    }\n\n    override fun move(\n        source: ParcelableObject,\n        target: ParcelableObject,\n        options: ParcelableCopyOptions,\n        callback: RemoteCallback\n    ): RemoteCallback {\n        val future = executorService.submit<Unit> {\n            val exception = ParcelableException()\n            tryRun(exception) {\n                provider.move(source.value(), target.value(), *options.value)\n            }\n            callback.sendResult(RemoteFileSystemProvider.CallbackArgs(exception).toBundle())\n        }\n        return RemoteCallback { future.cancel(true) }\n    }\n\n    override fun isSameFile(\n        path: ParcelableObject,\n        path2: ParcelableObject,\n        exception: ParcelableException\n    ): Boolean = tryRun(exception) { provider.isSameFile(path.value(), path2.value()) } ?: false\n\n    override fun isHidden(path: ParcelableObject, exception: ParcelableException): Boolean =\n        tryRun(exception) { provider.isHidden(path.value()) } ?: false\n\n    override fun getFileStore(\n        path: ParcelableObject,\n        exception: ParcelableException\n    ): ParcelableObject? = tryRun(exception) { provider.getFileStore(path.value()).toParcelable() }\n\n    override fun checkAccess(\n        path: ParcelableObject,\n        modes: ParcelableSerializable,\n        exception: ParcelableException\n    ) {\n        tryRun(exception) { provider.checkAccess(path.value(), *modes.value()) }\n    }\n\n    override fun readAttributes(\n        path: ParcelableObject,\n        type: ParcelableSerializable,\n        options: ParcelableSerializable,\n        exception: ParcelableException\n    ): ParcelableObject? =\n        tryRun(exception) {\n            provider.readAttributes(\n                // We have to explicitly specify the Class type here, or it will be resolved to the\n                // String overload.\n                path.value(), type.value<Class<BasicFileAttributes>>(), *options.value()\n            ).toParcelable()\n        }\n\n    override fun observe(\n        path: ParcelableObject,\n        intervalMillis: Long,\n        exception: ParcelableException\n    ): RemotePathObservable? =\n        tryRun(exception) {\n            (provider as PathObservableProvider).observe(path.value(), intervalMillis).toRemote()\n        }\n\n    override fun search(\n        directory: ParcelableObject,\n        query: String,\n        intervalMillis: Long,\n        listener: ParcelablePathListConsumer,\n        callback: RemoteCallback\n    ): RemoteCallback {\n        val future = executorService.submit<Unit> {\n            val exception = ParcelableException()\n            tryRun(exception) {\n                (provider as Searchable).search(\n                    directory.value(), query, intervalMillis, listener.value\n                )\n            }\n            callback.sendResult(RemoteFileSystemProvider.CallbackArgs(exception).toBundle())\n        }\n        return RemoteCallback { future.cancel(true) }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/remote/RemoteInputStream.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.remote\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport java.io.IOException\nimport java.io.InputStream\n\nclass RemoteInputStream : InputStream, Parcelable {\n    private val localInputStream: InputStream?\n    private val remoteInputStream: IRemoteInputStream?\n\n    constructor(inputStream: InputStream) {\n        localInputStream = inputStream\n        remoteInputStream = null\n    }\n\n    @Throws(IOException::class)\n    override fun read(): Int =\n        if (remoteInputStream != null) {\n            remoteInputStream.call { exception -> read(exception) }\n        } else {\n            localInputStream!!.read()\n        }\n\n    @Throws(IOException::class)\n    override fun read(buffer: ByteArray, offset: Int, length: Int): Int =\n        if (remoteInputStream != null) {\n            val remoteBuffer = ByteArray(length)\n            val size = remoteInputStream.call { exception -> read2(remoteBuffer, exception) }\n            if (size > 0) {\n                remoteBuffer.copyInto(buffer, offset, 0, size)\n            }\n            size\n        } else {\n            localInputStream!!.read(buffer, offset, length)\n        }\n\n    @Throws(IOException::class)\n    override fun skip(size: Long): Long =\n        if (remoteInputStream != null) {\n            remoteInputStream.call { exception -> skip(size, exception) }\n        } else {\n            localInputStream!!.skip(size)\n        }\n\n    @Throws(IOException::class)\n    override fun available(): Int =\n        if (remoteInputStream != null) {\n            remoteInputStream.call { exception -> available(exception) }\n        } else {\n            localInputStream!!.available()\n        }\n\n    @Throws(IOException::class)\n    override fun close() {\n        if (remoteInputStream != null) {\n            remoteInputStream.call { exception -> close(exception) }\n        } else {\n            localInputStream!!.close()\n        }\n    }\n\n    private class Stub(private val mInputStream: InputStream) : IRemoteInputStream.Stub() {\n        override fun read(exception: ParcelableException): Int =\n            tryRun(exception) { mInputStream.read() } ?: 0\n\n        override fun read2(buffer: ByteArray, exception: ParcelableException): Int =\n            tryRun(exception) { mInputStream.read(buffer) } ?: 0\n\n        override fun skip(size: Long, exception: ParcelableException): Long =\n            tryRun(exception) { mInputStream.skip(size) } ?: 0\n\n        override fun available(exception: ParcelableException): Int =\n            tryRun(exception) { mInputStream.available() } ?: 0\n\n        override fun close(exception: ParcelableException) {\n            tryRun(exception) { mInputStream.close() }\n        }\n    }\n\n    private constructor(source: Parcel) {\n        localInputStream = null\n        remoteInputStream = IRemoteInputStream.Stub.asInterface(source.readStrongBinder())\n    }\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        if (remoteInputStream != null) {\n            dest.writeStrongBinder(remoteInputStream.asBinder())\n        } else {\n            dest.writeStrongBinder(Stub(localInputStream!!).asBinder())\n        }\n    }\n\n    companion object {\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<RemoteInputStream> {\n            override fun createFromParcel(source: Parcel): RemoteInputStream =\n                RemoteInputStream(source)\n\n            override fun newArray(size: Int): Array<RemoteInputStream?> = arrayOfNulls(size)\n        }\n    }\n}\n\nfun InputStream.toRemote(): RemoteInputStream = RemoteInputStream(this)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/remote/RemoteInterface.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.remote\n\nimport android.os.IBinder.DeathRecipient\nimport android.os.IInterface\nimport android.os.RemoteException\nimport java.lang.ref.WeakReference\n\nclass RemoteInterface<T : IInterface>(\n    // @Throws(RemoteFileSystemException::class)\n    private val creator: () -> T\n) {\n    private var value: T? = null\n\n    private val lock = Any()\n\n    private val deathRecipient = WeakDeathRecipient(this)\n\n    fun has(): Boolean = synchronized(lock) { value != null }\n\n    @Throws(RemoteFileSystemException::class)\n    fun get(): T {\n        synchronized(lock) {\n            var value = value\n            if (value == null) {\n                value = creator()\n                this.value = value\n                try {\n                    value.asBinder().linkToDeath(deathRecipient, 0)\n                } catch (e: RemoteException) {\n                    // RemoteException is thrown if remote has already died.\n                    this.value = null\n                    throw RemoteFileSystemException(e)\n                }\n            }\n            return value\n        }\n    }\n\n    private fun binderDied() {\n        synchronized(lock) {\n            value!!.asBinder().unlinkToDeath(deathRecipient, 0)\n            value = null\n        }\n    }\n\n    protected fun finalize() {\n        if (value != null) {\n            // We have to do this or JavaBinder will complain about BinderProxy being destroyed\n            // without unlinkToDeath() being called first.\n            value!!.asBinder().unlinkToDeath(deathRecipient, 0)\n            value = null\n        }\n    }\n\n    // Avoid a strong reference to the BinderProxy so that it can be garbage collected.\n    private class WeakDeathRecipient<T : IInterface>(\n        remoteInterface: RemoteInterface<T>\n    ) : DeathRecipient {\n        private val weakRemoteInterface = WeakReference(remoteInterface)\n\n        override fun binderDied() {\n            weakRemoteInterface.get()?.binderDied()\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/remote/RemotePathObservable.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.remote\n\nimport android.os.Bundle\nimport android.os.Parcel\nimport android.os.Parcelable\nimport android.os.RemoteException\nimport me.zhanghai.android.files.provider.common.PathObservable\nimport me.zhanghai.android.files.util.RemoteCallback\nimport java.io.IOException\n\nclass RemotePathObservable : PathObservable, Parcelable {\n    private val localPathObservable: PathObservable?\n    private val remotePathObservable: IRemotePathObservable?\n    private val remoteObservers: MutableSet<() -> Unit>?\n    private var isRemoteInitialized = false\n    private val remoteLock: Any?\n\n    constructor(pathObservable: PathObservable) {\n        localPathObservable = pathObservable\n        remotePathObservable = null\n        remoteObservers = null\n        remoteLock = null\n    }\n\n    @Throws(IOException::class)\n    fun initializeForRemote() {\n        synchronized(remoteLock!!) {\n            check(!isRemoteInitialized)\n            try {\n                remotePathObservable!!.addObserver(RemoteCallback {\n                    synchronized(remoteLock) { remoteObservers!!.forEach { it() } }\n                })\n            } catch (e: RemoteException) {\n                close()\n                throw RemoteFileSystemException(e)\n            }\n            isRemoteInitialized = true\n        }\n    }\n\n    override fun addObserver(observer: () -> Unit) {\n        synchronized(remoteLock!!) {\n            check(isRemoteInitialized)\n            remoteObservers!!.add(observer)\n        }\n    }\n\n    override fun removeObserver(observer: () -> Unit) {\n        synchronized(remoteLock!!) {\n            check(isRemoteInitialized)\n            remoteObservers!!.remove(observer)\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun close() {\n        if (remotePathObservable != null) {\n            synchronized(remoteLock!!) {\n                remotePathObservable.call { exception -> close(exception) }\n                remoteObservers!!.clear()\n            }\n        } else {\n            localPathObservable!!.close()\n        }\n    }\n\n    private class Stub(private val pathObservable: PathObservable) : IRemotePathObservable.Stub() {\n        override fun addObserver(observer: RemoteCallback) {\n            pathObservable.addObserver { observer.sendResult(Bundle()) }\n        }\n\n        override fun close(exception: ParcelableException) {\n            tryRun(exception) { pathObservable.close() }\n        }\n    }\n\n    private constructor(source: Parcel) {\n        localPathObservable = null\n        remotePathObservable = IRemotePathObservable.Stub.asInterface(source.readStrongBinder())\n        remoteObservers = mutableSetOf()\n        remoteLock = Any()\n    }\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        check(remotePathObservable == null) { \"Already at the remote side\" }\n        dest.writeStrongBinder(Stub(localPathObservable!!).asBinder())\n    }\n\n    companion object {\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<RemotePathObservable> {\n            override fun createFromParcel(source: Parcel): RemotePathObservable =\n                RemotePathObservable(source)\n\n            override fun newArray(size: Int): Array<RemotePathObservable?> = arrayOfNulls(size)\n        }\n    }\n}\n\nfun PathObservable.toRemote(): RemotePathObservable = RemotePathObservable(this)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/remote/RemotePosixFileAttributeView.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.remote\n\nimport java8.nio.file.attribute.FileTime\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.PosixFileAttributeView\nimport me.zhanghai.android.files.provider.common.PosixFileAttributes\nimport me.zhanghai.android.files.provider.common.PosixFileModeBit\nimport me.zhanghai.android.files.provider.common.PosixGroup\nimport me.zhanghai.android.files.provider.common.PosixUser\nimport me.zhanghai.android.files.provider.common.toParcelable\nimport java.io.IOException\n\nabstract class RemotePosixFileAttributeView(\n    private val remoteInterface: RemoteInterface<IRemotePosixFileAttributeView>\n) : PosixFileAttributeView {\n    @Throws(IOException::class)\n    override fun readAttributes(): PosixFileAttributes =\n        remoteInterface.get().call { exception -> readAttributes(exception) }.value()\n\n    @Throws(IOException::class)\n    override fun setTimes(\n        lastModifiedTime: FileTime?,\n        lastAccessTime: FileTime?,\n        createTime: FileTime?\n    ) {\n        remoteInterface.get().call { exception ->\n            setTimes(\n                lastModifiedTime?.toParcelable(), lastAccessTime?.toParcelable(),\n                createTime?.toParcelable(), exception\n            )\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun setOwner(owner: PosixUser) {\n        remoteInterface.get().call { exception -> setOwner(owner, exception) }\n    }\n\n    @Throws(IOException::class)\n    override fun setGroup(group: PosixGroup) {\n        remoteInterface.get().call { exception -> setGroup(group, exception) }\n    }\n\n    @Throws(IOException::class)\n    override fun setMode(mode: Set<PosixFileModeBit>) {\n        remoteInterface.get().call { exception -> setMode(mode.toParcelable(), exception) }\n    }\n\n    @Throws(IOException::class)\n    override fun setSeLinuxContext(context: ByteString) {\n        remoteInterface.get().call { exception ->\n            setSeLinuxContext(context.toParcelable(), exception)\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun restoreSeLinuxContext() {\n        remoteInterface.get().call { exception -> restoreSeLinuxContext(exception) }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/remote/RemotePosixFileAttributeViewInterface.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.remote\n\nimport me.zhanghai.android.files.provider.common.ParcelableFileTime\nimport me.zhanghai.android.files.provider.common.ParcelablePosixFileMode\nimport me.zhanghai.android.files.provider.common.PosixFileAttributeView\nimport me.zhanghai.android.files.provider.common.PosixGroup\nimport me.zhanghai.android.files.provider.common.PosixUser\n\nclass RemotePosixFileAttributeViewInterface(\n    private val attributeView: PosixFileAttributeView\n) : IRemotePosixFileAttributeView.Stub() {\n    override fun readAttributes(exception: ParcelableException): ParcelableObject? =\n        tryRun(exception) { attributeView.readAttributes().toParcelable() }\n\n    override fun setTimes(\n        lastModifiedTime: ParcelableFileTime?,\n        lastAccessTime: ParcelableFileTime?,\n        createTime: ParcelableFileTime?,\n        exception: ParcelableException\n    ) {\n        tryRun(exception) {\n            attributeView.setTimes(\n                lastModifiedTime?.value, lastAccessTime?.value, createTime?.value\n            )\n        }\n    }\n\n    override fun setOwner(owner: PosixUser, exception: ParcelableException) {\n        tryRun(exception) { attributeView.setOwner(owner) }\n    }\n\n    override fun setGroup(group: PosixGroup, exception: ParcelableException) {\n        tryRun(exception) { attributeView.setGroup(group) }\n    }\n\n    override fun setMode(mode: ParcelablePosixFileMode, exception: ParcelableException) {\n        tryRun(exception) { attributeView.setMode(mode.value) }\n    }\n\n    override fun setSeLinuxContext(context: ParcelableObject, exception: ParcelableException) {\n        tryRun(exception) { attributeView.setSeLinuxContext(context.value()) }\n    }\n\n    override fun restoreSeLinuxContext(exception: ParcelableException) {\n        tryRun(exception) { attributeView.restoreSeLinuxContext() }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/remote/RemotePosixFileStore.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.remote\n\nimport java8.nio.file.attribute.FileAttributeView\nimport me.zhanghai.android.files.provider.common.PosixFileStore\nimport java.io.IOException\n\nabstract class RemotePosixFileStore(\n    private val remoteInterface: RemoteInterface<IRemotePosixFileStore>\n) : PosixFileStore() {\n    override fun refresh() {\n        throw AssertionError()\n    }\n\n    override fun name(): String {\n        throw AssertionError()\n    }\n\n    override fun type(): String {\n        throw AssertionError()\n    }\n\n    override fun isReadOnly(): Boolean {\n        throw AssertionError()\n    }\n\n    @Throws(IOException::class)\n    override fun setReadOnly(readOnly: Boolean) {\n        remoteInterface.get().call { exception -> setReadOnly(readOnly, exception) }\n    }\n\n    @Throws(IOException::class)\n    override fun getTotalSpace(): Long =\n        remoteInterface.get().call { exception -> getTotalSpace(exception) }\n\n    @Throws(IOException::class)\n    override fun getUsableSpace(): Long =\n        remoteInterface.get().call { exception -> getUsableSpace(exception) }\n\n    @Throws(IOException::class)\n    override fun getUnallocatedSpace(): Long =\n        remoteInterface.get().call { exception -> getUnallocatedSpace(exception) }\n\n    override fun supportsFileAttributeView(type: Class<out FileAttributeView>): Boolean {\n        throw AssertionError()\n    }\n\n    override fun supportsFileAttributeView(name: String): Boolean {\n        throw AssertionError()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/remote/RemotePosixFileStoreInterface.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.remote\n\nimport me.zhanghai.android.files.provider.common.PosixFileStore\n\nclass RemotePosixFileStoreInterface(\n    private val fileStore: PosixFileStore\n) : IRemotePosixFileStore.Stub() {\n    override fun setReadOnly(readOnly: Boolean, exception: ParcelableException) {\n        tryRun(exception) { fileStore.isReadOnly = readOnly }\n    }\n\n    override fun getTotalSpace(exception: ParcelableException): Long =\n        tryRun(exception) { fileStore.totalSpace } ?: 0\n\n    override fun getUsableSpace(exception: ParcelableException): Long =\n        tryRun(exception) { fileStore.usableSpace } ?: 0\n\n    override fun getUnallocatedSpace(exception: ParcelableException): Long =\n        tryRun(exception) { fileStore.unallocatedSpace } ?: 0\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/remote/RemoteSeekableByteChannel.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.remote\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport java8.nio.channels.SeekableByteChannel\nimport me.zhanghai.android.files.provider.common.ForceableChannel\nimport me.zhanghai.android.files.provider.common.force\nimport java.io.IOException\nimport java.nio.ByteBuffer\n\nclass RemoteSeekableByteChannel : ForceableChannel, SeekableByteChannel, Parcelable {\n    private val localChannel: SeekableByteChannel?\n    private val remoteChannel: IRemoteSeekableByteChannel?\n\n    @Volatile\n    private var isRemoteClosed = false\n\n    constructor(channel: SeekableByteChannel) {\n        localChannel = channel\n        remoteChannel = null\n    }\n\n    @Throws(IOException::class)\n    override fun read(destination: ByteBuffer): Int =\n        if (remoteChannel != null) {\n            val destinationBytes = ByteArray(destination.remaining())\n            val size = remoteChannel.call { exception -> read(destinationBytes, exception) }\n            if (size > 0) {\n                destination.put(destinationBytes, 0, size)\n            }\n            size\n        } else {\n            localChannel!!.read(destination)\n        }\n\n    @Throws(IOException::class)\n    override fun write(source: ByteBuffer): Int =\n        if (remoteChannel != null) {\n            val oldPosition = source.position()\n            val sourceBytes = ByteArray(source.remaining())\n            source.get(sourceBytes)\n            source.position(oldPosition)\n            val size = remoteChannel.call { exception -> write(sourceBytes, exception) }\n            source.position(oldPosition + size)\n            size\n        } else {\n            localChannel!!.write(source)\n        }\n\n    @Throws(IOException::class)\n    override fun position(): Long =\n        if (remoteChannel != null) {\n            remoteChannel.call { exception -> position(exception) }\n        } else {\n            localChannel!!.position()\n        }\n\n    @Throws(IOException::class)\n    override fun position(newPosition: Long): SeekableByteChannel {\n        if (remoteChannel != null) {\n            remoteChannel.call { exception -> position2(newPosition, exception) }\n        } else {\n            localChannel!!.position(newPosition)\n        }\n        return this\n    }\n\n    @Throws(IOException::class)\n    override fun size(): Long =\n        if (remoteChannel != null) {\n            remoteChannel.call { exception -> size(exception) }\n        } else {\n            localChannel!!.size()\n        }\n\n    @Throws(IOException::class)\n    override fun truncate(size: Long): SeekableByteChannel {\n        if (remoteChannel != null) {\n            remoteChannel.call { exception -> truncate(size, exception) }\n        } else {\n            return localChannel!!.truncate(size)\n        }\n        return this\n    }\n\n    @Throws(IOException::class)\n    override fun force(metaData: Boolean) {\n        if (remoteChannel != null) {\n            remoteChannel.call { exception -> force(metaData, exception) }\n        } else {\n            localChannel!!.force(metaData)\n        }\n    }\n\n    override fun isOpen(): Boolean =\n        if (remoteChannel != null) {\n            !isRemoteClosed\n        } else {\n            localChannel!!.isOpen\n        }\n\n    @Throws(IOException::class)\n    override fun close() {\n        if (remoteChannel != null) {\n            remoteChannel.call { exception -> close(exception) }\n            isRemoteClosed = true\n        } else {\n            localChannel!!.close()\n        }\n    }\n\n    private class Stub(\n        private val channel: SeekableByteChannel\n    ) : IRemoteSeekableByteChannel.Stub() {\n        override fun read(destination: ByteArray, exception: ParcelableException): Int =\n            tryRun(exception) { channel.read(ByteBuffer.wrap(destination)) } ?: 0\n\n        override fun write(source: ByteArray, exception: ParcelableException): Int =\n            tryRun(exception) { channel.write(ByteBuffer.wrap(source)) } ?: 0\n\n        override fun position(exception: ParcelableException): Long =\n            tryRun(exception) { channel.position() } ?: 0\n\n        override fun position2(newPosition: Long, exception: ParcelableException) {\n            tryRun(exception) { channel.position(newPosition) }\n        }\n\n        override fun size(exception: ParcelableException): Long =\n            tryRun(exception) { channel.size() } ?: 0\n\n        override fun truncate(size: Long, exception: ParcelableException) {\n            tryRun(exception) { channel.truncate(size) }\n        }\n\n        override fun force(metaData: Boolean, exception: ParcelableException) {\n            tryRun(exception) { channel.force(metaData) }\n        }\n\n        override fun close(exception: ParcelableException) {\n            tryRun(exception) { channel.close() }\n        }\n    }\n\n    private constructor(source: Parcel) {\n        localChannel = null\n        remoteChannel = IRemoteSeekableByteChannel.Stub.asInterface(source.readStrongBinder())\n    }\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        if (remoteChannel != null) {\n            dest.writeStrongBinder(remoteChannel.asBinder())\n        } else {\n            dest.writeStrongBinder(Stub(localChannel!!).asBinder())\n        }\n    }\n\n    companion object {\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<RemoteSeekableByteChannel> {\n            override fun createFromParcel(source: Parcel): RemoteSeekableByteChannel =\n                RemoteSeekableByteChannel(source)\n\n            override fun newArray(size: Int): Array<RemoteSeekableByteChannel?> = arrayOfNulls(size)\n        }\n    }\n}\n\nfun SeekableByteChannel.toRemote(): RemoteSeekableByteChannel = RemoteSeekableByteChannel(this)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/root/LibSuFileServiceLauncher.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.root\n\nimport android.content.ComponentName\nimport android.content.Context\nimport android.content.Intent\nimport android.content.ServiceConnection\nimport android.os.IBinder\nimport com.topjohnwu.superuser.NoShellException\nimport com.topjohnwu.superuser.Shell\nimport com.topjohnwu.superuser.ipc.RootService\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.TimeoutCancellationException\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.runBlocking\nimport kotlinx.coroutines.suspendCancellableCoroutine\nimport kotlinx.coroutines.withTimeout\nimport me.zhanghai.android.files.provider.remote.IRemoteFileService\nimport me.zhanghai.android.files.provider.remote.RemoteFileServiceInterface\nimport me.zhanghai.android.files.provider.remote.RemoteFileSystemException\nimport me.zhanghai.android.files.util.createIntent\nimport java.io.IOException\nimport java.util.concurrent.TimeUnit\nimport kotlin.coroutines.resume\nimport kotlin.coroutines.resumeWithException\n\nobject LibSuFileServiceLauncher {\n    private val lock = Any()\n\n    init {\n        Shell.enableVerboseLogging = true\n        Shell.setDefaultBuilder(\n            Shell.Builder.create()\n                .setInitializers(LibSuShellInitializer::class.java)\n                .setFlags(Shell.FLAG_MOUNT_MASTER or Shell.FLAG_REDIRECT_STDERR)\n                .setTimeout(TimeUnit.MILLISECONDS.toSeconds(RootFileService.TIMEOUT_MILLIS))\n        )\n    }\n\n    fun isSuAvailable(): Boolean =\n        // @see com.topjohnwu.superuser.Shell.rootAccess\n        try {\n            Runtime.getRuntime().exec(\"su --version\")\n            true\n        } catch (e: IOException) {\n            // java.io.IOException: Cannot run program \"su\": error=2, No such file or directory\n            false\n        }\n\n    @Throws(RemoteFileSystemException::class)\n    fun launchService(): IRemoteFileService {\n        synchronized(lock) {\n            // libsu won't call back when su isn't available.\n            if (!isSuAvailable()) {\n                throw RemoteFileSystemException(\"Root isn't available\")\n            }\n            return try {\n                runBlocking {\n                    try {\n                        withTimeout(RootFileService.TIMEOUT_MILLIS) {\n                            // Proactively create the shell because RootService doesn't allow us to\n                            // handle errors during shell creation.\n                            suspendCancellableCoroutine<Unit> { continuation ->\n                                // Shell.getShell(GetShellCallback) doesn't allow handling errors.\n                                Shell.EXECUTOR.submit {\n                                    try {\n                                        Shell.getShell()\n                                        continuation.resume(Unit)\n                                    } catch (e: NoShellException) {\n                                        continuation.resumeWithException(\n                                            RemoteFileSystemException(e)\n                                        )\n                                    }\n                                }\n                            }\n                            suspendCancellableCoroutine { continuation ->\n                                val intent = LibSuFileService::class.createIntent()\n                                val connection = object : ServiceConnection {\n                                    override fun onServiceConnected(\n                                        name: ComponentName,\n                                        service: IBinder\n                                    ) {\n                                        val serviceInterface =\n                                            IRemoteFileService.Stub.asInterface(service)\n                                        continuation.resume(serviceInterface)\n                                    }\n\n                                    override fun onServiceDisconnected(name: ComponentName) {\n                                        if (continuation.isActive) {\n                                            continuation.resumeWithException(\n                                                RemoteFileSystemException(\n                                                    \"libsu service disconnected\"\n                                                )\n                                            )\n                                        }\n                                    }\n\n                                    override fun onBindingDied(name: ComponentName) {\n                                        if (continuation.isActive) {\n                                            continuation.resumeWithException(\n                                                RemoteFileSystemException(\"libsu binding died\")\n                                            )\n                                        }\n                                    }\n\n                                    override fun onNullBinding(name: ComponentName) {\n                                        if (continuation.isActive) {\n                                            continuation.resumeWithException(\n                                                RemoteFileSystemException(\"libsu binding is null\")\n                                            )\n                                        }\n                                    }\n                                }\n                                launch(Dispatchers.Main.immediate) {\n                                    RootService.bind(intent, connection)\n                                    continuation.invokeOnCancellation {\n                                        launch(Dispatchers.Main.immediate) {\n                                            RootService.unbind(connection)\n                                        }\n                                    }\n                                }\n                            }\n                        }\n                    } catch (e: TimeoutCancellationException) {\n                        throw RemoteFileSystemException(e)\n                    }\n                }\n            } catch (e: InterruptedException) {\n                throw RemoteFileSystemException(e)\n            }\n        }\n    }\n}\n\nprivate class LibSuShellInitializer : Shell.Initializer() {\n    // Prevent normal shells from being created and set as the main shell.\n    override fun onInit(context: Context, shell: Shell): Boolean = shell.isRoot\n}\n\nclass LibSuFileService : RootService() {\n    override fun onCreate() {\n        super.onCreate()\n\n        RootFileService.main()\n    }\n\n    override fun onBind(intent: Intent): IBinder = RemoteFileServiceInterface()\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/root/RootFileService.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.root\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport android.os.Process\nimport android.util.Log\nimport me.zhanghai.android.files.BuildConfig\nimport me.zhanghai.android.files.provider.FileSystemProviders\nimport me.zhanghai.android.files.provider.remote.RemoteFileService\nimport me.zhanghai.android.files.provider.remote.RemoteInterface\nimport me.zhanghai.android.files.util.lazyReflectedMethod\n\nval isRunningAsRoot = Process.myUid() == 0\n\n@SuppressLint(\"StaticFieldLeak\")\nlateinit var rootContext: Context private set\n\nobject RootFileService : RemoteFileService(\n    RemoteInterface {\n        if (SuiFileServiceLauncher.isSuiAvailable()) {\n            SuiFileServiceLauncher.launchService()\n        } else {\n            LibSuFileServiceLauncher.launchService()\n        }\n    }\n) {\n    const val TIMEOUT_MILLIS = 15 * 1000L\n\n    private val LOG_TAG = RootFileService::class.java.simpleName\n\n    // Not actually restricted because there's no restriction when running as root.\n    //@RestrictedHiddenApi\n    private val activityThreadCurrentActivityThreadMethod by lazyReflectedMethod(\n        \"android.app.ActivityThread\", \"currentActivityThread\"\n    )\n    //@RestrictedHiddenApi\n    private val activityThreadGetSystemContextMethod by lazyReflectedMethod(\n        \"android.app.ActivityThread\", \"getSystemContext\"\n    )\n\n    fun main() {\n        Log.i(LOG_TAG, \"Creating package context\")\n        rootContext = createPackageContext(BuildConfig.APPLICATION_ID)\n        Log.i(LOG_TAG, \"Installing file system providers\")\n        FileSystemProviders.install()\n        FileSystemProviders.overflowWatchEvents = true\n    }\n\n    private fun createPackageContext(packageName: String): Context {\n        val activityThread = activityThreadCurrentActivityThreadMethod.invoke(null)\n        val systemContext = activityThreadGetSystemContextMethod.invoke(activityThread) as Context\n        return systemContext.createPackageContext(\n            packageName, Context.CONTEXT_IGNORE_SECURITY or Context.CONTEXT_INCLUDE_CODE\n        )\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/root/RootFileSystem.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.root\n\nimport java8.nio.file.FileStore\nimport java8.nio.file.FileSystem\nimport java8.nio.file.Path\nimport java8.nio.file.PathMatcher\nimport java8.nio.file.WatchService\nimport java8.nio.file.attribute.UserPrincipalLookupService\nimport java8.nio.file.spi.FileSystemProvider\nimport me.zhanghai.android.files.provider.remote.RemoteFileSystem\nimport me.zhanghai.android.files.provider.remote.RemoteInterface\nimport java.io.IOException\n\nopen class RootFileSystem(fileSystem: FileSystem) : RemoteFileSystem(\n    RemoteInterface { RootFileService.getRemoteFileSystemInterface(fileSystem) }\n) {\n    override fun provider(): FileSystemProvider {\n        throw AssertionError()\n    }\n\n    override fun isOpen(): Boolean {\n        throw AssertionError()\n    }\n\n    override fun isReadOnly(): Boolean {\n        throw AssertionError()\n    }\n\n    override fun getSeparator(): String {\n        throw AssertionError()\n    }\n\n    override fun getRootDirectories(): Iterable<Path> {\n        throw AssertionError()\n    }\n\n    override fun getFileStores(): Iterable<FileStore> {\n        throw AssertionError()\n    }\n\n    override fun supportedFileAttributeViews(): Set<String> {\n        throw AssertionError()\n    }\n\n    override fun getPath(first: String, vararg more: String): Path {\n        throw AssertionError()\n    }\n\n    override fun getPathMatcher(syntaxAndPattern: String): PathMatcher {\n        throw AssertionError()\n    }\n\n    override fun getUserPrincipalLookupService(): UserPrincipalLookupService {\n        throw AssertionError()\n    }\n\n    @Throws(IOException::class)\n    override fun newWatchService(): WatchService {\n        throw UnsupportedOperationException()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/root/RootFileSystemProvider.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.root\n\nimport java8.nio.file.FileSystem\nimport java8.nio.file.LinkOption\nimport java8.nio.file.Path\nimport java8.nio.file.attribute.FileAttributeView\nimport me.zhanghai.android.files.provider.remote.RemoteFileSystemProvider\nimport me.zhanghai.android.files.provider.remote.RemoteInterface\nimport java.net.URI\n\nopen class RootFileSystemProvider(scheme: String) : RemoteFileSystemProvider(\n    RemoteInterface { RootFileService.getRemoteFileSystemProviderInterface(scheme) }\n) {\n    override fun getScheme(): String {\n        throw AssertionError()\n    }\n\n    override fun newFileSystem(uri: URI, env: Map<String, *>): FileSystem {\n        throw AssertionError()\n    }\n\n    override fun getFileSystem(uri: URI): FileSystem {\n        throw AssertionError()\n    }\n\n    override fun getPath(uri: URI): Path {\n        throw AssertionError()\n    }\n\n    override fun <V : FileAttributeView> getFileAttributeView(\n        path: Path,\n        type: Class<V>,\n        vararg options: LinkOption\n    ): V? {\n        throw AssertionError()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/root/RootPosixFileAttributeView.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.root\n\nimport me.zhanghai.android.files.provider.common.PosixFileAttributeView\nimport me.zhanghai.android.files.provider.remote.RemoteInterface\nimport me.zhanghai.android.files.provider.remote.RemotePosixFileAttributeView\n\nopen class RootPosixFileAttributeView(\n    attributeView: PosixFileAttributeView\n) : RemotePosixFileAttributeView(\n    RemoteInterface { RootFileService.getRemotePosixFileAttributeViewInterface(attributeView) }\n) {\n    override fun name(): String {\n        throw AssertionError()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/root/RootPosixFileStore.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.root\n\nimport me.zhanghai.android.files.provider.common.PosixFileStore\nimport me.zhanghai.android.files.provider.remote.RemoteInterface\nimport me.zhanghai.android.files.provider.remote.RemotePosixFileStore\n\nclass RootPosixFileStore(fileStore: PosixFileStore) : RemotePosixFileStore(\n    RemoteInterface { RootFileService.getRemotePosixFileStoreInterface(fileStore) }\n)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/root/RootStrategy.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.root\n\nenum class RootStrategy {\n    NEVER,\n    AUTOMATIC,\n    ALWAYS\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/root/RootableFileSystem.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.root\n\nimport android.os.Parcelable\nimport java8.nio.file.FileStore\nimport java8.nio.file.FileSystem\nimport java8.nio.file.Path\nimport java8.nio.file.PathMatcher\nimport java8.nio.file.WatchService\nimport java8.nio.file.attribute.UserPrincipalLookupService\nimport java8.nio.file.spi.FileSystemProvider\nimport java.io.IOException\n\nabstract class RootableFileSystem(\n    localFileSystemCreator: (FileSystem) -> FileSystem,\n    rootFileSystemCreator: (FileSystem) -> RootFileSystem\n) : FileSystem(), Parcelable {\n    protected open val localFileSystem: FileSystem = localFileSystemCreator(this)\n    protected open val rootFileSystem: RootFileSystem = rootFileSystemCreator(this)\n\n    override fun provider(): FileSystemProvider = localFileSystem.provider()\n\n    @Throws(IOException::class)\n    override fun close() {\n        val wasOpen = localFileSystem.isOpen\n        localFileSystem.close()\n        // TODO: No need for this check?\n        if (wasOpen) {\n            rootFileSystem.close()\n        }\n    }\n\n    override fun isOpen(): Boolean = localFileSystem.isOpen\n\n    override fun isReadOnly(): Boolean = localFileSystem.isReadOnly\n\n    override fun getSeparator(): String = localFileSystem.separator\n\n    override fun getRootDirectories(): Iterable<Path> = localFileSystem.rootDirectories\n\n    // TODO: Consider using root? But when?\n    override fun getFileStores(): Iterable<FileStore> = localFileSystem.fileStores\n\n    override fun supportedFileAttributeViews(): Set<String> =\n        localFileSystem.supportedFileAttributeViews()\n\n    override fun getPath(first: String, vararg more: String): Path =\n        localFileSystem.getPath(first, *more)\n\n    override fun getPathMatcher(syntaxAndPattern: String): PathMatcher =\n        localFileSystem.getPathMatcher(syntaxAndPattern)\n\n    override fun getUserPrincipalLookupService(): UserPrincipalLookupService =\n        localFileSystem.userPrincipalLookupService\n\n    // We don't have RemoteWatchService for now, and I doubt we can have one.\n    @Throws(IOException::class)\n    override fun newWatchService(): WatchService = localFileSystem.newWatchService()\n\n    override fun equals(other: Any?): Boolean {\n        if (this === other) {\n            return true\n        }\n        if (javaClass != other?.javaClass) {\n            return false\n        }\n        other as RootableFileSystem\n        return localFileSystem == other.localFileSystem\n    }\n\n    override fun hashCode(): Int = localFileSystem.hashCode()\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/root/RootableFileSystemProvider.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.root\n\nimport java8.nio.channels.FileChannel\nimport java8.nio.channels.SeekableByteChannel\nimport java8.nio.file.AccessMode\nimport java8.nio.file.CopyOption\nimport java8.nio.file.DirectoryStream\nimport java8.nio.file.FileStore\nimport java8.nio.file.FileSystem\nimport java8.nio.file.LinkOption\nimport java8.nio.file.OpenOption\nimport java8.nio.file.Path\nimport java8.nio.file.attribute.BasicFileAttributes\nimport java8.nio.file.attribute.FileAttribute\nimport java8.nio.file.attribute.FileAttributeView\nimport java8.nio.file.spi.FileSystemProvider\nimport me.zhanghai.android.files.provider.common.PathObservable\nimport me.zhanghai.android.files.provider.common.PathObservableProvider\nimport me.zhanghai.android.files.provider.common.Searchable\nimport java.io.IOException\nimport java.io.InputStream\nimport java.io.OutputStream\nimport java.net.URI\n\nabstract class RootableFileSystemProvider(\n    localProviderCreator: (FileSystemProvider) -> FileSystemProvider,\n    rootProviderCreator: (FileSystemProvider) -> FileSystemProvider\n) : FileSystemProvider(), PathObservableProvider, Searchable {\n    protected open val localProvider: FileSystemProvider = localProviderCreator(this)\n    protected open val rootProvider: FileSystemProvider = rootProviderCreator(this)\n\n    override fun getScheme(): String = localProvider.scheme\n\n    @Throws(IOException::class)\n    override fun newFileSystem(uri: URI, env: Map<String, *>): FileSystem =\n        localProvider.newFileSystem(uri, env)\n\n    override fun getFileSystem(uri: URI): FileSystem = localProvider.getFileSystem(uri)\n\n    override fun getPath(uri: URI): Path = localProvider.getPath(uri)\n\n    @Throws(IOException::class)\n    override fun newInputStream(path: Path, vararg options: OpenOption): InputStream =\n        callRootable(path) { newInputStream(path, *options) }\n\n    @Throws(IOException::class)\n    override fun newOutputStream(path: Path, vararg options: OpenOption): OutputStream =\n        callRootable(path) { newOutputStream(path, *options) }\n\n    @Throws(IOException::class)\n    override fun newFileChannel(\n        path: Path,\n        options: Set<OpenOption>,\n        vararg attributes: FileAttribute<*>\n    ): FileChannel = callRootable(path) { newFileChannel(path, options, *attributes) }\n\n    @Throws(IOException::class)\n    override fun newByteChannel(\n        path: Path,\n        options: Set<OpenOption>,\n        vararg attributes: FileAttribute<*>\n    ): SeekableByteChannel = callRootable(path) { newByteChannel(path, options, *attributes) }\n\n    @Throws(IOException::class)\n    override fun newDirectoryStream(\n        directory: Path,\n        filter: DirectoryStream.Filter<in Path>\n    ): DirectoryStream<Path> = callRootable(directory) { newDirectoryStream(directory, filter) }\n\n    @Throws(IOException::class)\n    override fun createDirectory(directory: Path, vararg attributes: FileAttribute<*>) {\n        callRootable(directory) { createDirectory(directory, *attributes) }\n    }\n\n    @Throws(IOException::class)\n    override fun createSymbolicLink(link: Path, target: Path, vararg attributes: FileAttribute<*>) {\n        callRootable(link, target) { createSymbolicLink(link, target, *attributes) }\n    }\n\n    @Throws(IOException::class)\n    override fun createLink(link: Path, existing: Path) {\n        callRootable(link, existing) {createLink(link, existing) }\n    }\n\n    @Throws(IOException::class)\n    override fun delete(path: Path) {\n        callRootable(path) { delete(path) }\n    }\n\n    @Throws(IOException::class)\n    override fun readSymbolicLink(link: Path): Path = callRootable(link) { readSymbolicLink(link) }\n\n    @Throws(IOException::class)\n    override fun copy(source: Path, target: Path, vararg options: CopyOption) {\n        callRootable(source, target) { copy(source, target, *options) }\n    }\n\n    @Throws(IOException::class)\n    override fun move(source: Path, target: Path, vararg options: CopyOption) {\n        callRootable(source, target) { move(source, target, *options) }\n    }\n\n    @Throws(IOException::class)\n    override fun isSameFile(path: Path, path2: Path): Boolean =\n        callRootable(path, path2, true) { isSameFile(path, path2) }\n\n    @Throws(IOException::class)\n    override fun isHidden(path: Path): Boolean = callRootable(path, true) { isHidden(path) }\n\n    @Throws(IOException::class)\n    override fun getFileStore(path: Path): FileStore = localProvider.getFileStore(path)\n\n    @Throws(IOException::class)\n    override fun checkAccess(path: Path, vararg modes: AccessMode) {\n        callRootable(path) {checkAccess(path, *modes) }\n    }\n\n    override fun <V : FileAttributeView> getFileAttributeView(\n        path: Path,\n        type: Class<V>,\n        vararg options: LinkOption\n    ): V? = localProvider.getFileAttributeView(path, type, *options)\n\n    @Throws(IOException::class)\n    override fun <A : BasicFileAttributes> readAttributes(\n        path: Path,\n        type: Class<A>,\n        vararg options: LinkOption\n    ): A = callRootable(path, true) { readAttributes(path, type, *options) }\n\n    @Throws(IOException::class)\n    override fun readAttributes(\n        path: Path,\n        attributes: String,\n        vararg options: LinkOption\n    ): Map<String, Any> = callRootable(path, true) { readAttributes(path, attributes, *options) }\n\n    @Throws(IOException::class)\n    override fun setAttribute(\n        path: Path,\n        attribute: String,\n        value: Any,\n        vararg options: LinkOption\n    ) {\n        callRootable(path, true) { setAttribute(path, attribute, value, *options) }\n    }\n\n    @Throws(IOException::class)\n    override fun observe(path: Path, intervalMillis: Long): PathObservable {\n        if (localProvider !is PathObservableProvider) {\n            throw UnsupportedOperationException()\n        }\n        return callRootable(path) {\n            // observe() may or may not be able to detect denied access, and that is expensive on\n            // Linux (having to create the WatchService first before registering a WatchKey). So we\n            // check the access beforehand.\n            if (this == localProvider) {\n                val attributes = try {\n                    readAttributes(path, BasicFileAttributes::class.java)\n                } catch (ignored: IOException) {\n                    readAttributes(path, BasicFileAttributes::class.java, LinkOption.NOFOLLOW_LINKS)\n                }\n                if (attributes.isSymbolicLink) {\n                    readSymbolicLink(path)\n                } else {\n                    checkAccess(path, AccessMode.READ)\n                }\n            }\n            (this as PathObservableProvider).observe(path, intervalMillis)\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun search(\n        directory: Path,\n        query: String,\n        intervalMillis: Long,\n        listener: (List<Path>) -> Unit\n    ) {\n        callRootable(directory) {\n            (this as Searchable).search(directory, query, intervalMillis, listener)\n        }\n    }\n\n    @Throws(IOException::class)\n    private fun <R> callRootable(\n        path: Path,\n        isAttributeAccess: Boolean = false,\n        block: FileSystemProvider.() -> R\n    ): R = callRootable(path, isAttributeAccess, localProvider, rootProvider, block)\n\n    @Throws(IOException::class)\n    private fun <R> callRootable(\n        path1: Path,\n        path2: Path,\n        isAttributeAccess: Boolean = false,\n        block: FileSystemProvider.() -> R\n    ): R = callRootable(path1, path2, isAttributeAccess, localProvider, rootProvider, block)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/root/RootablePath.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.root\n\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.settings.Settings\nimport me.zhanghai.android.files.util.valueCompat\nimport java.io.IOException\n\ninterface RootablePath {\n    fun isRootRequired(isAttributeAccess: Boolean): Boolean\n}\n\nprivate val rootStrategy: RootStrategy\n    get() = if (isRunningAsRoot) RootStrategy.NEVER else Settings.ROOT_STRATEGY.valueCompat\n\n@Throws(IOException::class)\nfun <T, R> callRootable(\n    path: Path,\n    isAttributeAccess: Boolean,\n    localObject: T,\n    rootObject: T, block: T.() -> R\n): R {\n    path as? RootablePath ?: throw IllegalArgumentException(\"$path is not a RootablePath\")\n    return when (rootStrategy) {\n        RootStrategy.NEVER -> localObject.block()\n        RootStrategy.AUTOMATIC ->\n            if (path.isRootRequired(isAttributeAccess)) {\n                rootObject.block()\n            } else {\n                localObject.block()\n            }\n        RootStrategy.ALWAYS -> rootObject.block()\n    }\n}\n\n@Throws(IOException::class)\nfun <T, R> callRootable(\n    path1: Path,\n    path2: Path,\n    isAttributeAccess: Boolean,\n    localObject: T,\n    rootObject: T,\n    block: T.() -> R\n): R {\n    path1 as? RootablePath ?: throw IllegalArgumentException(\"$path1 is not a RootablePath\")\n    path2 as? RootablePath ?: throw IllegalArgumentException(\"$path2 is not a RootablePath\")\n    return when (rootStrategy) {\n        RootStrategy.NEVER ->\n            localObject.block()\n        RootStrategy.AUTOMATIC ->\n            if (path1.isRootRequired(isAttributeAccess)\n                || path2.isRootRequired(isAttributeAccess)) {\n                rootObject.block()\n            } else {\n                localObject.block()\n            }\n        RootStrategy.ALWAYS ->\n            rootObject.block()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/root/RootablePosixFileAttributeView.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.root\n\nimport android.os.Parcelable\nimport java8.nio.file.Path\nimport java8.nio.file.attribute.FileTime\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.PosixFileAttributeView\nimport me.zhanghai.android.files.provider.common.PosixFileAttributes\nimport me.zhanghai.android.files.provider.common.PosixFileModeBit\nimport me.zhanghai.android.files.provider.common.PosixGroup\nimport me.zhanghai.android.files.provider.common.PosixUser\nimport java.io.IOException\n\nabstract class RootablePosixFileAttributeView(\n    private val path: Path,\n    private val localAttributeView: PosixFileAttributeView,\n    rootAttributeViewCreator: (PosixFileAttributeView) -> RootPosixFileAttributeView\n) : PosixFileAttributeView, Parcelable {\n    private val rootAttributeView: RootPosixFileAttributeView = rootAttributeViewCreator(this)\n\n    override fun name(): String = localAttributeView.name()\n\n    @Throws(IOException::class)\n    override fun setTimes(\n        lastModifiedTime: FileTime?,\n        lastAccessTime: FileTime?,\n        createTime: FileTime?\n    ) {\n        callRootable(path) { setTimes(lastModifiedTime, lastAccessTime, createTime) }\n    }\n\n    @Throws(IOException::class)\n    override fun readAttributes(): PosixFileAttributes = callRootable(path) { readAttributes() }\n\n    @Throws(IOException::class)\n    override fun setOwner(owner: PosixUser) {\n        callRootable(path) { setOwner(owner) }\n    }\n\n    @Throws(IOException::class)\n    override fun setGroup(group: PosixGroup) {\n        callRootable(path) { setGroup(group) }\n    }\n\n    @Throws(IOException::class)\n    override fun setMode(mode: Set<PosixFileModeBit>) {\n        callRootable(path) { setMode(mode) }\n    }\n\n    @Throws(IOException::class)\n    override fun setSeLinuxContext(context: ByteString) {\n        callRootable(path) { setSeLinuxContext(context) }\n    }\n\n    @Throws(IOException::class)\n    override fun restoreSeLinuxContext() {\n        callRootable(path) { restoreSeLinuxContext() }\n    }\n\n    @Throws(IOException::class)\n    private fun <R> callRootable(path: Path, block: PosixFileAttributeView.() -> R): R =\n        callRootable(path, true, localAttributeView, rootAttributeView, block)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/root/RootablePosixFileStore.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.root\n\nimport android.os.Parcelable\nimport java8.nio.file.Path\nimport java8.nio.file.attribute.FileAttributeView\nimport me.zhanghai.android.files.provider.common.PosixFileStore\nimport java.io.IOException\n\nabstract class RootablePosixFileStore(\n    private val path: Path,\n    private val localFileStore: PosixFileStore,\n    rootFileStoreCreator: (PosixFileStore) -> RootPosixFileStore\n) : PosixFileStore(), Parcelable {\n    private val rootFileStore: RootPosixFileStore = rootFileStoreCreator(this)\n\n    @Throws(IOException::class)\n    override fun refresh() {\n        localFileStore.refresh()\n    }\n\n    override fun name(): String = localFileStore.name()\n\n    override fun type(): String = localFileStore.type()\n\n    override fun isReadOnly(): Boolean = localFileStore.isReadOnly\n\n    @Throws(IOException::class)\n    override fun setReadOnly(readOnly: Boolean) {\n        callRootable(path) {\n            isReadOnly = readOnly\n            if (this == rootFileStore) {\n                localFileStore.refresh()\n            }\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun getTotalSpace(): Long = callRootable(path) { totalSpace }\n\n    @Throws(IOException::class)\n    override fun getUsableSpace(): Long = callRootable(path) { usableSpace }\n\n    @Throws(IOException::class)\n    override fun getUnallocatedSpace(): Long = callRootable(path) { unallocatedSpace }\n\n    override fun supportsFileAttributeView(type: Class<out FileAttributeView>): Boolean =\n        localFileStore.supportsFileAttributeView(type)\n\n    override fun supportsFileAttributeView(name: String): Boolean =\n        localFileStore.supportsFileAttributeView(name)\n\n    @Throws(IOException::class)\n    private fun <R> callRootable(path: Path, block: PosixFileStore.() -> R): R =\n        callRootable(path, true, localFileStore, rootFileStore, block)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/root/SuiFileServiceLauncher.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.root\n\nimport android.content.ComponentName\nimport android.content.ServiceConnection\nimport android.content.pm.PackageManager\nimport android.os.Build\nimport android.os.IBinder\nimport androidx.annotation.ChecksSdkIntAtLeast\nimport androidx.annotation.Keep\nimport androidx.annotation.RequiresApi\nimport kotlinx.coroutines.TimeoutCancellationException\nimport kotlinx.coroutines.runBlocking\nimport kotlinx.coroutines.suspendCancellableCoroutine\nimport kotlinx.coroutines.withTimeout\nimport me.zhanghai.android.files.BuildConfig\nimport me.zhanghai.android.files.app.application\nimport me.zhanghai.android.files.provider.remote.IRemoteFileService\nimport me.zhanghai.android.files.provider.remote.RemoteFileServiceInterface\nimport me.zhanghai.android.files.provider.remote.RemoteFileSystemException\nimport rikka.shizuku.Shizuku\nimport rikka.sui.Sui\nimport kotlin.coroutines.resume\nimport kotlin.coroutines.resumeWithException\n\nobject SuiFileServiceLauncher {\n    private val lock = Any()\n\n    private var isSuiIntialized = false\n\n    @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.M)\n    fun isSuiAvailable(): Boolean {\n        synchronized(lock) {\n            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {\n                return false\n            }\n            if (!isSuiIntialized) {\n                Sui.init(application.packageName)\n                isSuiIntialized = true\n            }\n            return Sui.isSui()\n        }\n    }\n\n    @RequiresApi(Build.VERSION_CODES.M)\n    @Throws(RemoteFileSystemException::class)\n    fun launchService(): IRemoteFileService {\n        synchronized(lock) {\n            if (!isSuiAvailable()) {\n                throw RemoteFileSystemException(\"Sui isn't available\")\n            }\n            if (Shizuku.checkSelfPermission() != PackageManager.PERMISSION_GRANTED) {\n                val granted = try {\n                    runBlocking<Boolean> {\n                        suspendCancellableCoroutine { continuation ->\n                            val listener = object : Shizuku.OnRequestPermissionResultListener {\n                                override fun onRequestPermissionResult(\n                                    requestCode: Int,\n                                    grantResult: Int\n                                ) {\n                                    Shizuku.removeRequestPermissionResultListener(this)\n                                    val granted = grantResult == PackageManager.PERMISSION_GRANTED\n                                    continuation.resume(granted)\n                                }\n                            }\n                            Shizuku.addRequestPermissionResultListener(listener)\n                            continuation.invokeOnCancellation {\n                                Shizuku.removeRequestPermissionResultListener(listener)\n                            }\n                            Shizuku.requestPermission(listener.hashCode())\n                        }\n                    }\n                } catch (e: InterruptedException) {\n                    throw RemoteFileSystemException(e)\n                }\n                if (!granted) {\n                    throw RemoteFileSystemException(\"Sui permission isn't granted\")\n                }\n            }\n            return try {\n                runBlocking {\n                    try {\n                        withTimeout(RootFileService.TIMEOUT_MILLIS) {\n                            suspendCancellableCoroutine { continuation ->\n                                val serviceArgs = Shizuku.UserServiceArgs(\n                                    ComponentName(application, SuiFileServiceInterface::class.java)\n                                )\n                                    .debuggable(BuildConfig.DEBUG)\n                                    .daemon(false)\n                                    .processNameSuffix(\"sui\")\n                                    .version(BuildConfig.VERSION_CODE)\n                                val connection = object : ServiceConnection {\n                                    override fun onServiceConnected(\n                                        name: ComponentName,\n                                        service: IBinder\n                                    ) {\n                                        val serviceInterface =\n                                            IRemoteFileService.Stub.asInterface(service)\n                                        continuation.resume(serviceInterface)\n                                    }\n\n                                    override fun onServiceDisconnected(name: ComponentName) {\n                                        if (continuation.isActive) {\n                                            continuation.resumeWithException(\n                                                RemoteFileSystemException(\n                                                    \"Sui service disconnected\"\n                                                )\n                                            )\n                                        }\n                                    }\n\n                                    override fun onBindingDied(name: ComponentName) {\n                                        if (continuation.isActive) {\n                                            continuation.resumeWithException(\n                                                RemoteFileSystemException(\"Sui binding died\")\n                                            )\n                                        }\n                                    }\n\n                                    override fun onNullBinding(name: ComponentName) {\n                                        if (continuation.isActive) {\n                                            continuation.resumeWithException(\n                                                RemoteFileSystemException(\"Sui binding is null\")\n                                            )\n                                        }\n                                    }\n                                }\n                                Shizuku.bindUserService(serviceArgs, connection)\n                                continuation.invokeOnCancellation {\n                                    Shizuku.unbindUserService(serviceArgs, connection, true)\n                                }\n                            }\n                        }\n                    } catch (e: TimeoutCancellationException) {\n                        throw RemoteFileSystemException(e)\n                    }\n                }\n            } catch (e: InterruptedException) {\n                throw RemoteFileSystemException(e)\n            }\n        }\n    }\n}\n\n@Keep\n@RequiresApi(Build.VERSION_CODES.M)\nclass SuiFileServiceInterface : RemoteFileServiceInterface() {\n    init {\n        RootFileService.main()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/sftp/OpenOptionsSftpExtensions.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.sftp\n\nimport java8.nio.file.StandardOpenOption\nimport me.zhanghai.android.files.provider.common.OpenOptions\nimport me.zhanghai.android.files.util.enumSetOf\nimport net.schmizz.sshj.sftp.OpenMode\n\ninternal fun OpenOptions.toSftpFlags(): Set<OpenMode> =\n    enumSetOf<OpenMode>().apply {\n        if (read && write) {\n            this += OpenMode.READ\n            this += OpenMode.WRITE\n        } else if (write) {\n            this += OpenMode.WRITE\n        } else {\n            this += OpenMode.READ\n        }\n        if (append) {\n            this += OpenMode.APPEND\n        }\n        if (truncateExisting) {\n            this += OpenMode.TRUNC\n        }\n        if (createNew) {\n            this += OpenMode.CREAT\n            this += OpenMode.EXCL\n        } else if (create) {\n            this += OpenMode.CREAT\n        }\n        if (deleteOnClose) {\n            throw UnsupportedOperationException(StandardOpenOption.DELETE_ON_CLOSE.toString())\n        }\n        if (sync) {\n            throw UnsupportedOperationException(StandardOpenOption.SYNC.toString())\n        }\n        if (dsync) {\n            throw UnsupportedOperationException(StandardOpenOption.DSYNC.toString())\n        }\n    }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/sftp/PathSftpExtensions.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.sftp\n\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.provider.sftp.client.Authority\n\nfun Authority.createSftpRootPath(): Path =\n    SftpFileSystemProvider.getOrNewFileSystem(this).rootDirectory\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/sftp/PosixFileModeSftpExtensions.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.sftp\n\nimport me.zhanghai.android.files.provider.common.PosixFileModeBit\nimport me.zhanghai.android.files.provider.common.toInt\nimport net.schmizz.sshj.sftp.FileAttributes\n\nfun Set<PosixFileModeBit>.toSftpAttributes(): FileAttributes =\n    FileAttributes.Builder().withPermissions(toInt()).build()\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/sftp/SftpCopyMove.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.sftp\n\nimport java.io.IOException\nimport java.time.Instant\nimport java8.nio.file.FileAlreadyExistsException\nimport java8.nio.file.FileSystemException\nimport java8.nio.file.NoSuchFileException\nimport java8.nio.file.StandardCopyOption\nimport me.zhanghai.android.files.provider.common.CopyOptions\nimport me.zhanghai.android.files.provider.common.copyTo\nimport me.zhanghai.android.files.provider.common.newInputStream\nimport me.zhanghai.android.files.provider.common.newOutputStream\nimport me.zhanghai.android.files.provider.sftp.client.Client\nimport me.zhanghai.android.files.provider.sftp.client.ClientException\nimport me.zhanghai.android.files.util.enumSetOf\nimport net.schmizz.sshj.sftp.FileAttributes\nimport net.schmizz.sshj.sftp.FileMode\nimport net.schmizz.sshj.sftp.OpenMode\n\ninternal object SftpCopyMove {\n    @Throws(IOException::class)\n    fun copy(source: SftpPath, target: SftpPath, copyOptions: CopyOptions) {\n        if (copyOptions.atomicMove) {\n            throw UnsupportedOperationException(StandardCopyOption.ATOMIC_MOVE.toString())\n        }\n        val sourceAttributes = try {\n            if (copyOptions.noFollowLinks) Client.lstat(source) else Client.stat(source)\n        } catch (e: ClientException) {\n            throw e.toFileSystemException(source.toString())\n        }\n        if (!sourceAttributes.has(FileAttributes.Flag.MODE)) {\n            throw FileSystemException(\n                source.toString(), null, \"Missing SSH_FILEXFER_ATTR_PERMISSIONS\"\n            )\n        }\n        val targetAttributes = try {\n            Client.lstat(target)\n        } catch (e: ClientException) {\n            val exception = e.toFileSystemException(target.toString())\n            if (exception !is NoSuchFileException) {\n                throw exception\n            }\n            // Ignored.\n            null\n        }\n        val sourceSize = if (sourceAttributes.has(FileAttributes.Flag.SIZE)) {\n            sourceAttributes.size\n        } else {\n            0\n        }\n        if (targetAttributes != null) {\n            if (source == target) {\n                copyOptions.progressListener?.invoke(sourceSize)\n                return\n            }\n            if (!copyOptions.replaceExisting) {\n                throw FileAlreadyExistsException(source.toString(), target.toString(), null)\n            }\n            // Symbolic links may not be supported so we cannot simply delete the target here.\n        }\n        val sourceType = sourceAttributes.type\n        val sourceModeAttributes = FileAttributes.Builder()\n            .apply {\n                if (sourceAttributes.has(FileAttributes.Flag.MODE)) {\n                    withPermissions(sourceAttributes.mode.mask)\n                }\n            }\n            .build()\n        when (sourceType) {\n            FileMode.Type.REGULAR -> {\n                if (targetAttributes != null) {\n                    try {\n                        Client.remove(target)\n                    } catch (e: ClientException) {\n                        val exception = e.toFileSystemException(target.toString())\n                        if (exception !is NoSuchFileException) {\n                            throw exception\n                        }\n                    }\n                }\n                val sourceInputStream = try {\n                    Client.openByteChannel(source, enumSetOf(OpenMode.READ), FileAttributes.EMPTY)\n                } catch (e: ClientException) {\n                    throw e.toFileSystemException(source.toString())\n                }.newInputStream()\n                try {\n                    val targetFlags = enumSetOf(OpenMode.WRITE, OpenMode.TRUNC, OpenMode.CREAT)\n                    if (!copyOptions.replaceExisting) {\n                        targetFlags += OpenMode.EXCL\n                    }\n                    val targetOutputStream = try {\n                        Client.openByteChannel(target, targetFlags, sourceModeAttributes)\n                    } catch (e: ClientException) {\n                        throw e.toFileSystemException(target.toString())\n                    }.newOutputStream()\n                    var successful = false\n                    try {\n                        sourceInputStream.copyTo(\n                            targetOutputStream, copyOptions.progressIntervalMillis,\n                            copyOptions.progressListener\n                        )\n                        successful = true\n                    } finally {\n                        try {\n                            targetOutputStream.close()\n                        } catch (e: IOException) {\n                            throw ClientException(e).toFileSystemException(target.toString())\n                        } finally {\n                            if (!successful) {\n                                try {\n                                    Client.remove(target)\n                                } catch (e: ClientException) {\n                                    e.printStackTrace()\n                                }\n                            }\n                        }\n                    }\n                } finally {\n                    try {\n                        sourceInputStream.close()\n                    } catch (e: IOException) {\n                        throw ClientException(e).toFileSystemException(source.toString())\n                    }\n                }\n            }\n            FileMode.Type.DIRECTORY -> {\n                if (targetAttributes != null) {\n                    try {\n                        Client.remove(target)\n                    } catch (e: ClientException) {\n                        val exception = e.toFileSystemException(target.toString())\n                        if (exception !is NoSuchFileException) {\n                            throw exception\n                        }\n                    }\n                }\n                try {\n                    Client.mkdir(target, sourceModeAttributes)\n                } catch (e: ClientException) {\n                    throw e.toFileSystemException(target.toString())\n                }\n                copyOptions.progressListener?.invoke(sourceSize)\n            }\n            FileMode.Type.SYMLINK -> {\n                val sourceTarget = try {\n                    Client.readlink(source)\n                } catch (e: ClientException) {\n                    throw e.toFileSystemException(source.toString())\n                }\n                try {\n                    Client.symlink(target, sourceTarget)\n                } catch (e: ClientException) {\n                    val exception = e.toFileSystemException(target.toString())\n                    if (exception is FileAlreadyExistsException && copyOptions.replaceExisting) {\n                        try {\n                            Client.remove(target)\n                        } catch (e2: ClientException) {\n                            if (e2.toFileSystemException(target.toString())\n                                    !is NoSuchFileException) {\n                                e2.addSuppressed(exception)\n                                throw e2.toFileSystemException(target.toString())\n                            }\n                        }\n                        try {\n                            Client.symlink(target, sourceTarget)\n                        } catch (e2: ClientException) {\n                            e2.addSuppressed(exception)\n                            throw e2.toFileSystemException(target.toString())\n                        }\n                    }\n                    throw e.toFileSystemException(target.toString())\n                }\n                copyOptions.progressListener?.invoke(sourceSize)\n            }\n            else -> throw FileSystemException(source.toString(), null, \"type $sourceType\")\n        }\n        // We don't take error when copying attribute fatal, so errors will only be logged from now\n        // on.\n        if (sourceType != FileMode.Type.SYMLINK) {\n            val attributes = FileAttributes.Builder()\n                .apply {\n                    if (copyOptions.copyAttributes\n                        && sourceAttributes.has(FileAttributes.Flag.UIDGID)) {\n                        withUIDGID(sourceAttributes.uid, sourceAttributes.gid)\n                    }\n                    if (sourceAttributes.type != FileMode.Type.SYMLINK\n                        && sourceAttributes.has(FileAttributes.Flag.MODE)) {\n                        withPermissions(sourceAttributes.mode.mask)\n                    }\n                    if (sourceAttributes.has(FileAttributes.Flag.ACMODTIME)) {\n                        withAtimeMtime(\n                            if (copyOptions.copyAttributes) {\n                                sourceAttributes.atime\n                            } else {\n                                // We cannot leave atime unchanged in SFTP, but since we've just\n                                // written the file, its atime is simply now.\n                                Instant.now().epochSecond\n                            }, sourceAttributes.mtime\n                        )\n                    }\n                }\n                .build()\n            try {\n                Client.setstat(target, attributes)\n            } catch (e: ClientException) {\n                e.printStackTrace()\n            }\n        }\n    }\n\n    @Throws(IOException::class)\n    fun move(source: SftpPath, target: SftpPath, copyOptions: CopyOptions) {\n        val sourceAttributes = try {\n            Client.lstat(source)\n        } catch (e: ClientException) {\n            throw e.toFileSystemException(source.toString())\n        }\n        if (!sourceAttributes.has(FileAttributes.Flag.MODE)) {\n            throw FileSystemException(\n                source.toString(), null, \"Missing SSH_FILEXFER_ATTR_PERMISSIONS\"\n            )\n        }\n        val targetAttributes = try {\n            Client.lstat(target)\n        } catch (e: ClientException) {\n            val exception = e.toFileSystemException(target.toString())\n            if (exception !is NoSuchFileException) {\n                throw exception\n            }\n            // Ignored.\n            null\n        }\n        val sourceSize = if (sourceAttributes.has(FileAttributes.Flag.SIZE)) {\n            sourceAttributes.size\n        } else {\n            0\n        }\n        if (targetAttributes != null) {\n            if (source == target) {\n                copyOptions.progressListener?.invoke(sourceSize)\n                return\n            }\n            if (!copyOptions.replaceExisting) {\n                throw FileAlreadyExistsException(source.toString(), target.toString(), null)\n            }\n            try {\n                Client.remove(target)\n            } catch (e: ClientException) {\n                throw e.toFileSystemException(target.toString())\n            }\n        }\n        var renameSuccessful = false\n        try {\n            Client.rename(source, target)\n            renameSuccessful = true\n        } catch (e: ClientException) {\n            if (copyOptions.atomicMove) {\n                throw e.toFileSystemException(source.toString(), target.toString())\n            }\n            // Ignored.\n        }\n        if (renameSuccessful) {\n            copyOptions.progressListener?.invoke(sourceSize)\n            return\n        }\n        if (copyOptions.atomicMove) {\n            throw AssertionError()\n        }\n        var copyOptions = copyOptions\n        if (!copyOptions.copyAttributes || !copyOptions.noFollowLinks) {\n            copyOptions = CopyOptions(\n                copyOptions.replaceExisting, true, false, true, copyOptions.progressIntervalMillis,\n                copyOptions.progressListener\n            )\n        }\n        copy(source, target, copyOptions)\n        try {\n            Client.remove(source)\n        } catch (e: ClientException) {\n            if (e.toFileSystemException(source.toString()) !is NoSuchFileException) {\n                try {\n                    Client.remove(target)\n                } catch (e2: ClientException) {\n                    e.addSuppressed(e2.toFileSystemException(target.toString()))\n                }\n            }\n            throw e.toFileSystemException(source.toString())\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/sftp/SftpFileAttributeView.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.sftp\n\nimport java8.nio.file.LinkOption\nimport java8.nio.file.attribute.FileTime\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.PosixFileAttributeView\nimport me.zhanghai.android.files.provider.common.PosixFileModeBit\nimport me.zhanghai.android.files.provider.common.PosixGroup\nimport me.zhanghai.android.files.provider.common.PosixUser\nimport me.zhanghai.android.files.provider.common.toInt\nimport me.zhanghai.android.files.provider.sftp.client.Client\nimport me.zhanghai.android.files.provider.sftp.client.ClientException\nimport net.schmizz.sshj.sftp.FileAttributes\nimport java.io.IOException\n\ninternal class SftpFileAttributeView(\n    private val path: SftpPath,\n    private val noFollowLinks: Boolean\n) : PosixFileAttributeView {\n    override fun name(): String = NAME\n\n    @Throws(IOException::class)\n    override fun readAttributes(): SftpFileAttributes {\n        val attributes = getAttributes()\n        return SftpFileAttributes.from(attributes, path)\n    }\n\n    override fun setTimes(\n        lastModifiedTime: FileTime?,\n        lastAccessTime: FileTime?,\n        createTime: FileTime?\n    ) {\n        if (lastAccessTime == null && lastModifiedTime == null) {\n            // Only throw if caller is trying to set only create time, so that foreign copy move can\n            // still set other times.\n            if (createTime != null) {\n                throw UnsupportedOperationException(\"createTime\")\n            }\n            return\n        }\n        if (noFollowLinks) {\n            throw UnsupportedOperationException(LinkOption.NOFOLLOW_LINKS.toString())\n        }\n        val currentAttributes = if (lastAccessTime == null || lastModifiedTime == null) {\n            getAttributes().also {\n                if (!it.has(FileAttributes.Flag.ACMODTIME)) {\n                    throw UnsupportedOperationException(\"Missing SSH_FILEXFER_ACMODTIME\")\n                }\n            }\n        } else {\n            null\n        }\n        val attributes = FileAttributes.Builder()\n            .withAtimeMtime(\n                lastAccessTime?.toInstant()?.epochSecond ?: currentAttributes!!.atime,\n                lastModifiedTime?.toInstant()?.epochSecond ?: currentAttributes!!.mtime\n            )\n            .build()\n        try {\n            Client.setstat(path, attributes)\n        } catch (e: ClientException) {\n            throw e.toFileSystemException(path.toString())\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun setOwner(owner: PosixUser) {\n        if (noFollowLinks) {\n            throw UnsupportedOperationException(LinkOption.NOFOLLOW_LINKS.toString())\n        }\n        val currentAttributes = getAttributes()\n        if (!currentAttributes.has(FileAttributes.Flag.UIDGID)) {\n            throw UnsupportedOperationException(\"Missing SSH_FILEXFER_ATTR_UIDGID\")\n        }\n        val attributes = FileAttributes.Builder()\n            .withUIDGID(owner.id, currentAttributes.gid)\n            .build()\n        try {\n            Client.setstat(path, attributes)\n        } catch (e: ClientException) {\n            throw e.toFileSystemException(path.toString())\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun setGroup(group: PosixGroup) {\n        if (noFollowLinks) {\n            throw UnsupportedOperationException(LinkOption.NOFOLLOW_LINKS.toString())\n        }\n        val currentAttributes = getAttributes()\n        if (!currentAttributes.has(FileAttributes.Flag.UIDGID)) {\n            throw UnsupportedOperationException(\"Missing SSH_FILEXFER_ATTR_UIDGID\")\n        }\n        val attributes = FileAttributes.Builder()\n            .withUIDGID(currentAttributes.uid, group.id)\n            .build()\n        try {\n            Client.setstat(path, attributes)\n        } catch (e: ClientException) {\n            throw e.toFileSystemException(path.toString())\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun setMode(mode: Set<PosixFileModeBit>) {\n        if (noFollowLinks) {\n            throw UnsupportedOperationException(\"Cannot set mode for symbolic links\")\n        }\n        val attributes = FileAttributes.Builder()\n            .withPermissions(mode.toInt())\n            .build()\n        try {\n            Client.setstat(path, attributes)\n        } catch (e: ClientException) {\n            throw e.toFileSystemException(path.toString())\n        }\n    }\n\n    @Throws(IOException::class)\n    private fun getAttributes(): FileAttributes =\n        try {\n            if (noFollowLinks) Client.lstat(path) else Client.stat(path)\n        } catch (e: ClientException) {\n            throw e.toFileSystemException(path.toString())\n        }\n\n    @Throws(IOException::class)\n    override fun setSeLinuxContext(context: ByteString) {\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun restoreSeLinuxContext() {\n        throw UnsupportedOperationException()\n    }\n\n    companion object {\n        private val NAME = SftpFileSystemProvider.scheme\n\n        val SUPPORTED_NAMES = setOf(\"basic\", \"posix\", NAME)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/sftp/SftpFileAttributes.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.sftp\n\nimport android.os.Parcelable\nimport java.time.Instant\nimport java8.nio.file.attribute.FileTime\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.provider.common.AbstractPosixFileAttributes\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.EPOCH\nimport me.zhanghai.android.files.provider.common.FileTimeParceler\nimport me.zhanghai.android.files.provider.common.PosixFileMode\nimport me.zhanghai.android.files.provider.common.PosixFileModeBit\nimport me.zhanghai.android.files.provider.common.PosixFileType\nimport me.zhanghai.android.files.provider.common.PosixGroup\nimport me.zhanghai.android.files.provider.common.PosixUser\nimport net.schmizz.sshj.sftp.FileAttributes\n\n@Parcelize\ninternal data class SftpFileAttributes(\n    override val lastModifiedTime: @WriteWith<FileTimeParceler> FileTime,\n    override val lastAccessTime: @WriteWith<FileTimeParceler> FileTime,\n    override val creationTime: @WriteWith<FileTimeParceler> FileTime,\n    override val type: PosixFileType,\n    override val size: Long,\n    override val fileKey: Parcelable,\n    override val owner: PosixUser?,\n    override val group: PosixGroup?,\n    override val mode: Set<PosixFileModeBit>?,\n    override val seLinuxContext: ByteString?\n) : AbstractPosixFileAttributes() {\n    companion object {\n        fun from(attributes: FileAttributes, path: SftpPath): SftpFileAttributes {\n            val lastModifiedTime: FileTime\n            val lastAccessTime: FileTime\n            if (attributes.has(FileAttributes.Flag.ACMODTIME)) {\n                lastModifiedTime = FileTime.from(Instant.ofEpochSecond(attributes.mtime))\n                lastAccessTime = FileTime.from(Instant.ofEpochSecond(attributes.atime))\n            } else {\n                lastModifiedTime = FileTime::class.EPOCH\n                lastAccessTime = lastModifiedTime\n            }\n            val creationTime = lastModifiedTime\n            val type: PosixFileType\n            val mode: Set<PosixFileModeBit>?\n            if (attributes.has(FileAttributes.Flag.MODE)) {\n                val modeInt = attributes.mode.mask\n                type = PosixFileType.fromMode(modeInt)\n                mode = PosixFileMode.fromInt(modeInt)\n            } else {\n                type = PosixFileType.REGULAR_FILE\n                mode = null\n            }\n            val size = if (attributes.has(FileAttributes.Flag.SIZE)) attributes.size else 0\n            val fileKey = path\n            val owner: PosixUser?\n            val group: PosixGroup?\n            if (attributes.has(FileAttributes.Flag.UIDGID)) {\n                owner = PosixUser(attributes.uid, null)\n                group = PosixGroup(attributes.gid, null)\n            } else {\n                owner = null\n                group = null\n            }\n            val seLinuxContext = null\n            return SftpFileAttributes(\n                lastModifiedTime, lastAccessTime, creationTime, type, size, fileKey, owner, group,\n                mode, seLinuxContext\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/sftp/SftpFileSystem.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.sftp\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport java8.nio.file.FileStore\nimport java8.nio.file.FileSystem\nimport java8.nio.file.Path\nimport java8.nio.file.PathMatcher\nimport java8.nio.file.WatchService\nimport java8.nio.file.attribute.UserPrincipalLookupService\nimport java8.nio.file.spi.FileSystemProvider\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.ByteStringBuilder\nimport me.zhanghai.android.files.provider.common.ByteStringListPathCreator\nimport me.zhanghai.android.files.provider.common.LocalWatchService\nimport me.zhanghai.android.files.provider.common.toByteString\nimport me.zhanghai.android.files.provider.sftp.client.Authority\nimport me.zhanghai.android.files.util.readParcelable\nimport java.io.IOException\n\ninternal class SftpFileSystem(\n    private val provider: SftpFileSystemProvider,\n    val authority: Authority\n) : FileSystem(), ByteStringListPathCreator, Parcelable {\n    val rootDirectory = SftpPath(this, SEPARATOR_BYTE_STRING)\n\n    init {\n        if (!rootDirectory.isAbsolute) {\n            throw AssertionError(\"Root directory must be absolute\")\n        }\n        if (rootDirectory.nameCount != 0) {\n            throw AssertionError(\"Root directory must contain no names\")\n        }\n    }\n\n    private val lock = Any()\n\n    private var isOpen = true\n\n    val defaultDirectory: SftpPath\n        get() = rootDirectory\n\n    override fun provider(): FileSystemProvider = provider\n\n    override fun close() {\n        synchronized(lock) {\n            if (!isOpen) {\n                return\n            }\n            provider.removeFileSystem(this)\n            isOpen = false\n        }\n    }\n\n    override fun isOpen(): Boolean = synchronized(lock) { isOpen }\n\n    override fun isReadOnly(): Boolean = false\n\n    override fun getSeparator(): String = SEPARATOR_STRING\n\n    override fun getRootDirectories(): Iterable<Path> = listOf(rootDirectory)\n\n    override fun getFileStores(): Iterable<FileStore> {\n        // TODO\n        throw UnsupportedOperationException()\n    }\n\n    override fun supportedFileAttributeViews(): Set<String> =\n        SftpFileAttributeView.SUPPORTED_NAMES\n\n    override fun getPath(first: String, vararg more: String): SftpPath {\n        val path = ByteStringBuilder(first.toByteString())\n            .apply { more.forEach { append(SEPARATOR).append(it.toByteString()) } }\n            .toByteString()\n        return SftpPath(this, path)\n    }\n\n    override fun getPath(first: ByteString, vararg more: ByteString): SftpPath {\n        val path = ByteStringBuilder(first)\n            .apply { more.forEach { append(SEPARATOR).append(it) } }\n            .toByteString()\n        return SftpPath(this, path)\n    }\n\n    override fun getPathMatcher(syntaxAndPattern: String): PathMatcher {\n        throw UnsupportedOperationException()\n    }\n\n    override fun getUserPrincipalLookupService(): UserPrincipalLookupService {\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun newWatchService(): WatchService = LocalWatchService()\n\n    override fun equals(other: Any?): Boolean {\n        if (this === other) {\n            return true\n        }\n        if (javaClass != other?.javaClass) {\n            return false\n        }\n        other as SftpFileSystem\n        return authority == other.authority\n    }\n\n    override fun hashCode(): Int = authority.hashCode()\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        dest.writeParcelable(authority, flags)\n    }\n\n    companion object {\n        const val SEPARATOR = '/'.code.toByte()\n        private val SEPARATOR_BYTE_STRING = SEPARATOR.toByteString()\n        private const val SEPARATOR_STRING = SEPARATOR.toInt().toChar().toString()\n\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<SftpFileSystem> {\n            override fun createFromParcel(source: Parcel): SftpFileSystem {\n                val authority = source.readParcelable<Authority>()!!\n                return SftpFileSystemProvider.getOrNewFileSystem(authority)\n            }\n\n            override fun newArray(size: Int): Array<SftpFileSystem?> = arrayOfNulls(size)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/sftp/SftpFileSystemProvider.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.sftp\n\nimport java8.nio.channels.FileChannel\nimport java8.nio.channels.SeekableByteChannel\nimport java8.nio.file.AccessMode\nimport java8.nio.file.CopyOption\nimport java8.nio.file.DirectoryStream\nimport java8.nio.file.FileStore\nimport java8.nio.file.FileSystem\nimport java8.nio.file.FileSystemAlreadyExistsException\nimport java8.nio.file.FileSystemNotFoundException\nimport java8.nio.file.LinkOption\nimport java8.nio.file.OpenOption\nimport java8.nio.file.Path\nimport java8.nio.file.ProviderMismatchException\nimport java8.nio.file.attribute.BasicFileAttributes\nimport java8.nio.file.attribute.FileAttribute\nimport java8.nio.file.attribute.FileAttributeView\nimport java8.nio.file.spi.FileSystemProvider\nimport me.zhanghai.android.files.provider.common.ByteStringPath\nimport me.zhanghai.android.files.provider.common.PathListDirectoryStream\nimport me.zhanghai.android.files.provider.common.PathObservable\nimport me.zhanghai.android.files.provider.common.PathObservableProvider\nimport me.zhanghai.android.files.provider.common.PosixFileMode\nimport me.zhanghai.android.files.provider.common.Searchable\nimport me.zhanghai.android.files.provider.common.WalkFileTreeSearchable\nimport me.zhanghai.android.files.provider.common.WatchServicePathObservable\nimport me.zhanghai.android.files.provider.common.decodedPathByteString\nimport me.zhanghai.android.files.provider.common.toAccessModes\nimport me.zhanghai.android.files.provider.common.toByteString\nimport me.zhanghai.android.files.provider.common.toCopyOptions\nimport me.zhanghai.android.files.provider.common.toLinkOptions\nimport me.zhanghai.android.files.provider.common.toOpenOptions\nimport me.zhanghai.android.files.provider.sftp.client.Authority\nimport me.zhanghai.android.files.provider.sftp.client.Client\nimport me.zhanghai.android.files.provider.sftp.client.ClientException\nimport me.zhanghai.android.files.provider.sftp.client.SecurityProviderHelper\nimport me.zhanghai.android.files.util.enumSetOf\nimport net.schmizz.sshj.sftp.OpenMode\nimport java.io.IOException\nimport java.net.URI\n\nobject SftpFileSystemProvider : FileSystemProvider(), PathObservableProvider, Searchable {\n    private const val SCHEME = \"sftp\"\n\n    private val HIDDEN_FILE_NAME_PREFIX = \".\".toByteString()\n\n    private val fileSystems = mutableMapOf<Authority, SftpFileSystem>()\n\n    private val lock = Any()\n\n    init {\n        SecurityProviderHelper.init()\n    }\n\n    override fun getScheme(): String = SCHEME\n\n    override fun newFileSystem(uri: URI, env: Map<String, *>): FileSystem {\n        uri.requireSameScheme()\n        val authority = uri.sftpAuthority\n        synchronized(lock) {\n            if (fileSystems[authority] != null) {\n                throw FileSystemAlreadyExistsException(authority.toString())\n            }\n            return newFileSystemLocked(authority)\n        }\n    }\n\n    internal fun getOrNewFileSystem(authority: Authority): SftpFileSystem =\n        synchronized(lock) { fileSystems[authority] ?: newFileSystemLocked(authority) }\n\n    private fun newFileSystemLocked(authority: Authority): SftpFileSystem {\n        val fileSystem = SftpFileSystem(this, authority)\n        fileSystems[authority] = fileSystem\n        return fileSystem\n    }\n\n    override fun getFileSystem(uri: URI): FileSystem {\n        uri.requireSameScheme()\n        val authority = uri.sftpAuthority\n        return synchronized(lock) { fileSystems[authority] }\n            ?: throw FileSystemNotFoundException(authority.toString())\n    }\n\n    internal fun removeFileSystem(fileSystem: SftpFileSystem) {\n        val authority = fileSystem.authority\n        synchronized(lock) { fileSystems.remove(authority) }\n    }\n\n    override fun getPath(uri: URI): Path {\n        uri.requireSameScheme()\n        val authority = uri.sftpAuthority\n        val path = uri.decodedPathByteString\n            ?: throw IllegalArgumentException(\"URI must have a path\")\n        return getOrNewFileSystem(authority).getPath(path)\n    }\n\n    private fun URI.requireSameScheme() {\n        val scheme = scheme\n        require(scheme == SCHEME) { \"URI scheme $scheme must be $SCHEME\" }\n    }\n\n    private val URI.sftpAuthority: Authority\n        get() {\n            val port = if (port != -1) port else Authority.DEFAULT_PORT\n            val username = userInfo.orEmpty()\n            return Authority(host, port, username)\n        }\n\n    @Throws(IOException::class)\n    override fun newFileChannel(\n        file: Path,\n        options: Set<OpenOption>,\n        vararg attributes: FileAttribute<*>\n    ): FileChannel {\n        file as? SftpPath ?: throw ProviderMismatchException(file.toString())\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun newByteChannel(\n        file: Path,\n        options: Set<OpenOption>,\n        vararg attributes: FileAttribute<*>\n    ): SeekableByteChannel {\n        file as? SftpPath ?: throw ProviderMismatchException(file.toString())\n        val openOptions = options.toOpenOptions()\n        val flags = openOptions.toSftpFlags()\n        val sftpAttributes = (PosixFileMode.fromAttributes(attributes)\n            ?: PosixFileMode.CREATE_FILE_DEFAULT).toSftpAttributes()\n        return try {\n            Client.openByteChannel(file, flags, sftpAttributes)\n        } catch (e: ClientException) {\n            throw e.toFileSystemException(file.toString())\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun newDirectoryStream(\n        directory: Path,\n        filter: DirectoryStream.Filter<in Path>\n    ): DirectoryStream<Path> {\n        directory as? SftpPath ?: throw ProviderMismatchException(directory.toString())\n        val paths = try {\n            @Suppress(\"UNCHECKED_CAST\")\n            Client.scandir(directory) as List<Path>\n        } catch (e: ClientException) {\n            throw e.toFileSystemException(directory.toString())\n        }\n        return PathListDirectoryStream(paths, filter)\n    }\n\n    @Throws(IOException::class)\n    override fun createDirectory(directory: Path, vararg attributes: FileAttribute<*>) {\n        directory as? SftpPath ?: throw ProviderMismatchException(directory.toString())\n        val sftpAttributes = (PosixFileMode.fromAttributes(attributes)\n            ?: PosixFileMode.CREATE_DIRECTORY_DEFAULT).toSftpAttributes()\n        try {\n            Client.mkdir(directory, sftpAttributes)\n        } catch (e: ClientException) {\n            throw e.toFileSystemException(directory.toString())\n        }\n    }\n\n    override fun createSymbolicLink(link: Path, target: Path, vararg attributes: FileAttribute<*>) {\n        link as? SftpPath ?: throw ProviderMismatchException(link.toString())\n        val targetString = when (target) {\n            is SftpPath -> target.toString()\n            is ByteStringPath -> target.toString()\n            else -> throw ProviderMismatchException(target.toString())\n        }\n        if (attributes.isNotEmpty()) {\n            throw UnsupportedOperationException(attributes.contentToString())\n        }\n        try {\n            Client.symlink(link, targetString)\n        } catch (e: ClientException) {\n            throw e.toFileSystemException(link.toString(), targetString)\n        }\n    }\n\n    override fun createLink(link: Path, existing: Path) {\n        link as? SftpPath ?: throw ProviderMismatchException(link.toString())\n        existing as? SftpPath ?: throw ProviderMismatchException(existing.toString())\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun delete(path: Path) {\n        path as? SftpPath ?: throw ProviderMismatchException(path.toString())\n        try {\n            Client.remove(path)\n        } catch (e: ClientException) {\n            throw e.toFileSystemException(path.toString())\n        }\n    }\n\n    override fun readSymbolicLink(link: Path): Path {\n        link as? SftpPath ?: throw ProviderMismatchException(link.toString())\n        val target = try {\n            Client.readlink(link)\n        } catch (e: ClientException) {\n            throw e.toFileSystemException(link.toString())\n        }\n        return ByteStringPath(target.toByteString())\n    }\n\n    @Throws(IOException::class)\n    override fun copy(source: Path, target: Path, vararg options: CopyOption) {\n        source as? SftpPath ?: throw ProviderMismatchException(source.toString())\n        target as? SftpPath ?: throw ProviderMismatchException(target.toString())\n        val copyOptions = options.toCopyOptions()\n        SftpCopyMove.copy(source, target, copyOptions)\n    }\n\n    @Throws(IOException::class)\n    override fun move(source: Path, target: Path, vararg options: CopyOption) {\n        source as? SftpPath ?: throw ProviderMismatchException(source.toString())\n        target as? SftpPath ?: throw ProviderMismatchException(target.toString())\n        val copyOptions = options.toCopyOptions()\n        SftpCopyMove.move(source, target, copyOptions)\n    }\n\n    override fun isSameFile(path: Path, path2: Path): Boolean {\n        path as? SftpPath ?: throw ProviderMismatchException(path.toString())\n        return path == path2\n    }\n\n    override fun isHidden(path: Path): Boolean {\n        path as? SftpPath ?: throw ProviderMismatchException(path.toString())\n        val fileName = path.fileNameByteString ?: return false\n        return fileName.startsWith(HIDDEN_FILE_NAME_PREFIX)\n    }\n\n    override fun getFileStore(path: Path): FileStore {\n        path as? SftpPath ?: throw ProviderMismatchException(path.toString())\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun checkAccess(path: Path, vararg modes: AccessMode) {\n        path as? SftpPath ?: throw ProviderMismatchException(path.toString())\n        val accessModes = modes.toAccessModes()\n        if (accessModes.execute) {\n            throw UnsupportedOperationException(AccessMode.EXECUTE.toString())\n        }\n        val flags = enumSetOf<OpenMode>().apply {\n            if (accessModes.read) {\n                this += OpenMode.READ\n            }\n            if (accessModes.write) {\n                this += OpenMode.WRITE\n            }\n        }\n        try {\n            Client.access(path, flags)\n        } catch (e: ClientException) {\n            throw e.toFileSystemException(path.toString())\n        }\n    }\n\n    override fun <V : FileAttributeView> getFileAttributeView(\n        path: Path,\n        type: Class<V>,\n        vararg options: LinkOption\n    ): V? {\n        if (!supportsFileAttributeView(type)) {\n            return null\n        }\n        @Suppress(\"UNCHECKED_CAST\")\n        return getFileAttributeView(path, *options) as V\n    }\n\n    internal fun supportsFileAttributeView(type: Class<out FileAttributeView>): Boolean =\n        type.isAssignableFrom(SftpFileAttributeView::class.java)\n\n    @Throws(IOException::class)\n    override fun <A : BasicFileAttributes> readAttributes(\n        path: Path,\n        type: Class<A>,\n        vararg options: LinkOption\n    ): A {\n        if (!type.isAssignableFrom(BasicFileAttributes::class.java)) {\n            throw UnsupportedOperationException(type.toString())\n        }\n        @Suppress(\"UNCHECKED_CAST\")\n        return getFileAttributeView(path, *options).readAttributes() as A\n    }\n\n    private fun getFileAttributeView(path: Path, vararg options: LinkOption): SftpFileAttributeView {\n        path as? SftpPath ?: throw ProviderMismatchException(path.toString())\n        val linkOptions = options.toLinkOptions()\n        return SftpFileAttributeView(path, linkOptions.noFollowLinks)\n    }\n\n    override fun readAttributes(\n        path: Path,\n        attributes: String,\n        vararg options: LinkOption\n    ): Map<String, Any> {\n        path as? SftpPath ?: throw ProviderMismatchException(path.toString())\n        throw UnsupportedOperationException()\n    }\n\n    override fun setAttribute(\n        path: Path,\n        attribute: String,\n        value: Any,\n        vararg options: LinkOption\n    ) {\n        path as? SftpPath ?: throw ProviderMismatchException(path.toString())\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun observe(path: Path, intervalMillis: Long): PathObservable {\n        path as? SftpPath ?: throw ProviderMismatchException(path.toString())\n        return WatchServicePathObservable(path, intervalMillis)\n    }\n\n    @Throws(IOException::class)\n    override fun search(\n        directory: Path,\n        query: String,\n        intervalMillis: Long,\n        listener: (List<Path>) -> Unit\n    ) {\n        directory as? SftpPath ?: throw ProviderMismatchException(directory.toString())\n        WalkFileTreeSearchable.search(directory, query, intervalMillis, listener)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/sftp/SftpPath.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.sftp\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport java8.nio.file.FileSystem\nimport java8.nio.file.LinkOption\nimport java8.nio.file.Path\nimport java8.nio.file.ProviderMismatchException\nimport java8.nio.file.WatchEvent\nimport java8.nio.file.WatchKey\nimport java8.nio.file.WatchService\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.ByteStringListPath\nimport me.zhanghai.android.files.provider.common.LocalWatchService\nimport me.zhanghai.android.files.provider.common.UriAuthority\nimport me.zhanghai.android.files.provider.sftp.client.Authority\nimport me.zhanghai.android.files.provider.sftp.client.Client\nimport me.zhanghai.android.files.util.readParcelable\nimport java.io.File\nimport java.io.IOException\n\ninternal class SftpPath : ByteStringListPath<SftpPath>, Client.Path {\n    private val fileSystem: SftpFileSystem\n\n    constructor(\n        fileSystem: SftpFileSystem,\n        path: ByteString\n    ) : super(SftpFileSystem.SEPARATOR, path) {\n        this.fileSystem = fileSystem\n    }\n\n    private constructor(\n        fileSystem: SftpFileSystem,\n        absolute: Boolean,\n        segments: List<ByteString>\n    ) : super(SftpFileSystem.SEPARATOR, absolute, segments) {\n        this.fileSystem = fileSystem\n    }\n\n    override fun isPathAbsolute(path: ByteString): Boolean =\n        path.isNotEmpty() && path[0] == SftpFileSystem.SEPARATOR\n\n    override fun createPath(path: ByteString): SftpPath = SftpPath(fileSystem, path)\n\n    override fun createPath(absolute: Boolean, segments: List<ByteString>): SftpPath =\n        SftpPath(fileSystem, absolute, segments)\n\n    override val uriAuthority: UriAuthority\n        get() = fileSystem.authority.toUriAuthority()\n\n    override val defaultDirectory: SftpPath\n        get() = fileSystem.defaultDirectory\n\n    override fun getFileSystem(): FileSystem = fileSystem\n\n    override fun getRoot(): SftpPath? = if (isAbsolute) fileSystem.rootDirectory else null\n\n    @Throws(IOException::class)\n    override fun toRealPath(vararg options: LinkOption): SftpPath {\n        throw UnsupportedOperationException()\n    }\n\n    override fun toFile(): File {\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun register(\n        watcher: WatchService,\n        events: Array<WatchEvent.Kind<*>>,\n        vararg modifiers: WatchEvent.Modifier\n    ): WatchKey {\n        if (watcher !is LocalWatchService) {\n            throw ProviderMismatchException(watcher.toString())\n        }\n        return watcher.register(this, events, *modifiers)\n    }\n\n    override val authority: Authority\n        get() = fileSystem.authority\n\n    override val remotePath: String\n        get() = toString()\n\n    private constructor(source: Parcel) : super(source) {\n        fileSystem = source.readParcelable()!!\n    }\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        super.writeToParcel(dest, flags)\n\n        dest.writeParcelable(fileSystem, flags)\n    }\n\n    companion object {\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<SftpPath> {\n            override fun createFromParcel(source: Parcel): SftpPath = SftpPath(source)\n\n            override fun newArray(size: Int): Array<SftpPath?> = arrayOfNulls(size)\n        }\n    }\n}\n\nval Path.isSftpPath: Boolean\n    get() = this is SftpPath\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/sftp/client/Authentication.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.sftp.client\n\nimport android.os.Parcelable\nimport kotlinx.parcelize.Parcelize\nimport net.schmizz.sshj.DefaultConfig\nimport net.schmizz.sshj.common.Factory\nimport net.schmizz.sshj.userauth.keyprovider.KeyProvider\nimport net.schmizz.sshj.userauth.keyprovider.KeyProviderUtil\nimport net.schmizz.sshj.userauth.method.AuthMethod\nimport net.schmizz.sshj.userauth.method.AuthPassword\nimport net.schmizz.sshj.userauth.method.AuthPublickey\nimport net.schmizz.sshj.userauth.password.PasswordUtils\nimport java.io.IOException\n\nsealed class Authentication : Parcelable {\n    abstract fun toAuthMethod(): AuthMethod\n}\n\n@Parcelize\ndata class PasswordAuthentication(\n    val password: String\n) : Authentication() {\n    override fun toAuthMethod(): AuthMethod =\n        AuthPassword(PasswordUtils.createOneOff(password.toCharArray()))\n}\n\n@Parcelize\ndata class PublicKeyAuthentication(\n    val privateKey: String,\n    val privateKeyPassword: String?\n) : Authentication() {\n    override fun toAuthMethod(): AuthMethod =\n        AuthPublickey(createKeyProvider(privateKey, privateKeyPassword))\n\n    companion object {\n        private val KEY_PROVIDER_FACTORIES = DefaultConfig().fileKeyProviderFactories\n\n        fun validate(privateKey: String, privateKeyPassword: String?): IOException? =\n            try {\n                createKeyProvider(privateKey, privateKeyPassword).private\n                null\n            } catch (e: IOException) {\n                e\n            }\n\n        /**\n         * @see net.schmizz.sshj.SSHClient.loadKeys\n         */\n        @Throws(IOException::class)\n        private fun createKeyProvider(\n            privateKey: String,\n            privateKeyPassword: String?\n        ): KeyProvider {\n            val format = KeyProviderUtil.detectKeyFileFormat(privateKey, false)\n            val keyProvider = Factory.Named.Util.create(KEY_PROVIDER_FACTORIES, format.toString())\n                ?: throw IOException(\"No key provider factory found for $format\")\n            keyProvider.init(\n                privateKey, null,\n                privateKeyPassword?.let { PasswordUtils.createOneOff(it.toCharArray()) }\n            )\n            return keyProvider\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/sftp/client/Authenticator.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.sftp.client\n\ninterface Authenticator {\n    fun getAuthentication(authority: Authority): Authentication?\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/sftp/client/Authority.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.sftp.client\n\nimport android.os.Parcelable\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.provider.common.UriAuthority\nimport me.zhanghai.android.files.util.takeIfNotEmpty\nimport net.schmizz.sshj.SSHClient\n\n@Parcelize\ndata class Authority(\n    val host: String,\n    val port: Int,\n    val username: String\n) : Parcelable {\n    fun toUriAuthority(): UriAuthority {\n        val userInfo = username.takeIfNotEmpty()\n        val uriPort = port.takeIf { it != DEFAULT_PORT }\n        return UriAuthority(userInfo, host, uriPort)\n    }\n\n    override fun toString(): String = toUriAuthority().toString()\n\n    companion object {\n        const val DEFAULT_PORT = SSHClient.DEFAULT_PORT\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/sftp/client/Client.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.sftp.client\n\nimport java8.nio.channels.SeekableByteChannel\nimport me.zhanghai.android.files.provider.common.LocalWatchService\nimport me.zhanghai.android.files.provider.common.NotifyEntryModifiedSeekableByteChannel\nimport me.zhanghai.android.files.util.closeSafe\nimport net.schmizz.sshj.SSHClient\nimport net.schmizz.sshj.sftp.FileAttributes\nimport net.schmizz.sshj.sftp.FileMode\nimport net.schmizz.sshj.sftp.OpenMode\nimport net.schmizz.sshj.sftp.RemoteFile\nimport net.schmizz.sshj.sftp.Response\nimport net.schmizz.sshj.sftp.SFTPClient\nimport net.schmizz.sshj.sftp.SFTPException\nimport net.schmizz.sshj.transport.TransportException\nimport net.schmizz.sshj.transport.verification.PromiscuousVerifier\nimport net.schmizz.sshj.userauth.UserAuthException\nimport java.io.IOException\nimport java.util.Collections\nimport java.util.WeakHashMap\nimport java8.nio.file.Path as Java8Path\n\nobject Client {\n    @Volatile\n    lateinit var authenticator: Authenticator\n\n    private val clients = mutableMapOf<Authority, SFTPClient>()\n\n    private val directoryFileAttributesCache =\n        Collections.synchronizedMap(WeakHashMap<Path, FileAttributes>())\n\n    @Throws(ClientException::class)\n    fun access(path: Path, flags: Set<OpenMode>) {\n        val file = open(path, flags, FileAttributes.EMPTY)\n        try {\n            file.close()\n        } catch (e: IOException) {\n            throw ClientException(e)\n        }\n    }\n\n    @Throws(ClientException::class)\n    fun lstat(path: Path): FileAttributes {\n        val client = getClient(path.authority)\n        synchronized(directoryFileAttributesCache) {\n            directoryFileAttributesCache[path]?.let {\n                return it.also { directoryFileAttributesCache -= path }\n            }\n        }\n        return try {\n            client.lstat(path.remotePath)\n        } catch (e: IOException) {\n            throw ClientException(e)\n        }\n    }\n\n    @Throws(ClientException::class)\n    fun mkdir(path: Path, attributes: FileAttributes) {\n        val client = getClient(path.authority)\n        try {\n            client.sftpEngine.makeDir(path.remotePath, attributes)\n        } catch (e: IOException) {\n            throw ClientException(e)\n        }\n        LocalWatchService.onEntryCreated(path as Java8Path)\n    }\n\n    @Throws(ClientException::class)\n    private fun open(path: Path, flags: Set<OpenMode>, attributes: FileAttributes): RemoteFile {\n        val client = getClient(path.authority)\n        return try {\n            client.open(path.remotePath, flags, attributes)\n        } catch (e: IOException) {\n            throw ClientException(e)\n        }\n    }\n\n    @Throws(ClientException::class)\n    fun openByteChannel(\n        path: Path,\n        flags: Set<OpenMode>,\n        attributes: FileAttributes\n    ): SeekableByteChannel {\n        val file = open(path, flags, attributes)\n        return NotifyEntryModifiedSeekableByteChannel(\n            FileByteChannel(file, flags.contains(OpenMode.APPEND)), path as Java8Path\n        )\n    }\n\n    @Throws(ClientException::class)\n    fun readlink(path: Path): String {\n        val client = getClient(path.authority)\n        return try {\n            client.readlink(path.remotePath)\n        } catch (e: IOException) {\n            throw ClientException(e)\n        }\n    }\n\n    @Throws(ClientException::class)\n    fun realpath(path: Path): Path {\n        val client = getClient(path.authority)\n        val realPath = try {\n            client.canonicalize(path.remotePath)\n        } catch (e: IOException) {\n            throw ClientException(e)\n        }\n        return path.resolve(realPath)\n    }\n\n    @Throws(ClientException::class)\n    fun remove(path: Path) {\n        val attributes = lstat(path)\n        val isDirectory = attributes.type == FileMode.Type.DIRECTORY\n        if (isDirectory) {\n            rmdir(path)\n        } else {\n            unlink(path)\n        }\n    }\n\n    // Note that unlike POSIX rename(), this won't overwrite an existing file.\n    @Throws(ClientException::class)\n    fun rename(path: Path, newPath: Path) {\n        if (newPath.authority != path.authority) {\n            throw ClientException(\n                SFTPException(Response.StatusCode.FAILURE, \"Paths aren't on the same authority\")\n            )\n        }\n        val client = getClient(path.authority)\n        try {\n            client.rename(path.remotePath, newPath.remotePath)\n        } catch (e: IOException) {\n            throw ClientException(e)\n        }\n        directoryFileAttributesCache -= path\n        directoryFileAttributesCache -= newPath\n        LocalWatchService.onEntryDeleted(path as Java8Path)\n        LocalWatchService.onEntryCreated(newPath as Java8Path)\n    }\n\n    @Throws(ClientException::class)\n    fun rmdir(path: Path) {\n        val client = getClient(path.authority)\n        try {\n            client.rmdir(path.remotePath)\n        } catch (e: IOException) {\n            throw ClientException(e)\n        }\n        directoryFileAttributesCache -= path\n        LocalWatchService.onEntryDeleted(path as Java8Path)\n    }\n\n    @Throws(ClientException::class)\n    fun scandir(path: Path): List<Path> {\n        val client = getClient(path.authority)\n        val files = try {\n            client.ls(path.remotePath)\n        } catch (e: IOException) {\n            throw ClientException(e)\n        }\n        return files.map { file ->\n            // The attributes here are from lstat().\n            // https://github.com/openssh/openssh-portable/blob/71241fc05db4bbb11bb29340b44b92e2575373d8/sftp-server.c#L1110\n            path.resolve(file.name).also { directoryFileAttributesCache[it] = file.attributes }\n        }\n    }\n\n    @Throws(ClientException::class)\n    fun setstat(path: Path, attributes: FileAttributes) {\n        val client = getClient(path.authority)\n        try {\n            client.setattr(path.remotePath, attributes)\n        } catch (e: IOException) {\n            throw ClientException(e)\n        }\n        directoryFileAttributesCache -= path\n        LocalWatchService.onEntryModified(path as Java8Path)\n    }\n\n    @Throws(ClientException::class)\n    fun stat(path: Path): FileAttributes {\n        val client = getClient(path.authority)\n        synchronized(directoryFileAttributesCache) {\n            directoryFileAttributesCache[path]?.let {\n                if (it.type != FileMode.Type.SYMLINK) {\n                    return it.also { directoryFileAttributesCache -= path }\n                }\n            }\n        }\n        return try {\n            client.stat(path.remotePath)\n        } catch (e: IOException) {\n            throw ClientException(e)\n        }\n    }\n\n    @Throws(ClientException::class)\n    fun symlink(link: Path, target: String) {\n        val client = getClient(link.authority)\n        try {\n            client.symlink(link.remotePath, target)\n        } catch (e: IOException) {\n            throw ClientException(e)\n        }\n        LocalWatchService.onEntryCreated(link as Java8Path)\n    }\n\n    @Throws(ClientException::class)\n    fun unlink(path: Path) {\n        val client = getClient(path.authority)\n        try {\n            client.rm(path.remotePath)\n        } catch (e: IOException) {\n            throw ClientException(e)\n        }\n        directoryFileAttributesCache -= path\n        LocalWatchService.onEntryDeleted(path as Java8Path)\n    }\n\n    @Throws(ClientException::class)\n    private fun getClient(authority: Authority): SFTPClient {\n        synchronized(clients) {\n            var client = clients[authority]\n            if (client != null) {\n                if (client.sftpEngine.subsystem.isOpen) {\n                    return client\n                } else {\n                    client.closeSafe()\n                    clients -= authority\n                }\n            }\n            val authentication = authenticator.getAuthentication(authority)\n                ?: throw ClientException(\"No authentication found for $authority\")\n            val sshClient = SSHClient().apply { addHostKeyVerifier(PromiscuousVerifier()) }\n            try {\n                sshClient.connect(authority.host, authority.port)\n            } catch (e: IOException) {\n                sshClient.closeSafe()\n                throw ClientException(e)\n            }\n            try {\n                sshClient.auth(authority.username, authentication.toAuthMethod())\n            } catch (e: UserAuthException) {\n                sshClient.closeSafe()\n                throw ClientException(e)\n            } catch (e: TransportException) {\n                sshClient.closeSafe()\n                throw ClientException(e)\n            }\n            client = sshClient.newSFTPClient()\n            clients[authority] = client\n            return client\n        }\n    }\n\n    interface Path {\n        val authority: Authority\n        val remotePath: String\n        fun resolve(other: String): Path\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/sftp/client/ClientException.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.sftp.client\n\nimport java8.nio.file.AccessDeniedException\nimport java8.nio.file.DirectoryNotEmptyException\nimport java8.nio.file.FileAlreadyExistsException\nimport java8.nio.file.FileSystemException\nimport java8.nio.file.FileSystemLoopException\nimport java8.nio.file.NoSuchFileException\nimport java8.nio.file.NotDirectoryException\nimport me.zhanghai.android.files.provider.common.InvalidFileNameException\nimport me.zhanghai.android.files.provider.common.IsDirectoryException\nimport me.zhanghai.android.files.provider.common.ReadOnlyFileSystemException\nimport net.schmizz.sshj.sftp.Response\nimport net.schmizz.sshj.sftp.SFTPException\n\nclass ClientException : Exception {\n    constructor() : super()\n\n    constructor(message: String?) : super(message)\n\n    constructor(message: String?, cause: Throwable?) : super(message, cause)\n\n    constructor(cause: Throwable?) : super(cause)\n\n    private val statusCode: Response.StatusCode? = (cause as? SFTPException)?.statusCode\n\n    fun toFileSystemException(file: String?, other: String? = null): FileSystemException =\n        when (statusCode) {\n            Response.StatusCode.NO_SUCH_FILE, Response.StatusCode.NO_SUCH_PATH,\n            Response.StatusCode.DELETE_PENDING -> NoSuchFileException(file, other, message)\n            Response.StatusCode.PERMISSION_DENIED, Response.StatusCode.CANNOT_DELETE ->\n                AccessDeniedException(file, other, message)\n            Response.StatusCode.FILE_ALREADY_EXISTS ->\n                FileAlreadyExistsException(file, other, message)\n            Response.StatusCode.WRITE_PROTECT -> ReadOnlyFileSystemException(file, other, message)\n            Response.StatusCode.DIR_NOT_EMPTY -> DirectoryNotEmptyException(file)\n            Response.StatusCode.NOT_A_DIRECTORY -> NotDirectoryException(file)\n            Response.StatusCode.INVALID_FILENAME -> InvalidFileNameException(file, other, message)\n            Response.StatusCode.LINK_LOOP -> FileSystemLoopException(file)\n            Response.StatusCode.FILE_IS_A_DIRECTORY -> IsDirectoryException(file, other, message)\n            else -> FileSystemException(file, other, message)\n        }.apply { initCause(this@ClientException) }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/sftp/client/FileByteChannel.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.sftp.client\n\nimport me.zhanghai.android.files.provider.common.AbstractFileByteChannel\nimport me.zhanghai.android.files.provider.common.EMPTY\nimport me.zhanghai.android.files.provider.common.asFuture\nimport me.zhanghai.android.files.provider.common.map\nimport me.zhanghai.android.files.util.closeSafe\nimport me.zhanghai.android.files.util.findCauseByClass\nimport net.schmizz.sshj.sftp.PacketType\nimport net.schmizz.sshj.sftp.RemoteFile\nimport net.schmizz.sshj.sftp.RemoteFileAccessor\nimport net.schmizz.sshj.sftp.Response\nimport net.schmizz.sshj.sftp.SFTPException\nimport java.io.IOException\nimport java.nio.ByteBuffer\nimport java.nio.channels.AsynchronousCloseException\nimport java.nio.channels.ClosedByInterruptException\nimport java.util.concurrent.ExecutionException\nimport java.util.concurrent.Future\n\nclass FileByteChannel(\n    private val file: RemoteFile,\n    isAppend: Boolean\n) : AbstractFileByteChannel(isAppend) {\n    override fun onReadAsync(position: Long, size: Int, timeoutMillis: Long): Future<ByteBuffer> =\n        try {\n            RemoteFileAccessor.asyncRead(file, position, size)\n        } catch (e: IOException) {\n            throw e.maybeToSpecificException()\n        }\n            .asFuture()\n            .map(\n                { response ->\n                    val dataLength: Int\n                    when (response.type) {\n                        PacketType.STATUS -> {\n                            response.ensureStatusIs(Response.StatusCode.EOF)\n                            return@map ByteBuffer::class.EMPTY\n                        }\n                        PacketType.DATA -> {\n                            dataLength = response.readUInt32AsInt()\n                        }\n                        else -> throw SFTPException(\"Unexpected packet type ${response.type}\")\n                    }\n                    if (dataLength == 0) {\n                        return@map ByteBuffer::class.EMPTY\n                    }\n                    val length = dataLength.coerceAtMost(size)\n                    ByteBuffer.wrap(response.array(), response.rpos(), length)\n                }, { e ->\n                    ((e as? ExecutionException)?.cause as? IOException)?.maybeToSpecificException()\n                        ?.let { ExecutionException(it) } ?: e\n                }\n            )\n\n    @Throws(IOException::class)\n    override fun onWrite(position: Long, source: ByteBuffer) {\n        // I don't think we are using native or read-only ByteBuffer, so just call array() here.\n        try {\n            file.write(\n                position, source.array(), source.arrayOffset() + source.position(),\n                source.remaining()\n            )\n        } catch (e: IOException) {\n            throw e.maybeToSpecificException()\n        }\n        source.position(source.limit())\n    }\n\n    @Throws(IOException::class)\n    override fun onTruncate(size: Long) {\n        try {\n            file.setLength(size)\n        } catch (e: IOException) {\n            throw e.maybeToSpecificException()\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun onSize(): Long =\n        try{\n            file.length()\n        } catch (e: IOException) {\n            throw e.maybeToSpecificException()\n        }\n\n    private fun IOException.maybeToSpecificException(): IOException =\n        when {\n            this is SFTPException && statusCode == Response.StatusCode.INVALID_HANDLE -> {\n                setClosed()\n                AsynchronousCloseException().apply { initCause(this@maybeToSpecificException) }\n            }\n            findCauseByClass<InterruptedException>() != null -> {\n                closeSafe()\n                ClosedByInterruptException().apply { initCause(this@maybeToSpecificException) }\n            }\n            else -> this\n        }\n\n    @Throws(IOException::class)\n    override fun onClose() {\n        try {\n            file.close()\n        } catch (e: SFTPException) {\n            // NO_SUCH_FILE is returned when canceling an in-progress copy to SFTP server.\n            if (e.statusCode != Response.StatusCode.NO_SUCH_FILE) {\n                throw e\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/sftp/client/SecurityProviderHelper.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.sftp.client\n\nimport android.os.Build\nimport org.bouncycastle.jce.provider.BouncyCastleProvider\nimport java.security.Security\n\n// @see https://android-developers.googleblog.com/2018/03/cryptography-changes-in-android-p.html\n// @see net.schmizz.sshj.common.SecurityUtils\n// @see net.schmizz.sshj.DefaultConfig.DefaultConfig\n// SSHJ requires BouncyCastle to be registered before enabling most functionality by default, so we\n// better keep BouncyCastle registered.\nobject SecurityProviderHelper {\n    fun init() {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {\n            // On older Android versions, JarURLConnectionImpl.getInputStream() throws a\n            // \"SecurityException: Incorrect signature\" when it's called by Apache FTPServer if we\n            // replace Bouncy Castle. We are only required to replace Bouncy Castle on P and above\n            // anyway, so don't do that before Lollipop MR1.\n            return\n        }\n        val bouncyCastleProvider = BouncyCastleProvider()\n        Security.removeProvider(bouncyCastleProvider.name)\n        Security.addProvider(bouncyCastleProvider)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/smb/OpenOptionsSmbExtensions.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.smb\n\nimport com.hierynomus.msdtyp.AccessMask\nimport com.hierynomus.msfscc.FileAttributes\nimport com.hierynomus.mssmb2.SMB2CreateDisposition\nimport com.hierynomus.mssmb2.SMB2CreateOptions\nimport com.hierynomus.mssmb2.SMB2ShareAccess\nimport me.zhanghai.android.files.provider.common.OpenOptions\nimport me.zhanghai.android.files.util.enumSetOf\n\ninternal fun OpenOptions.toSmbDesiredAccess(): Set<AccessMask> =\n    enumSetOf<AccessMask>().apply {\n        if (read) {\n            this += AccessMask.GENERIC_READ\n        }\n        if (write) {\n            this += AccessMask.GENERIC_WRITE\n        }\n    }\n\ninternal fun OpenOptions.toSmbFileAttributes(): Set<FileAttributes> =\n    enumSetOf<FileAttributes>().apply {\n        if (sparse) {\n            this += FileAttributes.FILE_ATTRIBUTE_SPARSE_FILE\n        }\n    }\n\ninternal fun OpenOptions.toSmbShareAccess(): Set<SMB2ShareAccess> = SMB2ShareAccess.ALL\n\ninternal fun OpenOptions.toSmbCreateDisposition(): SMB2CreateDisposition =\n    when {\n        createNew -> SMB2CreateDisposition.FILE_CREATE\n        create && truncateExisting -> SMB2CreateDisposition.FILE_OVERWRITE_IF\n        create -> SMB2CreateDisposition.FILE_OPEN_IF\n        truncateExisting -> SMB2CreateDisposition.FILE_OVERWRITE\n        else -> SMB2CreateDisposition.FILE_OPEN\n    }\n\ninternal fun OpenOptions.toSmbCreateOptions(): Set<SMB2CreateOptions> =\n    enumSetOf<SMB2CreateOptions>().apply {\n        if (sync || dsync) {\n            this += SMB2CreateOptions.FILE_WRITE_THROUGH\n        }\n        if (deleteOnClose) {\n            this += SMB2CreateOptions.FILE_DELETE_ON_CLOSE\n        }\n        if (noFollowLinks || createNew) {\n            this += SMB2CreateOptions.FILE_OPEN_REPARSE_POINT\n        }\n    }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/smb/PathSmbExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.smb\n\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.provider.smb.client.Authority\n\nfun Authority.createSmbRootPath(): Path =\n    SmbFileSystemProvider.getOrNewFileSystem(this).rootDirectory\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/smb/SmbCopyMove.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.smb\n\nimport com.hierynomus.msdtyp.FileTime\nimport com.hierynomus.msfscc.FileAttributes\nimport com.hierynomus.msfscc.fileinformation.FileBasicInformation\nimport com.hierynomus.protocol.commons.EnumWithValue\nimport java8.nio.file.FileAlreadyExistsException\nimport java8.nio.file.FileSystemException\nimport java8.nio.file.NoSuchFileException\nimport java8.nio.file.StandardCopyOption\nimport me.zhanghai.android.files.provider.common.CopyOptions\nimport me.zhanghai.android.files.provider.smb.client.Client\nimport me.zhanghai.android.files.provider.smb.client.ClientException\nimport me.zhanghai.android.files.provider.smb.client.FileInformation\nimport me.zhanghai.android.files.util.enumSetOf\nimport me.zhanghai.android.files.util.hasBits\nimport java.io.IOException\nimport java.io.InterruptedIOException\n\ninternal object SmbCopyMove {\n    @Throws(IOException::class)\n    fun copy(source: SmbPath, target: SmbPath, copyOptions: CopyOptions) {\n        if (copyOptions.atomicMove) {\n            throw UnsupportedOperationException(StandardCopyOption.ATOMIC_MOVE.toString())\n        }\n        val sourceInformation = try {\n            Client.getPathInformation(source, copyOptions.noFollowLinks)\n        } catch (e: ClientException) {\n            throw e.toFileSystemException(source.toString())\n        }\n        sourceInformation as? FileInformation\n            ?: throw FileSystemException(source.toString(), null, \"Cannot copy shares\")\n        val targetInformation = try {\n            Client.getPathInformation(target, true)\n        } catch (e: ClientException) {\n            val exception = e.toFileSystemException(target.toString())\n            if (exception !is NoSuchFileException) {\n                throw exception\n            }\n            // Ignored.\n            null\n        }\n        if (targetInformation != null) {\n            targetInformation as? FileInformation\n                ?: throw FileSystemException(target.toString(), null, \"Cannot copy shares\")\n            if (SmbFileKey(source, sourceInformation.fileId)\n                == SmbFileKey(target, targetInformation.fileId)) {\n                copyOptions.progressListener?.invoke(sourceInformation.endOfFile)\n                return\n            }\n            if (!copyOptions.replaceExisting) {\n                throw FileAlreadyExistsException(source.toString(), target.toString(), null)\n            }\n            // Symbolic links may not be supported so we cannot simply delete the target here.\n        }\n        val sourceIsReparsePoint = sourceInformation.fileAttributes\n            .hasBits(FileAttributes.FILE_ATTRIBUTE_REPARSE_POINT.value)\n        val sourceIsDirectory = !sourceIsReparsePoint &&\n            sourceInformation.fileAttributes.hasBits(FileAttributes.FILE_ATTRIBUTE_DIRECTORY.value)\n        val sourceIsRegularFile = !sourceIsDirectory && !sourceIsReparsePoint\n        val attributesToCopy = if (copyOptions.copyAttributes) {\n            EnumWithValue.EnumUtils.toEnumSet(\n                sourceInformation.fileAttributes, FileAttributes::class.java\n            )\n        } else {\n            enumSetOf(FileAttributes.FILE_ATTRIBUTE_NORMAL)\n        }\n        if (sourceIsRegularFile) {\n            if (targetInformation != null) {\n                try {\n                    Client.delete(target)\n                } catch (e: ClientException) {\n                    val exception = e.toFileSystemException(target.toString())\n                    if (exception !is NoSuchFileException) {\n                        throw exception\n                    }\n                }\n            }\n            try {\n                Client.copyFile(\n                    source, target, copyOptions.copyAttributes, copyOptions.noFollowLinks,\n                    copyOptions.progressIntervalMillis, copyOptions.progressListener\n                )\n            } catch (e: ClientException) {\n                (e.cause as? InterruptedIOException)?.let { throw it }\n                e.maybeThrowInvalidFileNameException(target.toString())\n                throw e.toFileSystemException(target.toString())\n            }\n        } else if (sourceIsDirectory) {\n            if (targetInformation != null) {\n                try {\n                    Client.delete(target)\n                } catch (e: ClientException) {\n                    val exception = e.toFileSystemException(target.toString())\n                    if (exception !is NoSuchFileException) {\n                        throw exception\n                    }\n                }\n            }\n            try {\n                Client.createDirectory(target, attributesToCopy)\n            } catch (e: ClientException) {\n                e.maybeThrowInvalidFileNameException(target.toString())\n                throw e.toFileSystemException(target.toString())\n            }\n            copyOptions.progressListener?.invoke(sourceInformation.endOfFile)\n        } else if (sourceIsReparsePoint) {\n            val sourceReparseData = try {\n                Client.readSymbolicLink(source)\n            } catch (e: ClientException) {\n                throw e.toFileSystemException(source.toString())\n            }\n            try {\n                Client.createSymbolicLink(target, sourceReparseData, attributesToCopy)\n            } catch (e: ClientException) {\n                val exception = e.toFileSystemException(target.toString())\n                if (exception is FileAlreadyExistsException && copyOptions.replaceExisting) {\n                    try {\n                        Client.delete(target)\n                    } catch (e2: ClientException) {\n                        if (e2.toFileSystemException(target.toString()) !is NoSuchFileException) {\n                            e2.addSuppressed(exception)\n                            throw e2.toFileSystemException(target.toString())\n                        }\n                    }\n                    try {\n                        Client.createSymbolicLink(target, sourceReparseData, attributesToCopy)\n                    } catch (e2: ClientException) {\n                        e2.addSuppressed(exception)\n                        throw e2.toFileSystemException(target.toString())\n                    }\n                }\n                e.maybeThrowInvalidFileNameException(target.toString())\n                throw e.toFileSystemException(target.toString())\n            }\n            copyOptions.progressListener?.invoke(sourceInformation.endOfFile)\n        } else {\n            throw AssertionError()\n        }\n        // We don't take error when copying attribute fatal, so errors will only be logged from now\n        // on.\n        // TODO: Copy SecurityDescriptor.\n        // TODO: Change modified time last?\n        try {\n            val fileInformation = FileBasicInformation(\n                if (copyOptions.copyAttributes) sourceInformation.creationTime else FileTime(0),\n                if (copyOptions.copyAttributes) sourceInformation.lastAccessTime else FileTime(0),\n                sourceInformation.lastWriteTime,\n                if (copyOptions.copyAttributes) sourceInformation.changeTime else FileTime(0), 0\n            )\n            Client.setFileInformation(target, true, fileInformation)\n        } catch (e: ClientException) {\n            e.printStackTrace()\n        }\n        // TODO: Copy FileFullEaInformation.\n    }\n\n    @Throws(IOException::class)\n    fun move(source: SmbPath, target: SmbPath, copyOptions: CopyOptions) {\n        val sourceInformation = try {\n            Client.getPathInformation(source, true)\n        } catch (e: ClientException) {\n            throw e.toFileSystemException(source.toString())\n        }\n        sourceInformation as? FileInformation\n            ?: throw FileSystemException(source.toString(), null, \"Cannot move shares\")\n        val targetInformation = try {\n            Client.getPathInformation(target, true)\n        } catch (e: ClientException) {\n            val exception = e.toFileSystemException(target.toString())\n            if (exception !is NoSuchFileException) {\n                throw exception\n            }\n            // Ignored.\n            null\n        }\n        if (targetInformation != null) {\n            targetInformation as? FileInformation\n                ?: throw FileSystemException(target.toString(), null, \"Cannot move shares\")\n            if (SmbFileKey(source, sourceInformation.fileId)\n                == SmbFileKey(target, targetInformation.fileId)) {\n                copyOptions.progressListener?.invoke(sourceInformation.endOfFile)\n                return\n            }\n            if (!copyOptions.replaceExisting) {\n                throw FileAlreadyExistsException(source.toString(), target.toString(), null)\n            }\n            try {\n                Client.delete(target)\n            } catch (e: ClientException) {\n                throw e.toFileSystemException(target.toString())\n            }\n        }\n        var renameSuccessful = false\n        try {\n            Client.rename(source, target)\n            renameSuccessful = true\n        } catch (e: ClientException) {\n            if (copyOptions.atomicMove) {\n                e.maybeThrowAtomicMoveNotSupportedException(source.toString(), target.toString())\n                e.maybeThrowInvalidFileNameException(target.toString())\n                throw e.toFileSystemException(source.toString(), target.toString())\n            }\n            // Ignored.\n        }\n        if (renameSuccessful) {\n            copyOptions.progressListener?.invoke(sourceInformation.endOfFile)\n            return\n        }\n        if (copyOptions.atomicMove) {\n            throw AssertionError()\n        }\n        var copyOptions = copyOptions\n        if (!copyOptions.copyAttributes || !copyOptions.noFollowLinks) {\n            copyOptions = CopyOptions(\n                copyOptions.replaceExisting, true, false, true, copyOptions.progressIntervalMillis,\n                copyOptions.progressListener\n            )\n        }\n        copy(source, target, copyOptions)\n        try {\n            Client.delete(source)\n        } catch (e: ClientException) {\n            if (e.toFileSystemException(source.toString()) !is NoSuchFileException) {\n                try {\n                    Client.delete(target)\n                } catch (e2: ClientException) {\n                    e.addSuppressed(e2.toFileSystemException(target.toString()))\n                }\n            }\n            throw e.toFileSystemException(source.toString())\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/smb/SmbFileAttributeView.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.smb\n\nimport com.hierynomus.msfscc.fileinformation.FileBasicInformation\nimport java8.nio.file.attribute.BasicFileAttributeView\nimport java8.nio.file.attribute.BasicFileAttributes\nimport java8.nio.file.attribute.FileTime\nimport me.zhanghai.android.files.provider.smb.client.Client\nimport me.zhanghai.android.files.provider.smb.client.ClientException\nimport me.zhanghai.android.files.provider.smb.client.FileInformation\nimport me.zhanghai.android.files.provider.smb.client.ShareInformation\nimport java.io.IOException\nimport com.hierynomus.msdtyp.FileTime as SmbFileTime\n\ninternal class SmbFileAttributeView(\n    private val path: SmbPath,\n    private val noFollowLinks: Boolean\n) : BasicFileAttributeView {\n    override fun name(): String = NAME\n\n    @Throws(IOException::class)\n    override fun readAttributes(): BasicFileAttributes {\n        val pathInformation = try {\n            Client.getPathInformation(path, noFollowLinks)\n        } catch (e: ClientException) {\n            throw e.toFileSystemException(path.toString())\n        }\n        return when (pathInformation) {\n            is FileInformation -> SmbFileAttributes.from(pathInformation, path)\n            is ShareInformation -> SmbShareFileAttributes.from(pathInformation, path)\n        }\n    }\n\n    override fun setTimes(\n        lastModifiedTime: FileTime?,\n        lastAccessTime: FileTime?,\n        createTime: FileTime?\n    ) {\n        if (createTime == null && lastAccessTime == null && lastModifiedTime == null) {\n            return\n        }\n        val fileInformation = FileBasicInformation(\n            createTime.toSmbFileTime(), lastAccessTime.toSmbFileTime(),\n            lastModifiedTime.toSmbFileTime(), SmbFileTime(0), 0\n        )\n        try {\n            Client.setFileInformation(path, noFollowLinks, fileInformation)\n        } catch (e: ClientException) {\n            throw e.toFileSystemException(path.toString())\n        }\n    }\n\n    private fun FileTime?.toSmbFileTime(): SmbFileTime =\n        if (this != null) SmbFileTime.ofEpochMillis(toMillis()) else SmbFileTime(0)\n\n    companion object {\n        private val NAME = SmbFileSystemProvider.scheme\n\n        val SUPPORTED_NAMES = setOf(\"basic\", NAME)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/smb/SmbFileAttributes.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.smb\n\nimport android.os.Parcelable\nimport com.hierynomus.msfscc.FileAttributes\nimport java.time.Instant\nimport java8.nio.file.attribute.FileTime\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.provider.common.AbstractBasicFileAttributes\nimport me.zhanghai.android.files.provider.common.BasicFileType\nimport me.zhanghai.android.files.provider.common.FileTimeParceler\nimport me.zhanghai.android.files.provider.smb.client.FileInformation\nimport me.zhanghai.android.files.util.hasBits\n\n@Parcelize\ninternal class SmbFileAttributes(\n    override val lastModifiedTime: @WriteWith<FileTimeParceler> FileTime,\n    override val lastAccessTime: @WriteWith<FileTimeParceler> FileTime,\n    override val creationTime: @WriteWith<FileTimeParceler> FileTime,\n    override val type: BasicFileType,\n    override val size: Long,\n    override val fileKey: Parcelable,\n    private val attributes: Long\n) : AbstractBasicFileAttributes() {\n    fun attributes(): Long = attributes\n\n    companion object {\n        fun from(fileInformation: FileInformation, path: SmbPath): SmbFileAttributes {\n            // lastWriteTime is returned by GetFileTime(), while changeTime isn't returned.\n            // https://docs.microsoft.com/zh-cn/windows/win32/api/fileapi/nf-fileapi-getfiletime\n            val lastModifiedTime =\n                FileTime.from(Instant.ofEpochMilli(fileInformation.lastWriteTime.toEpochMillis()))\n            val lastAccessTime =\n                FileTime.from(Instant.ofEpochMilli(fileInformation.lastAccessTime.toEpochMillis()))\n            val creationTime =\n                FileTime.from(Instant.ofEpochMilli(fileInformation.creationTime.toEpochMillis()))\n            val attributes = fileInformation.fileAttributes\n            val type = when {\n                attributes.hasBits(FileAttributes.FILE_ATTRIBUTE_REPARSE_POINT.value) ->\n                    BasicFileType.SYMBOLIC_LINK\n                attributes.hasBits(FileAttributes.FILE_ATTRIBUTE_DIRECTORY.value) ->\n                    BasicFileType.DIRECTORY\n                else -> BasicFileType.REGULAR_FILE\n            }\n            val size = fileInformation.endOfFile\n            val fileKey = SmbFileKey(path, fileInformation.fileId)\n            return SmbFileAttributes(\n                lastModifiedTime, lastAccessTime, creationTime, type, size, fileKey, attributes\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/smb/SmbFileKey.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.smb\n\nimport android.os.Parcelable\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.util.hash\n\n@Parcelize\ninternal class SmbFileKey(\n    private val path: SmbPath,\n    private val fileId: Long\n) : Parcelable {\n    override fun equals(other: Any?): Boolean {\n        if (this === other) {\n            return true\n        }\n        if (javaClass != other?.javaClass) {\n            return false\n        }\n        other as SmbFileKey\n        return if (fileId != 0L || other.fileId != 0L) {\n            path.authority == other.path.authority\n                && path.sharePath!!.name == other.path.sharePath!!.name\n                && fileId == other.fileId\n        } else {\n            path == other.path\n        }\n    }\n\n    override fun hashCode(): Int {\n        return if (fileId != 0L) {\n            hash(path.authority, path.sharePath!!.name, fileId)\n        } else {\n            path.hashCode()\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/smb/SmbFileSystem.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.smb\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport java8.nio.file.FileStore\nimport java8.nio.file.FileSystem\nimport java8.nio.file.Path\nimport java8.nio.file.PathMatcher\nimport java8.nio.file.WatchService\nimport java8.nio.file.attribute.UserPrincipalLookupService\nimport java8.nio.file.spi.FileSystemProvider\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.ByteStringBuilder\nimport me.zhanghai.android.files.provider.common.ByteStringListPathCreator\nimport me.zhanghai.android.files.provider.common.toByteString\nimport me.zhanghai.android.files.provider.smb.client.Authority\nimport me.zhanghai.android.files.util.readParcelable\nimport java.io.IOException\n\ninternal class SmbFileSystem(\n    private val provider: SmbFileSystemProvider,\n    val authority: Authority\n) : FileSystem(), ByteStringListPathCreator, Parcelable {\n    val rootDirectory = SmbPath(this, SEPARATOR_BYTE_STRING)\n\n    init {\n        if (!rootDirectory.isAbsolute) {\n            throw AssertionError(\"Root directory must be absolute\")\n        }\n        if (rootDirectory.nameCount != 0) {\n            throw AssertionError(\"Root directory must contain no names\")\n        }\n    }\n\n    private val lock = Any()\n\n    private var isOpen = true\n\n    val defaultDirectory: SmbPath\n        get() = rootDirectory\n\n    override fun provider(): FileSystemProvider = provider\n\n    override fun close() {\n        synchronized(lock) {\n            if (!isOpen) {\n                return\n            }\n            provider.removeFileSystem(this)\n            isOpen = false\n        }\n    }\n\n    override fun isOpen(): Boolean = synchronized(lock) { isOpen }\n\n    override fun isReadOnly(): Boolean = false\n\n    override fun getSeparator(): String = SEPARATOR_STRING\n\n    override fun getRootDirectories(): Iterable<Path> = listOf(rootDirectory)\n\n    override fun getFileStores(): Iterable<FileStore> {\n        // TODO\n        throw UnsupportedOperationException()\n    }\n\n    override fun supportedFileAttributeViews(): Set<String> =\n        SmbFileAttributeView.SUPPORTED_NAMES\n\n    override fun getPath(first: String, vararg more: String): SmbPath {\n        val path = ByteStringBuilder(first.toByteString())\n            .apply { more.forEach { append(SEPARATOR).append(it.toByteString()) } }\n            .toByteString()\n        return SmbPath(this, path)\n    }\n\n    override fun getPath(first: ByteString, vararg more: ByteString): SmbPath {\n        val path = ByteStringBuilder(first)\n            .apply { more.forEach { append(SEPARATOR).append(it) } }\n            .toByteString()\n        return SmbPath(this, path)\n    }\n\n    override fun getPathMatcher(syntaxAndPattern: String): PathMatcher {\n        throw UnsupportedOperationException()\n    }\n\n    override fun getUserPrincipalLookupService(): UserPrincipalLookupService {\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun newWatchService(): WatchService = SmbWatchService()\n\n    override fun equals(other: Any?): Boolean {\n        if (this === other) {\n            return true\n        }\n        if (javaClass != other?.javaClass) {\n            return false\n        }\n        other as SmbFileSystem\n        return authority == other.authority\n    }\n\n    override fun hashCode(): Int = authority.hashCode()\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        dest.writeParcelable(authority, flags)\n    }\n\n    companion object {\n        const val SEPARATOR = '/'.code.toByte()\n        private val SEPARATOR_BYTE_STRING = SEPARATOR.toByteString()\n        private const val SEPARATOR_STRING = SEPARATOR.toInt().toChar().toString()\n\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<SmbFileSystem> {\n            override fun createFromParcel(source: Parcel): SmbFileSystem {\n                val authority = source.readParcelable<Authority>()!!\n                return SmbFileSystemProvider.getOrNewFileSystem(authority)\n            }\n\n            override fun newArray(size: Int): Array<SmbFileSystem?> = arrayOfNulls(size)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/smb/SmbFileSystemProvider.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.smb\n\nimport com.hierynomus.msdtyp.AccessMask\nimport java8.nio.channels.FileChannel\nimport java8.nio.channels.SeekableByteChannel\nimport java8.nio.file.AccessMode\nimport java8.nio.file.CopyOption\nimport java8.nio.file.DirectoryStream\nimport java8.nio.file.FileStore\nimport java8.nio.file.FileSystem\nimport java8.nio.file.FileSystemAlreadyExistsException\nimport java8.nio.file.FileSystemNotFoundException\nimport java8.nio.file.LinkOption\nimport java8.nio.file.OpenOption\nimport java8.nio.file.Path\nimport java8.nio.file.ProviderMismatchException\nimport java8.nio.file.attribute.BasicFileAttributes\nimport java8.nio.file.attribute.FileAttribute\nimport java8.nio.file.attribute.FileAttributeView\nimport java8.nio.file.spi.FileSystemProvider\nimport me.zhanghai.android.files.provider.common.ByteStringPath\nimport me.zhanghai.android.files.provider.common.CloseableIterator\nimport me.zhanghai.android.files.provider.common.InvalidFileNameException\nimport me.zhanghai.android.files.provider.common.PathIteratorDirectoryStream\nimport me.zhanghai.android.files.provider.common.PathObservable\nimport me.zhanghai.android.files.provider.common.PathObservableProvider\nimport me.zhanghai.android.files.provider.common.Searchable\nimport me.zhanghai.android.files.provider.common.WalkFileTreeSearchable\nimport me.zhanghai.android.files.provider.common.WatchServicePathObservable\nimport me.zhanghai.android.files.provider.common.decodedPathByteString\nimport me.zhanghai.android.files.provider.common.toAccessModes\nimport me.zhanghai.android.files.provider.common.toByteString\nimport me.zhanghai.android.files.provider.common.toCopyOptions\nimport me.zhanghai.android.files.provider.common.toLinkOptions\nimport me.zhanghai.android.files.provider.common.toOpenOptions\nimport me.zhanghai.android.files.provider.smb.client.Authority\nimport me.zhanghai.android.files.provider.smb.client.Client\nimport me.zhanghai.android.files.provider.smb.client.ClientException\nimport me.zhanghai.android.files.provider.smb.client.FileInformation\nimport me.zhanghai.android.files.provider.smb.client.SymbolicLinkReparseData\nimport me.zhanghai.android.files.util.enumSetOf\nimport me.zhanghai.android.files.util.takeIfNotEmpty\nimport java.io.IOException\nimport java.net.URI\n\nobject SmbFileSystemProvider : FileSystemProvider(), PathObservableProvider, Searchable {\n    private const val SCHEME = \"smb\"\n\n    private val HIDDEN_FILE_NAME_PREFIX = \".\".toByteString()\n\n    private val fileSystems = mutableMapOf<Authority, SmbFileSystem>()\n\n    private val lock = Any()\n\n    override fun getScheme(): String = SCHEME\n\n    override fun newFileSystem(uri: URI, env: Map<String, *>): FileSystem {\n        uri.requireSameScheme()\n        val authority = uri.smbAuthority\n        synchronized(lock) {\n            if (fileSystems[authority] != null) {\n                throw FileSystemAlreadyExistsException(authority.toString())\n            }\n            return newFileSystemLocked(authority)\n        }\n    }\n\n    internal fun getOrNewFileSystem(authority: Authority): SmbFileSystem =\n        synchronized(lock) { fileSystems[authority] ?: newFileSystemLocked(authority) }\n\n    private fun newFileSystemLocked(authority: Authority): SmbFileSystem {\n        val fileSystem = SmbFileSystem(this, authority)\n        fileSystems[authority] = fileSystem\n        return fileSystem\n    }\n\n    override fun getFileSystem(uri: URI): FileSystem {\n        uri.requireSameScheme()\n        val authority = uri.smbAuthority\n        return synchronized(lock) { fileSystems[authority] }\n            ?: throw FileSystemNotFoundException(authority.toString())\n    }\n\n    internal fun removeFileSystem(fileSystem: SmbFileSystem) {\n        val authority = fileSystem.authority\n        synchronized(lock) { fileSystems.remove(authority) }\n    }\n\n    override fun getPath(uri: URI): Path {\n        uri.requireSameScheme()\n        val authority = uri.smbAuthority\n        val path = uri.decodedPathByteString\n            ?: throw IllegalArgumentException(\"URI must have a path\")\n        return getOrNewFileSystem(authority).getPath(path)\n    }\n\n    private fun URI.requireSameScheme() {\n        val scheme = scheme\n        require(scheme == SCHEME) { \"URI scheme $scheme must be $SCHEME\" }\n    }\n\n    private val URI.smbAuthority: Authority\n        get() {\n            val port = if (port != -1) port else Authority.DEFAULT_PORT\n            val userInfo = userInfo.orEmpty()\n            val domainSeparatorIndex = userInfo.indexOf('\\\\')\n            val username: String\n            val domain: String?\n            if (domainSeparatorIndex != -1) {\n                username = userInfo.substring(domainSeparatorIndex + 1)\n                domain = userInfo.substring(0, domainSeparatorIndex).takeIfNotEmpty()\n            } else {\n                username = userInfo\n                domain = null\n            }\n            return Authority(host, port, username, domain)\n        }\n\n    @Throws(IOException::class)\n    override fun newFileChannel(\n        file: Path,\n        options: Set<OpenOption>,\n        vararg attributes: FileAttribute<*>\n    ): FileChannel {\n        file as? SmbPath ?: throw ProviderMismatchException(file.toString())\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun newByteChannel(\n        file: Path,\n        options: Set<OpenOption>,\n        vararg attributes: FileAttribute<*>\n    ): SeekableByteChannel {\n        file as? SmbPath ?: throw ProviderMismatchException(file.toString())\n        val openOptions = options.toOpenOptions()\n        val desiredAccess = openOptions.toSmbDesiredAccess()\n        val fileAttributes = openOptions.toSmbFileAttributes()\n        val shareAccess = openOptions.toSmbShareAccess()\n        val createDisposition = openOptions.toSmbCreateDisposition()\n        val createOptions = openOptions.toSmbCreateOptions()\n        if (attributes.isNotEmpty()) {\n            throw UnsupportedOperationException(attributes.contentToString())\n        }\n        return try {\n            Client.openByteChannel(\n                file, desiredAccess, fileAttributes, shareAccess, createDisposition, createOptions,\n                openOptions.append\n            )\n        } catch (e: ClientException) {\n            throw e.toFileSystemException(file.toString())\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun newDirectoryStream(\n        directory: Path,\n        filter: DirectoryStream.Filter<in Path>\n    ): DirectoryStream<Path> {\n        directory as? SmbPath ?: throw ProviderMismatchException(directory.toString())\n        val iterator = try {\n            @Suppress(\"UNCHECKED_CAST\")\n            Client.openDirectoryIterator(directory) as CloseableIterator<Path>\n        } catch (e: ClientException) {\n            throw e.toFileSystemException(directory.toString())\n        }\n        return PathIteratorDirectoryStream(iterator, iterator, filter)\n    }\n\n    @Throws(IOException::class)\n    override fun createDirectory(directory: Path, vararg attributes: FileAttribute<*>) {\n        directory as? SmbPath ?: throw ProviderMismatchException(directory.toString())\n        if (attributes.isNotEmpty()) {\n            throw UnsupportedOperationException(attributes.contentToString())\n        }\n        try {\n            Client.createDirectory(directory)\n        } catch (e: ClientException) {\n            throw e.toFileSystemException(directory.toString())\n        }\n    }\n\n    override fun createSymbolicLink(link: Path, target: Path, vararg attributes: FileAttribute<*>) {\n        link as? SmbPath ?: throw ProviderMismatchException(link.toString())\n        val targetString: String\n        val isRelative: Boolean\n        when (target) {\n            is SmbPath -> {\n                if(target.isAbsolute && target.authority.port != Authority.DEFAULT_PORT) {\n                    throw InvalidFileNameException(\n                        target.toString(), null, \"Path is absolute but uses port ${\n                        target.authority.port} instead of the default port ${\n                        Authority.DEFAULT_PORT}\"\n                    )\n                }\n                targetString = target.toWindowsPath()\n                isRelative = !target.isAbsolute\n            }\n            is ByteStringPath -> {\n                targetString = target.toString()\n                isRelative = true\n            }\n            else -> throw ProviderMismatchException(target.toString())\n        }.toString()\n        if (attributes.isNotEmpty()) {\n            throw UnsupportedOperationException(attributes.contentToString())\n        }\n        val reparseData = SymbolicLinkReparseData(targetString, targetString, isRelative)\n        try {\n            Client.createSymbolicLink(link, reparseData)\n        } catch (e: ClientException) {\n            e.maybeThrowInvalidFileNameException(link.toString())\n            throw e.toFileSystemException(link.toString(), targetString)\n        }\n    }\n\n    override fun createLink(link: Path, existing: Path) {\n        link as? SmbPath ?: throw ProviderMismatchException(link.toString())\n        existing as? SmbPath ?: throw ProviderMismatchException(existing.toString())\n        try {\n            Client.createLink(existing, link, true)\n        } catch (e: ClientException) {\n            e.maybeThrowInvalidFileNameException(link.toString())\n            throw e.toFileSystemException(link.toString(), existing.toString())\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun delete(path: Path) {\n        path as? SmbPath ?: throw ProviderMismatchException(path.toString())\n        try {\n            Client.delete(path)\n        } catch (e: ClientException) {\n            throw e.toFileSystemException(path.toString())\n        }\n    }\n\n    override fun readSymbolicLink(link: Path): Path {\n        link as? SmbPath ?: throw ProviderMismatchException(link.toString())\n        val reparseData = try {\n            Client.readSymbolicLink(link)\n        } catch (e: ClientException) {\n            throw e.toFileSystemException(link.toString())\n        }\n        val target = reparseData.substituteName\n        return ByteStringPath(target.toByteString())\n    }\n\n    @Throws(IOException::class)\n    override fun copy(source: Path, target: Path, vararg options: CopyOption) {\n        source as? SmbPath ?: throw ProviderMismatchException(source.toString())\n        target as? SmbPath ?: throw ProviderMismatchException(target.toString())\n        val copyOptions = options.toCopyOptions()\n        SmbCopyMove.copy(source, target, copyOptions)\n    }\n\n    @Throws(IOException::class)\n    override fun move(source: Path, target: Path, vararg options: CopyOption) {\n        source as? SmbPath ?: throw ProviderMismatchException(source.toString())\n        target as? SmbPath ?: throw ProviderMismatchException(target.toString())\n        val copyOptions = options.toCopyOptions()\n        SmbCopyMove.move(source, target, copyOptions)\n    }\n\n    override fun isSameFile(path: Path, path2: Path): Boolean {\n        path as? SmbPath ?: throw ProviderMismatchException(path.toString())\n        if (path == path2) {\n            return true\n        }\n        if (path2 !is SmbPath) {\n            return false\n        }\n        if (path.authority != path2.authority) {\n            return false\n        }\n        val sharePath = path.sharePath\n        val sharePath2 = path2.sharePath\n        if (sharePath == null || sharePath2 == null || sharePath.name != sharePath2.name\n            || sharePath.path.isEmpty() || sharePath2.path.isEmpty()) {\n            return false\n        }\n        val pathInformation = try {\n            Client.getPathInformation(path, true)\n        } catch (e: ClientException) {\n            throw e.toFileSystemException(path.toString())\n        } as FileInformation\n        val path2Information = try {\n            Client.getPathInformation(path2, true)\n        } catch (e: ClientException) {\n            throw e.toFileSystemException(path2.toString())\n        } as FileInformation\n        return (SmbFileKey(path, pathInformation.fileId)\n            == SmbFileKey(path2, path2Information.fileId))\n    }\n\n    override fun isHidden(path: Path): Boolean {\n        path as? SmbPath ?: throw ProviderMismatchException(path.toString())\n        val fileName = path.fileNameByteString ?: return false\n        return fileName.startsWith(HIDDEN_FILE_NAME_PREFIX)\n    }\n\n    override fun getFileStore(path: Path): FileStore {\n        path as? SmbPath ?: throw ProviderMismatchException(path.toString())\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun checkAccess(path: Path, vararg modes: AccessMode) {\n        path as? SmbPath ?: throw ProviderMismatchException(path.toString())\n        val accessModes = modes.toAccessModes()\n        val desiredAccess = enumSetOf<AccessMask>()\n        if (accessModes.read) {\n            desiredAccess += AccessMask.GENERIC_READ\n        }\n        if (accessModes.write) {\n            desiredAccess += AccessMask.GENERIC_WRITE\n        }\n        if (accessModes.execute) {\n            desiredAccess += AccessMask.GENERIC_EXECUTE\n        }\n        try {\n            Client.checkAccess(path, desiredAccess, false)\n        } catch (e: ClientException) {\n            throw e.toFileSystemException(path.toString())\n        }\n    }\n\n    override fun <V : FileAttributeView> getFileAttributeView(\n        path: Path,\n        type: Class<V>,\n        vararg options: LinkOption\n    ): V? {\n        if (!supportsFileAttributeView(type)) {\n            return null\n        }\n        @Suppress(\"UNCHECKED_CAST\")\n        return getFileAttributeView(path, *options) as V\n    }\n\n    internal fun supportsFileAttributeView(type: Class<out FileAttributeView>): Boolean =\n        type.isAssignableFrom(SmbFileAttributeView::class.java)\n\n    @Throws(IOException::class)\n    override fun <A : BasicFileAttributes> readAttributes(\n        path: Path,\n        type: Class<A>,\n        vararg options: LinkOption\n    ): A {\n        if (!type.isAssignableFrom(BasicFileAttributes::class.java)) {\n            throw UnsupportedOperationException(type.toString())\n        }\n        @Suppress(\"UNCHECKED_CAST\")\n        return getFileAttributeView(path, *options).readAttributes() as A\n    }\n\n    private fun getFileAttributeView(path: Path, vararg options: LinkOption): SmbFileAttributeView {\n        path as? SmbPath ?: throw ProviderMismatchException(path.toString())\n        val linkOptions = options.toLinkOptions()\n        return SmbFileAttributeView(path, linkOptions.noFollowLinks)\n    }\n\n    override fun readAttributes(\n        path: Path,\n        attributes: String,\n        vararg options: LinkOption\n    ): Map<String, Any> {\n        path as? SmbPath ?: throw ProviderMismatchException(path.toString())\n        throw UnsupportedOperationException()\n    }\n\n    override fun setAttribute(\n        path: Path,\n        attribute: String,\n        value: Any,\n        vararg options: LinkOption\n    ) {\n        path as? SmbPath ?: throw ProviderMismatchException(path.toString())\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun observe(path: Path, intervalMillis: Long): PathObservable {\n        path as? SmbPath ?: throw ProviderMismatchException(path.toString())\n        return WatchServicePathObservable(path, intervalMillis)\n    }\n\n    @Throws(IOException::class)\n    override fun search(\n        directory: Path,\n        query: String,\n        intervalMillis: Long,\n        listener: (List<Path>) -> Unit\n    ) {\n        directory as? SmbPath ?: throw ProviderMismatchException(directory.toString())\n        WalkFileTreeSearchable.search(directory, query, intervalMillis, listener)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/smb/SmbPath.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.smb\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport java8.nio.file.FileSystem\nimport java8.nio.file.LinkOption\nimport java8.nio.file.Path\nimport java8.nio.file.ProviderMismatchException\nimport java8.nio.file.WatchEvent\nimport java8.nio.file.WatchKey\nimport java8.nio.file.WatchService\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.ByteStringListPath\nimport me.zhanghai.android.files.provider.common.UriAuthority\nimport me.zhanghai.android.files.provider.smb.client.Authority\nimport me.zhanghai.android.files.provider.smb.client.Client\nimport me.zhanghai.android.files.util.readParcelable\nimport java.io.File\nimport java.io.IOException\n\ninternal class SmbPath : ByteStringListPath<SmbPath>, Client.Path {\n    private val fileSystem: SmbFileSystem\n\n    constructor(\n        fileSystem: SmbFileSystem,\n        path: ByteString\n    ) : super(SmbFileSystem.SEPARATOR, path) {\n        this.fileSystem = fileSystem\n    }\n\n    private constructor(\n        fileSystem: SmbFileSystem,\n        absolute: Boolean,\n        segments: List<ByteString>\n    ) : super(SmbFileSystem.SEPARATOR, absolute, segments) {\n        this.fileSystem = fileSystem\n    }\n\n    override fun isPathAbsolute(path: ByteString): Boolean =\n        path.isNotEmpty() && path[0] == SmbFileSystem.SEPARATOR\n\n    override fun createPath(path: ByteString): SmbPath = SmbPath(fileSystem, path)\n\n    override fun createPath(absolute: Boolean, segments: List<ByteString>): SmbPath =\n        SmbPath(fileSystem, absolute, segments)\n\n    override val uriAuthority: UriAuthority\n        get() = fileSystem.authority.toUriAuthority()\n\n    override val defaultDirectory: SmbPath\n        get() = fileSystem.defaultDirectory\n\n    override fun getFileSystem(): FileSystem = fileSystem\n\n    override fun getRoot(): SmbPath? = if (isAbsolute) fileSystem.rootDirectory else null\n\n    @Throws(IOException::class)\n    override fun toRealPath(vararg options: LinkOption): SmbPath {\n        throw UnsupportedOperationException()\n    }\n\n    override fun toFile(): File {\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun register(\n        watcher: WatchService,\n        events: Array<WatchEvent.Kind<*>>,\n        vararg modifiers: WatchEvent.Modifier\n    ): WatchKey {\n        if (watcher !is SmbWatchService) {\n            throw ProviderMismatchException(watcher.toString())\n        }\n        return watcher.register(this, events, *modifiers)\n    }\n\n    override val authority: Authority\n        get() = fileSystem.authority\n\n    override val sharePath: Client.Path.SharePath? by lazy {\n        check(isAbsolute)\n        if (nameCount > 0) {\n            Client.Path.SharePath(\n                getNameByteString(0).toString(),\n                nameByteStrings.asSequence().drop(1).joinToString(\"\\\\\")\n            )\n        } else {\n            null\n        }\n    }\n\n    fun toWindowsPath(): String =\n        if (isAbsolute) {\n            // Port cannot be specified in a Windows UNC path for SMB, or otherwise it is resolved\n            // as a WebDAV path.\n            check(authority.port == Authority.DEFAULT_PORT) {\n                \"Path is absolute but uses port ${authority.port} instead of the default port ${\n                Authority.DEFAULT_PORT}\"\n            }\n            StringBuilder()\n                .append(\"\\\\\\\\\")\n                .append(authority.host)\n                .append(\"\\\\\")\n                .apply {\n                    val share = sharePath\n                    if (share != null) {\n                        append(share.name)\n                        append(\"\\\\\")\n                        append(share.path)\n                    }\n                }\n                .toString()\n        } else {\n            nameByteStrings.joinToString(\"\\\\\")\n        }\n\n    private constructor(source: Parcel) : super(source) {\n        fileSystem = source.readParcelable()!!\n    }\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        super.writeToParcel(dest, flags)\n\n        dest.writeParcelable(fileSystem, flags)\n    }\n\n    companion object {\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<SmbPath> {\n            override fun createFromParcel(source: Parcel): SmbPath = SmbPath(source)\n\n            override fun newArray(size: Int): Array<SmbPath?> = arrayOfNulls(size)\n        }\n    }\n}\n\nval Path.isSmbPath: Boolean\n    get() = this is SmbPath\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/smb/SmbShareFileAttributes.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.smb\n\nimport android.os.Parcelable\nimport java8.nio.file.attribute.FileTime\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.provider.common.AbstractBasicFileAttributes\nimport me.zhanghai.android.files.provider.common.BasicFileType\nimport me.zhanghai.android.files.provider.common.EPOCH\nimport me.zhanghai.android.files.provider.common.FileTimeParceler\nimport me.zhanghai.android.files.provider.smb.client.ShareInformation\nimport me.zhanghai.android.files.provider.smb.client.ShareType\n\n@Parcelize\ninternal class SmbShareFileAttributes(\n    override val lastModifiedTime: @WriteWith<FileTimeParceler> FileTime,\n    override val lastAccessTime: @WriteWith<FileTimeParceler> FileTime,\n    override val creationTime: @WriteWith<FileTimeParceler> FileTime,\n    override val type: BasicFileType,\n    override val size: Long,\n    override val fileKey: Parcelable,\n    private val totalSpace: Long?,\n    private val usableSpace: Long?,\n    private val unallocatedSpace: Long?\n) : AbstractBasicFileAttributes() {\n    fun totalSpace(): Long? = totalSpace\n\n    fun usableSpace(): Long? = usableSpace\n\n    fun unallocatedSpace(): Long? = unallocatedSpace\n\n    companion object {\n        fun from(shareInformation: ShareInformation, path: SmbPath): SmbShareFileAttributes {\n            val lastModifiedTime = FileTime::class.EPOCH\n            val lastAccessTime = lastModifiedTime\n            val creationTime = lastModifiedTime\n            val type = when (shareInformation.type) {\n                ShareType.DISK -> BasicFileType.DIRECTORY\n                else -> BasicFileType.OTHER\n            }\n            val size = 0L\n            val fileKey = path\n            val shareInfo = shareInformation.shareInfo\n            val totalSpace = shareInfo?.totalSpace\n            val usableSpace = shareInfo?.callerFreeSpace\n            val unallocatedSpace = shareInfo?.freeSpace\n            return SmbShareFileAttributes(\n                lastModifiedTime, lastAccessTime, creationTime, type, size, fileKey, totalSpace,\n                usableSpace, unallocatedSpace\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/smb/SmbWatchKey.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.smb\n\nimport me.zhanghai.android.files.provider.common.AbstractWatchKey\n\ninternal class SmbWatchKey(\n    watchService: SmbWatchService,\n    path: SmbPath\n) : AbstractWatchKey<SmbWatchKey, SmbPath>(watchService, path)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/smb/SmbWatchService.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.smb\n\nimport com.hierynomus.mserref.NtStatus\nimport com.hierynomus.msfscc.FileNotifyAction\nimport com.hierynomus.mssmb2.SMB2CompletionFilter\nimport com.hierynomus.mssmb2.SMBApiException\nimport com.hierynomus.mssmb2.messages.SMB2ChangeNotifyResponse\nimport com.hierynomus.smbj.share.Directory\nimport java8.nio.file.Path\nimport java8.nio.file.StandardWatchEventKinds\nimport java8.nio.file.WatchEvent\nimport me.zhanghai.android.files.provider.FileSystemProviders\nimport me.zhanghai.android.files.provider.common.AbstractWatchService\nimport me.zhanghai.android.files.provider.smb.client.Client\nimport me.zhanghai.android.files.provider.smb.client.ClientException\nimport me.zhanghai.android.files.util.closeSafe\nimport java.io.IOException\nimport java.io.InterruptedIOException\nimport java.util.concurrent.Future\nimport java.util.concurrent.atomic.AtomicInteger\n\n// @see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/05869c32-39f0-4726-afc9-671b76ae5ca7\ninternal class SmbWatchService : AbstractWatchService<SmbWatchKey>() {\n    private val notifiers = mutableMapOf<SmbPath, Notifier>()\n\n    @Throws(IOException::class)\n    fun register(\n        path: SmbPath,\n        kinds: Array<WatchEvent.Kind<*>>,\n        vararg modifiers: WatchEvent.Modifier\n    ): SmbWatchKey {\n        val kindSet = mutableSetOf<WatchEvent.Kind<*>>()\n        for (kind in kinds) {\n            when (kind) {\n                StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE,\n                StandardWatchEventKinds.ENTRY_MODIFY -> kindSet += kind\n                // Ignored.\n                StandardWatchEventKinds.OVERFLOW -> {}\n                else -> throw UnsupportedOperationException(kind.name())\n            }\n        }\n        for (modifier in modifiers) {\n            throw UnsupportedOperationException(modifier.name())\n        }\n        synchronized(notifiers) {\n            var notifier = notifiers[path]\n            if (notifier != null) {\n                notifier.kinds = kindSet\n            } else {\n                notifier = Notifier(this, path, kindSet)\n                notifiers[path] = notifier\n                notifier.start()\n            }\n            return notifier.key\n        }\n    }\n\n    private fun removeNotifier(notifier: Notifier) {\n        synchronized(notifiers) { notifiers -= notifier.key.watchable() }\n    }\n\n    override fun cancel(key: SmbWatchKey) {\n        val notifier = synchronized(notifiers) { notifiers.remove(key.watchable())!! }\n        notifier.interrupt()\n        try {\n            notifier.join()\n        } catch (e: InterruptedException) {\n            e.printStackTrace()\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun onClose() {\n        // Don't keep synchronized on notifiers, or we may get a deadlock when joining.\n        val notifiers = synchronized(notifiers) {\n            notifiers.values.toList().also { notifiers.clear() }\n        }\n        var exception: IOException? = null\n        for (notifier in notifiers) {\n            notifier.interrupt()\n            try {\n                notifier.join()\n            } catch (e: InterruptedException) {\n                val newException = InterruptedIOException().apply { initCause(e) }\n                if (exception == null) {\n                    exception = newException\n                } else {\n                    exception.addSuppressed(newException)\n                }\n            }\n        }\n        exception?.let { throw it }\n    }\n\n    private class Notifier @Throws(IOException::class) constructor(\n        private val watchService: SmbWatchService,\n        path: SmbPath,\n        @Volatile\n        var kinds: Set<WatchEvent.Kind<*>>\n    ) : Thread(\"SmbWatchService.Notifier-${id.getAndIncrement()}\") {\n        val key = SmbWatchKey(watchService, path)\n\n        private val directory: Directory\n\n        @Volatile\n        private var future: Future<SMB2ChangeNotifyResponse>\n\n        init {\n            isDaemon = true\n            try {\n                directory = Client.openDirectoryForChangeNotification(path)\n                future = Client.requestDirectoryChangeNotification(directory, COMPLETION_FILTER)\n            } catch (e: ClientException) {\n                throw e.toFileSystemException(path.toString())\n            }\n        }\n\n        override fun run() {\n            try {\n                loop@ while (true) {\n                    val response = future.get()\n                    when (response.header.statusCode) {\n                        NtStatus.STATUS_NOTIFY_ENUM_DIR.value ->\n                            key.addEvent(StandardWatchEventKinds.OVERFLOW, null)\n                        NtStatus.STATUS_SUCCESS.value -> {\n                            if (FileSystemProviders.overflowWatchEvents) {\n                                key.addEvent(StandardWatchEventKinds.OVERFLOW, null)\n                            } else {\n                                for (fileNotifyInfo in response.fileNotifyInfoList) {\n                                    val kind = fileNotifyInfo.action.toEventKind()\n                                    if (kind !in kinds) {\n                                        continue\n                                    }\n                                    val name = key.watchable().fileSystem\n                                        .getPath(fileNotifyInfo.fileName)\n                                    key.addEvent(kind, name)\n                                }\n                            }\n                        }\n                        else ->\n                            throw SMBApiException(\n                                response.header, \"Change notify failed for ${key.watchable()}\"\n                            )\n                    }\n                    future = Client.requestDirectoryChangeNotification(directory, COMPLETION_FILTER)\n                }\n            } catch (e: Exception) {\n                e.printStackTrace()\n                key.setInvalid()\n                if (!(e is InterruptedException || e is InterruptedIOException)) {\n                    key.signal()\n                }\n                watchService.removeNotifier(this)\n            } finally {\n                // FIXME: We should cancel the CHANGE_NOTIFY request, but it currently crashes SMBJ.\n                // https://github.com/hierynomus/smbj/issues/572\n                // com.hierynomus.smbj.common.SMBRuntimeException:\n                // Unknown SMB2 Message Command type: SMB2_CANCEL\n                //future.cancel(true)\n                directory.closeSafe()\n            }\n        }\n\n        private fun FileNotifyAction.toEventKind(): WatchEvent.Kind<Path> =\n            when (this) {\n                FileNotifyAction.FILE_ACTION_ADDED, FileNotifyAction.FILE_ACTION_RENAMED_NEW_NAME ->\n                    StandardWatchEventKinds.ENTRY_CREATE\n                FileNotifyAction.FILE_ACTION_REMOVED,\n                FileNotifyAction.FILE_ACTION_RENAMED_OLD_NAME ->\n                    StandardWatchEventKinds.ENTRY_DELETE\n                FileNotifyAction.FILE_ACTION_MODIFIED -> StandardWatchEventKinds.ENTRY_MODIFY\n                else -> throw AssertionError(this)\n            }\n\n        companion object {\n            private val COMPLETION_FILTER = setOf(\n                SMB2CompletionFilter.FILE_NOTIFY_CHANGE_FILE_NAME,\n                SMB2CompletionFilter.FILE_NOTIFY_CHANGE_DIR_NAME,\n                SMB2CompletionFilter.FILE_NOTIFY_CHANGE_ATTRIBUTES,\n                SMB2CompletionFilter.FILE_NOTIFY_CHANGE_SIZE,\n                SMB2CompletionFilter.FILE_NOTIFY_CHANGE_LAST_WRITE,\n                // We don't care about last access time and it might change too frequently.\n                //SMB2CompletionFilter.FILE_NOTIFY_CHANGE_LAST_ACCESS,\n                SMB2CompletionFilter.FILE_NOTIFY_CHANGE_CREATION,\n                SMB2CompletionFilter.FILE_NOTIFY_CHANGE_EA,\n                SMB2CompletionFilter.FILE_NOTIFY_CHANGE_SECURITY\n            )\n\n            private val id = AtomicInteger()\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/smb/client/Authenticator.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.smb.client\n\ninterface Authenticator {\n    fun getPassword(authority: Authority): String?\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/smb/client/Authority.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.smb.client\n\nimport android.os.Parcelable\nimport com.hierynomus.smbj.SMBClient\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.provider.common.UriAuthority\nimport me.zhanghai.android.files.util.takeIfNotEmpty\n\n@Parcelize\ndata class Authority(\n    val host: String,\n    val port: Int,\n    val username: String,\n    val domain: String?\n) : Parcelable {\n    fun toUriAuthority(): UriAuthority {\n        val userInfo = if (domain != null) \"$domain\\\\$username\" else username.takeIfNotEmpty()\n        val uriPort = port.takeIf { it != DEFAULT_PORT }\n        return UriAuthority(userInfo, host, uriPort)\n    }\n\n    override fun toString(): String = toUriAuthority().toString()\n\n    companion object {\n        const val DEFAULT_PORT = SMBClient.DEFAULT_PORT\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/smb/client/Client.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.smb.client\n\nimport com.hierynomus.msdtyp.AccessMask\nimport com.hierynomus.mserref.NtStatus\nimport com.hierynomus.msfscc.FileAttributes\nimport com.hierynomus.msfscc.fileinformation.FileBasicInformation\nimport com.hierynomus.msfscc.fileinformation.FileIdFullDirectoryInformation\nimport com.hierynomus.msfscc.fileinformation.FileSettableInformation\nimport com.hierynomus.msfscc.fileinformation.FileStandardInformation\nimport com.hierynomus.mssmb2.SMB2CompletionFilter\nimport com.hierynomus.mssmb2.SMB2CreateDisposition\nimport com.hierynomus.mssmb2.SMB2CreateOptions\nimport com.hierynomus.mssmb2.SMB2MessageCommandCode\nimport com.hierynomus.mssmb2.SMB2ShareAccess\nimport com.hierynomus.mssmb2.SMBApiException\nimport com.hierynomus.mssmb2.messages.SMB2ChangeNotifyResponse\nimport com.hierynomus.protocol.commons.EnumWithValue\nimport com.hierynomus.smbj.ProgressListener\nimport com.hierynomus.smbj.SMBClient\nimport com.hierynomus.smbj.auth.AuthenticationContext\nimport com.hierynomus.smbj.common.SMBRuntimeException\nimport com.hierynomus.smbj.session.Session\nimport com.hierynomus.smbj.share.Directory\nimport com.hierynomus.smbj.share.DiskShare\nimport com.hierynomus.smbj.share.PipeShare\nimport com.hierynomus.smbj.share.PrinterShare\nimport com.hierynomus.smbj.share.Share\nimport com.rapid7.client.dcerpc.mssrvs.ServerService\nimport com.rapid7.client.dcerpc.transport.SMBTransportFactories\nimport java8.nio.channels.SeekableByteChannel\nimport jcifs.context.SingletonContext\nimport me.zhanghai.android.files.provider.common.CloseableIterator\nimport me.zhanghai.android.files.provider.common.copyTo\nimport me.zhanghai.android.files.provider.common.newInputStream\nimport me.zhanghai.android.files.provider.common.newOutputStream\nimport me.zhanghai.android.files.util.closeSafe\nimport me.zhanghai.android.files.util.enumSetOf\nimport me.zhanghai.android.files.util.hasBits\nimport java.io.Closeable\nimport java.io.IOException\nimport java.net.Inet4Address\nimport java.net.UnknownHostException\nimport java.util.Collections\nimport java.util.WeakHashMap\nimport java.util.concurrent.Future\n\nobject Client {\n    @Volatile\n    lateinit var authenticator: Authenticator\n\n    private val client = SMBClient()\n\n    private val sessions = mutableMapOf<Authority, Session>()\n\n    private val directoryFileInformationCache =\n        Collections.synchronizedMap(WeakHashMap<Path, FileInformation>())\n\n    @Throws(ClientException::class)\n    fun openByteChannel(\n        path: Path,\n        desiredAccess: Set<AccessMask>,\n        fileAttributes: Set<FileAttributes>,\n        shareAccess: Set<SMB2ShareAccess>,\n        createDisposition: SMB2CreateDisposition,\n        createOptions: Set<SMB2CreateOptions>,\n        isAppend: Boolean\n    ): SeekableByteChannel {\n        val sharePath = path.sharePath ?: throw ClientException(\"$path does not have a share path\")\n        val session = getSession(path.authority)\n        val share = getDiskShare(session, sharePath.name)\n        val file = try {\n            share.openFile(\n                sharePath.path, desiredAccess, fileAttributes, shareAccess, createDisposition,\n                createOptions\n            )\n        } catch (e: SMBRuntimeException) {\n            throw ClientException(e)\n        }\n        return FileByteChannel(file, isAppend)\n    }\n\n    @Throws(ClientException::class)\n    fun openDirectoryIterator(path: Path): CloseableIterator<Path> {\n        val session = getSession(path.authority)\n        val sharePath = path.sharePath\n        if (sharePath == null) {\n            val transport = try {\n                SMBTransportFactories.SRVSVC.getTransport(session)\n            } catch (e: IOException) {\n                throw ClientException(e)\n            } catch (e: SMBRuntimeException) {\n                throw ClientException(e)\n            }\n            val serverService = ServerService(transport)\n            val netShareInfos = try {\n                serverService.shares1\n            } catch (e: IOException) {\n                throw ClientException(e)\n            } catch (e: SMBRuntimeException) {\n                throw ClientException(e)\n            }\n            val sharePaths = netShareInfos.mapNotNull {\n                if (!(it.type.hasBits(ShareTypes.STYPE_PRINTQ.value)\n                        || it.type.hasBits(ShareTypes.STYPE_DEVICE.value)\n                        || it.type.hasBits(ShareTypes.STYPE_IPC.value))) {\n                    path.resolve(it.netName)\n                } else {\n                    null\n                }\n            }\n            return object : CloseableIterator<Path>, Iterator<Path> by sharePaths.iterator() {\n                override fun close() {}\n            }\n        } else {\n            val share = getDiskShare(session, sharePath.name)\n            val directory = try {\n                share.openDirectory(\n                    sharePath.path, enumSetOf(\n                        AccessMask.FILE_LIST_DIRECTORY, AccessMask.FILE_READ_ATTRIBUTES,\n                        AccessMask.FILE_READ_EA\n                    ), null, SMB2ShareAccess.ALL, SMB2CreateDisposition.FILE_OPEN, null\n                )\n            } catch (e: SMBRuntimeException) {\n                throw ClientException(e)\n            }\n            val directoryIterator = directory.iterator(FileIdFullDirectoryInformation::class.java)\n                .asSequence()\n                .filter { fileInformation ->\n                    !fileInformation.fileName.let { it == \".\" || it == \"..\" }\n                }\n                .map { fileInformation ->\n                    path.resolve(fileInformation.fileName).also {\n                        directoryFileInformationCache[it] = fileInformation.toFileInformation()\n                    }\n                }\n                .iterator()\n            return object : CloseableIterator<Path>, Iterator<Path> by directoryIterator,\n                Closeable by directory {}\n        }\n    }\n\n    // @see https://gitlab.com/samba-team/devel/samba/-/blob/master/source3/libsmb/cli_smb2_fnum.c\n    // cli_smb2_mkdir_send\n    @Throws(ClientException::class)\n    fun createDirectory(path: Path, fileAttributes: Set<FileAttributes>? = null) {\n        val sharePath = path.sharePath ?: throw ClientException(\"$path does not have a share path\")\n        val session = getSession(path.authority)\n        val share = getDiskShare(session, sharePath.name)\n        val directory = try {\n            share.openDirectory(\n                sharePath.path,\n                enumSetOf(AccessMask.FILE_READ_ATTRIBUTES, AccessMask.FILE_READ_EA),\n                enumSetOf(FileAttributes.FILE_ATTRIBUTE_DIRECTORY)\n                    .apply { fileAttributes?.let { addAll(it) } }, SMB2ShareAccess.ALL,\n                SMB2CreateDisposition.FILE_CREATE,\n                enumSetOf(SMB2CreateOptions.FILE_OPEN_REPARSE_POINT)\n            )\n        } catch (e: SMBRuntimeException) {\n            throw ClientException(e)\n        }\n        try {\n            directory.close()\n        } catch (e: SMBRuntimeException) {\n            throw ClientException(e)\n        }\n    }\n\n    // @see https://gitlab.com/samba-team/devel/samba/-/blob/master/source3/libsmb/clisymlink.c\n    //      cli_symlink_send\n    @Throws(ClientException::class)\n    fun createSymbolicLink(\n        path: Path,\n        reparseData: SymbolicLinkReparseData,\n        fileAttributes: Set<FileAttributes>? = null\n    ) {\n        val sharePath = path.sharePath ?: throw ClientException(\"$path does not have a share path\")\n        val session = getSession(path.authority)\n        val share = getDiskShare(session, sharePath.name)\n        val diskEntry = try {\n            share.open(\n                sharePath.path, enumSetOf(\n                    AccessMask.FILE_READ_ATTRIBUTES, AccessMask.FILE_WRITE_ATTRIBUTES,\n                    AccessMask.FILE_READ_EA, AccessMask.FILE_WRITE_EA, AccessMask.DELETE,\n                    AccessMask.SYNCHRONIZE\n                ), enumSetOf<FileAttributes>().apply {\n                    fileAttributes?.let { addAll(it) }\n                    this -= FileAttributes.FILE_ATTRIBUTE_REPARSE_POINT\n                    if (isEmpty()) {\n                        this += FileAttributes.FILE_ATTRIBUTE_NORMAL\n                    }\n                }, null, SMB2CreateDisposition.FILE_CREATE, enumSetOf(\n                    SMB2CreateOptions.FILE_NON_DIRECTORY_FILE,\n                    SMB2CreateOptions.FILE_OPEN_REPARSE_POINT\n                )\n            )\n        } catch (e: SMBRuntimeException) {\n            throw ClientException(e)\n        }\n        try {\n            diskEntry.use {\n                var successful = false\n                try {\n                    it.setSymbolicLinkReparseData(reparseData)\n                    successful = true\n                } finally {\n                    if (!successful) {\n                        try {\n                            it.deleteOnClose()\n                        } catch (e: SMBRuntimeException) {\n                            e.printStackTrace()\n                        }\n                    }\n                }\n            }\n        } catch (e: SMBRuntimeException) {\n            throw ClientException(e)\n        }\n    }\n\n    // @see https://gitlab.com/samba-team/devel/samba/-/blob/master/source3/libsmb/clifile.c\n    //      cli_smb2_hardlink_send\n    @Throws(ClientException::class)\n    fun createLink(path: Path, link: Path, openReparsePoint: Boolean) {\n        val sharePath = path.sharePath ?: throw ClientException(\"$path does not have a share path\")\n        val linkSharePath = link.sharePath\n            ?: throw ClientException(\"$link does not have a share path\")\n        if (link.authority != path.authority || linkSharePath.name != sharePath.name) {\n            throw ClientException(\n                SMBApiException(\n                    NtStatus.STATUS_NOT_SAME_DEVICE.value, SMB2MessageCommandCode.SMB2_SET_INFO,\n                    null\n                )\n            )\n        }\n        val session = getSession(path.authority)\n        val share = getDiskShare(session, sharePath.name)\n        val diskEntry = try {\n            share.open(\n                sharePath.path,\n                enumSetOf(AccessMask.FILE_WRITE_ATTRIBUTES, AccessMask.FILE_WRITE_EA),\n                null, SMB2ShareAccess.ALL, SMB2CreateDisposition.FILE_OPEN,\n                // CreateHardLink doesn't work for directories.\n                enumSetOf(SMB2CreateOptions.FILE_NON_DIRECTORY_FILE).apply {\n                    if (openReparsePoint) {\n                        this += SMB2CreateOptions.FILE_OPEN_REPARSE_POINT\n                    }\n                }\n            )\n        } catch (e: SMBRuntimeException) {\n            throw ClientException(e)\n        }\n        try {\n            diskEntry.use { it.createHardlink(linkSharePath.path, false) }\n        } catch (e: SMBRuntimeException) {\n            throw ClientException(e)\n        }\n    }\n\n    @Throws(ClientException::class)\n    fun delete(path: Path) {\n        val sharePath = path.sharePath ?: throw ClientException(\"$path does not have a share path\")\n        val session = getSession(path.authority)\n        val share = getDiskShare(session, sharePath.name)\n        val diskEntry = try {\n            share.open(\n                sharePath.path, enumSetOf(AccessMask.DELETE), null, SMB2ShareAccess.ALL,\n                SMB2CreateDisposition.FILE_OPEN, enumSetOf(\n                    SMB2CreateOptions.FILE_DELETE_ON_CLOSE,\n                    SMB2CreateOptions.FILE_OPEN_REPARSE_POINT\n                )\n            )\n        } catch (e: SMBRuntimeException) {\n            if (e is SMBApiException && e.status == NtStatus.STATUS_DELETE_PENDING) {\n                return\n            }\n            throw ClientException(e)\n        }\n        try {\n            diskEntry.close()\n        } catch (e: SMBRuntimeException) {\n            throw ClientException(e)\n        }\n        directoryFileInformationCache -= path\n    }\n\n    // @see https://gitlab.com/samba-team/devel/samba/-/blob/master/source3/libsmb/clisymlink.c\n    //      cli_readlink_send\n    @Throws(ClientException::class)\n    fun readSymbolicLink(path: Path): SymbolicLinkReparseData {\n        val sharePath = path.sharePath ?: throw ClientException(\"$path does not have a share path\")\n        val session = getSession(path.authority)\n        val share = getDiskShare(session, sharePath.name)\n        val diskEntry = try {\n            share.open(\n                sharePath.path,\n                enumSetOf(AccessMask.FILE_READ_ATTRIBUTES, AccessMask.FILE_READ_EA), null,\n                SMB2ShareAccess.ALL, SMB2CreateDisposition.FILE_OPEN,\n                enumSetOf(SMB2CreateOptions.FILE_OPEN_REPARSE_POINT)\n            )\n        } catch (e: SMBRuntimeException) {\n            throw ClientException(e)\n        }\n        return try {\n            diskEntry.use { it.getSymbolicLinkReparseData() }\n        } catch (e: SMBRuntimeException) {\n            throw ClientException(e)\n        }\n    }\n\n    // @see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/cd0162e4-7650-4293-8a2a-d696923203ef\n    @Throws(ClientException::class)\n    fun copyFile(\n        source: Path,\n        target: Path,\n        copyAttributes: Boolean,\n        openReparsePoint: Boolean,\n        intervalMillis: Long,\n        listener: ((Long) -> Unit)?\n    ) {\n        val sourceSharePath = source.sharePath\n            ?: throw ClientException(\"$source does not have a share path\")\n        val targetSharePath = target.sharePath\n            ?: throw ClientException(\"$target does not have a share path\")\n        val sourceSession = getSession(source.authority)\n        val sourceShare = getDiskShare(sourceSession, sourceSharePath.name)\n        val targetSession = getSession(target.authority)\n        val targetShare = getDiskShare(targetSession, targetSharePath.name)\n        val sourceFile = try {\n            sourceShare.openFile(\n                sourceSharePath.path, enumSetOf(\n                    AccessMask.FILE_READ_DATA, AccessMask.FILE_READ_ATTRIBUTES,\n                    AccessMask.FILE_READ_EA\n                ), null, SMB2ShareAccess.ALL, SMB2CreateDisposition.FILE_OPEN,\n                if (openReparsePoint) {\n                    enumSetOf(SMB2CreateOptions.FILE_OPEN_REPARSE_POINT)\n                } else {\n                    null\n                }\n            )\n        } catch (e: SMBRuntimeException) {\n            throw ClientException(e)\n        }\n        try {\n            sourceFile.use {\n                val attributesToCopy = if (copyAttributes) {\n                    val sourceAttributes = try {\n                        sourceFile.getFileInformation(FileBasicInformation::class.java)\n                    } catch (e: SMBRuntimeException) {\n                        throw ClientException(e)\n                    }.fileAttributes\n                    EnumWithValue.EnumUtils.toEnumSet(sourceAttributes, FileAttributes::class.java)\n                } else {\n                    enumSetOf(FileAttributes.FILE_ATTRIBUTE_NORMAL)\n                }\n                val targetFile = try {\n                    targetShare.openFile(\n                        targetSharePath.path, enumSetOf(\n                        AccessMask.FILE_WRITE_DATA, AccessMask.FILE_WRITE_ATTRIBUTES,\n                        AccessMask.FILE_WRITE_EA, AccessMask.DELETE\n                    ), attributesToCopy, SMB2ShareAccess.ALL,\n                        SMB2CreateDisposition.FILE_CREATE,\n                        enumSetOf(SMB2CreateOptions.FILE_OPEN_REPARSE_POINT)\n                    )\n                } catch (e: SMBRuntimeException) {\n                    throw ClientException(e)\n                }\n                targetFile.use {\n                    var successful = false\n                    try {\n                        if (sourceSession == targetSession) {\n                            val length = try {\n                                sourceFile.getFileInformation(FileStandardInformation::class.java)\n                            } catch (e: SMBRuntimeException) {\n                                throw ClientException(e)\n                            }.endOfFile\n                            val progressListener = listener?.let {\n                                var lastCopiedSize = 0L\n                                ProgressListener { copiedSize, _ ->\n                                    it(copiedSize - lastCopiedSize)\n                                    lastCopiedSize = copiedSize\n                                }\n                            }\n                            try {\n                                sourceFile.serverCopy(0, targetFile, 0, length, progressListener)\n                            } catch (e: SMBRuntimeException) {\n                                throw ClientException(e)\n                            }\n                        } else {\n                            val sourceInputStream = FileByteChannel(sourceFile, false)\n                                .newInputStream()\n                            val targetOutputStream = FileByteChannel(targetFile, false)\n                                .newOutputStream()\n                            sourceInputStream.copyTo(targetOutputStream, intervalMillis, listener)\n                        }\n                        successful = true\n                    } finally {\n                        if (!successful) {\n                            try {\n                                targetFile.deleteOnClose()\n                            } catch (e: SMBRuntimeException) {\n                                e.printStackTrace()\n                            }\n                        }\n                    }\n                }\n            }\n        } catch (e: SMBRuntimeException) {\n            throw ClientException(e)\n        }\n    }\n\n    // @see https://gitlab.com/samba-team/devel/samba/-/blob/master/source3/libsmb/cli_smb2_fnum.c\n    //      cli_smb2_rename\n    @Throws(ClientException::class)\n    fun rename(path: Path, newPath: Path) {\n        val sharePath = path.sharePath ?: throw ClientException(\"$path does not have a share path\")\n        val newSharePath = newPath.sharePath\n            ?: throw ClientException(\"$newPath does not have a share path\")\n        if (newPath.authority != path.authority || newSharePath.name != sharePath.name) {\n            throw ClientException(\n                SMBApiException(\n                    NtStatus.STATUS_NOT_SAME_DEVICE.value, SMB2MessageCommandCode.SMB2_SET_INFO,\n                    null\n                )\n            )\n        }\n        val session = getSession(path.authority)\n        val share = getDiskShare(session, sharePath.name)\n        val diskEntry = try {\n            share.open(\n                sharePath.path, enumSetOf(AccessMask.DELETE), null, SMB2ShareAccess.ALL,\n                SMB2CreateDisposition.FILE_OPEN,\n                enumSetOf(SMB2CreateOptions.FILE_OPEN_REPARSE_POINT)\n            )\n        } catch (e: SMBRuntimeException) {\n            throw ClientException(e)\n        }\n        try {\n            diskEntry.use { it.rename(newSharePath.path, true) }\n        } catch (e: SMBRuntimeException) {\n            throw ClientException(e)\n        }\n        directoryFileInformationCache -= path\n        directoryFileInformationCache -= newPath\n    }\n\n    @Throws(ClientException::class)\n    fun getPathInformation(path: Path, openReparsePoint: Boolean): PathInformation {\n        val sharePath = path.sharePath ?: throw ClientException(\"$path does not have a share path\")\n        val session = getSession(path.authority)\n        if (sharePath.path.isEmpty()) {\n            return when (val share = getShare(session, sharePath.name)) {\n                is DiskShare -> {\n                    val shareInfo = try {\n                        share.shareInformation\n                    } catch (e: SMBRuntimeException) {\n                        e.printStackTrace()\n                        null\n                    }\n                    ShareInformation(ShareType.DISK, shareInfo)\n                    // Don't close the disk share, because it might still be in use, or might become\n                    // in use shortly. All shares are automatically closed when the session is\n                    // closed anyway.\n                }\n                is PipeShare -> ShareInformation(ShareType.PIPE, null).also { share.closeSafe() }\n                is PrinterShare -> ShareInformation(ShareType.PRINTER, null)\n                    .also { share.closeSafe() }\n                else -> throw AssertionError(share)\n            }\n        } else {\n            synchronized(directoryFileInformationCache) {\n                directoryFileInformationCache[path]?.let {\n                    if (openReparsePoint || !it.fileAttributes.hasBits(\n                            FileAttributes.FILE_ATTRIBUTE_REPARSE_POINT.value\n                        )) {\n                        return it.also { directoryFileInformationCache -= path }\n                    }\n                }\n            }\n            val share = getDiskShare(session, sharePath.name)\n            val diskEntry = try {\n                share.open(\n                    sharePath.path,\n                    enumSetOf(AccessMask.FILE_READ_ATTRIBUTES, AccessMask.FILE_READ_EA),\n                    null, SMB2ShareAccess.ALL, SMB2CreateDisposition.FILE_OPEN,\n                    if (openReparsePoint) {\n                        enumSetOf(SMB2CreateOptions.FILE_OPEN_REPARSE_POINT)\n                    } else {\n                        null\n                    }\n                )\n            } catch (e: SMBRuntimeException) {\n                throw ClientException(e)\n            }\n            val fileAllInformation = try {\n                diskEntry.use { it.fileInformation }\n            } catch (e: SMBRuntimeException) {\n                throw ClientException(e)\n            }\n            return fileAllInformation.toFileInformation()\n        }\n    }\n\n    @Throws(ClientException::class)\n    fun setFileInformation(\n        path: Path,\n        openReparsePoint: Boolean,\n        fileInformation: FileSettableInformation\n    ) {\n        val sharePath = path.sharePath ?: throw ClientException(\"$path does not have a share path\")\n        val session = getSession(path.authority)\n        val share = getDiskShare(session, sharePath.name)\n        val diskEntry = try {\n            share.open(\n                sharePath.path,\n                enumSetOf(AccessMask.FILE_WRITE_ATTRIBUTES, AccessMask.FILE_WRITE_EA),\n                null, SMB2ShareAccess.ALL, SMB2CreateDisposition.FILE_OPEN,\n                if (openReparsePoint) {\n                    enumSetOf(SMB2CreateOptions.FILE_OPEN_REPARSE_POINT)\n                } else {\n                    null\n                }\n            )\n        } catch (e: SMBRuntimeException) {\n            throw ClientException(e)\n        }\n        try {\n            diskEntry.use { it.setFileInformation(fileInformation) }\n        } catch (e: SMBRuntimeException) {\n            throw ClientException(e)\n        }\n        directoryFileInformationCache -= path\n    }\n\n    @Throws(ClientException::class)\n    fun checkAccess(path: Path, desiredAccess: Set<AccessMask>, openReparsePoint: Boolean) {\n        val sharePath = path.sharePath ?: throw ClientException(\"$path does not have a share path\")\n        val session = getSession(path.authority)\n        val share = getDiskShare(session, sharePath.name)\n        val diskEntry = try {\n            share.open(\n                sharePath.path, desiredAccess, null, SMB2ShareAccess.ALL,\n                SMB2CreateDisposition.FILE_OPEN, if (openReparsePoint) {\n                    enumSetOf(SMB2CreateOptions.FILE_OPEN_REPARSE_POINT)\n                } else {\n                    null\n                }\n            )\n        } catch (e: SMBRuntimeException) {\n            throw ClientException(e)\n        }\n        try {\n            diskEntry.close()\n        } catch (e: SMBRuntimeException) {\n            throw ClientException(e)\n        }\n    }\n\n    // @see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/05869c32-39f0-4726-afc9-671b76ae5ca7\n    @Throws(ClientException::class)\n    fun openDirectoryForChangeNotification(path: Path): Directory {\n        val sharePath = path.sharePath ?: throw ClientException(\"$path does not have a share path\")\n        val session = getSession(path.authority)\n        val share = getDiskShare(session, sharePath.name)\n        return try {\n            share.openDirectory(\n                sharePath.path, enumSetOf(AccessMask.FILE_LIST_DIRECTORY), null,\n                SMB2ShareAccess.ALL, SMB2CreateDisposition.FILE_OPEN, null\n            )\n        } catch (e: SMBRuntimeException) {\n            throw ClientException(e)\n        }\n    }\n\n    @Throws(ClientException::class)\n    fun requestDirectoryChangeNotification(\n        directory: Directory,\n        completionFilter: Set<SMB2CompletionFilter>\n    ): Future<SMB2ChangeNotifyResponse> {\n        return try {\n            directory.watchAsync(completionFilter, false)\n        } catch (e: SMBRuntimeException) {\n            throw ClientException(e)\n        }\n    }\n\n    @Throws(ClientException::class)\n    private fun getSession(authority: Authority): Session {\n        synchronized(sessions) {\n            var session = sessions[authority]\n            if (session != null) {\n                val connection = session.connection\n                if (connection.isConnected) {\n                    return session\n                } else {\n                    session.closeSafe()\n                    connection.closeSafe()\n                    sessions -= authority\n                }\n            }\n            val password = authenticator.getPassword(authority)\n                ?: throw ClientException(\"No password found for $authority\")\n            val hostAddress = resolveHostName(authority.host)\n            val connection = try {\n                client.connect(hostAddress, authority.port)\n            } catch (e: IOException) {\n                throw ClientException(e)\n            }\n            val authenticationContext =\n                AuthenticationContext(authority.username, password.toCharArray(), authority.domain)\n            session = try {\n                connection.authenticate(authenticationContext)\n            } catch (e: SMBRuntimeException) {\n                // We need to close the connection here, otherwise future authentications reusing it\n                // will receive an exception about no available credits.\n                connection.closeSafe()\n                throw ClientException(e)\n            // TODO: kotlinc: Type mismatch: inferred type is Session? but TypeVariable(V) was\n            //  expected\n            //}\n            }!!\n            sessions[authority] = session\n            return session\n        }\n    }\n\n    @Throws(ClientException::class)\n    private fun resolveHostName(hostName: String): String {\n        val nameServiceClient = SingletonContext.getInstance().nameServiceClient\n        val addresses = try {\n            nameServiceClient.getAllByName(hostName, false).mapNotNull { it.toInetAddress() }\n        } catch (e: UnknownHostException) {\n            throw ClientException(e)\n        }\n        val address = addresses.firstOrNull { it is Inet4Address } ?: addresses.first()\n        return address.hostAddress!!\n    }\n\n    @Throws(ClientException::class)\n    private fun getShare(session: Session, shareName: String): Share {\n        return try {\n            session.connectShare(shareName)\n        } catch (e: SMBRuntimeException) {\n            throw ClientException(e)\n        }\n    }\n\n    @Throws(ClientException::class)\n    private fun getDiskShare(session: Session, shareName: String): DiskShare =\n        getShare(session, shareName) as? DiskShare\n            ?: throw ClientException(\"$shareName is not a DiskShare\")\n\n    interface Path {\n        val authority: Authority\n        val sharePath: SharePath?\n        fun resolve(other: String): Path\n\n        data class SharePath(\n            val name: String,\n            val path: String\n        )\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/smb/client/ClientException.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.smb.client\n\nimport com.hierynomus.mserref.NtStatus\nimport com.hierynomus.mssmb2.SMBApiException\nimport java8.nio.file.AccessDeniedException\nimport java8.nio.file.AtomicMoveNotSupportedException\nimport java8.nio.file.DirectoryNotEmptyException\nimport java8.nio.file.FileAlreadyExistsException\nimport java8.nio.file.FileSystemException\nimport java8.nio.file.NoSuchFileException\nimport java8.nio.file.NotDirectoryException\nimport java8.nio.file.NotLinkException\nimport me.zhanghai.android.files.provider.common.InvalidFileNameException\nimport me.zhanghai.android.files.provider.common.IsDirectoryException\n\nclass ClientException : Exception {\n    constructor() : super()\n\n    constructor(message: String?) : super(message)\n\n    constructor(message: String?, cause: Throwable?) : super(message, cause)\n\n    constructor(cause: Throwable?) : super(cause)\n\n    private val status: NtStatus? = (cause as? SMBApiException)?.status\n    private val statusCode: Long? = (cause as? SMBApiException)?.statusCode\n\n    @Throws(AtomicMoveNotSupportedException::class)\n    fun maybeThrowAtomicMoveNotSupportedException(file: String?, other: String?) {\n        if (status == NtStatus.STATUS_NOT_SAME_DEVICE) {\n            throw AtomicMoveNotSupportedException(file, other, message)\n                .apply { initCause(this@ClientException) }\n        }\n    }\n\n    @Throws(InvalidFileNameException::class)\n    fun maybeThrowInvalidFileNameException(file: String?) {\n        if (status == NtStatus.STATUS_OBJECT_NAME_INVALID) {\n            throw InvalidFileNameException(file, null, message)\n                .apply { initCause(this@ClientException) }\n        }\n    }\n\n    fun toFileSystemException(file: String?, other: String? = null): FileSystemException =\n        when (status) {\n            NtStatus.STATUS_ACCESS_DENIED, NtStatus.STATUS_SHARING_VIOLATION,\n            NtStatus.STATUS_PRIVILEGE_NOT_HELD, NtStatus.STATUS_LOGON_FAILURE,\n            NtStatus.STATUS_PASSWORD_EXPIRED, NtStatus.STATUS_ACCOUNT_DISABLED,\n            NtStatus.STATUS_OPLOCK_NOT_GRANTED, NtStatus.STATUS_CANNOT_DELETE,\n            NtStatus.STATUS_LOGON_TYPE_NOT_GRANTED, NtStatus.STATUS_USER_SESSION_DELETED,\n            NtStatus.STATUS_FILE_ENCRYPTED, NtStatus.STATUS_NETWORK_SESSION_EXPIRED ->\n                AccessDeniedException(file, other, message)\n            NtStatus.STATUS_OBJECT_NAME_COLLISION ->\n                FileAlreadyExistsException(file, other, message)\n            NtStatus.STATUS_FILE_IS_A_DIRECTORY -> IsDirectoryException(file, other, message)\n            NtStatus.STATUS_NOT_A_DIRECTORY -> NotDirectoryException(file)\n            NtStatus.STATUS_DIRECTORY_NOT_EMPTY -> DirectoryNotEmptyException(file)\n            NtStatus.STATUS_NO_SUCH_FILE, NtStatus.STATUS_OBJECT_NAME_NOT_FOUND,\n            NtStatus.STATUS_OBJECT_PATH_NOT_FOUND, NtStatus.STATUS_DELETE_PENDING,\n            NtStatus.STATUS_BAD_NETWORK_PATH, NtStatus.STATUS_NETWORK_NAME_DELETED,\n            NtStatus.STATUS_BAD_NETWORK_NAME, NtStatus.STATUS_NOT_FOUND ->\n                NoSuchFileException(file, other, message)\n            else -> when (statusCode) {\n                NtStatuses.STATUS_NOT_A_REPARSE_POINT, NtStatuses.STATUS_IO_REPARSE_TAG_INVALID,\n                NtStatuses.STATUS_IO_REPARSE_TAG_MISMATCH,\n                NtStatus.STATUS_IO_REPARSE_TAG_NOT_HANDLED.value ->\n                    NotLinkException(file, other, message)\n                else -> FileSystemException(file, other, message)\n            }\n        }.apply { initCause(this@ClientException) }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/smb/client/DiskEntryExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.smb.client\n\nimport com.hierynomus.mssmb2.SMB2MessageCommandCode\nimport com.hierynomus.mssmb2.SMBApiException\nimport com.hierynomus.protocol.commons.buffer.Buffer\nimport com.hierynomus.protocol.commons.buffer.Endian\nimport com.hierynomus.smbj.common.SMBRuntimeException\nimport com.hierynomus.smbj.share.DiskEntry\nimport me.zhanghai.android.files.util.hasBits\n\n// @see https://github.com/torvalds/linux/blob/master/fs/cifs/smbfsctl.h\nprivate const val FSCTL_SET_REPARSE_POINT = 0x000900A4\nprivate const val FSCTL_GET_REPARSE_POINT = 0x000900A8\n\n// @see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/c8e77b37-3909-4fe6-a4ea-2b9d423b1ee4\nprivate const val IO_REPARSE_TAG_SYMLINK = 0xA000000CL\n\nprivate const val PATH_BUFFER_OFFSET = 20\n\n// @see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/b41f1cbf-10df-4a47-98d4-1c52a833d913\nprivate const val SYMLINK_FLAG_RELATIVE = 0x00000001L\n\n// @see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/b41f1cbf-10df-4a47-98d4-1c52a833d913\nclass SymbolicLinkReparseData(\n    val substituteName: String,\n    val printName: String,\n    val isRelative: Boolean\n)\n\n// @see https://gitlab.com/samba-team/devel/samba/-/blob/master/source3/libsmb/cli_smb2_fnum.c\n//      cli_smb2_get_reparse_point_fnum_send\n// @see https://gitlab.com/samba-team/devel/samba/-/blob/master/source3/libsmb/reparse_symlink.c\n//      symlink_reparse_buffer_parse\n// @see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/d86a4c4d-a996-403a-8b92-9c0e1a300e22\n// @see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/b41f1cbf-10df-4a47-98d4-1c52a833d913\n@Throws(SMBRuntimeException::class)\ninternal fun DiskEntry.getSymbolicLinkReparseData(): SymbolicLinkReparseData {\n    val buffer = Buffer.PlainBuffer(\n        ioctl(FSCTL_GET_REPARSE_POINT, true, ByteArray(0), 0, 0), Endian.LE\n    )\n    return try {\n        // ReparseTag (4 bytes)\n        val reparseTag = buffer.readUInt32()\n        if (reparseTag != IO_REPARSE_TAG_SYMLINK) {\n            throw SMBApiException(\n                NtStatuses.STATUS_IO_REPARSE_TAG_MISMATCH, SMB2MessageCommandCode.SMB2_IOCTL, null\n            )\n        }\n        // ReparseDataLength (2 bytes)\n        buffer.readUInt16()\n        // Reserved (2 bytes)\n        buffer.readUInt16()\n        // SubstituteNameOffset (2 bytes)\n        val substituteNameOffset = buffer.readUInt16()\n        // SubstituteNameLength (2 bytes)\n        val substituteNameLength = buffer.readUInt16()\n        // PrintNameOffset (2 bytes)\n        val printNameOffset = buffer.readUInt16()\n        // PrintNameLength (2 bytes)\n        val printNameLength = buffer.readUInt16()\n        // Flags (4 bytes)\n        val flags = buffer.readUInt32()\n        buffer.rpos(PATH_BUFFER_OFFSET + substituteNameOffset)\n        val substituteName = buffer.readString(Charsets.UTF_16LE, substituteNameLength / 2)\n        buffer.rpos(PATH_BUFFER_OFFSET + printNameOffset)\n        val printName = buffer.readString(Charsets.UTF_16LE, printNameLength / 2)\n        val isRelative = flags.hasBits(SYMLINK_FLAG_RELATIVE)\n        SymbolicLinkReparseData(substituteName, printName, isRelative)\n    } catch (e: Buffer.BufferException) {\n        throw SMBRuntimeException(e)\n    }\n}\n\n// @see https://gitlab.com/samba-team/devel/samba/-/blob/master/source3/libsmb/cli_smb2_fnum.c\n//      cli_smb2_set_reparse_point_fnum_send\n// @see https://gitlab.com/samba-team/devel/samba/-/blob/master/source3/libsmb/reparse_symlink.c\n//      symlink_reparse_buffer_marshall\n// @see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/4dc2b168-f177-4eec-a14b-25a51cbba2cf\n// @see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/b41f1cbf-10df-4a47-98d4-1c52a833d913\n@Throws(SMBRuntimeException::class)\ninternal fun DiskEntry.setSymbolicLinkReparseData(reparseData: SymbolicLinkReparseData) {\n    val substituteNameLength = reparseData.substituteName.length * 2\n    val printNameLength = reparseData.printName.length * 2\n    val reparseDataLength = 12 + substituteNameLength + printNameLength\n    val buffer = Buffer.PlainBuffer(Endian.LE)\n    // ReparseTag (4 bytes)\n    buffer.putUInt32(IO_REPARSE_TAG_SYMLINK)\n    // ReparseDataLength (2 bytes)\n    buffer.putUInt16(reparseDataLength)\n    // Reserved (2 bytes)\n    buffer.putUInt16(0)\n    // SubstituteNameOffset (2 bytes)\n    buffer.putUInt16(0)\n    // SubstituteNameLength (2 bytes)\n    buffer.putUInt16(substituteNameLength)\n    // PrintNameOffset (2 bytes)\n    buffer.putUInt16(substituteNameLength)\n    // PrintNameLength (2 bytes)\n    buffer.putUInt16(printNameLength)\n    // Flags (4 bytes)\n    buffer.putUInt32(if (reparseData.isRelative) SYMLINK_FLAG_RELATIVE else 0)\n    // PathBuffer (variable)\n    buffer.putString(reparseData.substituteName, Charsets.UTF_16LE)\n    buffer.putString(reparseData.printName, Charsets.UTF_16LE)\n    ioctl(FSCTL_SET_REPARSE_POINT, true, buffer.array(), buffer.rpos(), buffer.available())\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/smb/client/FileByteChannel.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.smb.client\n\nimport com.hierynomus.mserref.NtStatus\nimport com.hierynomus.msfscc.fileinformation.FileStandardInformation\nimport com.hierynomus.mssmb2.SMBApiException\nimport com.hierynomus.smbj.common.SMBRuntimeException\nimport com.hierynomus.smbj.io.ByteChunkProvider\nimport com.hierynomus.smbj.share.File\nimport com.hierynomus.smbj.share.FileAccessor\nimport me.zhanghai.android.files.provider.common.AbstractFileByteChannel\nimport me.zhanghai.android.files.provider.common.EMPTY\nimport me.zhanghai.android.files.provider.common.map\nimport me.zhanghai.android.files.util.closeSafe\nimport me.zhanghai.android.files.util.findCauseByClass\nimport java.io.IOException\nimport java.io.InterruptedIOException\nimport java.nio.ByteBuffer\nimport java.nio.channels.AsynchronousCloseException\nimport java.nio.channels.ClosedByInterruptException\nimport java.util.concurrent.ExecutionException\nimport java.util.concurrent.Future\n\nclass FileByteChannel(\n    private val file: File,\n    isAppend: Boolean\n// Cancelling reads leads to TransportException: Received response with unknown sequence number\n) : AbstractFileByteChannel(isAppend, shouldCancelRead = false) {\n    @Throws(IOException::class)\n    override fun onReadAsync(position: Long, size: Int, timeoutMillis: Long): Future<ByteBuffer> =\n        try {\n            FileAccessor.readAsync(file, position, size)\n        } catch (e: SMBRuntimeException) {\n            throw e.toIOException()\n        }\n            .map(\n                { response ->\n                    when (response.header.statusCode) {\n                        NtStatus.STATUS_END_OF_FILE.value -> {\n                            return@map ByteBuffer::class.EMPTY\n                        }\n                        NtStatus.STATUS_SUCCESS.value -> {}\n                        else -> throw SMBApiException(response.header, \"Read failed for $this\")\n                            .toIOException()\n                    }\n                    val data = response.data\n                    if (data.isEmpty()) {\n                        return@map ByteBuffer::class.EMPTY\n                    }\n                    val length = data.size.coerceAtMost(size)\n                    ByteBuffer.wrap(data, 0, length)\n                }, { e ->\n                    ExecutionException(SMBRuntimeException(e).toIOException())\n                }\n            )\n\n    @Throws(IOException::class)\n    override fun onWrite(position: Long, source: ByteBuffer) {\n        val sourcePosition = source.position()\n        val bytesWritten = try {\n            file.write(ByteBufferChunkProvider(source, position))\n        } catch (e: SMBRuntimeException) {\n            throw e.toIOException()\n        }\n        source.position(sourcePosition + bytesWritten)\n    }\n\n    @Throws(IOException::class)\n    override fun onTruncate(size: Long) {\n        try {\n            file.setLength(size)\n        } catch (e: SMBRuntimeException) {\n            throw e.toIOException()\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun onSize(): Long =\n        try {\n            file.getFileInformation(FileStandardInformation::class.java).endOfFile\n        } catch (e: SMBRuntimeException) {\n            throw e.toIOException()\n        }\n\n    @Throws(IOException::class)\n    override fun onForce(metaData: Boolean) {\n        try {\n            file.flush()\n        } catch (e: SMBRuntimeException) {\n            throw e.toIOException()\n        }\n    }\n\n    private fun SMBRuntimeException.toIOException(): IOException =\n        when {\n            findCauseByClass<SMBApiException>()\n                .let { it != null && it.status == NtStatus.STATUS_FILE_CLOSED } -> {\n                setClosed()\n                AsynchronousCloseException().apply { initCause(this@toIOException) }\n            }\n            findCauseByClass<InterruptedException>() != null -> {\n                closeSafe()\n                ClosedByInterruptException().apply { initCause(this@toIOException) }\n            }\n            else -> IOException(this)\n        }\n\n    @Throws(IOException::class)\n    override fun onClose() {\n        try {\n            file.close()\n        } catch (e: SMBRuntimeException) {\n            throw when {\n                e.findCauseByClass<InterruptedException>() != null ->\n                    InterruptedIOException().apply { initCause(e) }\n                else -> IOException(e)\n            }\n        }\n    }\n\n    private class ByteBufferChunkProvider(\n        private val buffer: ByteBuffer,\n        offset: Long\n    ) : ByteChunkProvider() {\n        init {\n            this.offset = offset\n        }\n\n        override fun isAvailable(): Boolean = buffer.hasRemaining()\n\n        override fun bytesLeft(): Int = buffer.remaining()\n\n        override fun prepareWrite(maxBytesToPrepare: Int) {}\n\n        override fun getChunk(chunk: ByteArray): Int {\n            val length = chunk.size.coerceAtMost(buffer.remaining())\n            buffer.get(chunk, 0, length)\n            return length\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/smb/client/FileExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.smb.client\n\nimport com.hierynomus.mserref.NtStatus\nimport com.hierynomus.mssmb2.SMB2PacketHeader\nimport com.hierynomus.mssmb2.SMBApiException\nimport com.hierynomus.mssmb2.copy.CopyChunkRequest\nimport com.hierynomus.mssmb2.copy.CopyChunkResponse\nimport com.hierynomus.protocol.commons.buffer.Buffer\nimport com.hierynomus.smb.SMBBuffer\nimport com.hierynomus.smbj.ProgressListener\nimport com.hierynomus.smbj.common.SMBRuntimeException\nimport com.hierynomus.smbj.share.File\nimport com.hierynomus.smbj.share.ShareAccessor\nimport com.hierynomus.smbj.share.StatusHandler\nimport java.io.InterruptedIOException\n\n// @see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/cd0162e4-7650-4293-8a2a-d696923203ef\n@Throws(InterruptedIOException::class, SMBRuntimeException::class)\ninternal fun File.serverCopy(\n    sourceOffset: Long,\n    target: File,\n    targetOffset: Long,\n    length: Long,\n    listener: ProgressListener?\n) {\n    val resumeKey = requestResumeKey()\n    var maxChunkSize = 1024 * 1024L\n    var maxNumberOfChunks = 16L\n    var maxRequestSize = maxNumberOfChunks * maxChunkSize\n    var totalBytesWritten = 0L\n    while (true) {\n        val chunksSourceOffset = sourceOffset + totalBytesWritten\n        val chunksTargetOffset = targetOffset + totalBytesWritten\n        val chunksLength = length - totalBytesWritten\n        val chunks = createServerCopyChunks(\n            chunksSourceOffset, chunksTargetOffset, chunksLength, maxNumberOfChunks, maxChunkSize,\n            maxRequestSize\n        )\n        val request = CopyChunkRequest(resumeKey, chunks)\n        val (responseHeader, response) = target.serverCopyChunk(request)\n        if (responseHeader.statusCode == NtStatus.STATUS_INVALID_PARAMETER.value) {\n            // @see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/a1935898-6a86-4491-a8a3-942ec83b75a4\n            maxNumberOfChunks = response.chunksWritten\n            maxChunkSize = response.chunkBytesWritten.coerceAtMost(response.totalBytesWritten)\n            maxRequestSize = response.totalBytesWritten\n        } else {\n            totalBytesWritten += response.totalBytesWritten\n            listener?.onProgressChanged(totalBytesWritten, length)\n            if (totalBytesWritten >= length) {\n                break\n            }\n        }\n        throwIfInterrupted()\n    }\n}\n\n@Throws(InterruptedIOException::class)\nprivate fun throwIfInterrupted() {\n    if (Thread.interrupted()) {\n        throw InterruptedIOException()\n    }\n}\n\n// @see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/5c03c9d6-15de-48a2-9835-8fb37f8a79d8\nprivate const val FSCTL_SRV_REQUEST_RESUME_KEY = 0x00140078\n\n// @see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/594abdf9-c122-4951-aba9-577b714674c4\n@Throws(SMBRuntimeException::class)\nprivate fun File.requestResumeKey(): ByteArray? {\n    val buffer = SMBBuffer(ioctl(FSCTL_SRV_REQUEST_RESUME_KEY, true, ByteArray(0), 0, 0))\n    return buffer.readRawBytes(24)\n}\n\n// @see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/676ae4b4-6758-4930-9f73-f0853fcad081\nprivate fun createServerCopyChunks(\n    sourceOffset: Long,\n    targetOffset: Long,\n    length: Long,\n    maxNumberOfChunks: Long,\n    maxChunkSize: Long,\n    maxRequestSize: Long\n): List<CopyChunkRequest.Chunk> {\n    val chunks = mutableListOf<CopyChunkRequest.Chunk>()\n    var numberOfChunks = 0L\n    var requestSize = 0L\n    val length = length.coerceAtMost(maxRequestSize)\n    while (numberOfChunks < maxNumberOfChunks && requestSize < length) {\n        val chunkSourceOffset = sourceOffset + requestSize\n        val chunkTargetOffset = targetOffset + requestSize\n        val chunkSize = maxChunkSize.coerceAtMost(length - requestSize)\n        chunks.add(CopyChunkRequest.Chunk(chunkSourceOffset, chunkTargetOffset, chunkSize))\n        ++numberOfChunks\n        requestSize += chunkSize\n    }\n    return chunks\n}\n\nprivate val SERVER_COPY_CHUNK_STATUS_HANDLER = StatusHandler {\n    it == NtStatus.STATUS_SUCCESS.value || it == NtStatus.STATUS_INVALID_PARAMETER.value\n}\n\n// @see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-smb2/5c03c9d6-15de-48a2-9835-8fb37f8a79d8\n@Throws(SMBRuntimeException::class)\nprivate fun File.serverCopyChunk(\n    request: CopyChunkRequest\n): Pair<SMB2PacketHeader, CopyChunkResponse> {\n    // FSCTL_SRV_COPYCHUNK and FSCTL_SRV_COPYCHUNK_WRITE FSCTL codes are used for performing server\n    // side copy operations. These FSCTLs are issued by the application against an open handle to\n    // the target file.\n    val share = diskShare\n    val buffer = SMBBuffer()\n    request.write(buffer)\n    //val readTimeout = share.readTimeout\n    val readTimeout = share.treeConnect.config.readTimeout\n    val ioctlResponse = ShareAccessor.ioctl(\n        share, fileId, CopyChunkRequest.getCtlCode(), true, buffer.array(), buffer.rpos(),\n        buffer.available(), SERVER_COPY_CHUNK_STATUS_HANDLER, readTimeout\n    )\n    if (ioctlResponse.error != null) {\n        throw SMBApiException(ioctlResponse.header, \"FSCTL_SRV_COPYCHUNK failed\")\n    }\n    val response = CopyChunkResponse()\n    try {\n        response.read(SMBBuffer(ioctlResponse.outputBuffer))\n    } catch (e: Buffer.BufferException) {\n        throw SMBRuntimeException(e)\n    }\n    return ioctlResponse.header to response\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/smb/client/NtStatuses.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.smb.client\n\n// @see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55\nobject NtStatuses {\n    const val STATUS_NOT_A_REPARSE_POINT = 0xC0000275L\n    const val STATUS_IO_REPARSE_TAG_INVALID = 0xC0000276\n    const val STATUS_IO_REPARSE_TAG_MISMATCH = 0xC0000277L\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/smb/client/PathInformation.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.smb.client\n\nimport com.hierynomus.msdtyp.FileTime\nimport com.hierynomus.msfscc.fileinformation.FileAllInformation\nimport com.hierynomus.msfscc.fileinformation.FileIdFullDirectoryInformation\nimport com.hierynomus.msfscc.fileinformation.ShareInfo\nimport com.hierynomus.smbj.common.SMBRuntimeException\n\nsealed class PathInformation\n\nclass FileInformation(\n    val creationTime: FileTime,\n    val lastAccessTime: FileTime,\n    val lastWriteTime: FileTime,\n    val changeTime: FileTime,\n    val endOfFile: Long,\n    val fileAttributes: Long,\n    val fileId: Long\n) : PathInformation()\n\n@Throws(SMBRuntimeException::class)\nfun FileIdFullDirectoryInformation.toFileInformation(): FileInformation =\n    FileInformation(\n        creationTime, lastAccessTime, lastWriteTime, changeTime, endOfFile, fileAttributes, fileId\n    )\n\nfun FileAllInformation.toFileInformation(): FileInformation =\n    FileInformation(\n        basicInformation.creationTime, basicInformation.lastAccessTime,\n        basicInformation.lastWriteTime, basicInformation.changeTime, standardInformation.endOfFile,\n        basicInformation.fileAttributes, internalInformation.indexNumber\n    )\n\nclass ShareInformation(\n    val type: ShareType,\n    val shareInfo: ShareInfo?\n) : PathInformation()\n\nenum class ShareType {\n    DISK,\n    PIPE,\n    PRINTER,\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/smb/client/ShareTypes.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.smb.client\n\n// @see https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-srvs/6069f8c0-c93f-43a0-a5b4-7ed447eb4b84\nenum class ShareTypes(val value: Int) {\n    STYPE_DISKTREE(0x00000000),\n    STYPE_PRINTQ(0x00000001),\n    STYPE_DEVICE(0x00000002),\n    STYPE_IPC(0x00000003),\n    STYPE_CLUSTER_FS(0x02000000),\n    STYPE_CLUSTER_SOFS(0x04000000),\n    STYPE_CLUSTER_DFS(0x08000000),\n    STYPE_SPECIAL(0x80000000.toInt()),\n    STYPE_TEMPORARY(0x40000000)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/webdav/DavExceptionExtensions.kt",
    "content": "package me.zhanghai.android.files.provider.webdav\n\nimport at.bitfire.dav4jvm.exception.ConflictException\nimport at.bitfire.dav4jvm.exception.DavException\nimport at.bitfire.dav4jvm.exception.ForbiddenException\nimport at.bitfire.dav4jvm.exception.NotFoundException\nimport at.bitfire.dav4jvm.exception.UnauthorizedException\nimport java8.nio.file.AccessDeniedException\nimport java8.nio.file.FileAlreadyExistsException\nimport java8.nio.file.FileSystemException\nimport java8.nio.file.NoSuchFileException\nimport me.zhanghai.android.files.provider.webdav.client.DavIOException\n\nfun DavException.toFileSystemException(\n    file: String?,\n    other: String? = null\n): FileSystemException {\n    return when (this) {\n        is DavIOException ->\n            return FileSystemException(file, other, message).apply { initCause(cause) }\n        is UnauthorizedException, is ForbiddenException ->\n            AccessDeniedException(file, other, message)\n        is NotFoundException -> NoSuchFileException(file, other, message)\n        is ConflictException -> FileAlreadyExistsException(file, other, message)\n        else -> FileSystemException(file, other, message)\n    }.apply { initCause(this@toFileSystemException) }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/webdav/OpenOptionsWebDavExtensions.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.webdav\n\nimport java8.nio.file.StandardOpenOption\nimport me.zhanghai.android.files.provider.common.OpenOptions\n\ninternal fun OpenOptions.checkForWebDav() {\n    if (deleteOnClose) {\n        throw UnsupportedOperationException(StandardOpenOption.DELETE_ON_CLOSE.toString())\n    }\n    if (sync) {\n        throw UnsupportedOperationException(StandardOpenOption.SYNC.toString())\n    }\n    if (dsync) {\n        throw UnsupportedOperationException(StandardOpenOption.DSYNC.toString())\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/webdav/PathWebDavExtensions.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.webdav\n\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.provider.webdav.client.Authority\n\nfun Authority.createWebDavRootPath(): Path =\n    WebDavFileSystemProvider.getOrNewFileSystem(this).rootDirectory\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/webdav/WebDavCopyMove.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.webdav\n\nimport at.bitfire.dav4jvm.exception.DavException\nimport java8.nio.file.FileAlreadyExistsException\nimport java8.nio.file.NoSuchFileException\nimport java8.nio.file.StandardCopyOption\nimport me.zhanghai.android.files.provider.common.CopyOptions\nimport me.zhanghai.android.files.provider.common.copyTo\nimport me.zhanghai.android.files.provider.webdav.client.Client\nimport me.zhanghai.android.files.provider.webdav.client.isDirectory\nimport me.zhanghai.android.files.provider.webdav.client.isSymbolicLink\nimport me.zhanghai.android.files.provider.webdav.client.lastModifiedTime\nimport me.zhanghai.android.files.provider.webdav.client.size\nimport java.io.IOException\n\ninternal object WebDavCopyMove {\n    @Throws(IOException::class)\n    fun copy(source: WebDavPath, target: WebDavPath, copyOptions: CopyOptions) {\n        if (copyOptions.atomicMove) {\n            throw UnsupportedOperationException(StandardCopyOption.ATOMIC_MOVE.toString())\n        }\n        val sourceResponse = try {\n            Client.findProperties(source, copyOptions.noFollowLinks)\n        } catch (e: DavException) {\n            throw e.toFileSystemException(source.toString())\n        }\n        val targetFile = try {\n            Client.findPropertiesOrNull(target, true)\n        } catch (e: DavException) {\n            throw e.toFileSystemException(target.toString())\n        }\n        val sourceSize = sourceResponse.size\n        if (targetFile != null) {\n            if (source == target) {\n                copyOptions.progressListener?.invoke(sourceSize)\n                return\n            }\n            if (!copyOptions.replaceExisting) {\n                throw FileAlreadyExistsException(source.toString(), target.toString(), null)\n            }\n            try {\n                Client.delete(target)\n            } catch (e: DavException) {\n                throw e.toFileSystemException(target.toString())\n            }\n        }\n        when {\n            sourceResponse.isDirectory -> {\n                try {\n                    Client.makeCollection(target)\n                } catch (e: DavException) {\n                    throw e.toFileSystemException(target.toString())\n                }\n                copyOptions.progressListener?.invoke(sourceSize)\n            }\n            sourceResponse.isSymbolicLink ->\n                throw UnsupportedOperationException(\"Cannot copy symbolic links\")\n            else -> {\n                val sourceInputStream = try {\n                    Client.get(source)\n                } catch (e: DavException) {\n                    throw e.toFileSystemException(source.toString())\n                }\n                try {\n                    val targetOutputStream = try {\n                        Client.put(target)\n                    } catch (e: DavException) {\n                        throw e.toFileSystemException(target.toString())\n                    }\n                    var successful = false\n                    try {\n                        sourceInputStream.copyTo(\n                            targetOutputStream, copyOptions.progressIntervalMillis,\n                            copyOptions.progressListener\n                        )\n                        successful = true\n                    } finally {\n                        try {\n                            targetOutputStream.close()\n                        } catch (e: DavException) {\n                            throw e.toFileSystemException(target.toString())\n                        } finally {\n                            if (!successful) {\n                                try {\n                                    Client.delete(target)\n                                } catch (e: DavException) {\n                                    e.printStackTrace()\n                                }\n                            }\n                        }\n                    }\n                } finally {\n                    try {\n                        sourceInputStream.close()\n                    } catch (e: DavException) {\n                        throw e.toFileSystemException(source.toString())\n                    }\n                }\n            }\n        }\n        // We don't take error when copying attribute fatal, so errors will only be logged from now\n        // on.\n        if (!sourceResponse.isSymbolicLink) {\n            val lastModifiedTime = sourceResponse.lastModifiedTime\n            if (lastModifiedTime != null) {\n                try {\n                    Client.setLastModifiedTime(target, lastModifiedTime)\n                } catch (e: DavException) {\n                    e.printStackTrace()\n                }\n            }\n        }\n    }\n\n    @Throws(IOException::class)\n    fun move(source: WebDavPath, target: WebDavPath, copyOptions: CopyOptions) {\n        val sourceResponse = try {\n            Client.findProperties(source, copyOptions.noFollowLinks)\n        } catch (e: DavException) {\n            throw e.toFileSystemException(source.toString())\n        }\n        val targetResponse = try {\n            Client.findPropertiesOrNull(target, true)\n        } catch (e: DavException) {\n            throw e.toFileSystemException(target.toString())\n        }\n        val sourceSize = sourceResponse.size\n        if (targetResponse != null) {\n            if (source == target) {\n                copyOptions.progressListener?.invoke(sourceSize)\n                return\n            }\n            if (!copyOptions.replaceExisting) {\n                throw FileAlreadyExistsException(source.toString(), target.toString(), null)\n            }\n            try {\n                Client.delete(target)\n            } catch (e: DavException) {\n                throw e.toFileSystemException(target.toString())\n            }\n        }\n        var renameSuccessful = false\n        try {\n            Client.move(source, target)\n            renameSuccessful = true\n        } catch (e: DavException) {\n            if (copyOptions.atomicMove) {\n                throw e.toFileSystemException(source.toString(), target.toString())\n            }\n            // Ignored.\n        }\n        if (renameSuccessful) {\n            copyOptions.progressListener?.invoke(sourceSize)\n            return\n        }\n        if (copyOptions.atomicMove) {\n            throw AssertionError()\n        }\n        var copyOptions = copyOptions\n        if (!copyOptions.copyAttributes || !copyOptions.noFollowLinks) {\n            copyOptions = CopyOptions(\n                copyOptions.replaceExisting, true, false, true, copyOptions.progressIntervalMillis,\n                copyOptions.progressListener\n            )\n        }\n        copy(source, target, copyOptions)\n        try {\n            Client.delete(source)\n        } catch (e: DavException) {\n            if (e.toFileSystemException(source.toString()) !is NoSuchFileException) {\n                try {\n                    Client.delete(target)\n                } catch (e2: DavException) {\n                    e.addSuppressed(e2.toFileSystemException(target.toString()))\n                }\n            }\n            throw e.toFileSystemException(source.toString())\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/webdav/WebDavFileAttributeView.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.webdav\n\nimport at.bitfire.dav4jvm.exception.DavException\nimport java8.nio.file.LinkOption\nimport java8.nio.file.attribute.BasicFileAttributeView\nimport java8.nio.file.attribute.FileTime\nimport me.zhanghai.android.files.provider.webdav.client.Client\nimport java.io.IOException\n\ninternal class WebDavFileAttributeView(\n    private val path: WebDavPath,\n    private val noFollowLinks: Boolean\n) : BasicFileAttributeView {\n    override fun name(): String = NAME\n\n    @Throws(IOException::class)\n    override fun readAttributes(): WebDavFileAttributes {\n        val file = try {\n            Client.findProperties(path, noFollowLinks)\n        } catch (e: DavException) {\n            throw e.toFileSystemException(path.toString())\n        }\n        return WebDavFileAttributes.from(file, path)\n    }\n\n    override fun setTimes(\n        lastModifiedTime: FileTime?,\n        lastAccessTime: FileTime?,\n        createTime: FileTime?\n    ) {\n        if (lastModifiedTime == null) {\n            // Only throw if caller is trying to set only last access time and/or create time, so\n            // that foreign copy move can still set last modified time.\n            if (lastAccessTime != null) {\n                throw UnsupportedOperationException(\"lastAccessTime\")\n            }\n            if (createTime != null) {\n                throw UnsupportedOperationException(\"createTime\")\n            }\n            return\n        }\n        if (noFollowLinks) {\n            throw UnsupportedOperationException(LinkOption.NOFOLLOW_LINKS.toString())\n        }\n        try {\n            Client.setLastModifiedTime(path, lastModifiedTime.toInstant())\n        } catch (e: DavException) {\n            throw e.toFileSystemException(path.toString())\n        }\n    }\n\n    companion object {\n        private val NAME = WebDavFileSystemProvider.scheme\n\n        val SUPPORTED_NAMES = setOf(\"basic\", NAME)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/webdav/WebDavFileAttributes.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.webdav\n\nimport android.os.Parcelable\nimport at.bitfire.dav4jvm.Response\nimport java.time.Instant\nimport java8.nio.file.attribute.FileTime\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.provider.common.AbstractBasicFileAttributes\nimport me.zhanghai.android.files.provider.common.BasicFileType\nimport me.zhanghai.android.files.provider.common.EPOCH\nimport me.zhanghai.android.files.provider.common.FileTimeParceler\nimport me.zhanghai.android.files.provider.webdav.client.creationTime\nimport me.zhanghai.android.files.provider.webdav.client.isDirectory\nimport me.zhanghai.android.files.provider.webdav.client.isSymbolicLink\nimport me.zhanghai.android.files.provider.webdav.client.lastModifiedTime\nimport me.zhanghai.android.files.provider.webdav.client.size\n\n@Parcelize\ninternal data class WebDavFileAttributes(\n    override val lastModifiedTime: @WriteWith<FileTimeParceler> FileTime,\n    override val lastAccessTime: @WriteWith<FileTimeParceler> FileTime,\n    override val creationTime: @WriteWith<FileTimeParceler> FileTime,\n    override val type: BasicFileType,\n    override val size: Long,\n    override val fileKey: Parcelable\n) : AbstractBasicFileAttributes() {\n    companion object {\n        fun from(response: Response, path: WebDavPath): WebDavFileAttributes =\n            when {\n                response.isSuccess() -> {\n                    val lastModifiedTime = FileTime.from(response.lastModifiedTime ?: Instant.EPOCH)\n                    val lastAccessTime = lastModifiedTime\n                    val creationTime =\n                        response.creationTime?.let { FileTime.from(it) } ?: lastModifiedTime\n                    val type = if (response.isDirectory) {\n                        BasicFileType.DIRECTORY\n                    } else {\n                        BasicFileType.REGULAR_FILE\n                    }\n                    val size = response.size\n                    val fileKey = path\n                    WebDavFileAttributes(\n                        lastModifiedTime, lastAccessTime, creationTime, type, size, fileKey\n                    )\n                }\n                response.isSymbolicLink -> {\n                    val lastModifiedTime = FileTime::class.EPOCH\n                    val lastAccessTime = lastModifiedTime\n                    val creationTime = lastModifiedTime\n                    val type = BasicFileType.SYMBOLIC_LINK\n                    val size = 0L\n                    val fileKey = path\n                    WebDavFileAttributes(\n                        lastModifiedTime, lastAccessTime, creationTime, type, size, fileKey\n                    )\n                }\n                else -> error(response)\n            }\n        }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/webdav/WebDavFileSystem.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.webdav\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport java8.nio.file.FileStore\nimport java8.nio.file.FileSystem\nimport java8.nio.file.Path\nimport java8.nio.file.PathMatcher\nimport java8.nio.file.WatchService\nimport java8.nio.file.attribute.UserPrincipalLookupService\nimport java8.nio.file.spi.FileSystemProvider\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.ByteStringBuilder\nimport me.zhanghai.android.files.provider.common.ByteStringListPathCreator\nimport me.zhanghai.android.files.provider.common.LocalWatchService\nimport me.zhanghai.android.files.provider.common.toByteString\nimport me.zhanghai.android.files.provider.webdav.client.Authority\nimport me.zhanghai.android.files.util.readParcelable\nimport java.io.IOException\n\ninternal class WebDavFileSystem(\n    private val provider: WebDavFileSystemProvider,\n    val authority: Authority\n) : FileSystem(), ByteStringListPathCreator, Parcelable {\n    val rootDirectory = WebDavPath(this, SEPARATOR_BYTE_STRING)\n\n    init {\n        if (!rootDirectory.isAbsolute) {\n            throw AssertionError(\"Root directory must be absolute\")\n        }\n        if (rootDirectory.nameCount != 0) {\n            throw AssertionError(\"Root directory must contain no names\")\n        }\n    }\n\n    private val lock = Any()\n\n    private var isOpen = true\n\n    val defaultDirectory: WebDavPath\n        get() = rootDirectory\n\n    override fun provider(): FileSystemProvider = provider\n\n    override fun close() {\n        synchronized(lock) {\n            if (!isOpen) {\n                return\n            }\n            provider.removeFileSystem(this)\n            isOpen = false\n        }\n    }\n\n    override fun isOpen(): Boolean = synchronized(lock) { isOpen }\n\n    override fun isReadOnly(): Boolean = false\n\n    override fun getSeparator(): String = SEPARATOR_STRING\n\n    override fun getRootDirectories(): Iterable<Path> = listOf(rootDirectory)\n\n    override fun getFileStores(): Iterable<FileStore> {\n        // TODO\n        throw UnsupportedOperationException()\n    }\n\n    override fun supportedFileAttributeViews(): Set<String> =\n        WebDavFileAttributeView.SUPPORTED_NAMES\n\n    override fun getPath(first: String, vararg more: String): WebDavPath {\n        val path = ByteStringBuilder(first.toByteString())\n            .apply { more.forEach { append(SEPARATOR).append(it.toByteString()) } }\n            .toByteString()\n        return WebDavPath(this, path)\n    }\n\n    override fun getPath(first: ByteString, vararg more: ByteString): WebDavPath {\n        val path = ByteStringBuilder(first)\n            .apply { more.forEach { append(SEPARATOR).append(it) } }\n            .toByteString()\n        return WebDavPath(this, path)\n    }\n\n    override fun getPathMatcher(syntaxAndPattern: String): PathMatcher {\n        throw UnsupportedOperationException()\n    }\n\n    override fun getUserPrincipalLookupService(): UserPrincipalLookupService {\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun newWatchService(): WatchService = LocalWatchService()\n\n    override fun equals(other: Any?): Boolean {\n        if (this === other) {\n            return true\n        }\n        if (javaClass != other?.javaClass) {\n            return false\n        }\n        other as WebDavFileSystem\n        return authority == other.authority\n    }\n\n    override fun hashCode(): Int = authority.hashCode()\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        dest.writeParcelable(authority, flags)\n    }\n\n    companion object {\n        const val SEPARATOR = '/'.code.toByte()\n        private val SEPARATOR_BYTE_STRING = SEPARATOR.toByteString()\n        private const val SEPARATOR_STRING = SEPARATOR.toInt().toChar().toString()\n\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<WebDavFileSystem> {\n            override fun createFromParcel(source: Parcel): WebDavFileSystem {\n                val authority = source.readParcelable<Authority>()!!\n                return WebDavFileSystemProvider.getOrNewFileSystem(authority)\n            }\n\n            override fun newArray(size: Int): Array<WebDavFileSystem?> = arrayOfNulls(size)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/webdav/WebDavFileSystemProvider.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.webdav\n\nimport at.bitfire.dav4jvm.exception.DavException\nimport java8.nio.channels.FileChannel\nimport java8.nio.channels.SeekableByteChannel\nimport java8.nio.file.AccessMode\nimport java8.nio.file.CopyOption\nimport java8.nio.file.DirectoryStream\nimport java8.nio.file.FileAlreadyExistsException\nimport java8.nio.file.FileStore\nimport java8.nio.file.FileSystem\nimport java8.nio.file.FileSystemAlreadyExistsException\nimport java8.nio.file.FileSystemException\nimport java8.nio.file.FileSystemNotFoundException\nimport java8.nio.file.LinkOption\nimport java8.nio.file.NoSuchFileException\nimport java8.nio.file.NotLinkException\nimport java8.nio.file.OpenOption\nimport java8.nio.file.Path\nimport java8.nio.file.ProviderMismatchException\nimport java8.nio.file.StandardOpenOption\nimport java8.nio.file.attribute.BasicFileAttributes\nimport java8.nio.file.attribute.FileAttribute\nimport java8.nio.file.attribute.FileAttributeView\nimport java8.nio.file.spi.FileSystemProvider\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.ByteStringPath\nimport me.zhanghai.android.files.provider.common.DelegateSchemeFileSystemProvider\nimport me.zhanghai.android.files.provider.common.PathListDirectoryStream\nimport me.zhanghai.android.files.provider.common.PathObservable\nimport me.zhanghai.android.files.provider.common.PathObservableProvider\nimport me.zhanghai.android.files.provider.common.Searchable\nimport me.zhanghai.android.files.provider.common.WalkFileTreeSearchable\nimport me.zhanghai.android.files.provider.common.WatchServicePathObservable\nimport me.zhanghai.android.files.provider.common.decodedPathByteString\nimport me.zhanghai.android.files.provider.common.toAccessModes\nimport me.zhanghai.android.files.provider.common.toByteString\nimport me.zhanghai.android.files.provider.common.toCopyOptions\nimport me.zhanghai.android.files.provider.common.toLinkOptions\nimport me.zhanghai.android.files.provider.common.toOpenOptions\nimport me.zhanghai.android.files.provider.webdav.client.Authority\nimport me.zhanghai.android.files.provider.webdav.client.Client\nimport me.zhanghai.android.files.provider.webdav.client.Protocol\nimport me.zhanghai.android.files.provider.webdav.client.isSymbolicLink\nimport java.io.IOException\nimport java.io.InputStream\nimport java.io.OutputStream\nimport java.net.URI\n\nobject WebDavFileSystemProvider : FileSystemProvider(), PathObservableProvider, Searchable {\n    private val HIDDEN_FILE_NAME_PREFIX = \".\".toByteString()\n\n    private val fileSystems = mutableMapOf<Authority, WebDavFileSystem>()\n\n    private val lock = Any()\n\n    override fun getScheme(): String = Protocol.DAV.scheme\n\n    override fun newFileSystem(uri: URI, env: Map<String, *>): FileSystem {\n        uri.requireSameScheme()\n        val authority = uri.webDavAuthority\n        synchronized(lock) {\n            if (fileSystems[authority] != null) {\n                throw FileSystemAlreadyExistsException(authority.toString())\n            }\n            return newFileSystemLocked(authority)\n        }\n    }\n\n    internal fun getOrNewFileSystem(authority: Authority): WebDavFileSystem =\n        synchronized(lock) { fileSystems[authority] ?: newFileSystemLocked(authority) }\n\n    private fun newFileSystemLocked(authority: Authority): WebDavFileSystem {\n        val fileSystem = WebDavFileSystem(this, authority)\n        fileSystems[authority] = fileSystem\n        return fileSystem\n    }\n\n    override fun getFileSystem(uri: URI): FileSystem {\n        uri.requireSameScheme()\n        val authority = uri.webDavAuthority\n        return synchronized(lock) { fileSystems[authority] }\n            ?: throw FileSystemNotFoundException(authority.toString())\n    }\n\n    internal fun removeFileSystem(fileSystem: WebDavFileSystem) {\n        val authority = fileSystem.authority\n        synchronized(lock) { fileSystems.remove(authority) }\n    }\n\n    override fun getPath(uri: URI): Path {\n        uri.requireSameScheme()\n        val authority = uri.webDavAuthority\n        val path = uri.decodedPathByteString\n            ?: throw IllegalArgumentException(\"URI must have a path\")\n        return getOrNewFileSystem(authority).getPath(path)\n    }\n\n    private fun URI.requireSameScheme() {\n        val scheme = scheme\n        require(scheme in Protocol.SCHEMES) { \"URI scheme $scheme must be in ${Protocol.SCHEMES}\" }\n    }\n\n    private val URI.webDavAuthority: Authority\n        get() {\n            val protocol = Protocol.fromScheme(scheme)\n            val port = if (port != -1) port else protocol.defaultPort\n            val username = userInfo.orEmpty()\n            return Authority(protocol, host, port, username)\n        }\n\n    @Throws(IOException::class)\n    override fun newInputStream(file: Path, vararg options: OpenOption): InputStream {\n        file as? WebDavPath ?: throw ProviderMismatchException(file.toString())\n        val openOptions = options.toOpenOptions()\n        openOptions.checkForWebDav()\n        if (openOptions.write) {\n            throw UnsupportedOperationException(StandardOpenOption.WRITE.toString())\n        }\n        if (openOptions.append) {\n            throw UnsupportedOperationException(StandardOpenOption.APPEND.toString())\n        }\n        if (openOptions.truncateExisting) {\n            throw UnsupportedOperationException(StandardOpenOption.TRUNCATE_EXISTING.toString())\n        }\n        if (openOptions.create || openOptions.createNew || openOptions.noFollowLinks) {\n            val fileResponse = try {\n                Client.findPropertiesOrNull(file, true)\n            } catch (e: DavException) {\n                throw e.toFileSystemException(file.toString())\n            }\n            if (openOptions.noFollowLinks && fileResponse != null && fileResponse.isSymbolicLink) {\n                throw FileSystemException(\n                    file.toString(), null, \"File is a symbolic link: $fileResponse\"\n                )\n            }\n            if (openOptions.createNew && fileResponse != null) {\n                throw FileAlreadyExistsException(file.toString())\n            }\n            if ((openOptions.create || openOptions.createNew) && fileResponse == null) {\n                try {\n                    Client.makeFile(file)\n                } catch (e: DavException) {\n                    throw e.toFileSystemException(file.toString())\n                }\n            }\n        }\n        try {\n            return Client.get(file)\n        } catch (e: DavException) {\n            throw e.toFileSystemException(file.toString())\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun newOutputStream(file: Path, vararg options: OpenOption): OutputStream {\n        file as? WebDavPath ?: throw ProviderMismatchException(file.toString())\n        val optionsSet = mutableSetOf(*options)\n        if (optionsSet.isEmpty()) {\n            optionsSet += StandardOpenOption.CREATE\n            optionsSet += StandardOpenOption.TRUNCATE_EXISTING\n        }\n        optionsSet += StandardOpenOption.WRITE\n        val openOptions = optionsSet.toOpenOptions()\n        openOptions.checkForWebDav()\n        if (!openOptions.truncateExisting && !openOptions.createNew) {\n            throw UnsupportedOperationException(\"Missing ${StandardOpenOption.TRUNCATE_EXISTING}\")\n        }\n        val fileResponse = try {\n            Client.findPropertiesOrNull(file, true)\n        } catch (e: DavException) {\n            throw e.toFileSystemException(file.toString())\n        }\n        if (openOptions.createNew && fileResponse != null) {\n            throw FileAlreadyExistsException(file.toString())\n        }\n        if (!(openOptions.create || openOptions.createNew) && fileResponse == null) {\n            throw NoSuchFileException(file.toString())\n        }\n        try {\n            return Client.put(file)\n        } catch (e: DavException) {\n            throw e.toFileSystemException(file.toString())\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun newFileChannel(\n        file: Path,\n        options: Set<OpenOption>,\n        vararg attributes: FileAttribute<*>\n    ): FileChannel {\n        file as? WebDavPath ?: throw ProviderMismatchException(file.toString())\n        options.toOpenOptions().checkForWebDav()\n        if (attributes.isNotEmpty()) {\n            throw UnsupportedOperationException(attributes.contentToString())\n        }\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun newByteChannel(\n        file: Path,\n        options: Set<OpenOption>,\n        vararg attributes: FileAttribute<*>\n    ): SeekableByteChannel {\n        file as? WebDavPath ?: throw ProviderMismatchException(file.toString())\n        val openOptions = options.toOpenOptions()\n        openOptions.checkForWebDav()\n        if (openOptions.write && !openOptions.truncateExisting) {\n            throw UnsupportedOperationException(\"Missing ${StandardOpenOption.TRUNCATE_EXISTING}\")\n        }\n        if (openOptions.write || openOptions.create || openOptions.createNew ||\n            openOptions.noFollowLinks) {\n            val fileResponse = try {\n                Client.findPropertiesOrNull(file, true)\n            } catch (e: DavException) {\n                throw e.toFileSystemException(file.toString())\n            }\n            if (openOptions.createNew && fileResponse != null) {\n                throw FileAlreadyExistsException(file.toString())\n            }\n            if (openOptions.noFollowLinks && fileResponse != null && fileResponse.isSymbolicLink) {\n                throw FileSystemException(\n                    file.toString(), null, \"File is a symbolic link: $fileResponse\"\n                )\n            }\n            if (fileResponse == null) {\n                if (!(openOptions.create || openOptions.createNew)) {\n                    throw NoSuchFileException(file.toString())\n                }\n                try {\n                    Client.makeFile(file)\n                } catch (e: DavException) {\n                    throw e.toFileSystemException(file.toString())\n                }\n            }\n        }\n        if (attributes.isNotEmpty()) {\n            throw UnsupportedOperationException(attributes.contentToString())\n        }\n        try {\n            return Client.openByteChannel(file, openOptions.append)\n        } catch (e: DavException) {\n            throw e.toFileSystemException(file.toString())\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun newDirectoryStream(\n        directory: Path,\n        filter: DirectoryStream.Filter<in Path>\n    ): DirectoryStream<Path> {\n        directory as? WebDavPath ?: throw ProviderMismatchException(directory.toString())\n        val paths = try {\n            @Suppress(\"UNCHECKED_CAST\")\n            Client.findCollectionMembers(directory) as List<Path>\n        } catch (e: DavException) {\n            throw e.toFileSystemException(directory.toString())\n        }\n        return PathListDirectoryStream(paths, filter)\n    }\n\n    @Throws(IOException::class)\n    override fun createDirectory(directory: Path, vararg attributes: FileAttribute<*>) {\n        directory as? WebDavPath ?: throw ProviderMismatchException(directory.toString())\n        if (attributes.isNotEmpty()) {\n            throw UnsupportedOperationException(attributes.contentToString())\n        }\n        try {\n            Client.makeCollection(directory)\n        } catch (e: DavException) {\n            throw e.toFileSystemException(directory.toString())\n        }\n    }\n\n    override fun createSymbolicLink(link: Path, target: Path, vararg attributes: FileAttribute<*>) {\n        link as? WebDavPath ?: throw ProviderMismatchException(link.toString())\n        when (target) {\n            is WebDavPath, is ByteStringPath -> {}\n            else -> throw ProviderMismatchException(target.toString())\n        }\n        if (attributes.isNotEmpty()) {\n            throw UnsupportedOperationException(attributes.contentToString())\n        }\n        throw UnsupportedOperationException()\n    }\n\n    override fun createLink(link: Path, existing: Path) {\n        link as? WebDavPath ?: throw ProviderMismatchException(link.toString())\n        existing as? WebDavPath ?: throw ProviderMismatchException(existing.toString())\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun delete(path: Path) {\n        path as? WebDavPath ?: throw ProviderMismatchException(path.toString())\n        try {\n            Client.delete(path)\n        } catch (e: DavException) {\n            throw e.toFileSystemException(path.toString())\n        }\n    }\n\n    override fun readSymbolicLink(link: Path): Path {\n        link as? WebDavPath ?: throw ProviderMismatchException(link.toString())\n        val linkResponse = try {\n            Client.findProperties(link, true)\n        } catch (e: DavException) {\n            throw e.toFileSystemException(link.toString())\n        }\n        val target = linkResponse.newLocation?.toString()\n            ?: throw NotLinkException(link.toString(), null, linkResponse.toString())\n        // TODO: Convert to webdav(s) scheme?\n        return ByteStringPath(ByteString.fromString(target))\n    }\n\n    @Throws(IOException::class)\n    override fun copy(source: Path, target: Path, vararg options: CopyOption) {\n        source as? WebDavPath ?: throw ProviderMismatchException(source.toString())\n        target as? WebDavPath ?: throw ProviderMismatchException(target.toString())\n        val copyOptions = options.toCopyOptions()\n        WebDavCopyMove.copy(source, target, copyOptions)\n    }\n\n    @Throws(IOException::class)\n    override fun move(source: Path, target: Path, vararg options: CopyOption) {\n        source as? WebDavPath ?: throw ProviderMismatchException(source.toString())\n        target as? WebDavPath ?: throw ProviderMismatchException(target.toString())\n        val copyOptions = options.toCopyOptions()\n        WebDavCopyMove.move(source, target, copyOptions)\n    }\n\n    override fun isSameFile(path: Path, path2: Path): Boolean {\n        path as? WebDavPath ?: throw ProviderMismatchException(path.toString())\n        return path == path2\n    }\n\n    override fun isHidden(path: Path): Boolean {\n        path as? WebDavPath ?: throw ProviderMismatchException(path.toString())\n        val fileName = path.fileNameByteString ?: return false\n        return fileName.startsWith(HIDDEN_FILE_NAME_PREFIX)\n    }\n\n    override fun getFileStore(path: Path): FileStore {\n        path as? WebDavPath ?: throw ProviderMismatchException(path.toString())\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun checkAccess(path: Path, vararg modes: AccessMode) {\n        path as? WebDavPath ?: throw ProviderMismatchException(path.toString())\n        val accessModes = modes.toAccessModes()\n        if (accessModes.write) {\n            throw UnsupportedOperationException(AccessMode.WRITE.toString())\n        }\n        if (accessModes.execute) {\n            throw UnsupportedOperationException(AccessMode.EXECUTE.toString())\n        }\n        // Assume the file can be read if it can be listed.\n        try {\n            Client.findProperties(path, false)\n        } catch (e: DavException) {\n            throw e.toFileSystemException(path.toString())\n        }\n    }\n\n    override fun <V : FileAttributeView> getFileAttributeView(\n        path: Path,\n        type: Class<V>,\n        vararg options: LinkOption\n    ): V? {\n        if (!supportsFileAttributeView(type)) {\n            return null\n        }\n        @Suppress(\"UNCHECKED_CAST\")\n        return getFileAttributeView(path, *options) as V\n    }\n\n    internal fun supportsFileAttributeView(type: Class<out FileAttributeView>): Boolean =\n        type.isAssignableFrom(WebDavFileAttributeView::class.java)\n\n    @Throws(IOException::class)\n    override fun <A : BasicFileAttributes> readAttributes(\n        path: Path,\n        type: Class<A>,\n        vararg options: LinkOption\n    ): A {\n        if (!type.isAssignableFrom(BasicFileAttributes::class.java)) {\n            throw UnsupportedOperationException(type.toString())\n        }\n        @Suppress(\"UNCHECKED_CAST\")\n        return getFileAttributeView(path, *options).readAttributes() as A\n    }\n\n    private fun getFileAttributeView(path: Path, vararg options: LinkOption): WebDavFileAttributeView {\n        path as? WebDavPath ?: throw ProviderMismatchException(path.toString())\n        val linkOptions = options.toLinkOptions()\n        return WebDavFileAttributeView(path, linkOptions.noFollowLinks)\n    }\n\n    override fun readAttributes(\n        path: Path,\n        attributes: String,\n        vararg options: LinkOption\n    ): Map<String, Any> {\n        path as? WebDavPath ?: throw ProviderMismatchException(path.toString())\n        throw UnsupportedOperationException()\n    }\n\n    override fun setAttribute(\n        path: Path,\n        attribute: String,\n        value: Any,\n        vararg options: LinkOption\n    ) {\n        path as? WebDavPath ?: throw ProviderMismatchException(path.toString())\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun observe(path: Path, intervalMillis: Long): PathObservable {\n        path as? WebDavPath ?: throw ProviderMismatchException(path.toString())\n        return WatchServicePathObservable(path, intervalMillis)\n    }\n\n    @Throws(IOException::class)\n    override fun search(\n        directory: Path,\n        query: String,\n        intervalMillis: Long,\n        listener: (List<Path>) -> Unit\n    ) {\n        directory as? WebDavPath ?: throw ProviderMismatchException(directory.toString())\n        WalkFileTreeSearchable.search(directory, query, intervalMillis, listener)\n    }\n}\n\nval WebDavsFileSystemProvider =\n    DelegateSchemeFileSystemProvider(Protocol.DAVS.scheme, WebDavFileSystemProvider)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/webdav/WebDavPath.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.webdav\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport java8.nio.file.FileSystem\nimport java8.nio.file.LinkOption\nimport java8.nio.file.Path\nimport java8.nio.file.ProviderMismatchException\nimport java8.nio.file.WatchEvent\nimport java8.nio.file.WatchKey\nimport java8.nio.file.WatchService\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.ByteStringListPath\nimport me.zhanghai.android.files.provider.common.LocalWatchService\nimport me.zhanghai.android.files.provider.common.UriAuthority\nimport me.zhanghai.android.files.provider.webdav.client.Authority\nimport me.zhanghai.android.files.provider.webdav.client.Client\nimport me.zhanghai.android.files.util.readParcelable\nimport okhttp3.HttpUrl\nimport java.io.File\nimport java.io.IOException\n\ninternal class WebDavPath : ByteStringListPath<WebDavPath>, Client.Path {\n    private val fileSystem: WebDavFileSystem\n\n    constructor(\n        fileSystem: WebDavFileSystem,\n        path: ByteString\n    ) : super(WebDavFileSystem.SEPARATOR, path) {\n        this.fileSystem = fileSystem\n    }\n\n    private constructor(\n        fileSystem: WebDavFileSystem,\n        absolute: Boolean,\n        segments: List<ByteString>\n    ) : super(WebDavFileSystem.SEPARATOR, absolute, segments) {\n        this.fileSystem = fileSystem\n    }\n\n    override fun isPathAbsolute(path: ByteString): Boolean =\n        path.isNotEmpty() && path[0] == WebDavFileSystem.SEPARATOR\n\n    override fun createPath(path: ByteString): WebDavPath = WebDavPath(fileSystem, path)\n\n    override fun createPath(absolute: Boolean, segments: List<ByteString>): WebDavPath =\n        WebDavPath(fileSystem, absolute, segments)\n\n    override val uriScheme: String\n        get() = fileSystem.authority.protocol.scheme\n\n    override val uriAuthority: UriAuthority\n        get() = fileSystem.authority.toUriAuthority()\n\n    override val defaultDirectory: WebDavPath\n        get() = fileSystem.defaultDirectory\n\n    override fun getFileSystem(): FileSystem = fileSystem\n\n    override fun getRoot(): WebDavPath? = if (isAbsolute) fileSystem.rootDirectory else null\n\n    @Throws(IOException::class)\n    override fun toRealPath(vararg options: LinkOption): WebDavPath {\n        throw UnsupportedOperationException()\n    }\n\n    override fun toFile(): File {\n        throw UnsupportedOperationException()\n    }\n\n    @Throws(IOException::class)\n    override fun register(\n        watcher: WatchService,\n        events: Array<WatchEvent.Kind<*>>,\n        vararg modifiers: WatchEvent.Modifier\n    ): WatchKey {\n        if (watcher !is LocalWatchService) {\n            throw ProviderMismatchException(watcher.toString())\n        }\n        return watcher.register(this, events, *modifiers)\n    }\n\n    override val authority: Authority\n        get() = fileSystem.authority\n\n    override val url: HttpUrl\n        get() = HttpUrl.Builder()\n            .scheme(authority.protocol.httpScheme)\n            .host(authority.host)\n            .apply {\n                val port = authority.port\n                if (port != authority.protocol.defaultPort) {\n                    port(port)\n                }\n            }\n            .addPathSegments(toString().removePrefix(\"/\"))\n            .build()\n\n    private constructor(source: Parcel) : super(source) {\n        fileSystem = source.readParcelable()!!\n    }\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        super.writeToParcel(dest, flags)\n\n        dest.writeParcelable(fileSystem, flags)\n    }\n\n    companion object {\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<WebDavPath> {\n            override fun createFromParcel(source: Parcel): WebDavPath = WebDavPath(source)\n\n            override fun newArray(size: Int): Array<WebDavPath?> = arrayOfNulls(size)\n        }\n    }\n}\n\nval Path.isWebDavPath: Boolean\n    get() = this is WebDavPath\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/webdav/client/Authentication.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.webdav.client\n\nimport android.os.Parcelable\nimport android.util.Log\nimport at.bitfire.dav4jvm.BasicDigestAuthHandler\nimport at.bitfire.dav4jvm.UrlUtils\nimport kotlinx.parcelize.Parcelize\nimport okhttp3.Authenticator\nimport okhttp3.Interceptor\nimport okhttp3.Request\nimport okhttp3.Response\nimport okhttp3.Route\n\nsealed class Authentication : Parcelable {\n    abstract fun createAuthenticatorInterceptor(authority: Authority): AuthenticatorInterceptor\n}\n\ninterface AuthenticatorInterceptor : Authenticator, Interceptor\n\n@Parcelize\ndata object NoneAuthentication : Authentication() {\n    override fun createAuthenticatorInterceptor(authority: Authority): AuthenticatorInterceptor =\n        object : AuthenticatorInterceptor {\n            override fun authenticate(route: Route?, response: Response): Request? = null\n\n            override fun intercept(chain: Interceptor.Chain): Response =\n                chain.proceed(chain.request())\n        }\n}\n\n@Parcelize\ndata class PasswordAuthentication(\n    val password: String\n) : Authentication() {\n    override fun createAuthenticatorInterceptor(authority: Authority): AuthenticatorInterceptor =\n        object : AuthenticatorInterceptor {\n            private val basicDigestAuthHandler = BasicDigestAuthHandler(\n                UrlUtils.hostToDomain(authority.host), authority.username, password\n            )\n\n            override fun authenticate(route: Route?, response: Response): Request? =\n                basicDigestAuthHandler.authenticate(route, response)\n\n            override fun intercept(chain: Interceptor.Chain): Response =\n                basicDigestAuthHandler.intercept(chain)\n        }\n}\n\n@Parcelize\ndata class AccessTokenAuthentication(\n    val accessToken: String\n) : Authentication() {\n    override fun createAuthenticatorInterceptor(authority: Authority): AuthenticatorInterceptor =\n        object : AuthenticatorInterceptor {\n            override fun authenticate(route: Route?, response: Response): Request? = null\n\n            override fun intercept(chain: Interceptor.Chain): Response {\n                val request = chain.request()\n                val requestHost = request.url.host\n                val domain = UrlUtils.hostToDomain(authority.host)\n                if (!UrlUtils.hostToDomain(requestHost).equals(domain, true)) {\n                    Log.w(\n                        LOG_TAG,\n                        \"Not authenticating against $requestHost because it doesn't belong to \" +\n                            domain\n                    )\n                    return chain.proceed(request)\n                }\n                val newRequest = request.newBuilder()\n                    .header(\"Authorization\", \"Bearer $accessToken\")\n                    .build()\n                return chain.proceed(newRequest)\n            }\n        }\n\n    companion object {\n        private val LOG_TAG = AccessTokenAuthentication::class.java.simpleName\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/webdav/client/Authenticator.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.webdav.client\n\ninterface Authenticator {\n    fun getAuthentication(authority: Authority): Authentication?\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/webdav/client/Authority.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.webdav.client\n\nimport android.os.Parcelable\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.provider.common.UriAuthority\nimport me.zhanghai.android.files.util.takeIfNotEmpty\n\n@Parcelize\ndata class Authority(\n    val protocol: Protocol,\n    val host: String,\n    val port: Int,\n    val username: String\n) : Parcelable {\n    fun toUriAuthority(): UriAuthority {\n        val userInfo = username.takeIfNotEmpty()\n        val uriPort = port.takeIf { it != protocol.defaultPort }\n        return UriAuthority(userInfo, host, uriPort)\n    }\n\n    override fun toString(): String = toUriAuthority().toString()\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/webdav/client/Client.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.webdav.client\n\nimport java8.nio.file.Path as Java8Path\nimport okhttp3.Response as OkHttpResponse\nimport at.bitfire.dav4jvm.DavCollection\nimport at.bitfire.dav4jvm.DavResource\nimport at.bitfire.dav4jvm.HttpUtils\nimport at.bitfire.dav4jvm.Property\nimport at.bitfire.dav4jvm.Response\nimport at.bitfire.dav4jvm.exception.ConflictException\nimport at.bitfire.dav4jvm.exception.DavException\nimport at.bitfire.dav4jvm.exception.ForbiddenException\nimport at.bitfire.dav4jvm.exception.HttpException\nimport at.bitfire.dav4jvm.exception.NotFoundException\nimport at.bitfire.dav4jvm.exception.PreconditionFailedException\nimport at.bitfire.dav4jvm.exception.ServiceUnavailableException\nimport at.bitfire.dav4jvm.exception.UnauthorizedException\nimport at.bitfire.dav4jvm.property.webdav.CreationDate\nimport at.bitfire.dav4jvm.property.webdav.GetContentLength\nimport at.bitfire.dav4jvm.property.webdav.GetLastModified\nimport at.bitfire.dav4jvm.property.webdav.ResourceType\nimport java.io.IOException\nimport java.io.InputStream\nimport java.io.OutputStream\nimport java.net.HttpURLConnection\nimport java.time.Instant\nimport java.util.Collections\nimport java.util.WeakHashMap\nimport java8.nio.channels.SeekableByteChannel\nimport me.zhanghai.android.files.app.okHttpClient\nimport me.zhanghai.android.files.provider.common.LocalWatchService\nimport me.zhanghai.android.files.provider.common.NotifyEntryModifiedOutputStream\nimport me.zhanghai.android.files.provider.common.NotifyEntryModifiedSeekableByteChannel\nimport okhttp3.HttpUrl\nimport okhttp3.Interceptor\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport okhttp3.Route\n\n// See also https://github.com/miquels/webdavfs/blob/master/fuse.go\nobject Client {\n    private val FILE_PROPERTIES = arrayOf(\n        ResourceType.NAME,\n        CreationDate.NAME,\n        GetContentLength.NAME,\n        GetLastModified.NAME\n    )\n\n    @Volatile\n    lateinit var authenticator: Authenticator\n\n    private val clients = mutableMapOf<Authority, OkHttpClient>()\n\n    private val collectionMemberCache = Collections.synchronizedMap(WeakHashMap<Path, Response>())\n\n    @Throws(IOException::class)\n    private fun getClient(authority: Authority): OkHttpClient {\n        synchronized(clients) {\n            var client = clients[authority]\n            if (client == null) {\n                val authenticatorInterceptor =\n                    OkHttpAuthenticatorInterceptor(authenticator, authority)\n                client = okHttpClient.newBuilder()\n                    // Turn off follow redirects for PROPFIND.\n                    .followRedirects(false)\n                    .cookieJar(MemoryCookieJar())\n                    .addNetworkInterceptor(authenticatorInterceptor)\n                    .authenticator(authenticatorInterceptor)\n                    .build()\n                clients[authority] = client\n            }\n            return client\n        }\n    }\n\n    @Throws(DavException::class)\n    fun makeCollection(path: Path) {\n        try {\n            DavResource(getClient(path.authority), path.url).mkCol(null) {}\n        } catch (e: IOException) {\n            throw e.toDavException()\n        }\n        LocalWatchService.onEntryCreated(path as Java8Path)\n    }\n\n    @Throws(DavException::class)\n    fun makeFile(path: Path) {\n        try {\n            put(path).close()\n        } catch (e: IOException) {\n            throw e.toDavException()\n        }\n        LocalWatchService.onEntryCreated(path as Java8Path)\n    }\n\n    @Throws(DavException::class)\n    fun delete(path: Path) {\n        try {\n            DavResource(getClient(path.authority), path.url).delete {}\n        } catch (e: IOException) {\n            throw e.toDavException()\n        }\n        collectionMemberCache -= path\n        LocalWatchService.onEntryDeleted(path as Java8Path)\n    }\n\n    @Throws(DavException::class)\n    fun move(source: Path, target: Path) {\n        if (source.authority != target.authority) {\n            throw IOException(\"Paths aren't on the same authority\")\n        }\n        try {\n            DavResource(getClient(source.authority), source.url).move(target.url, false) {}\n        } catch (e: IOException) {\n            throw e.toDavException()\n        }\n        collectionMemberCache -= source\n        collectionMemberCache -= target\n        LocalWatchService.onEntryDeleted(source as Java8Path)\n        LocalWatchService.onEntryCreated(target as Java8Path)\n    }\n\n    @Throws(DavException::class)\n    fun get(path: Path): InputStream =\n        try {\n            DavResource(getClient(path.authority), path.url).getCompat(\"*/*\", null)\n        } catch (e: IOException) {\n            throw e.toDavException()\n        }\n\n    @Throws(DavException::class)\n    fun findCollectionMembers(path: Path): List<Path> =\n        buildList {\n            try {\n                DavCollection(getClient(path.authority), path.url)\n                    .propfind(1, *FILE_PROPERTIES) { response, relation ->\n                        if (relation != Response.HrefRelation.MEMBER) {\n                            return@propfind\n                        }\n                        this += path.resolve(response.hrefName())\n                            .also {\n                                if (response.isSuccess()) {\n                                    collectionMemberCache[it] = response\n                                }\n                            }\n                    }\n            } catch (e: IOException) {\n                throw e.toDavException()\n            }\n        }\n\n    @Throws(DavException::class)\n    fun findPropertiesOrNull(path: Path, noFollowLinks: Boolean): Response? =\n        try {\n            findProperties(path, noFollowLinks)\n        } catch (e: NotFoundException) {\n            null\n        } catch (e: IOException) {\n            throw e.toDavException()\n        }\n\n    // TODO: Support noFollowLinks.\n    @Throws(DavException::class)\n    fun findProperties(path: Path, noFollowLinks: Boolean): Response {\n        synchronized(collectionMemberCache) {\n            collectionMemberCache.remove(path)?.let { return it }\n        }\n        try {\n            return findProperties(\n                DavResource(getClient(path.authority), path.url), *FILE_PROPERTIES\n            )\n        } catch (e: IOException) {\n            throw e.toDavException()\n        }\n    }\n\n    @Throws(DavException::class, IOException::class)\n    internal fun findProperties(resource: DavResource, vararg properties: Property.Name): Response {\n        var responseRef: Response? = null\n        resource.propfind(0, *properties) { response, relation ->\n            if (relation != Response.HrefRelation.SELF) {\n                return@propfind\n            }\n            if (responseRef != null) {\n                throw DavException(\"Duplicate response for self\")\n            }\n            responseRef = response\n        }\n        val response = responseRef ?: throw DavException(\"Couldn't find a response for self\")\n        response.checkSuccess()\n        return response\n    }\n\n    @Throws(DavException::class)\n    fun openByteChannel(path: Path, isAppend: Boolean): SeekableByteChannel {\n        try {\n            val client = getClient(path.authority)\n            val resource = DavResource(client, path.url)\n            val patchSupport = resource.getPatchSupport()\n            return NotifyEntryModifiedSeekableByteChannel(\n                FileByteChannel(resource, patchSupport, isAppend), path as Java8Path\n            )\n        } catch (e: IOException) {\n            throw e.toDavException()\n        }\n    }\n\n    @Throws(DavException::class)\n    fun setLastModifiedTime(path: Path, lastModifiedTime: Instant) {\n        if (true) {\n            return\n        }\n        // The following doesn't work on most servers. See also\n        // https://github.com/sabre-io/dav/issues/1277\n        try {\n            DavResource(getClient(path.authority), path.url).proppatch(\n                mapOf(GetLastModified.NAME to HttpUtils.formatDate(lastModifiedTime)), emptyList()\n            ) { response, _ -> response.checkSuccess() }\n        } catch (e: IOException) {\n            throw e.toDavException()\n        }\n        LocalWatchService.onEntryModified(path as Java8Path)\n    }\n\n    @Throws(DavException::class)\n    fun put(path: Path): OutputStream =\n        try {\n            NotifyEntryModifiedOutputStream(\n                DavResource(getClient(path.authority), path.url).putCompat(), path as Java8Path\n            )\n        } catch (e: IOException) {\n            throw e.toDavException()\n        }\n\n    // @see DavResource.checkStatus\n    private fun Response.checkSuccess() {\n        if (isSuccess()) {\n            return\n        }\n        val status = status!!\n        throw when (status.code) {\n            HttpURLConnection.HTTP_UNAUTHORIZED -> UnauthorizedException(status.message)\n            HttpURLConnection.HTTP_FORBIDDEN -> ForbiddenException(status.message)\n            HttpURLConnection.HTTP_NOT_FOUND -> NotFoundException(status.message)\n            HttpURLConnection.HTTP_CONFLICT -> ConflictException(status.message)\n            HttpURLConnection.HTTP_PRECON_FAILED -> PreconditionFailedException(status.message)\n            HttpURLConnection.HTTP_UNAVAILABLE -> ServiceUnavailableException(status.message)\n            else -> HttpException(status.code, status.message)\n        }\n    }\n\n    interface Path {\n        val authority: Authority\n        val url: HttpUrl\n        fun resolve(other: String): Path\n    }\n\n    private class OkHttpAuthenticatorInterceptor(\n        private val authenticator: Authenticator,\n        private val authority: Authority\n    ) : AuthenticatorInterceptor {\n        private var authenticatorInterceptorCache: Pair<Authentication, AuthenticatorInterceptor>? =\n            null\n\n        private fun getAuthenticatorInterceptor(): AuthenticatorInterceptor {\n            val authentication = authenticator.getAuthentication(authority)\n                ?: throw IOException(\"No authentication found for $authority\")\n            authenticatorInterceptorCache?.let {\n                (cachedAuthentication, cachedAuthenticatorInterceptor) ->\n                if (cachedAuthentication == authentication) {\n                    return cachedAuthenticatorInterceptor\n                }\n            }\n            return authentication.createAuthenticatorInterceptor(authority).also {\n                authenticatorInterceptorCache = authentication to it\n            }\n        }\n\n        override fun authenticate(route: Route?, response: OkHttpResponse): Request? =\n            getAuthenticatorInterceptor().authenticate(route, response)\n\n        override fun intercept(chain: Interceptor.Chain): OkHttpResponse =\n            getAuthenticatorInterceptor().intercept(chain)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/webdav/client/DavIOException.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.webdav.client\n\nimport at.bitfire.dav4jvm.exception.DavException\nimport java.io.IOException\n\nclass DavIOException(cause: IOException) : DavException(cause.message ?: \"\", cause) {\n    override val cause: Throwable\n        get() = super.cause!!\n}\n\nfun IOException.toDavException(): DavIOException = DavIOException(this)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/webdav/client/DavResourceCompat.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.webdav.client\n\nimport at.bitfire.dav4jvm.DavResource\nimport at.bitfire.dav4jvm.DavResourceAccessor\nimport at.bitfire.dav4jvm.QuotedStringUtils\nimport at.bitfire.dav4jvm.ResponseCallback\nimport at.bitfire.dav4jvm.exception.DavException\nimport at.bitfire.dav4jvm.exception.HttpException\nimport me.zhanghai.android.files.provider.common.DelegateOutputStream\nimport okhttp3.Call\nimport okhttp3.Callback\nimport okhttp3.Headers\nimport okhttp3.MediaType\nimport okhttp3.MediaType.Companion.toMediaType\nimport okhttp3.Request\nimport okhttp3.RequestBody\nimport okhttp3.Response\nimport okio.BufferedSink\nimport okio.Pipe\nimport okio.buffer\nimport java.io.IOException\nimport java.io.InputStream\nimport java.io.OutputStream\nimport java.net.HttpURLConnection\nimport java.nio.ByteBuffer\nimport java.util.concurrent.CountDownLatch\n\n@Throws(DavException::class, IOException::class)\nfun DavResource.getCompat(accept: String, headers: Headers?): InputStream =\n    get(accept, headers).also { checkStatus(it) }.body!!.byteStream()\n\n@Throws(DavException::class, IOException::class)\nfun DavResource.getRangeCompat(\n    accept: String,\n    offset: Long,\n    size: Int,\n    headers: Headers?\n): InputStream =\n    followRedirects {\n        val request = Request.Builder().get().url(location)\n        if (headers != null) {\n            request.headers(headers)\n        }\n        request.header(\"Accept\", accept)\n        val lastIndex = offset + size - 1\n        request.header(\"Range\", \"bytes=$offset-$lastIndex\")\n        httpClient.newCall(request.build()).execute()\n    }\n        .also {\n            checkStatus(it)\n            if (it.code != HttpURLConnection.HTTP_PARTIAL) {\n                throw HttpException(it)\n            }\n        }\n        .body!!.byteStream()\n\n// This doesn't follow redirects since the request body is one-shot anyway.\n@Throws(DavException::class, IOException::class)\nfun DavResource.putCompat(\n    ifETag: String? = null,\n    ifScheduleTag: String? = null,\n    ifNoneMatch: Boolean = false,\n    headers: Map<String, String> = emptyMap(),\n): OutputStream {\n    val pipe = Pipe(DEFAULT_BUFFER_SIZE.toLong())\n    val body = object : RequestBody() {\n        override fun contentType(): MediaType? = null\n        override fun isOneShot() = true\n        override fun writeTo(sink: BufferedSink) {\n            sink.writeAll(pipe.source)\n        }\n    }\n    val builder = Request.Builder().put(body).url(location)\n    if (ifETag != null) {\n        // only overwrite specific version\n        builder.header(\"If-Match\", QuotedStringUtils.asQuotedString(ifETag))\n    }\n    if (ifScheduleTag != null) {\n        // only overwrite specific version\n        builder.header(\"If-Schedule-Tag-Match\", QuotedStringUtils.asQuotedString(ifScheduleTag))\n    }\n    if (ifNoneMatch) {\n        // don't overwrite anything existing\n        builder.header(\"If-None-Match\", \"*\")\n    }\n    // Add custom headers\n    for ((key, value) in headers) {\n        builder.header(key, value)\n    }\n    var exceptionRef: IOException? = null\n    var responseRef: Response? = null\n    val callbackLatch = CountDownLatch(1)\n    httpClient.newCall(builder.build()).enqueue(\n        object : Callback {\n            override fun onFailure(call: Call, e: IOException) {\n                exceptionRef = e\n                callbackLatch.countDown()\n            }\n\n            override fun onResponse(call: Call, response: Response) {\n                responseRef = response\n                callbackLatch.countDown()\n            }\n        }\n    )\n    return object : DelegateOutputStream(pipe.sink.buffer().outputStream()) {\n        override fun close() {\n            super.close()\n            callbackLatch.await()\n            exceptionRef?.let { throw it }\n            checkStatus(responseRef!!)\n        }\n    }\n}\n\nenum class PatchSupport {\n    NONE,\n    APACHE,\n    SABRE\n}\n\n@Throws(DavException::class, IOException::class)\nfun DavResource.getPatchSupport(): PatchSupport {\n    lateinit var patchSupport: PatchSupport\n    options { davCapabilities, response ->\n        patchSupport = when {\n            response.headers[\"Server\"]?.contains(\"Apache\") == true &&\n                \"<http://apache.org/dav/propset/fs/1>\" in davCapabilities ->\n                PatchSupport.APACHE\n\n            \"sabredav-partialupdate\" in davCapabilities -> PatchSupport.SABRE\n            else -> PatchSupport.NONE\n        }\n    }\n    return patchSupport\n}\n\n// https://sabre.io/dav/http-patch/\n@Throws(DavException::class, IOException::class)\nfun DavResource.patchCompat(\n    buffer: ByteBuffer,\n    offset: Long,\n    ifETag: String? = null,\n    ifScheduleTag: String? = null,\n    ifNoneMatch: Boolean = false,\n    callback: ResponseCallback\n) {\n    followRedirects {\n        val builder = Request.Builder()\n            .patch(buffer.toRequestBody(\"application/x-sabredav-partialupdate\".toMediaType()))\n            .url(location)\n        val lastIndex = offset + buffer.remaining() - 1\n        builder.header(\"X-Update-Range\", \"bytes=$offset-$lastIndex\")\n        if (ifETag != null) {\n            // only overwrite specific version\n            builder.header(\"If-Match\", QuotedStringUtils.asQuotedString(ifETag))\n        }\n        if (ifScheduleTag != null) {\n            // only overwrite specific version\n            builder.header(\"If-Schedule-Tag-Match\", QuotedStringUtils.asQuotedString(ifScheduleTag))\n        }\n        if (ifNoneMatch) {\n            // don't overwrite anything existing\n            builder.header(\"If-None-Match\", \"*\")\n        }\n        httpClient.newCall(builder.build()).execute()\n    }.use { response ->\n        checkStatus(response)\n        callback.onResponse(response)\n    }\n}\n\n@Throws(DavException::class, IOException::class)\nfun DavResource.putRangeCompat(\n    buffer: ByteBuffer,\n    offset: Long,\n    ifETag: String? = null,\n    ifScheduleTag: String? = null,\n    ifNoneMatch: Boolean = false,\n    callback: ResponseCallback\n) {\n    followRedirects {\n        val builder = Request.Builder()\n            .put(buffer.toRequestBody())\n            .url(location)\n        val lastIndex = offset + buffer.remaining() - 1\n        builder.header(\"Range\", \"bytes=$offset-$lastIndex/*\")\n        if (ifETag != null) {\n            // only overwrite specific version\n            builder.header(\"If-Match\", QuotedStringUtils.asQuotedString(ifETag))\n        }\n        if (ifScheduleTag != null) {\n            // only overwrite specific version\n            builder.header(\"If-Schedule-Tag-Match\", QuotedStringUtils.asQuotedString(ifScheduleTag))\n        }\n        if (ifNoneMatch) {\n            // don't overwrite anything existing\n            builder.header(\"If-None-Match\", \"*\")\n        }\n        httpClient.newCall(builder.build()).execute()\n    }.use { response ->\n        checkStatus(response)\n        callback.onResponse(response)\n    }\n}\n\n@Throws(HttpException::class)\nprivate fun DavResource.checkStatus(response: Response) {\n    DavResourceAccessor.checkStatus(this, response)\n}\n\nprivate fun DavResource.followRedirects(sendRequest: () -> Response): Response =\n    DavResourceAccessor.followRedirects(this, sendRequest)\n\nprivate fun ByteBuffer.toRequestBody(contentType: MediaType? = null): RequestBody {\n    val contentLength = remaining().toLong()\n    mark()\n    return object : RequestBody() {\n        override fun contentType() = contentType\n\n        override fun contentLength(): Long = contentLength\n\n        override fun writeTo(sink: BufferedSink) {\n            reset()\n            sink.write(this@toRequestBody)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/webdav/client/FileByteChannel.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.webdav.client\n\nimport at.bitfire.dav4jvm.DavResource\nimport at.bitfire.dav4jvm.exception.HttpException\nimport at.bitfire.dav4jvm.property.webdav.GetContentLength\nimport me.zhanghai.android.files.provider.common.AbstractFileByteChannel\nimport me.zhanghai.android.files.provider.common.EMPTY\nimport me.zhanghai.android.files.provider.common.readFully\nimport okhttp3.RequestBody.Companion.toRequestBody\nimport java.io.IOException\nimport java.io.OutputStream\nimport java.nio.ByteBuffer\n\n// https://blog.sphere.chronosempire.org.uk/2012/11/21/webdav-and-the-http-patch-nightmare\nclass FileByteChannel(\n    private val resource: DavResource,\n    private val patchSupport: PatchSupport,\n    isAppend: Boolean\n) : AbstractFileByteChannel(isAppend) {\n    private var nextSequentialWritePosition = 0L\n    private var sequentialWriteOutputStream: OutputStream? = null\n\n    @Throws(IOException::class)\n    override fun onRead(position: Long, size: Int): ByteBuffer {\n        val inputStream = try {\n            resource.getRangeCompat(\"*/*\", position, size, null)\n        } catch (e: HttpException) {\n            if (e.code == HTTP_RANGE_NOT_SATISFIABLE) {\n                // We were reading at/past end of file\n                return ByteBuffer::class.EMPTY\n            }\n            throw e\n        }\n        val destination = ByteBuffer.allocate(size)\n        val limit = inputStream.use {\n            it.readFully(destination.array(), destination.arrayOffset(), size)\n        }\n        destination.limit(limit)\n        return destination\n    }\n\n    @Throws(IOException::class)\n    override fun onWrite(position: Long, source: ByteBuffer) {\n        when (patchSupport) {\n            PatchSupport.APACHE ->\n                resource.putRangeCompat(source, position) {}\n            PatchSupport.SABRE ->\n                resource.patchCompat(source, position) {}\n            PatchSupport.NONE -> {\n                if (position != nextSequentialWritePosition) {\n                    throw IOException(\"Unsupported non-sequential write\")\n                }\n                val outputStream = sequentialWriteOutputStream\n                    ?: resource.putCompat().also { sequentialWriteOutputStream = it }\n                val remaining = source.remaining()\n                // I don't think we are using native or read-only ByteBuffer, so just call array()\n                // here.\n                outputStream.write(\n                    source.array(), source.arrayOffset() + source.position(), remaining\n                )\n                nextSequentialWritePosition += remaining\n            }\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun onTruncate(size: Long) {\n        if (size == 0L) {\n            resource.put(byteArrayOf().toRequestBody()) {}\n        } else {\n            throw IOException(\"Unsupported truncate to non-zero size\")\n        }\n    }\n\n    @Throws(IOException::class)\n    override fun onSize(): Long {\n        val getContentLength =\n            Client.findProperties(resource, GetContentLength.NAME)[GetContentLength::class.java]\n                ?: throw IOException(\"Missing GetContentLength\")\n        return getContentLength.contentLength ?: throw IOException(\"Invalid GetContentLength\")\n    }\n\n    @Throws(IOException::class)\n    override fun onClose() {\n        sequentialWriteOutputStream?.close()\n    }\n\n    companion object {\n        private const val HTTP_RANGE_NOT_SATISFIABLE = 416\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/webdav/client/MemoryCookieJar.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.webdav.client\n\nimport okhttp3.Cookie\nimport okhttp3.CookieJar\nimport okhttp3.HttpUrl\n\nclass MemoryCookieJar : CookieJar {\n    private val cookieMap = mutableMapOf<Triple<String, String, String>, Cookie>()\n\n    override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {\n        synchronized(cookieMap) {\n            for (cookie in cookies) {\n                cookieMap[Triple(cookie.domain, cookie.path, cookie.name)] = cookie\n            }\n        }\n    }\n\n    override fun loadForRequest(url: HttpUrl): List<Cookie> =\n        buildList {\n            synchronized(cookieMap) {\n                val iterator = cookieMap.values.iterator()\n                val currentTimeMillis = System.currentTimeMillis()\n                while (iterator.hasNext()) {\n                    val cookie = iterator.next()\n                    if (cookie.expiresAt <= currentTimeMillis) {\n                        iterator.remove()\n                        continue\n                    }\n                    if (cookie.matches(url)) {\n                        this += cookie\n                    }\n                }\n            }\n        }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/webdav/client/Protocol.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.webdav.client\n\nenum class Protocol(val scheme: String, val httpScheme: String, val defaultPort: Int) {\n    DAV(\"dav\", \"http\", 80),\n    DAVS(\"davs\", \"https\", 443);\n\n    companion object {\n        val SCHEMES = entries.map { it.scheme }\n\n        fun fromScheme(scheme: String): Protocol =\n            entries.firstOrNull { it.scheme == scheme } ?: throw IllegalArgumentException(scheme)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/provider/webdav/client/ResponseExtensions.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.provider.webdav.client\n\nimport at.bitfire.dav4jvm.HttpUtils\nimport at.bitfire.dav4jvm.Response\nimport at.bitfire.dav4jvm.property.webdav.CreationDate\nimport at.bitfire.dav4jvm.property.webdav.GetContentLength\nimport at.bitfire.dav4jvm.property.webdav.GetLastModified\nimport at.bitfire.dav4jvm.property.webdav.ResourceType\nimport java.time.Instant\n\nval Response.creationTime: Instant?\n    get() = this[CreationDate::class.java]?.creationDate?.let { HttpUtils.parseDate(it) }\n\nval Response.isDirectory: Boolean\n    get() = this[ResourceType::class.java]?.types?.contains(ResourceType.COLLECTION) == true\n\nval Response.isSymbolicLink: Boolean\n    get() = newLocation != null\n\nval Response.lastModifiedTime: Instant?\n    get() = this[GetLastModified::class.java]?.lastModified\n\nval Response.size: Long\n    get() = this[GetContentLength::class.java]?.contentLength ?: 0\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/settings/BookmarkDirectoriesPreference.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.settings\n\nimport android.content.Context\nimport android.text.TextUtils\nimport android.util.AttributeSet\nimport android.widget.TextView\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport androidx.lifecycle.Observer\nimport androidx.preference.Preference\nimport androidx.preference.PreferenceViewHolder\nimport me.zhanghai.android.files.compat.ListFormatterCompat\nimport me.zhanghai.android.files.navigation.BookmarkDirectory\nimport me.zhanghai.android.files.util.createIntent\nimport me.zhanghai.android.files.util.startActivitySafe\n\nclass BookmarkDirectoriesPreference : Preference {\n    private var emptySummary = summary\n\n    private val observer = Observer<List<BookmarkDirectory>> { onBookmarkDirectoryListChanged(it) }\n\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    constructor(\n        context: Context,\n        attrs: AttributeSet?,\n        @AttrRes defStyleAttr: Int,\n        @StyleRes defStyleRes: Int\n    ) : super(context, attrs, defStyleAttr, defStyleRes)\n\n    init {\n        isPersistent = false\n    }\n\n    override fun onAttached() {\n        super.onAttached()\n\n        Settings.BOOKMARK_DIRECTORIES.observeForever(observer)\n    }\n\n    override fun onDetached() {\n        super.onDetached()\n\n        Settings.BOOKMARK_DIRECTORIES.removeObserver(observer)\n    }\n\n    private fun onBookmarkDirectoryListChanged(bookmarkDirectories: List<BookmarkDirectory>) {\n        val names = bookmarkDirectories.map { it.name }\n        val summary = if (names.isNotEmpty()) ListFormatterCompat.format(names) else emptySummary\n        setSummary(summary)\n    }\n\n    override fun onBindViewHolder(holder: PreferenceViewHolder) {\n        super.onBindViewHolder(holder)\n\n        val summaryText = holder.findViewById(android.R.id.summary) as TextView\n        summaryText.ellipsize = TextUtils.TruncateAt.END\n        summaryText.isSingleLine = true\n    }\n\n    override fun onClick() {\n        context.startActivitySafe(BookmarkDirectoryListActivity::class.createIntent())\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/settings/BookmarkDirectoryListActivity.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.settings\n\nimport android.os.Bundle\nimport android.view.View\nimport androidx.fragment.app.add\nimport androidx.fragment.app.commit\nimport me.zhanghai.android.files.app.AppActivity\n\nclass BookmarkDirectoryListActivity : AppActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        // Calls ensureSubDecor().\n        findViewById<View>(android.R.id.content)\n        if (savedInstanceState == null) {\n            supportFragmentManager.commit {\n                add<BookmarkDirectoryListFragment>(android.R.id.content)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/settings/BookmarkDirectoryListAdapter.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.settings\n\nimport android.view.ViewGroup\nimport com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter\nimport com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange\nimport com.h6ah4i.android.widget.advrecyclerview.utils.AbstractDraggableItemViewHolder\nimport me.zhanghai.android.files.compat.foregroundCompat\nimport me.zhanghai.android.files.compat.isTransformedTouchPointInViewCompat\nimport me.zhanghai.android.files.databinding.BookmarkDirectoryItemBinding\nimport me.zhanghai.android.files.filelist.toUserFriendlyString\nimport me.zhanghai.android.files.navigation.BookmarkDirectory\nimport me.zhanghai.android.files.ui.SimpleAdapter\nimport me.zhanghai.android.files.util.layoutInflater\n\nclass BookmarkDirectoryListAdapter(\n    private val listener: Listener\n) : SimpleAdapter<BookmarkDirectory, BookmarkDirectoryListAdapter.ViewHolder>(),\n    DraggableItemAdapter<BookmarkDirectoryListAdapter.ViewHolder> {\n    override val hasStableIds: Boolean\n        get() = true\n\n    override fun getItemId(position: Int): Long = getItem(position).id\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder =\n        ViewHolder(\n            BookmarkDirectoryItemBinding.inflate(parent.context.layoutInflater, parent, false)\n        )\n\n    override fun onBindViewHolder(holder: ViewHolder, position: Int) {\n        val bookmarkDirectory = getItem(position)\n        val binding = holder.binding\n        // Need to remove the ripple before it's drawn onto the bitmap for dragging.\n        binding.root.foregroundCompat!!.mutate().setVisible(!holder.dragState.isActive, false)\n        binding.root.setOnClickListener { listener.editBookmarkDirectory(bookmarkDirectory) }\n        binding.nameText.text = bookmarkDirectory.name\n        binding.pathText.text = bookmarkDirectory.path.toUserFriendlyString()\n    }\n\n    override fun onCheckCanStartDrag(holder: ViewHolder, position: Int, x: Int, y: Int): Boolean =\n        (holder.binding.root as ViewGroup).isTransformedTouchPointInViewCompat(\n            x.toFloat(), y.toFloat(), holder.binding.dragHandleView, null\n        )\n\n    override fun onGetItemDraggableRange(holder: ViewHolder, position: Int): ItemDraggableRange? =\n        null\n\n    override fun onCheckCanDrop(draggingPosition: Int, dropPosition: Int): Boolean = true\n\n    override fun onItemDragStarted(position: Int) {\n        notifyDataSetChanged()\n    }\n\n    override fun onItemDragFinished(fromPosition: Int, toPosition: Int, result: Boolean) {\n        notifyDataSetChanged()\n    }\n\n    override fun onMoveItem(fromPosition: Int, toPosition: Int) {\n        if (fromPosition == toPosition) {\n            return\n        }\n        listener.moveBookmarkDirectory(fromPosition, toPosition)\n    }\n\n    class ViewHolder(val binding: BookmarkDirectoryItemBinding) : AbstractDraggableItemViewHolder(\n        binding.root\n    )\n\n    interface Listener {\n        fun editBookmarkDirectory(bookmarkDirectory: BookmarkDirectory)\n        fun moveBookmarkDirectory(fromPosition: Int, toPosition: Int)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/settings/BookmarkDirectoryListFragment.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.settings\n\nimport android.graphics.drawable.NinePatchDrawable\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.fragment.app.Fragment\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport androidx.recyclerview.widget.RecyclerView\nimport com.h6ah4i.android.widget.advrecyclerview.animator.DraggableItemAnimator\nimport com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager\nimport com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.databinding.BookmarkDirectoryListFragmentBinding\nimport me.zhanghai.android.files.filelist.FileListActivity\nimport me.zhanghai.android.files.navigation.BookmarkDirectories\nimport me.zhanghai.android.files.navigation.BookmarkDirectory\nimport me.zhanghai.android.files.navigation.EditBookmarkDirectoryDialogActivity\nimport me.zhanghai.android.files.navigation.EditBookmarkDirectoryDialogFragment\nimport me.zhanghai.android.files.ui.ScrollingViewOnApplyWindowInsetsListener\nimport me.zhanghai.android.files.util.createIntent\nimport me.zhanghai.android.files.util.fadeToVisibilityUnsafe\nimport me.zhanghai.android.files.util.getDrawable\nimport me.zhanghai.android.files.util.launchSafe\nimport me.zhanghai.android.files.util.putArgs\nimport me.zhanghai.android.files.util.startActivitySafe\n\nclass BookmarkDirectoryListFragment : Fragment(), BookmarkDirectoryListAdapter.Listener {\n    private val openPathLauncher =\n        registerForActivityResult(FileListActivity.OpenDirectoryContract(), ::onOpenPathResult)\n\n    private lateinit var binding: BookmarkDirectoryListFragmentBinding\n\n    private lateinit var adapter: BookmarkDirectoryListAdapter\n    private lateinit var dragDropManager: RecyclerViewDragDropManager\n    private lateinit var wrappedAdapter: RecyclerView.Adapter<*>\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View =\n        BookmarkDirectoryListFragmentBinding.inflate(inflater, container, false)\n            .also { binding = it }\n            .root\n\n    override fun onActivityCreated(savedInstanceState: Bundle?) {\n        super.onActivityCreated(savedInstanceState)\n\n        val activity = requireActivity() as AppCompatActivity\n        activity.setSupportActionBar(binding.toolbar)\n        activity.supportActionBar!!.setDisplayHomeAsUpEnabled(true)\n        binding.recyclerView.layoutManager = LinearLayoutManager(\n            activity, RecyclerView.VERTICAL, false\n        )\n        adapter = BookmarkDirectoryListAdapter(this)\n        dragDropManager = RecyclerViewDragDropManager().apply {\n            setDraggingItemShadowDrawable(\n                getDrawable(\n                    com.h6ah4i.android.materialshadowninepatch.R.drawable.ms9_composite_shadow_z2\n                ) as NinePatchDrawable\n            )\n        }\n        wrappedAdapter = dragDropManager.createWrappedAdapter(adapter)\n        binding.recyclerView.adapter = wrappedAdapter\n        binding.recyclerView.itemAnimator = DraggableItemAnimator()\n        dragDropManager.attachRecyclerView(binding.recyclerView)\n        binding.recyclerView.setOnApplyWindowInsetsListener(\n            ScrollingViewOnApplyWindowInsetsListener(binding.recyclerView)\n        )\n        binding.fab.setOnClickListener { onAddBookmarkDirectory() }\n\n        Settings.BOOKMARK_DIRECTORIES.observe(viewLifecycleOwner) {\n            onBookmarkDirectoryListChanged(it)\n        }\n    }\n\n    override fun onPause() {\n        super.onPause()\n\n        dragDropManager.cancelDrag()\n    }\n\n    override fun onDestroyView() {\n        super.onDestroyView()\n\n        dragDropManager.release()\n        WrapperAdapterUtils.releaseAll(wrappedAdapter)\n    }\n\n    private fun onBookmarkDirectoryListChanged(bookmarkDirectories: List<BookmarkDirectory>) {\n        binding.emptyView.fadeToVisibilityUnsafe(bookmarkDirectories.isEmpty())\n        adapter.replace(bookmarkDirectories)\n    }\n\n    private fun onAddBookmarkDirectory() {\n        openPathLauncher.launchSafe(null, this)\n    }\n\n    private fun onOpenPathResult(result: Path?) {\n        result ?: return\n        BookmarkDirectories.add(BookmarkDirectory(null, result))\n    }\n\n    override fun editBookmarkDirectory(bookmarkDirectory: BookmarkDirectory) {\n        startActivitySafe(\n            EditBookmarkDirectoryDialogActivity::class.createIntent()\n                .putArgs(EditBookmarkDirectoryDialogFragment.Args(bookmarkDirectory))\n        )\n    }\n\n    override fun moveBookmarkDirectory(fromPosition: Int, toPosition: Int) {\n        BookmarkDirectories.move(fromPosition, toPosition)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/settings/CharsetPreference.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.settings\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport androidx.preference.ListPreference\nimport java.nio.charset.Charset\n\nclass CharsetPreference : ListPreference {\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    constructor(\n        context: Context,\n        attrs: AttributeSet?,\n        @AttrRes defStyleAttr: Int,\n        @StyleRes defStyleRes: Int\n    ) : super(context, attrs, defStyleAttr, defStyleRes)\n\n    init {\n        val charsets = Charset.availableCharsets()\n        entries = charsets.values.map { it.displayName() }.toTypedArray<CharSequence>()\n        entryValues = charsets.keys.toTypedArray<CharSequence>()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/settings/DefaultDirectoryPreference.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.settings\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.util.valueCompat\n\nclass DefaultDirectoryPreference : PathPreference {\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    constructor(\n        context: Context,\n        attrs: AttributeSet?,\n        @AttrRes defStyleAttr: Int,\n        @StyleRes defStyleRes: Int\n    ) : super(context, attrs, defStyleAttr, defStyleRes)\n\n    override var persistedPath: Path\n        get() = Settings.FILE_LIST_DEFAULT_DIRECTORY.valueCompat\n        set(value) {\n            Settings.FILE_LIST_DEFAULT_DIRECTORY.putValue(value)\n        }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/settings/DefaultIfEmptyEditTextPreference.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.settings\n\nimport android.content.Context\nimport android.content.res.TypedArray\nimport android.util.AttributeSet\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport com.takisoft.preferencex.EditTextPreference\n\nclass DefaultIfEmptyEditTextPreference : EditTextPreference {\n    private var defaultValue: String? = null\n\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    constructor(\n        context: Context,\n        attrs: AttributeSet?,\n        @AttrRes defStyleAttr: Int,\n        @StyleRes defStyleRes: Int\n    ) : super(context, attrs, defStyleAttr, defStyleRes)\n\n    override fun onGetDefaultValue(a: TypedArray, index: Int): Any? =\n        super.onGetDefaultValue(a, index).also { defaultValue = it as String? }\n\n    override fun setDefaultValue(defaultValue: Any?) {\n        super.setDefaultValue(defaultValue)\n\n        this.defaultValue = defaultValue as String?\n    }\n\n    override fun setText(text: String?) {\n        val text = if (!text.isNullOrEmpty()) text else defaultValue\n        super.setText(text)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/settings/LocalePreference.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.settings\n\nimport android.content.Context\nimport android.content.Intent\nimport android.net.Uri\nimport android.os.Build\nimport android.provider.Settings\nimport android.util.AttributeSet\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport androidx.core.app.LocaleManagerCompat\nimport androidx.core.os.LocaleListCompat\nimport androidx.preference.ListPreference\nimport androidx.preference.Preference.SummaryProvider\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.app.application\nimport me.zhanghai.android.files.compat.LocaleConfigCompat\nimport me.zhanghai.android.files.util.toList\nimport java.util.Locale\n\nclass LocalePreference : ListPreference {\n    lateinit var setApplicationLocalesPre33: (LocaleListCompat) -> Unit\n\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    constructor(\n        context: Context,\n        attrs: AttributeSet?,\n        @AttrRes defStyleAttr: Int,\n        @StyleRes defStyleRes: Int\n    ) : super(context, attrs, defStyleAttr, defStyleRes)\n\n    init {\n        val context = context\n        val systemDefaultEntry = context.getString(R.string.system_default)\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {\n            // Prefer using the system setting because it has better support for locales.\n            intent = Intent(\n                Settings.ACTION_APP_LOCALE_SETTINGS,\n                Uri.fromParts(\"package\", context.packageName, null)\n            )\n            summaryProvider = SummaryProvider<LocalePreference> {\n                applicationLocale?.sentenceCasedLocalizedDisplayName ?: systemDefaultEntry\n            }\n        } else {\n            setDefaultValue(VALUE_SYSTEM_DEFAULT)\n            val supportedLocales = LocaleConfigCompat(context).supportedLocales!!.toList()\n                .sortedBy { it.toLanguageTag() }\n            entries = supportedLocales.mapTo(mutableListOf(systemDefaultEntry)) {\n                it.sentenceCasedLocalizedDisplayName\n            }.toTypedArray<CharSequence>()\n            entryValues =\n                supportedLocales\n                    .mapTo(mutableListOf(VALUE_SYSTEM_DEFAULT)) { it.toLanguageTag() }\n                    .toTypedArray<CharSequence>()\n            summaryProvider = SimpleSummaryProvider.getInstance()\n        }\n    }\n\n    private val Locale.sentenceCasedLocalizedDisplayName: String\n        // See com.android.internal.app.LocaleHelper.toSentenceCase() for a proper case conversion\n        // implementation which requires android.icu.text.CaseMap that's only available on API 29+.\n        @Suppress(\"DEPRECATION\")\n        get() = getDisplayName(this).capitalize(this)\n\n    override fun getPersistedString(defaultReturnValue: String?): String =\n        applicationLocale?.toLanguageTag() ?: VALUE_SYSTEM_DEFAULT\n\n    override fun persistString(value: String?): Boolean {\n        applicationLocale = if (value != null && value != VALUE_SYSTEM_DEFAULT) {\n            Locale.forLanguageTag(value)\n        } else {\n            null\n        }\n        return true\n    }\n\n    private var applicationLocale: Locale?\n        get() = LocaleManagerCompat.getApplicationLocales(application).toList().firstOrNull()\n        set(value) {\n            check(Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU)\n            if (value == applicationLocale) {\n                return\n            }\n            val locales = if (value != null) {\n                LocaleListCompat.create(value)\n            } else {\n                LocaleListCompat.getEmptyLocaleList()\n            }\n            setApplicationLocalesPre33(locales)\n        }\n\n    override fun onClick() {\n        // Don't show dialog if we have an intent.\n        if (intent != null) {\n            return\n        }\n\n        super.onClick()\n    }\n\n    // Exposed for SettingsPreferenceFragment.onResume().\n    public override fun notifyChanged() {\n        super.notifyChanged()\n    }\n\n    companion object {\n        private const val VALUE_SYSTEM_DEFAULT = \"\"\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/settings/NonNegativeIntegerPreference.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.settings\n\nimport android.content.Context\nimport android.content.res.TypedArray\nimport android.os.Parcelable\nimport android.util.AttributeSet\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport androidx.preference.EditTextPreference.OnBindEditTextListener\nimport com.takisoft.preferencex.EditTextPreference\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.compat.DigitsKeyListenerCompat\nimport me.zhanghai.android.files.util.ParcelableState\n\nclass NonNegativeIntegerPreference : EditTextPreference {\n    private var isIntegerSet = false\n    var integer: Int = 0\n        set(integer) {\n            if (integer < 0) {\n                return\n            }\n            val changed = field != integer\n            if (changed || !isIntegerSet) {\n                field = integer\n                isIntegerSet = true\n                persistInt(field)\n                if (changed) {\n                    notifyChanged()\n                }\n            }\n        }\n\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    constructor(\n        context: Context,\n        attrs: AttributeSet?,\n        @AttrRes defStyleAttr: Int,\n        @StyleRes defStyleRes: Int\n    ) : super(context, attrs, defStyleAttr, defStyleRes)\n\n    init {\n        onBindEditTextListener = OnBindEditTextListener {\n            it.keyListener = DigitsKeyListenerCompat.getInstance(null, false, false)\n        }\n    }\n\n    override fun onGetDefaultValue(a: TypedArray, index: Int): Int = a.getInteger(index, 0)\n\n    override fun onSetInitialValue(defaultValue: Any?) {\n        val defaultValueInt = if (defaultValue != null) defaultValue as Int else 0\n        integer = getPersistedInt(defaultValueInt)\n    }\n\n    override fun setText(text: String?) {\n        text ?: return\n        integer = try {\n            text.toInt()\n        } catch (e: NumberFormatException) {\n            return\n        }\n    }\n\n    override fun getText(): String = integer.toString()\n\n    override fun shouldDisableDependents(): Boolean = !isEnabled\n\n    override fun onSaveInstanceState(): Parcelable? {\n        val superState = super.onSaveInstanceState()\n        if (isPersistent) {\n            return superState\n        }\n\n        return State(superState, integer)\n    }\n\n    override fun onRestoreInstanceState(state: Parcelable?) {\n        if (state == null || state !is State) {\n            super.onRestoreInstanceState(state)\n            return\n        }\n\n        super.onRestoreInstanceState(state.superState)\n        integer = state.integer\n    }\n\n    @Parcelize\n    private class State(val superState: Parcelable?, val integer: Int) : ParcelableState\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/settings/PasswordPreference.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.settings\n\nimport android.content.Context\nimport android.text.method.PasswordTransformationMethod\nimport android.util.AttributeSet\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport com.takisoft.preferencex.EditTextPreference\nimport androidx.preference.EditTextPreference as AndroidXEditTextPreference\n\nclass PasswordPreference : EditTextPreference {\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    constructor(\n        context: Context,\n        attrs: AttributeSet?,\n        @AttrRes defStyleAttr: Int,\n        @StyleRes defStyleRes: Int\n    ) : super(context, attrs, defStyleAttr, defStyleRes)\n\n    init {\n        if (summaryProvider is AndroidXEditTextPreference.SimpleSummaryProvider) {\n            summaryProvider = SimpleSummaryProvider\n        }\n    }\n\n    object SimpleSummaryProvider : SummaryProvider<EditTextPreference> {\n        override fun provideSummary(preference: EditTextPreference): CharSequence? {\n            val text = preference.text\n            return if (!text.isNullOrEmpty()) {\n                PasswordTransformationMethod.getInstance().getTransformation(text, null)\n            } else {\n                AndroidXEditTextPreference.SimpleSummaryProvider.getInstance().provideSummary(\n                    preference\n                )\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/settings/PathPreference.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.settings\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport android.content.Intent\nimport android.util.AttributeSet\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport androidx.core.content.res.TypedArrayUtils\nimport androidx.core.content.res.use\nimport androidx.preference.Preference\nimport com.takisoft.preferencex.PreferenceActivityResultListener\nimport com.takisoft.preferencex.PreferenceFragmentCompat\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.filelist.FileListActivity\nimport me.zhanghai.android.files.filelist.toUserFriendlyString\nimport me.zhanghai.android.files.navigation.NavigationRootMapLiveData\nimport me.zhanghai.android.files.util.startActivityForResultSafe\nimport me.zhanghai.android.files.util.valueCompat\n\nabstract class PathPreference : Preference, PreferenceActivityResultListener {\n    private val openPathContract = FileListActivity.OpenDirectoryContract()\n\n    var path: Path = persistedPath\n        set(value) {\n            if (field == value) {\n                return\n            }\n            field = value\n            persistedPath = value\n            notifyChanged()\n        }\n\n    constructor(context: Context) : super(context) {\n        init(null, 0, 0)\n    }\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {\n        init(attrs, 0, 0)\n    }\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    ) {\n        init(attrs, defStyleAttr, 0)\n    }\n\n    constructor(\n        context: Context,\n        attrs: AttributeSet?,\n        @AttrRes defStyleAttr: Int,\n        @StyleRes defStyleRes: Int\n    ) : super(context, attrs, defStyleAttr, defStyleRes) {\n        init(attrs, defStyleAttr, defStyleRes)\n    }\n\n    @SuppressLint(\"PrivateResource\", \"RestrictedApi\")\n    private fun init(attrs: AttributeSet?, @AttrRes defStyleAttr: Int, @StyleRes defStyleRes: Int) {\n        isPersistent = false\n        context.obtainStyledAttributes(\n            attrs, androidx.preference.R.styleable.EditTextPreference, defStyleAttr, defStyleRes\n        ).use {\n            if (TypedArrayUtils.getBoolean(\n                it, androidx.preference.R.styleable.EditTextPreference_useSimpleSummaryProvider,\n                androidx.preference.R.styleable.EditTextPreference_useSimpleSummaryProvider, false\n            )) {\n                summaryProvider = SimpleSummaryProvider\n            }\n        }\n    }\n\n    override fun onPreferenceClick(fragment: PreferenceFragmentCompat, preference: Preference) {\n        fragment.startActivityForResultSafe(\n            openPathContract.createIntent(fragment.requireContext(), path), requestCode\n        )\n    }\n\n    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {\n        if (requestCode == this.requestCode) {\n            val result = openPathContract.parseResult(resultCode, data)\n            if (result != null) {\n                path = result\n            }\n        }\n    }\n\n    private val requestCode: Int\n        // @see FragmentActivity#checkForValidRequestCode()\n        get() = key.hashCode() and 0x0000FFFF\n\n    protected abstract var persistedPath: Path\n\n    object SimpleSummaryProvider : SummaryProvider<PathPreference> {\n        override fun provideSummary(preference: PathPreference): CharSequence? {\n            val path = preference.path\n            val navigationRoot = NavigationRootMapLiveData.valueCompat[path]\n            return navigationRoot?.getName(preference.context) ?: path.toUserFriendlyString()\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/settings/PathSettings.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.settings\n\nimport androidx.core.content.res.ResourcesCompat\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.filelist.FileSortOptions\nimport me.zhanghai.android.files.filelist.FileViewType\n\nobject PathSettings {\n    private const val NAME_SUFFIX = \"path\"\n\n    @Suppress(\"UNCHECKED_CAST\")\n    fun getFileListViewType(path: Path): SettingLiveData<FileViewType?> =\n        EnumSettingLiveData(\n            NAME_SUFFIX, R.string.pref_key_file_list_view_type, path.toString(),\n            ResourcesCompat.ID_NULL, FileViewType::class.java\n        ) as SettingLiveData<FileViewType?>\n\n    fun getFileListSortOptions(path: Path): SettingLiveData<FileSortOptions?> =\n        ParcelValueSettingLiveData(\n            NAME_SUFFIX, R.string.pref_key_file_list_sort_options, path.toString(), null\n        )\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/settings/RootStrategyPreference.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.settings\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport com.takisoft.preferencex.PreferenceFragmentCompat\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.filejob.FileJobService\nimport me.zhanghai.android.files.ui.MaterialPreferenceDialogFragmentCompat\nimport rikka.preference.SimpleMenuPreference\n\nclass RootStrategyPreference : SimpleMenuPreference {\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    constructor(\n        context: Context,\n        attrs: AttributeSet?,\n        @AttrRes defStyleAttr: Int,\n        @StyleRes defStyleRes: Int\n    ) : super(context, attrs, defStyleAttr, defStyleRes)\n\n    override fun onClick() {\n        val jobCount = FileJobService.runningJobCount\n        if (jobCount == 0) {\n            super.onClick()\n            return\n        }\n        dialogTitle = null\n        dialogMessage = context.resources.getQuantityString(\n            R.plurals.settings_root_strategy_message_format, jobCount, jobCount\n        )\n        setPositiveButtonText(android.R.string.yes)\n        setNegativeButtonText(R.string.maybe_later)\n        preferenceManager.showDialog(this)\n    }\n\n    private fun superOnClick() {\n        super.onClick()\n    }\n\n    companion object {\n        init {\n            PreferenceFragmentCompat.registerPreferenceFragment(\n                RootStrategyPreference::class.java, DialogFragment::class.java\n            )\n        }\n    }\n\n    private class DialogFragment : MaterialPreferenceDialogFragmentCompat() {\n        override fun onDialogClosed(positiveResult: Boolean) {\n            val preference = preference as RootStrategyPreference\n            if (positiveResult) {\n                preference.superOnClick()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/settings/SettingLiveData.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.settings\n\nimport android.content.SharedPreferences\nimport android.content.SharedPreferences.OnSharedPreferenceChangeListener\nimport androidx.annotation.AnyRes\nimport androidx.annotation.StringRes\nimport androidx.lifecycle.LiveData\nimport me.zhanghai.android.files.app.application\nimport me.zhanghai.android.files.app.defaultSharedPreferences\nimport me.zhanghai.android.files.compat.PreferenceManagerCompat\n\nabstract class SettingLiveData<T>(\n    nameSuffix: String?,\n    @StringRes keyRes: Int,\n    keySuffix: String?,\n    @AnyRes private val defaultValueRes: Int\n) : LiveData<T>(), OnSharedPreferenceChangeListener {\n    private val sharedPreferences = getSharedPreferences(nameSuffix)\n    private val key = getKey(keyRes, keySuffix)\n    private var defaultValue: T? = null\n\n    constructor(@StringRes keyRes: Int, @AnyRes defaultValueRes: Int) : this(\n        null, keyRes, null, defaultValueRes\n    )\n\n    protected fun init() {\n        defaultValue = getDefaultValue(defaultValueRes)\n        loadValue()\n        // Only a weak reference is stored so we don't need to worry about unregistering.\n        sharedPreferences.registerOnSharedPreferenceChangeListener(this)\n    }\n\n    private fun getSharedPreferences(nameSuffix: String?): SharedPreferences =\n        if (nameSuffix == null) {\n            defaultSharedPreferences\n        } else {\n            val name = \"${PreferenceManagerCompat.getDefaultSharedPreferencesName(application)\n            }_$nameSuffix\"\n            val mode = PreferenceManagerCompat.defaultSharedPreferencesMode\n            application.getSharedPreferences(name, mode)\n        }\n\n    private fun getKey(@StringRes keyRes: Int, keySuffix: String?): String {\n        val key = application.getString(keyRes)\n        return if (keySuffix != null) \"${key}_$keySuffix\" else key\n    }\n\n    protected abstract fun getDefaultValue(@AnyRes defaultValueRes: Int): T\n\n    private fun loadValue() {\n        @Suppress(\"UNCHECKED_CAST\")\n        value = getValue(sharedPreferences, key, defaultValue as T)\n    }\n\n    protected abstract fun getValue(\n        sharedPreferences: SharedPreferences,\n        key: String,\n        defaultValue: T\n    ): T\n\n    override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) {\n        if (key == this.key) {\n            loadValue()\n        }\n    }\n\n    fun putValue(value: T) {\n        putValue(sharedPreferences, key, value)\n    }\n\n    protected abstract fun putValue(sharedPreferences: SharedPreferences, key: String, value: T)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/settings/SettingLiveDatas.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.settings\n\nimport android.content.SharedPreferences\nimport android.os.Parcel\nimport androidx.annotation.AnyRes\nimport androidx.annotation.ArrayRes\nimport androidx.annotation.BoolRes\nimport androidx.annotation.DimenRes\nimport androidx.annotation.IntegerRes\nimport androidx.annotation.StringRes\nimport androidx.core.content.edit\nimport androidx.core.content.res.ResourcesCompat\nimport me.zhanghai.android.files.app.appClassLoader\nimport me.zhanghai.android.files.app.application\nimport me.zhanghai.android.files.util.Base64\nimport me.zhanghai.android.files.util.asBase64\nimport me.zhanghai.android.files.util.getBoolean\nimport me.zhanghai.android.files.util.getFloat\nimport me.zhanghai.android.files.util.getInteger\nimport me.zhanghai.android.files.util.getStringArray\nimport me.zhanghai.android.files.util.toBase64\nimport me.zhanghai.android.files.util.toByteArray\nimport me.zhanghai.android.files.util.use\n\nclass StringSettingLiveData(\n    nameSuffix: String?,\n    @StringRes keyRes: Int,\n    keySuffix: String?,\n    @StringRes defaultValueRes: Int\n) : SettingLiveData<String>(nameSuffix, keyRes, keySuffix, defaultValueRes) {\n    constructor(@StringRes keyRes: Int, @StringRes defaultValueRes: Int) : this(\n        null, keyRes, null, defaultValueRes\n    )\n\n    init {\n        init()\n    }\n\n    override fun getDefaultValue(@StringRes defaultValueRes: Int): String =\n        application.getString(defaultValueRes)\n\n    override fun getValue(\n        sharedPreferences: SharedPreferences,\n        key: String,\n        defaultValue: String\n    ): String = sharedPreferences.getString(key, defaultValue)!!\n\n    override fun putValue(sharedPreferences: SharedPreferences, key: String, value: String) {\n        sharedPreferences.edit { putString(key, value) }\n    }\n}\n\nclass StringSetSettingLiveData(\n    nameSuffix: String?,\n    @StringRes keyRes: Int,\n    keySuffix: String?,\n    @ArrayRes defaultValueRes: Int\n) : SettingLiveData<Set<String>>(nameSuffix, keyRes, keySuffix, defaultValueRes) {\n    constructor(@StringRes keyRes: Int, @ArrayRes defaultValueRes: Int) : this(\n        null, keyRes, null, defaultValueRes\n    )\n\n    init {\n        init()\n    }\n\n    override fun getDefaultValue(@StringRes defaultValueRes: Int): Set<String> =\n        application.getStringArray(defaultValueRes).toSet()\n\n    override fun getValue(\n        sharedPreferences: SharedPreferences,\n        key: String,\n        defaultValue: Set<String>\n    ): Set<String> = sharedPreferences.getStringSet(key, defaultValue)!!\n\n    override fun putValue(sharedPreferences: SharedPreferences, key: String, value: Set<String>) {\n        sharedPreferences.edit { putStringSet(key, value) }\n    }\n}\n\nclass IntegerSettingLiveData(\n    nameSuffix: String?,\n    @StringRes keyRes: Int,\n    keySuffix: String?,\n    @IntegerRes defaultValueRes: Int\n) : SettingLiveData<Int>(nameSuffix, keyRes, keySuffix, defaultValueRes) {\n    constructor(@StringRes keyRes: Int, @IntegerRes defaultValueRes: Int) : this(\n        null, keyRes, null, defaultValueRes\n    )\n\n    init {\n        init()\n    }\n\n    override fun getDefaultValue(@IntegerRes defaultValueRes: Int): Int =\n        application.getInteger(defaultValueRes)\n\n    override fun getValue(\n        sharedPreferences: SharedPreferences,\n        key: String,\n        defaultValue: Int\n    ): Int = sharedPreferences.getInt(key, defaultValue)\n\n    override fun putValue(sharedPreferences: SharedPreferences, key: String, value: Int) {\n        sharedPreferences.edit { putInt(key, value) }\n    }\n}\n\nclass LongSettingLiveData(\n    nameSuffix: String?,\n    @StringRes keyRes: Int,\n    keySuffix: String?,\n    @StringRes defaultValueRes: Int\n) : SettingLiveData<Long>(nameSuffix, keyRes, keySuffix, defaultValueRes) {\n    constructor(@StringRes keyRes: Int, @StringRes defaultValueRes: Int) : this(\n        null, keyRes, null, defaultValueRes\n    )\n\n    init {\n        init()\n    }\n\n    override fun getDefaultValue(@StringRes defaultValueRes: Int): Long =\n        application.getString(defaultValueRes).toLong()\n\n    override fun getValue(\n        sharedPreferences: SharedPreferences,\n        key: String,\n        defaultValue: Long\n    ): Long =\n        sharedPreferences.getLong(key, defaultValue)\n\n    override fun putValue(sharedPreferences: SharedPreferences, key: String, value: Long) {\n        sharedPreferences.edit { putLong(key, value) }\n    }\n}\n\nclass FloatSettingLiveData(\n    nameSuffix: String?,\n    @StringRes keyRes: Int,\n    keySuffix: String?,\n    @DimenRes defaultValueRes: Int\n) : SettingLiveData<Float>(nameSuffix, keyRes, keySuffix, defaultValueRes) {\n    constructor(@StringRes keyRes: Int, @DimenRes defaultValueRes: Int) : this(\n        null, keyRes, null, defaultValueRes\n    )\n\n    init {\n        init()\n    }\n\n    override fun getDefaultValue(@DimenRes defaultValueRes: Int): Float =\n        application.getFloat(defaultValueRes)\n\n    override fun getValue(\n        sharedPreferences: SharedPreferences,\n        key: String,\n        defaultValue: Float\n    ): Float = sharedPreferences.getFloat(key, defaultValue)\n\n    override fun putValue(sharedPreferences: SharedPreferences, key: String, value: Float) {\n        sharedPreferences.edit { putFloat(key, value) }\n    }\n}\n\nclass BooleanSettingLiveData(\n    nameSuffix: String?,\n    @StringRes keyRes: Int,\n    keySuffix: String?,\n    @BoolRes defaultValueRes: Int\n) : SettingLiveData<Boolean>(nameSuffix, keyRes, keySuffix, defaultValueRes) {\n    constructor(@StringRes keyRes: Int, @BoolRes defaultValueRes: Int) : this(\n        null, keyRes, null, defaultValueRes\n    )\n\n    init {\n        init()\n    }\n\n    override fun getDefaultValue(@BoolRes defaultValueRes: Int): Boolean =\n        application.getBoolean(defaultValueRes)\n\n    override fun getValue(\n        sharedPreferences: SharedPreferences,\n        key: String,\n        defaultValue: Boolean\n    ): Boolean = sharedPreferences.getBoolean(key, defaultValue)\n\n    override fun putValue(sharedPreferences: SharedPreferences, key: String, value: Boolean) {\n        sharedPreferences.edit { putBoolean(key, value) }\n    }\n}\n\n// Use string resource for default value so that we can support ListPreference.\n// TODO: kotlinc: Type argument is not within its bounds: should be subtype of 'Enum<E>'\n//  https://youtrack.jetbrains.com/issue/KT-60985\n//class EnumSettingLiveData<E : Enum<E>?>(\nclass EnumSettingLiveData<E : Enum<*>?>(\n    nameSuffix: String?,\n    @StringRes keyRes: Int,\n    keySuffix: String?,\n    @StringRes defaultValueRes: Int,\n    enumClass: Class<E>\n) : SettingLiveData<E>(nameSuffix, keyRes, keySuffix, defaultValueRes) {\n    private val enumValues = enumClass.enumConstants!!\n\n    constructor(\n        @StringRes keyRes: Int,\n        @StringRes defaultValueRes: Int,\n        enumClass: Class<E>\n    ) : this(null, keyRes, null, defaultValueRes, enumClass)\n\n    init {\n        init()\n    }\n\n    override fun getDefaultValue(@StringRes defaultValueRes: Int): E =\n        if (defaultValueRes != ResourcesCompat.ID_NULL) {\n            enumValues[application.getString(defaultValueRes).toInt()]\n        } else {\n            @Suppress(\"UNCHECKED_CAST\")\n            null as E\n        }\n\n    override fun getValue(\n        sharedPreferences: SharedPreferences,\n        key: String,\n        defaultValue: E\n    ): E {\n        val valueOrdinal = sharedPreferences.getString(key, null)?.toInt() ?: return defaultValue\n        return if (valueOrdinal in enumValues.indices) enumValues[valueOrdinal] else defaultValue\n    }\n\n    override fun putValue(sharedPreferences: SharedPreferences, key: String, value: E) {\n        sharedPreferences.edit { putString(key, value?.ordinal?.toString()) }\n    }\n}\n\nclass ResourceIdSettingLiveData(\n    nameSuffix: String?,\n    @StringRes keyRes: Int,\n    keySuffix: String?,\n    @AnyRes defaultValue: Int\n) : SettingLiveData<Int>(nameSuffix, keyRes, keySuffix, defaultValue) {\n    constructor(@StringRes keyRes: Int, @AnyRes defaultValue: Int) : this(\n        null, keyRes, null, defaultValue\n    )\n\n    init {\n        init()\n    }\n\n    @AnyRes\n    override fun getDefaultValue(@AnyRes defaultValueRes: Int): Int = defaultValueRes\n\n    override fun getValue(\n        sharedPreferences: SharedPreferences,\n        key: String,\n        @AnyRes defaultValue: Int\n    ): Int {\n        val valueString = sharedPreferences.getString(key, null) ?: return defaultValue\n        val value = application.resources.getIdentifier(valueString, null, application.packageName)\n        return if (value != 0) value else defaultValue\n    }\n\n    override fun putValue(sharedPreferences: SharedPreferences, key: String, @AnyRes value: Int) {\n        sharedPreferences.edit { putString(key, application.resources.getResourceName(value)) }\n    }\n}\n\nclass ParcelValueSettingLiveData<T>(\n    nameSuffix: String?,\n    @StringRes keyRes: Int,\n    keySuffix: String?,\n    private val defaultValue: T\n) : SettingLiveData<T>(nameSuffix, keyRes, keySuffix, 0) {\n    constructor(@StringRes keyRes: Int, defaultValue: T) : this(null, keyRes, null, defaultValue)\n\n    init {\n        init()\n    }\n\n    override fun getDefaultValue(@AnyRes defaultValueRes: Int): T = defaultValue\n\n    override fun getValue(\n        sharedPreferences: SharedPreferences,\n        key: String,\n        defaultValue: T\n    ): T =\n        try {\n            sharedPreferences.getString(key, null)?.asBase64()?.toParcelValue()\n        } catch (e: Exception) {\n            e.printStackTrace()\n            null\n        } ?: defaultValue\n\n    override fun putValue(sharedPreferences: SharedPreferences, key: String, value: T) {\n        sharedPreferences.edit { putString(key, value?.toParcelBase64()?.value) }\n    }\n\n    private fun Base64.toParcelValue(): T {\n        val bytes = toByteArray()\n        return Parcel.obtain().use { parcel ->\n            parcel.unmarshall(bytes, 0, bytes.size)\n            parcel.setDataPosition(0)\n            @Suppress(\"UNCHECKED_CAST\")\n            parcel.readValue(appClassLoader) as T\n        }\n    }\n\n    private fun T.toParcelBase64(): Base64 {\n        val bytes = Parcel.obtain().use { parcel ->\n            parcel.writeValue(this)\n            parcel.marshall()\n        }\n        return bytes.toBase64()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/settings/Settings.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.settings\n\nimport android.os.Environment\nimport android.text.TextUtils\nimport java8.nio.file.Path\nimport java8.nio.file.Paths\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.app.application\nimport me.zhanghai.android.files.compat.EnvironmentCompat2\nimport me.zhanghai.android.files.filelist.FileSortOptions\nimport me.zhanghai.android.files.filelist.FileViewType\nimport me.zhanghai.android.files.filelist.OpenApkDefaultAction\nimport me.zhanghai.android.files.navigation.BookmarkDirectory\nimport me.zhanghai.android.files.navigation.StandardDirectorySettings\nimport me.zhanghai.android.files.provider.root.RootStrategy\nimport me.zhanghai.android.files.storage.FileSystemRoot\nimport me.zhanghai.android.files.storage.PrimaryStorageVolume\nimport me.zhanghai.android.files.storage.Storage\nimport me.zhanghai.android.files.theme.custom.ThemeColor\nimport me.zhanghai.android.files.theme.night.NightMode\nimport java.io.File\n\nobject Settings {\n    val STORAGES: SettingLiveData<List<Storage>> =\n        ParcelValueSettingLiveData(\n            R.string.pref_key_storages,\n            listOf(FileSystemRoot(null, true), PrimaryStorageVolume(null, true))\n        )\n\n    val FILE_LIST_DEFAULT_DIRECTORY: SettingLiveData<Path> =\n        ParcelValueSettingLiveData(\n            R.string.pref_key_file_list_default_directory,\n            @Suppress(\"DEPRECATION\")\n            Paths.get(Environment.getExternalStorageDirectory().absolutePath)\n        )\n\n    val FILE_LIST_PERSISTENT_DRAWER_OPEN: SettingLiveData<Boolean> =\n        BooleanSettingLiveData(\n            R.string.pref_key_file_list_persistent_drawer_open,\n            R.bool.pref_default_value_file_list_persistent_drawer_open\n        )\n\n    val FILE_LIST_SHOW_HIDDEN_FILES: SettingLiveData<Boolean> =\n        BooleanSettingLiveData(\n            R.string.pref_key_file_list_show_hidden_files,\n            R.bool.pref_default_value_file_list_show_hidden_files\n        )\n\n    val FILE_LIST_VIEW_TYPE: SettingLiveData<FileViewType> =\n        EnumSettingLiveData(\n            R.string.pref_key_file_list_view_type, R.string.pref_default_value_file_list_view_type,\n            FileViewType::class.java\n        )\n\n    val FILE_LIST_SORT_OPTIONS: SettingLiveData<FileSortOptions> =\n        ParcelValueSettingLiveData(\n            R.string.pref_key_file_list_sort_options,\n            FileSortOptions(FileSortOptions.By.NAME, FileSortOptions.Order.ASCENDING, true)\n        )\n\n    val CREATE_ARCHIVE_TYPE: SettingLiveData<Int> =\n        ResourceIdSettingLiveData(R.string.pref_key_create_archive_type, R.id.zipRadio)\n\n    val FTP_SERVER_ANONYMOUS_LOGIN: SettingLiveData<Boolean> =\n        BooleanSettingLiveData(\n            R.string.pref_key_ftp_server_anonymous_login,\n            R.bool.pref_default_value_ftp_server_anonymous_login\n        )\n\n    val FTP_SERVER_USERNAME: SettingLiveData<String> =\n        StringSettingLiveData(\n            R.string.pref_key_ftp_server_username, R.string.pref_default_value_ftp_server_username\n        )\n\n    val FTP_SERVER_PASSWORD: SettingLiveData<String> =\n        StringSettingLiveData(\n            R.string.pref_key_ftp_server_password, R.string.pref_default_value_empty\n        )\n\n    val FTP_SERVER_PORT: SettingLiveData<Int> =\n        IntegerSettingLiveData(\n            R.string.pref_key_ftp_server_port, R.integer.pref_default_value_ftp_server_port\n        )\n\n    val FTP_SERVER_HOME_DIRECTORY: SettingLiveData<Path> =\n        ParcelValueSettingLiveData(\n            R.string.pref_key_ftp_server_home_directory,\n            @Suppress(\"DEPRECATION\")\n            Paths.get(Environment.getExternalStorageDirectory().absolutePath)\n        )\n\n    val FTP_SERVER_WRITABLE: SettingLiveData<Boolean> =\n        BooleanSettingLiveData(\n            R.string.pref_key_ftp_server_writable, R.bool.pref_default_value_ftp_server_writable\n        )\n\n    val THEME_COLOR: SettingLiveData<ThemeColor> =\n        EnumSettingLiveData(\n            R.string.pref_key_theme_color, R.string.pref_default_value_theme_color,\n            ThemeColor::class.java\n        )\n\n    val MATERIAL_DESIGN_3: SettingLiveData<Boolean> =\n        BooleanSettingLiveData(\n            R.string.pref_key_material_design_3, R.bool.pref_default_value_material_design_3\n        )\n\n    val NIGHT_MODE: SettingLiveData<NightMode> =\n        EnumSettingLiveData(\n            R.string.pref_key_night_mode, R.string.pref_default_value_night_mode,\n            NightMode::class.java\n        )\n\n    val BLACK_NIGHT_MODE: SettingLiveData<Boolean> =\n        BooleanSettingLiveData(\n            R.string.pref_key_black_night_mode, R.bool.pref_default_value_black_night_mode\n        )\n\n    val FILE_LIST_ANIMATION: SettingLiveData<Boolean> =\n        BooleanSettingLiveData(\n            R.string.pref_key_file_list_animation, R.bool.pref_default_value_file_list_animation\n        )\n\n    val FILE_NAME_ELLIPSIZE: SettingLiveData<TextUtils.TruncateAt> =\n        EnumSettingLiveData(\n            R.string.pref_key_file_name_ellipsize, R.string.pref_default_value_file_name_ellipsize,\n            TextUtils.TruncateAt::class.java\n        )\n\n    val STANDARD_DIRECTORY_SETTINGS: SettingLiveData<List<StandardDirectorySettings>> =\n        ParcelValueSettingLiveData(R.string.pref_key_standard_directory_settings, emptyList())\n\n    val BOOKMARK_DIRECTORIES: SettingLiveData<List<BookmarkDirectory>> =\n        ParcelValueSettingLiveData(\n            R.string.pref_key_bookmark_directories, listOf(\n                BookmarkDirectory(\n                    application.getString(R.string.settings_bookmark_directory_screenshots),\n                    Paths.get(\n                        File(\n                            @Suppress(\"DEPRECATION\")\n                            Environment.getExternalStoragePublicDirectory(\n                                Environment.DIRECTORY_PICTURES\n                            ), EnvironmentCompat2.DIRECTORY_SCREENSHOTS\n                        ).absolutePath\n                    )\n                )\n            )\n        )\n\n    val ROOT_STRATEGY: SettingLiveData<RootStrategy> =\n        EnumSettingLiveData(\n            R.string.pref_key_root_strategy, R.string.pref_default_value_root_strategy,\n            RootStrategy::class.java\n        )\n\n    val ARCHIVE_FILE_NAME_ENCODING: SettingLiveData<String> =\n        StringSettingLiveData(\n            R.string.pref_key_archive_file_name_encoding,\n            R.string.pref_default_value_archive_file_name_encoding\n        )\n\n    val OPEN_APK_DEFAULT_ACTION: SettingLiveData<OpenApkDefaultAction> =\n        EnumSettingLiveData(\n            R.string.pref_key_open_apk_default_action,\n            R.string.pref_default_value_open_apk_default_action,\n            OpenApkDefaultAction::class.java\n        )\n\n    val SHOW_PDF_THUMBNAIL_PRE_28: SettingLiveData<Boolean> = BooleanSettingLiveData(\n        R.string.pref_key_show_pdf_thumbnail_pre_28,\n        R.bool.pref_default_value_show_pdf_thumbnail_pre_28\n    )\n\n    val READ_REMOTE_FILES_FOR_THUMBNAIL: SettingLiveData<Boolean> =\n        BooleanSettingLiveData(\n            R.string.pref_key_read_remote_files_for_thumbnail,\n            R.bool.pref_default_value_read_remote_files_for_thumbnail\n        )\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/settings/SettingsActivity.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.settings\n\nimport android.annotation.SuppressLint\nimport android.os.Bundle\nimport android.view.KeyEvent\nimport android.view.MotionEvent\nimport android.view.View\nimport androidx.annotation.StyleRes\nimport androidx.appcompat.app.AppCompatDelegate\nimport androidx.core.os.LocaleListCompat\nimport androidx.fragment.app.add\nimport androidx.fragment.app.commit\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.app.AppActivity\nimport me.zhanghai.android.files.theme.custom.CustomThemeHelper.OnThemeChangedListener\nimport me.zhanghai.android.files.theme.night.NightModeHelper.OnNightModeChangedListener\nimport me.zhanghai.android.files.util.BundleParceler\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.createIntent\nimport me.zhanghai.android.files.util.getArgsOrNull\nimport me.zhanghai.android.files.util.putArgs\nimport me.zhanghai.android.files.util.startActivitySafe\n\nclass SettingsActivity : AppActivity(), OnThemeChangedListener, OnNightModeChangedListener {\n    private var isRestarting = false\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        val args = intent.extras?.getArgsOrNull<Args>()\n        val savedInstanceState = savedInstanceState ?: args?.savedInstanceState\n        super.onCreate(savedInstanceState)\n\n        // Calls ensureSubDecor().\n        findViewById<View>(android.R.id.content)\n        if (savedInstanceState == null) {\n            supportFragmentManager.commit { add<SettingsFragment>(android.R.id.content) }\n        }\n    }\n\n    fun setApplicationLocalesPre33(locales: LocaleListCompat) {\n        // HACK: Prevent this activity from being recreated due to locale change.\n        delegate.onDestroy()\n        AppCompatDelegate.setApplicationLocales(locales)\n        restart()\n    }\n\n    override fun onThemeChanged(@StyleRes theme: Int) {\n        // ActivityCompat.recreate() may call ActivityRecreator.recreate() without calling\n        // Activity.recreate(), so we cannot simply override it. To work around this, we just\n        // manually call restart().\n        restart()\n    }\n\n    override fun onNightModeChangedFromHelper(nightMode: Int) {\n        // ActivityCompat.recreate() may call ActivityRecreator.recreate() without calling\n        // Activity.recreate(), so we cannot simply override it. To work around this, we just\n        // manually call restart().\n        restart()\n    }\n\n    private fun restart() {\n        val savedInstanceState = Bundle().apply {\n            onSaveInstanceState(this)\n        }\n        finish()\n        val intent = SettingsActivity::class.createIntent().putArgs(Args(savedInstanceState))\n        startActivitySafe(intent)\n        overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)\n        isRestarting = true\n    }\n\n    override fun dispatchKeyEvent(event: KeyEvent): Boolean {\n        return isRestarting || super.dispatchKeyEvent(event)\n    }\n\n    @SuppressLint(\"RestrictedApi\")\n    override fun dispatchKeyShortcutEvent(event: KeyEvent): Boolean {\n        return isRestarting || super.dispatchKeyShortcutEvent(event)\n    }\n\n    override fun dispatchTouchEvent(event: MotionEvent): Boolean {\n        return isRestarting || super.dispatchTouchEvent(event)\n    }\n\n    override fun dispatchTrackballEvent(event: MotionEvent): Boolean {\n        return isRestarting || super.dispatchTrackballEvent(event)\n    }\n\n    override fun dispatchGenericMotionEvent(event: MotionEvent): Boolean {\n        return isRestarting || super.dispatchGenericMotionEvent(event)\n    }\n\n    @Parcelize\n    class Args(val savedInstanceState: @WriteWith<BundleParceler> Bundle?) : ParcelableArgs\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/settings/SettingsFragment.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.settings\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.fragment.app.Fragment\nimport me.zhanghai.android.files.databinding.SettingsFragmentBinding\n\nclass SettingsFragment : Fragment() {\n    private lateinit var binding: SettingsFragmentBinding\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View =\n        SettingsFragmentBinding.inflate(inflater, container, false)\n            .also { binding = it }\n            .root\n\n    override fun onActivityCreated(savedInstanceState: Bundle?) {\n        super.onActivityCreated(savedInstanceState)\n\n        val activity = requireActivity() as AppCompatActivity\n        activity.setSupportActionBar(binding.toolbar)\n        activity.supportActionBar!!.setDisplayHomeAsUpEnabled(true)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/settings/SettingsPreferenceFragment.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.settings\n\nimport android.os.Build\nimport android.os.Bundle\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.theme.custom.CustomThemeHelper\nimport me.zhanghai.android.files.theme.custom.ThemeColor\nimport me.zhanghai.android.files.theme.night.NightMode\nimport me.zhanghai.android.files.theme.night.NightModeHelper\nimport me.zhanghai.android.files.ui.PreferenceFragmentCompat\n\nclass SettingsPreferenceFragment : PreferenceFragmentCompat() {\n    private lateinit var localePreference: LocalePreference\n\n    override fun onCreatePreferencesFix(savedInstanceState: Bundle?, rootKey: String?) {\n        addPreferencesFromResource(R.xml.settings)\n\n        localePreference = preferenceScreen.findPreference(getString(R.string.pref_key_locale))!!\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {\n            localePreference.setApplicationLocalesPre33 = { locales ->\n                val activity = requireActivity() as SettingsActivity\n                activity.setApplicationLocalesPre33(locales)\n            }\n        }\n    }\n\n    override fun onActivityCreated(savedInstanceState: Bundle?) {\n        super.onActivityCreated(savedInstanceState)\n\n        val viewLifecycleOwner = viewLifecycleOwner\n        // The following may end up passing the same lambda instance to the observer because it has\n        // no capture, and result in an IllegalArgumentException \"Cannot add the same observer with\n        // different lifecycles\" if activity is finished and instantly started again. To work around\n        // this, always use an instance method reference.\n        // https://stackoverflow.com/a/27524543\n        //Settings.THEME_COLOR.observe(viewLifecycleOwner) { CustomThemeHelper.sync() }\n        //Settings.MATERIAL_DESIGN_3.observe(viewLifecycleOwner) { CustomThemeHelper.sync() }\n        //Settings.NIGHT_MODE.observe(viewLifecycleOwner) { NightModeHelper.sync() }\n        //Settings.BLACK_NIGHT_MODE.observe(viewLifecycleOwner) { CustomThemeHelper.sync() }\n        Settings.THEME_COLOR.observe(viewLifecycleOwner, this::onThemeColorChanged)\n        Settings.MATERIAL_DESIGN_3.observe(viewLifecycleOwner, this::onMaterialDesign3Changed)\n        Settings.NIGHT_MODE.observe(viewLifecycleOwner, this::onNightModeChanged)\n        Settings.BLACK_NIGHT_MODE.observe(viewLifecycleOwner, this::onBlackNightModeChanged)\n    }\n\n    private fun onThemeColorChanged(themeColor: ThemeColor) {\n        CustomThemeHelper.sync()\n    }\n\n    private fun onMaterialDesign3Changed(isMaterialDesign3: Boolean) {\n        CustomThemeHelper.sync()\n    }\n\n    private fun onNightModeChanged(nightMode: NightMode) {\n        NightModeHelper.sync()\n    }\n\n    private fun onBlackNightModeChanged(blackNightMode: Boolean) {\n        CustomThemeHelper.sync()\n    }\n\n    override fun onResume() {\n        super.onResume()\n\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {\n            // Refresh locale preference summary because we aren't notified for an external change\n            // between system default and the locale that's the current system default.\n            localePreference.notifyChanged()\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/settings/StandardDirectoriesPreference.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.settings\n\nimport android.content.Context\nimport android.text.TextUtils\nimport android.util.AttributeSet\nimport android.widget.TextView\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport androidx.lifecycle.Observer\nimport androidx.preference.Preference\nimport androidx.preference.PreferenceViewHolder\nimport me.zhanghai.android.files.compat.ListFormatterCompat\nimport me.zhanghai.android.files.navigation.StandardDirectoriesLiveData\nimport me.zhanghai.android.files.navigation.StandardDirectory\nimport me.zhanghai.android.files.util.createIntent\n\nclass StandardDirectoriesPreference : Preference {\n    private val observer = Observer<List<StandardDirectory>> { onStandardDirectoriesChanged(it) }\n    private var emptySummary = summary\n\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    constructor(\n        context: Context,\n        attrs: AttributeSet?,\n        @AttrRes defStyleAttr: Int,\n        @StyleRes defStyleRes: Int\n    ) : super(context, attrs, defStyleAttr, defStyleRes)\n\n    init {\n        isPersistent = false\n    }\n\n    override fun onAttached() {\n        super.onAttached()\n\n        StandardDirectoriesLiveData.observeForever(observer)\n    }\n\n    override fun onDetached() {\n        super.onDetached()\n\n        StandardDirectoriesLiveData.removeObserver(observer)\n    }\n\n    private fun onStandardDirectoriesChanged(standardDirectories: List<StandardDirectory>) {\n        val context = context\n        val titles = standardDirectories.filter { it.isEnabled }.map { it.getTitle(context) }\n        val summary = if (titles.isNotEmpty()) ListFormatterCompat.format(titles) else emptySummary\n        setSummary(summary)\n    }\n\n    override fun onBindViewHolder(holder: PreferenceViewHolder) {\n        super.onBindViewHolder(holder)\n\n        val summaryText = holder.findViewById(android.R.id.summary) as TextView\n        summaryText.ellipsize = TextUtils.TruncateAt.END\n        summaryText.isSingleLine = true\n    }\n\n    override fun onClick() {\n        context.startActivity(StandardDirectoryListActivity::class.createIntent())\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/settings/StandardDirectoryListActivity.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.settings\n\nimport android.os.Bundle\nimport android.view.View\nimport androidx.fragment.app.add\nimport androidx.fragment.app.commit\nimport me.zhanghai.android.files.app.AppActivity\n\nclass StandardDirectoryListActivity : AppActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        // Calls ensureSubDecor().\n        findViewById<View>(android.R.id.content)\n        if (savedInstanceState == null) {\n            supportFragmentManager.commit {\n                add<StandardDirectoryListFragment>(android.R.id.content)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/settings/StandardDirectoryListFragment.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.settings\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.fragment.app.Fragment\nimport me.zhanghai.android.files.databinding.StandardDirectoryListFragmentBinding\n\nclass StandardDirectoryListFragment : Fragment() {\n    private lateinit var binding: StandardDirectoryListFragmentBinding\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View =\n        StandardDirectoryListFragmentBinding.inflate(inflater, container, false)\n            .also { binding = it }\n            .root\n\n    override fun onActivityCreated(savedInstanceState: Bundle?) {\n        super.onActivityCreated(savedInstanceState)\n\n        val activity = requireActivity() as AppCompatActivity\n        activity.setSupportActionBar(binding.toolbar)\n        activity.supportActionBar!!.setDisplayHomeAsUpEnabled(true)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/settings/StandardDirectoryListPreferenceFragment.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.settings\n\nimport android.os.Bundle\nimport androidx.preference.Preference\nimport androidx.preference.SwitchPreferenceCompat\nimport me.zhanghai.android.files.compat.getDrawableCompat\nimport me.zhanghai.android.files.compat.setTintCompat\nimport me.zhanghai.android.files.navigation.StandardDirectoriesLiveData\nimport me.zhanghai.android.files.navigation.StandardDirectory\nimport me.zhanghai.android.files.navigation.getExternalStorageDirectory\nimport me.zhanghai.android.files.ui.PreferenceFragmentCompat\nimport me.zhanghai.android.files.util.getColorByAttr\nimport me.zhanghai.android.files.util.valueCompat\n\nclass StandardDirectoryListPreferenceFragment : PreferenceFragmentCompat(),\n    Preference.OnPreferenceClickListener {\n    override fun onCreatePreferencesFix(\n        savedInstanceState: Bundle?,\n        rootKey: String?\n    ) {}\n\n    override fun onActivityCreated(savedInstanceState: Bundle?) {\n        super.onActivityCreated(savedInstanceState)\n\n        StandardDirectoriesLiveData.observe(viewLifecycleOwner) { onStandardDirectoriesChanged(it) }\n    }\n\n    private fun onStandardDirectoriesChanged(\n        standardDirectories: List<StandardDirectory>\n    ) {\n        var preferenceScreen = preferenceScreen\n        val context = requireContext()\n        val oldPreferences = mutableMapOf<String, Preference>()\n        if (preferenceScreen == null) {\n            preferenceScreen = preferenceManager.createPreferenceScreen(context)\n            setPreferenceScreen(preferenceScreen)\n        } else {\n            for (index in preferenceScreen.preferenceCount - 1 downTo 0) {\n                val preference = preferenceScreen.getPreference(index)\n                preferenceScreen.removePreference(preference)\n                oldPreferences[preference.key] = preference\n            }\n        }\n        val secondaryTextColor = context.getColorByAttr(android.R.attr.textColorSecondary)\n        for (standardDirectory in standardDirectories) {\n            val key = standardDirectory.key\n            var preference = oldPreferences[key] as SwitchPreferenceCompat?\n            if (preference == null) {\n                preference = SwitchPreferenceCompat(context).apply {\n                    this.key = key\n                    isPersistent = false\n                    onPreferenceClickListener = this@StandardDirectoryListPreferenceFragment\n                }\n            }\n            preference.apply {\n                icon = context.getDrawableCompat(standardDirectory.iconRes).apply {\n                    mutate()\n                    setTintCompat(secondaryTextColor)\n                }\n                title = standardDirectory.getTitle(context)\n                summary = getExternalStorageDirectory(standardDirectory.relativePath)\n                isChecked = standardDirectory.isEnabled\n            }\n            preferenceScreen.addPreference(preference)\n        }\n    }\n\n    override fun onPreferenceClick(preference: Preference): Boolean {\n        preference as SwitchPreferenceCompat\n        val id = preference.key\n        val isEnabled = preference.isChecked\n        val settingsList = Settings.STANDARD_DIRECTORY_SETTINGS.valueCompat.toMutableList()\n        val index = settingsList.indexOfFirst { it.id == id }\n        if (index != -1) {\n            settingsList[index] = settingsList[index].copy(isEnabled = isEnabled)\n        } else {\n            val standardDirectory = StandardDirectoriesLiveData.valueCompat.find { it.key == id }!!\n            settingsList += standardDirectory.toSettings().copy(isEnabled = isEnabled)\n        }\n        Settings.STANDARD_DIRECTORY_SETTINGS.putValue(settingsList)\n        return true\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/settings/StoragesPreference.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.settings\n\nimport android.content.Context\nimport android.text.TextUtils\nimport android.util.AttributeSet\nimport android.widget.TextView\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport androidx.lifecycle.Observer\nimport androidx.preference.Preference\nimport androidx.preference.PreferenceViewHolder\nimport me.zhanghai.android.files.compat.ListFormatterCompat\nimport me.zhanghai.android.files.storage.Storage\nimport me.zhanghai.android.files.storage.StorageListActivity\nimport me.zhanghai.android.files.util.createIntent\nimport me.zhanghai.android.files.util.startActivitySafe\n\nclass StoragesPreference : Preference {\n    private var emptySummary = summary\n\n    private val observer = Observer<List<Storage>> { onStorageListChanged(it) }\n\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    constructor(\n        context: Context,\n        attrs: AttributeSet?,\n        @AttrRes defStyleAttr: Int,\n        @StyleRes defStyleRes: Int\n    ) : super(context, attrs, defStyleAttr, defStyleRes)\n\n    init {\n        isPersistent = false\n    }\n\n    override fun onAttached() {\n        super.onAttached()\n\n        Settings.STORAGES.observeForever(observer)\n    }\n\n    override fun onDetached() {\n        super.onDetached()\n\n        Settings.STORAGES.removeObserver(observer)\n    }\n\n    private fun onStorageListChanged(storages: List<Storage>) {\n        val context = context\n        val names = storages.filter { it.isVisible }.map { it.getName(context) }\n        val summary = if (names.isNotEmpty()) ListFormatterCompat.format(names) else emptySummary\n        setSummary(summary)\n    }\n\n    override fun onBindViewHolder(holder: PreferenceViewHolder) {\n        super.onBindViewHolder(holder)\n\n        val summaryText = holder.findViewById(android.R.id.summary) as TextView\n        summaryText.ellipsize = TextUtils.TruncateAt.END\n        summaryText.isSingleLine = true\n    }\n\n    override fun onClick() {\n        context.startActivitySafe(StorageListActivity::class.createIntent())\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/AddDocumentTreeActivity.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.os.Bundle\nimport android.view.View\nimport androidx.fragment.app.commit\nimport me.zhanghai.android.files.app.AppActivity\n\nclass AddDocumentTreeActivity : AppActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        // Calls ensureSubDecor().\n        findViewById<View>(android.R.id.content)\n        if (savedInstanceState == null) {\n            supportFragmentManager.commit { add(android.R.id.content, AddDocumentTreeFragment()) }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/AddDocumentTreeFragment.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.net.Uri\nimport android.os.Bundle\nimport androidx.activity.result.contract.ActivityResultContracts\nimport androidx.fragment.app.Fragment\nimport me.zhanghai.android.files.file.DocumentTreeUri\nimport me.zhanghai.android.files.file.asDocumentTreeUriOrNull\nimport me.zhanghai.android.files.file.takePersistablePermission\nimport me.zhanghai.android.files.util.finish\nimport me.zhanghai.android.files.util.launchSafe\n\nclass AddDocumentTreeFragment : Fragment() {\n    private val openDocumentTreeLauncher = registerForActivityResult(\n        ActivityResultContracts.OpenDocumentTree(), this::onOpenDocumentTreeResult\n    )\n\n    override fun onActivityCreated(savedInstanceState: Bundle?) {\n        super.onActivityCreated(savedInstanceState)\n\n        if (savedInstanceState == null) {\n            openDocumentTreeLauncher.launchSafe(null, this)\n        }\n    }\n\n    private fun onOpenDocumentTreeResult(result: Uri?) {\n        val treeUri = result?.asDocumentTreeUriOrNull()\n        if (treeUri != null) {\n            addDocumentTree(treeUri)\n        }\n        finish()\n    }\n\n    private fun addDocumentTree(treeUri: DocumentTreeUri) {\n        treeUri.takePersistablePermission()\n        val documentTree = DocumentTree(null, null, treeUri)\n        Storages.addOrReplace(documentTree)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/AddExternalStorageShortcutActivity.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.os.Bundle\nimport android.view.View\nimport androidx.fragment.app.commit\nimport me.zhanghai.android.files.app.AppActivity\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.putArgs\n\nclass AddExternalStorageShortcutActivity : AppActivity() {\n    private val args by args<AddExternalStorageShortcutFragment.Args>()\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        // Calls ensureSubDecor().\n        findViewById<View>(android.R.id.content)\n        if (savedInstanceState == null) {\n            val fragment = AddExternalStorageShortcutFragment().putArgs(args)\n            supportFragmentManager.commit {\n                add(fragment, AddExternalStorageShortcutFragment::class.java.name)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/AddExternalStorageShortcutFragment.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.os.Bundle\nimport androidx.annotation.StringRes\nimport androidx.fragment.app.Fragment\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.app.packageManager\nimport me.zhanghai.android.files.file.ExternalStorageUri\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.createDocumentsUiViewDirectoryIntent\nimport me.zhanghai.android.files.util.finish\nimport me.zhanghai.android.files.util.showToast\n\nclass AddExternalStorageShortcutFragment : Fragment() {\n    private val args by args<Args>()\n\n    override fun onActivityCreated(savedInstanceState: Bundle?) {\n        super.onActivityCreated(savedInstanceState)\n\n        val uri = args.uri\n        val hasDocumentsUi = uri.value.createDocumentsUiViewDirectoryIntent()\n            .resolveActivity(packageManager) != null\n        if (hasDocumentsUi) {\n            val externalStorageShortcut = ExternalStorageShortcut(\n                null, args.customNameRes?.let { getString(it) }, uri\n            )\n            Storages.addOrReplace(externalStorageShortcut)\n        } else {\n            showToast(R.string.activity_not_found)\n        }\n        finish()\n    }\n\n    @Parcelize\n    class Args(\n        @StringRes val customNameRes: Int?,\n        val uri: ExternalStorageUri\n    ) : ParcelableArgs\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/AddLanSmbServerActivity.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.os.Bundle\nimport android.view.View\nimport androidx.fragment.app.commit\nimport me.zhanghai.android.files.app.AppActivity\n\nclass AddLanSmbServerActivity : AppActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        // Calls ensureSubDecor().\n        findViewById<View>(android.R.id.content)\n        if (savedInstanceState == null) {\n            supportFragmentManager.commit { add(android.R.id.content, AddLanSmbServerFragment()) }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/AddLanSmbServerFragment.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.fragment.app.Fragment\nimport androidx.recyclerview.widget.ConcatAdapter\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.databinding.AddLanSmbServerFragmentBinding\nimport me.zhanghai.android.files.ui.StaticAdapter\nimport me.zhanghai.android.files.util.Failure\nimport me.zhanghai.android.files.util.Loading\nimport me.zhanghai.android.files.util.Stateful\nimport me.zhanghai.android.files.util.fadeToVisibilityUnsafe\nimport me.zhanghai.android.files.util.finish\nimport me.zhanghai.android.files.util.launchSafe\nimport me.zhanghai.android.files.util.viewModels\n\nclass AddLanSmbServerFragment : Fragment() {\n    private val addSmbServerLauncher = registerForActivityResult(\n        EditSmbServerActivity.Contract(), this::onAddSmbServerResult\n    )\n\n    private val viewModel by viewModels { { AddLanSmbServerViewModel() } }\n\n    private lateinit var binding: AddLanSmbServerFragmentBinding\n\n    private lateinit var loadingAdapter: StaticAdapter\n    private lateinit var serverListAdapter: LanSmbServerListAdapter\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View =\n        AddLanSmbServerFragmentBinding.inflate(inflater, container, false)\n            .also { binding = it }\n            .root\n\n    override fun onActivityCreated(savedInstanceState: Bundle?) {\n        super.onActivityCreated(savedInstanceState)\n\n        val activity = requireActivity() as AppCompatActivity\n        activity.setSupportActionBar(binding.toolbar)\n        activity.supportActionBar!!.setDisplayHomeAsUpEnabled(true)\n\n        binding.swipeRefreshLayout.setOnRefreshListener { viewModel.reload() }\n        binding.recyclerView.layoutManager = LinearLayoutManager(activity)\n        loadingAdapter = StaticAdapter(R.layout.lan_smb_server_loading_item)\n        serverListAdapter = LanSmbServerListAdapter { addSmbServer(it) }\n        val addAdapter = StaticAdapter(R.layout.lan_smb_server_add_item) { addSmbServer(null) }\n        binding.recyclerView.adapter = ConcatAdapter(\n            ConcatAdapter.Config.Builder()\n                .setStableIdMode(ConcatAdapter.Config.StableIdMode.ISOLATED_STABLE_IDS)\n                .build(), loadingAdapter, serverListAdapter, addAdapter\n        )\n\n        viewModel.lanSmbServerListLiveData.observe(viewLifecycleOwner) {\n            onLanSmbServerListChanged(it)\n        }\n    }\n\n    private fun onLanSmbServerListChanged(stateful: Stateful<List<LanSmbServer>>) {\n        if (stateful is Failure) {\n            stateful.throwable.printStackTrace()\n        }\n        val isLoading = stateful is Loading\n        binding.swipeRefreshLayout.isEnabled = !isLoading\n        binding.swipeRefreshLayout.isRefreshing = false\n        binding.progress.fadeToVisibilityUnsafe(isLoading)\n        val servers = stateful.value ?: emptyList()\n        loadingAdapter.itemCount = if (isLoading && servers.isEmpty()) 1 else 0\n        serverListAdapter.replace(servers)\n    }\n\n    private fun addSmbServer(server: LanSmbServer?) {\n        addSmbServerLauncher.launchSafe(EditSmbServerFragment.Args(host = server?.host), this)\n    }\n\n    private fun onAddSmbServerResult(result: Boolean) {\n        if (result) {\n            finish()\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/AddLanSmbServerViewModel.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.ViewModel\nimport me.zhanghai.android.files.util.Stateful\n\nclass AddLanSmbServerViewModel : ViewModel() {\n    private val _lanSmbServerListLiveData = LanSmbServerListLiveData()\n    val lanSmbServerListLiveData: LiveData<Stateful<List<LanSmbServer>>> = _lanSmbServerListLiveData\n\n    fun reload() {\n        _lanSmbServerListLiveData.loadValue()\n    }\n\n    override fun onCleared() {\n        _lanSmbServerListLiveData.close()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/AddStorageDialogActivity.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.os.Bundle\nimport android.view.View\nimport androidx.fragment.app.add\nimport androidx.fragment.app.commit\nimport me.zhanghai.android.files.app.AppActivity\n\nclass AddStorageDialogActivity : AppActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        // Calls ensureSubDecor().\n        findViewById<View>(android.R.id.content)\n        if (savedInstanceState == null) {\n            supportFragmentManager.commit {\n                add<AddStorageDialogFragment>(AddStorageDialogFragment::class.java.name)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/AddStorageDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.app.Dialog\nimport android.content.DialogInterface\nimport android.os.Build\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatDialogFragment\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.file.asExternalStorageUri\nimport me.zhanghai.android.files.provider.document.resolver.ExternalStorageProviderHacks\nimport me.zhanghai.android.files.util.createIntent\nimport me.zhanghai.android.files.util.finish\nimport me.zhanghai.android.files.util.putArgs\nimport me.zhanghai.android.files.util.startActivitySafe\n\nclass AddStorageDialogFragment : AppCompatDialogFragment() {\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =\n        MaterialAlertDialogBuilder(requireContext(), theme)\n            .setTitle(R.string.storage_add_storage_title)\n            .apply {\n                val items = STORAGE_TYPES.map { getString(it.first) }.toTypedArray<CharSequence>()\n                setItems(items) { _, which ->\n                    startActivitySafe(STORAGE_TYPES[which].second)\n                    finish()\n                }\n            }\n            .create()\n\n    override fun onCancel(dialog: DialogInterface) {\n        super.onCancel(dialog)\n\n        finish()\n    }\n\n    companion object {\n        private val STORAGE_TYPES = listOfNotNull(\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {\n                R.string.storage_add_storage_android_data to\n                    AddExternalStorageShortcutActivity ::class.createIntent().putArgs(\n                        AddExternalStorageShortcutFragment.Args(\n                            R.string.storage_add_storage_android_data,\n                            ExternalStorageProviderHacks.DOCUMENT_URI_ANDROID_DATA\n                                .asExternalStorageUri()\n                        )\n                    )\n            } else null,\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {\n                R.string.storage_add_storage_android_obb to\n                    AddExternalStorageShortcutActivity ::class.createIntent().putArgs(\n                        AddExternalStorageShortcutFragment.Args(\n                            R.string.storage_add_storage_android_obb,\n                            ExternalStorageProviderHacks.DOCUMENT_URI_ANDROID_OBB\n                                .asExternalStorageUri()\n                        )\n                    )\n            } else null,\n            R.string.storage_add_storage_document_tree to\n                AddDocumentTreeActivity::class.createIntent(),\n            R.string.storage_add_storage_ftp_server to\n                EditFtpServerActivity::class.createIntent().putArgs(EditFtpServerFragment.Args()),\n            R.string.storage_add_storage_sftp_server to\n                EditSftpServerActivity::class.createIntent().putArgs(EditSftpServerFragment.Args()),\n            R.string.storage_add_storage_smb_server to\n                AddLanSmbServerActivity::class.createIntent(),\n            R.string.storage_add_storage_webdav_server to\n                EditWebDavServerActivity::class.createIntent()\n                    .putArgs(EditWebDavServerFragment.Args()),\n        )\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/DeviceStorage.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.content.Context\nimport android.content.Intent\nimport android.os.storage.StorageVolume\nimport androidx.annotation.DrawableRes\nimport java8.nio.file.Path\nimport java8.nio.file.Paths\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.compat.getDescriptionCompat\nimport me.zhanghai.android.files.compat.isPrimaryCompat\nimport me.zhanghai.android.files.compat.pathCompat\nimport me.zhanghai.android.files.util.createIntent\nimport me.zhanghai.android.files.util.putArgs\nimport me.zhanghai.android.files.util.valueCompat\n\nsealed class DeviceStorage : Storage() {\n    override val description: String\n        get() = linuxPath\n\n    override val path: Path\n        get() = Paths.get(linuxPath)\n\n    abstract override val linuxPath: String\n\n    override fun createEditIntent(): Intent =\n        EditDeviceStorageDialogActivity::class.createIntent()\n            .putArgs(EditDeviceStorageDialogFragment.Args(this))\n\n    fun copy_(\n        customName: String? = this.customName,\n        isVisible: Boolean = this.isVisible\n    ): DeviceStorage =\n        when (this) {\n            is FileSystemRoot -> copy(customName, isVisible)\n            is PrimaryStorageVolume -> copy(customName, isVisible)\n        }\n}\n\n@Parcelize\ndata class FileSystemRoot(\n    override val customName: String?,\n    override val isVisible: Boolean\n) : DeviceStorage() {\n    override val id: Long\n        get() = \"FileSystemRoot\".hashCode().toLong()\n\n    override val iconRes: Int\n        @DrawableRes\n        get() = R.drawable.device_icon_white_24dp\n\n    override fun getDefaultName(context: Context): String =\n        context.getString(R.string.storage_file_system_root_title)\n\n    override val linuxPath: String\n        get() = LINUX_PATH\n\n    companion object {\n        const val LINUX_PATH = \"/\"\n    }\n}\n\n@Parcelize\ndata class PrimaryStorageVolume(\n    override val customName: String?,\n    override val isVisible: Boolean\n) : DeviceStorage() {\n    override val id: Long\n        get() = \"PrimaryStorageVolume\".hashCode().toLong()\n\n    override val iconRes: Int\n        @DrawableRes\n        get() = R.drawable.sd_card_icon_white_24dp\n\n    override fun getDefaultName(context: Context): String =\n        storageVolume.getDescriptionCompat(context)\n\n    override val linuxPath: String\n        get() = storageVolume.pathCompat\n\n    private val storageVolume: StorageVolume\n        get() = StorageVolumeListLiveData.valueCompat.find { it.isPrimaryCompat }!!\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/DocumentTree.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Environment\nimport androidx.annotation.DrawableRes\nimport java8.nio.file.Path\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.compat.getDescriptionCompat\nimport me.zhanghai.android.files.compat.isPrimaryCompat\nimport me.zhanghai.android.files.compat.pathCompat\nimport me.zhanghai.android.files.file.DocumentTreeUri\nimport me.zhanghai.android.files.file.displayName\nimport me.zhanghai.android.files.file.storageVolume\nimport me.zhanghai.android.files.provider.document.createDocumentTreeRootPath\nimport me.zhanghai.android.files.util.createIntent\nimport me.zhanghai.android.files.util.putArgs\nimport me.zhanghai.android.files.util.supportsExternalStorageManager\nimport kotlin.random.Random\n\n@Parcelize\ndata class DocumentTree(\n    override val id: Long,\n    override val customName: String?,\n    val uri: DocumentTreeUri\n) : Storage() {\n    constructor(\n        id: Long?,\n        customName: String?,\n        uri: DocumentTreeUri\n    ) : this(id ?: Random.nextLong(), customName, uri)\n\n    override val iconRes: Int\n        @DrawableRes\n        // Error: Call requires API level 24 (current min is 21):\n        // android.os.storage.StorageVolume#equals [NewApi]\n        @SuppressLint(\"NewApi\")\n        get() =\n            // We are using MANAGE_EXTERNAL_STORAGE to access all storage volumes when supported.\n            if (!Environment::class.supportsExternalStorageManager()\n                && uri.storageVolume.let { it != null && !it.isPrimaryCompat }) {\n                R.drawable.sd_card_icon_white_24dp\n            } else {\n                super.iconRes\n            }\n\n    override fun getDefaultName(context: Context): String =\n        uri.storageVolume?.getDescriptionCompat(context) ?: uri.displayName\n            ?: uri.value.lastPathSegment ?: uri.value.toString()\n\n    override val description: String\n        get() = uri.value.toString()\n\n    override val path: Path\n        get() = uri.value.createDocumentTreeRootPath()\n\n    override val linuxPath: String?\n        get() = uri.storageVolume?.pathCompat\n\n    override fun createEditIntent(): Intent =\n        EditDocumentTreeDialogActivity::class.createIntent()\n            .putArgs(EditDocumentTreeDialogFragment.Args(this))\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/EditDeviceStorageDialogActivity.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.os.Bundle\nimport android.view.View\nimport androidx.fragment.app.commit\nimport me.zhanghai.android.files.app.AppActivity\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.putArgs\n\nclass EditDeviceStorageDialogActivity : AppActivity() {\n    private val args by args<EditDeviceStorageDialogFragment.Args>()\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        // Calls ensureSubDecor().\n        findViewById<View>(android.R.id.content)\n        if (savedInstanceState == null) {\n            val fragment = EditDeviceStorageDialogFragment().putArgs(args)\n            supportFragmentManager.commit {\n                add(fragment, EditDeviceStorageDialogFragment::class.java.name)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/EditDeviceStorageDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.app.Dialog\nimport android.content.DialogInterface\nimport android.os.Bundle\nimport android.view.WindowManager\nimport androidx.appcompat.app.AppCompatDialogFragment\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.databinding.EditDeviceStorageDialogBinding\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.finish\nimport me.zhanghai.android.files.util.layoutInflater\nimport me.zhanghai.android.files.util.setTextWithSelection\n\nclass EditDeviceStorageDialogFragment : AppCompatDialogFragment() {\n    private val args by args<Args>()\n\n    private lateinit var binding: EditDeviceStorageDialogBinding\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {\n        val deviceStorage = args.deviceStorage\n        return MaterialAlertDialogBuilder(requireContext(), theme)\n            .setTitle(R.string.storage_edit_device_storage_title)\n            .apply {\n                binding = EditDeviceStorageDialogBinding.inflate(context.layoutInflater)\n                binding.nameLayout.placeholderText = deviceStorage.getDefaultName(\n                    binding.nameLayout.context\n                )\n                if (savedInstanceState == null) {\n                    binding.nameEdit.setTextWithSelection(\n                        deviceStorage.getName(binding.nameEdit.context)\n                    )\n                }\n                binding.pathText.setText(deviceStorage.linuxPath)\n                setView(binding.root)\n            }\n            .setPositiveButton(android.R.string.ok) { _, _ -> save() }\n            .setNegativeButton(android.R.string.cancel) { dialog, _ -> dialog.cancel() }\n            .setNeutralButton(\n                if (deviceStorage.isVisible) R.string.hide else R.string.show\n            ) { _, _ -> toggleVisibility() }\n            .create()\n            .apply {\n                window!!.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)\n            }\n    }\n\n    private fun save() {\n        val customName = binding.nameEdit.text.toString()\n            .takeIf { it.isNotEmpty() && it != binding.nameLayout.placeholderText }\n        val deviceStorage = args.deviceStorage.copy_(customName = customName)\n        Storages.replace(deviceStorage)\n        finish()\n    }\n\n    private fun toggleVisibility() {\n        val deviceStorage = args.deviceStorage.let { it.copy_(isVisible = !it.isVisible) }\n        Storages.replace(deviceStorage)\n        finish()\n    }\n\n    override fun onCancel(dialog: DialogInterface) {\n        super.onCancel(dialog)\n\n        finish()\n    }\n\n    @Parcelize\n    class Args(val deviceStorage: DeviceStorage) : ParcelableArgs\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/EditDocumentTreeDialogActivity.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.os.Bundle\nimport android.view.View\nimport androidx.fragment.app.commit\nimport me.zhanghai.android.files.app.AppActivity\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.putArgs\n\nclass EditDocumentTreeDialogActivity : AppActivity() {\n    private val args by args<EditDocumentTreeDialogFragment.Args>()\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        // Calls ensureSubDecor().\n        findViewById<View>(android.R.id.content)\n        if (savedInstanceState == null) {\n            val fragment = EditDocumentTreeDialogFragment().putArgs(args)\n            supportFragmentManager.commit {\n                add(fragment, EditDocumentTreeDialogFragment::class.java.name)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/EditDocumentTreeDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.app.Dialog\nimport android.content.DialogInterface\nimport android.os.Bundle\nimport android.view.WindowManager\nimport androidx.appcompat.app.AppCompatDialogFragment\nimport androidx.core.view.isVisible\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.databinding.EditDocumentTreeDialogBinding\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.finish\nimport me.zhanghai.android.files.util.layoutInflater\nimport me.zhanghai.android.files.util.setTextWithSelection\n\nclass EditDocumentTreeDialogFragment : AppCompatDialogFragment() {\n    private val args by args<Args>()\n\n    private lateinit var binding: EditDocumentTreeDialogBinding\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =\n        MaterialAlertDialogBuilder(requireContext(), theme)\n            .setTitle(R.string.storage_edit_document_tree_title)\n            .apply {\n                binding = EditDocumentTreeDialogBinding.inflate(context.layoutInflater)\n                val documentTree = args.documentTree\n                binding.nameLayout.placeholderText = documentTree.getDefaultName(\n                    binding.nameLayout.context\n                )\n                if (savedInstanceState == null) {\n                    binding.nameEdit.setTextWithSelection(\n                        documentTree.getName(binding.nameEdit.context)\n                    )\n                }\n                binding.uriText.setText(documentTree.uri.value.toString())\n                val linuxPath = documentTree.linuxPath\n                binding.pathLayout.isVisible = linuxPath != null\n                binding.pathText.setText(linuxPath)\n                setView(binding.root)\n            }\n            .setPositiveButton(android.R.string.ok) { _, _ -> save() }\n            .setNegativeButton(android.R.string.cancel) { dialog, _ -> dialog.cancel() }\n            .setNeutralButton(R.string.remove) { _, _ -> remove() }\n            .create()\n            .apply {\n                window!!.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)\n            }\n\n    private fun save() {\n        val customName = binding.nameEdit.text.toString()\n            .takeIf { it.isNotEmpty() && it != binding.nameLayout.placeholderText }\n        val documentTree = args.documentTree.copy(customName = customName)\n        Storages.replace(documentTree)\n        finish()\n    }\n\n    private fun remove() {\n        Storages.remove(args.documentTree)\n        finish()\n    }\n\n    override fun onCancel(dialog: DialogInterface) {\n        super.onCancel(dialog)\n\n        finish()\n    }\n\n    @Parcelize\n    class Args(val documentTree: DocumentTree) : ParcelableArgs\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/EditExternalStorageShortcutDialogActivity.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.os.Bundle\nimport android.view.View\nimport androidx.fragment.app.commit\nimport me.zhanghai.android.files.app.AppActivity\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.putArgs\n\nclass EditExternalStorageShortcutDialogActivity : AppActivity() {\n    private val args by args<EditExternalStorageShortcutDialogFragment.Args>()\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        // Calls ensureSubDecor().\n        findViewById<View>(android.R.id.content)\n        if (savedInstanceState == null) {\n            val fragment = EditExternalStorageShortcutDialogFragment().putArgs(args)\n            supportFragmentManager.commit {\n                add(fragment, EditExternalStorageShortcutDialogFragment::class.java.name)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/EditExternalStorageShortcutDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.app.Dialog\nimport android.content.DialogInterface\nimport android.os.Bundle\nimport android.view.WindowManager\nimport androidx.appcompat.app.AlertDialog\nimport androidx.appcompat.app.AppCompatDialogFragment\nimport androidx.core.widget.doAfterTextChanged\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport com.google.android.material.textfield.TextInputEditText\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.compat.DocumentsContractCompat\nimport me.zhanghai.android.files.databinding.EditExternalStorageShortcutDialogBinding\nimport me.zhanghai.android.files.file.ExternalStorageUri\nimport me.zhanghai.android.files.file.displayName\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.finish\nimport me.zhanghai.android.files.util.hideTextInputLayoutErrorOnTextChange\nimport me.zhanghai.android.files.util.layoutInflater\nimport me.zhanghai.android.files.util.setTextWithSelection\nimport me.zhanghai.android.files.util.takeIfNotEmpty\n\nclass EditExternalStorageShortcutDialogFragment : AppCompatDialogFragment() {\n    private val args by args<Args>()\n\n    private lateinit var binding: EditExternalStorageShortcutDialogBinding\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =\n        MaterialAlertDialogBuilder(requireContext(), theme)\n            .setTitle(R.string.storage_edit_external_storage_shortcut_title)\n            .apply {\n                binding = EditExternalStorageShortcutDialogBinding.inflate(context.layoutInflater)\n                val externalStorageShortcut = args.externalStorageShortcut\n                binding.rootIdEdit.hideTextInputLayoutErrorOnTextChange(binding.rootIdLayout)\n                binding.rootIdEdit.doAfterTextChanged { updateNamePlaceholder() }\n                binding.pathEdit.doAfterTextChanged { updateNamePlaceholder() }\n                if (savedInstanceState == null) {\n                    binding.nameEdit.setTextWithSelection(\n                        externalStorageShortcut.getName(binding.nameEdit.context)\n                    )\n                    val uri = externalStorageShortcut.uri\n                    binding.rootIdEdit.setText(uri.rootId)\n                    binding.pathEdit.setText(uri.path)\n                }\n                setView(binding.root)\n            }\n            .setPositiveButton(android.R.string.ok, null)\n            .setNegativeButton(android.R.string.cancel) { dialog, _ -> dialog.cancel() }\n            .setNeutralButton(R.string.remove) { _, _ -> remove() }\n            .create()\n            .apply {\n                window!!.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)\n                // Override the listener here so that we have control over when to close the dialog.\n                setOnShowListener {\n                    getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { save() }\n                }\n            }\n\n    private fun updateNamePlaceholder() {\n        val rootId = binding.rootIdEdit.text.toString().takeIfNotEmpty()\n            ?: DocumentsContractCompat.EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID\n        val path = binding.pathEdit.text.toString().dropWhile { it == '/' }\n        binding.nameLayout.placeholderText = ExternalStorageUri(rootId, path).displayName\n    }\n\n    private fun save() {\n        val externalStorageShortcut = getExternalStorageShortcutOrSetError() ?: return\n        Storages.replace(externalStorageShortcut)\n        finish()\n    }\n\n    private fun getExternalStorageShortcutOrSetError(): ExternalStorageShortcut? {\n        var errorEdit: TextInputEditText? = null\n        val customName = binding.nameEdit.text.toString()\n            .takeIf { it.isNotEmpty() && it != binding.nameLayout.placeholderText }\n        val rootId = binding.rootIdEdit.text.toString().takeIfNotEmpty()\n        if (rootId == null) {\n            binding.rootIdLayout.error =\n                getString(R.string.storage_edit_external_storage_shortcut_root_id_error_empty)\n            if (errorEdit == null) {\n                errorEdit = binding.rootIdEdit\n            }\n        }\n        val path = binding.pathEdit.text.toString().dropWhile { it == '/' }\n        val uri = rootId?.let { ExternalStorageUri(rootId, path) }\n        if (errorEdit != null) {\n            errorEdit.requestFocus()\n            return null\n        }\n        return ExternalStorageShortcut(args.externalStorageShortcut.id, customName, uri!!)\n    }\n\n    private fun remove() {\n        Storages.remove(args.externalStorageShortcut)\n        finish()\n    }\n\n    override fun onCancel(dialog: DialogInterface) {\n        super.onCancel(dialog)\n\n        finish()\n    }\n\n    @Parcelize\n    class Args(val externalStorageShortcut: ExternalStorageShortcut) : ParcelableArgs\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/EditFtpServerActivity.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.os.Bundle\nimport android.view.View\nimport androidx.fragment.app.commit\nimport me.zhanghai.android.files.app.AppActivity\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.putArgs\n\nclass EditFtpServerActivity : AppActivity() {\n    private val args by args<EditFtpServerFragment.Args>()\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        // Calls ensureSubDecor().\n        findViewById<View>(android.R.id.content)\n        if (savedInstanceState == null) {\n            val fragment = EditFtpServerFragment().putArgs(args)\n            supportFragmentManager.commit { add(android.R.id.content, fragment) }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/EditFtpServerFragment.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.app.Activity\nimport android.os.Bundle\nimport android.text.TextUtils\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.core.view.isVisible\nimport androidx.core.widget.doAfterTextChanged\nimport androidx.fragment.app.Fragment\nimport androidx.lifecycle.lifecycleScope\nimport com.google.android.material.textfield.TextInputEditText\nimport kotlinx.coroutines.launch\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.databinding.EditFtpServerFragmentBinding\nimport me.zhanghai.android.files.provider.ftp.client.Authority\nimport me.zhanghai.android.files.provider.ftp.client.Mode\nimport me.zhanghai.android.files.provider.ftp.client.Protocol\nimport me.zhanghai.android.files.ui.UnfilteredArrayAdapter\nimport me.zhanghai.android.files.util.ActionState\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.fadeToVisibilityUnsafe\nimport me.zhanghai.android.files.util.finish\nimport me.zhanghai.android.files.util.getTextArray\nimport me.zhanghai.android.files.util.hideTextInputLayoutErrorOnTextChange\nimport me.zhanghai.android.files.util.isReady\nimport me.zhanghai.android.files.util.setResult\nimport me.zhanghai.android.files.util.showToast\nimport me.zhanghai.android.files.util.takeIfNotEmpty\nimport me.zhanghai.android.files.util.viewModels\nimport java.net.URI\n\nclass EditFtpServerFragment : Fragment() {\n    private val args by args<Args>()\n\n    private val viewModel by viewModels { { EditFtpServerViewModel() } }\n\n    private lateinit var binding: EditFtpServerFragmentBinding\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        lifecycleScope.launchWhenStarted {\n            launch { viewModel.connectState.collect { onConnectStateChanged(it) } }\n        }\n    }\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View =\n        EditFtpServerFragmentBinding.inflate(inflater, container, false)\n            .also { binding = it }\n            .root\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n\n        val activity = requireActivity() as AppCompatActivity\n        activity.lifecycleScope.launchWhenCreated {\n            activity.setSupportActionBar(binding.toolbar)\n            activity.supportActionBar!!.setDisplayHomeAsUpEnabled(true)\n            activity.setTitle(\n                if (args.server != null) {\n                    R.string.storage_edit_ftp_server_title_edit\n                } else {\n                    R.string.storage_edit_ftp_server_title_add\n                }\n            )\n        }\n\n        binding.hostEdit.hideTextInputLayoutErrorOnTextChange(binding.hostLayout)\n        binding.hostEdit.doAfterTextChanged { updateNamePlaceholder() }\n        binding.portEdit.hideTextInputLayoutErrorOnTextChange(binding.portLayout)\n        binding.portEdit.doAfterTextChanged { updateNamePlaceholder() }\n        binding.pathEdit.doAfterTextChanged { updateNamePlaceholder() }\n        binding.protocolEdit.setAdapter(\n            UnfilteredArrayAdapter(\n                binding.protocolEdit.context, R.layout.dropdown_item,\n                objects = getTextArray(R.array.storage_edit_ftp_server_protocol_entries)\n            )\n        )\n        protocol = Protocol.FTP\n        binding.protocolEdit.doAfterTextChanged {\n            updateNamePlaceholder()\n            updatePortPlaceholder()\n        }\n        binding.authenticationTypeEdit.setAdapter(\n            UnfilteredArrayAdapter(\n                binding.authenticationTypeEdit.context, R.layout.dropdown_item,\n                objects = getTextArray(R.array.storage_edit_ftp_server_authentication_type_entries)\n            )\n        )\n        authenticationType = AuthenticationType.PASSWORD\n        binding.authenticationTypeEdit.doAfterTextChanged {\n            onAuthenticationTypeChanged(authenticationType)\n            updateNamePlaceholder()\n        }\n        binding.usernameEdit.hideTextInputLayoutErrorOnTextChange(binding.usernameLayout)\n        binding.usernameEdit.doAfterTextChanged { updateNamePlaceholder() }\n        binding.modeEdit.setAdapter(\n            UnfilteredArrayAdapter(\n                binding.modeEdit.context, R.layout.dropdown_item,\n                objects = getTextArray(R.array.storage_edit_ftp_server_mode_entries)\n            )\n        )\n        mode = Authority.DEFAULT_MODE\n        binding.encodingEdit.setAdapter(\n            UnfilteredArrayAdapter(\n                binding.encodingEdit.context, R.layout.dropdown_item,\n                objects = viewModel.charsets.map { it.displayName() }\n            )\n        )\n        encoding = Authority.DEFAULT_ENCODING\n        binding.saveOrConnectAndAddButton.setText(\n            if (args.server != null) {\n                R.string.save\n            } else {\n                R.string.storage_edit_ftp_server_connect_and_add\n            }\n        )\n        binding.saveOrConnectAndAddButton.setOnClickListener {\n            if (args.server != null) {\n                saveOrAdd()\n            } else {\n                connectAndAdd()\n            }\n        }\n        binding.cancelButton.setOnClickListener { finish() }\n        binding.removeOrAddButton.setText(\n            if (args.server != null) R.string.remove else R.string.storage_edit_ftp_server_add\n        )\n        binding.removeOrAddButton.setOnClickListener {\n            if (args.server != null) {\n                remove()\n            } else {\n                saveOrAdd()\n            }\n        }\n\n        if (savedInstanceState == null) {\n            val server = args.server\n            if (server != null) {\n                val authority = server.authority\n                binding.hostEdit.setText(authority.host)\n                protocol = authority.protocol\n                if (authority.port != protocol.defaultPort) {\n                    binding.portEdit.setText(authority.port.toString())\n                }\n                when {\n                    authority.username == Authority.ANONYMOUS_USERNAME\n                        && server.password == Authority.ANONYMOUS_PASSWORD ->\n                        authenticationType = AuthenticationType.ANONYMOUS\n                    else -> {\n                        authenticationType = AuthenticationType.PASSWORD\n                        binding.usernameEdit.setText(authority.username)\n                        binding.passwordEdit.setText(server.password)\n                    }\n                }\n                binding.pathEdit.setText(server.relativePath)\n                binding.nameEdit.setText(server.customName)\n                mode = authority.mode\n                encoding = authority.encoding\n            } else {\n                val host = args.host\n                if (host != null) {\n                    binding.hostEdit.setText(host)\n                }\n            }\n        }\n    }\n\n    private fun updateNamePlaceholder() {\n        val host = binding.hostEdit.text.toString().takeIfNotEmpty()\n        val port = binding.portEdit.text.toString().takeIfNotEmpty()?.toIntOrNull()\n            ?: protocol.defaultPort\n        val path = binding.pathEdit.text.toString().trim()\n        val username = when (authenticationType) {\n            AuthenticationType.PASSWORD -> binding.usernameEdit.text.toString()\n            AuthenticationType.ANONYMOUS -> Authority.ANONYMOUS_USERNAME\n        }\n        binding.nameLayout.placeholderText = if (host != null) {\n            val authority = Authority(protocol, host, port, username, mode, encoding)\n            if (path.isNotEmpty()) \"$authority/$path\" else authority.toString()\n        } else {\n            getString(R.string.storage_edit_ftp_server_name_placeholder)\n        }\n    }\n\n    private fun updatePortPlaceholder() {\n        binding.portLayout.placeholderText = protocol.defaultPort.toString()\n    }\n\n    private var protocol: Protocol\n        get() {\n            val adapter = binding.protocolEdit.adapter\n            val items = List(adapter.count) { adapter.getItem(it) as CharSequence }\n            val selectedItem = binding.protocolEdit.text\n            val selectedIndex = items.indexOfFirst { TextUtils.equals(it, selectedItem) }\n            return Protocol.entries[selectedIndex]\n        }\n        set(value) {\n            val adapter = binding.protocolEdit.adapter\n            val item = adapter.getItem(value.ordinal) as CharSequence\n            binding.protocolEdit.setText(item, false)\n        }\n\n    private var authenticationType: AuthenticationType\n        get() {\n            val adapter = binding.authenticationTypeEdit.adapter\n            val items = List(adapter.count) { adapter.getItem(it) as CharSequence }\n            val selectedItem = binding.authenticationTypeEdit.text\n            val selectedIndex = items.indexOfFirst { TextUtils.equals(it, selectedItem) }\n            return AuthenticationType.entries[selectedIndex]\n        }\n        set(value) {\n            val adapter = binding.authenticationTypeEdit.adapter\n            val item = adapter.getItem(value.ordinal) as CharSequence\n            binding.authenticationTypeEdit.setText(item, false)\n            onAuthenticationTypeChanged(value)\n        }\n\n    private fun onAuthenticationTypeChanged(authenticationType: AuthenticationType) {\n        binding.passwordAuthenticationLayout.isVisible =\n            authenticationType == AuthenticationType.PASSWORD\n    }\n\n    private var mode: Mode\n        get() {\n            val adapter = binding.modeEdit.adapter\n            val items = List(adapter.count) { adapter.getItem(it) as CharSequence }\n            val selectedItem = binding.modeEdit.text\n            val selectedIndex = items.indexOfFirst { TextUtils.equals(it, selectedItem) }\n            return Mode.entries[selectedIndex]\n        }\n        set(value) {\n            val adapter = binding.modeEdit.adapter\n            val item = adapter.getItem(value.ordinal) as CharSequence\n            binding.modeEdit.setText(item, false)\n        }\n\n    private var encoding: String\n        get() {\n            val adapter = binding.encodingEdit.adapter\n            val items = List(adapter.count) { adapter.getItem(it) as CharSequence }\n            val selectedItem = binding.encodingEdit.text\n            val selectedIndex = items.indexOfFirst { TextUtils.equals(it, selectedItem) }\n            return viewModel.charsets[selectedIndex].name()\n        }\n        set(value) {\n            val adapter = binding.encodingEdit.adapter\n            val item = adapter.getItem(viewModel.charsets.indexOfFirst { it.name() == value })\n                as CharSequence\n            binding.encodingEdit.setText(item, false)\n        }\n\n    private fun saveOrAdd() {\n        val server = getServerOrSetError() ?: return\n        Storages.addOrReplace(server)\n        setResult(Activity.RESULT_OK)\n        finish()\n    }\n\n    private fun connectAndAdd() {\n        if (!viewModel.connectState.value.isReady) {\n            return\n        }\n        val server = getServerOrSetError() ?: return\n        viewModel.connect(server)\n    }\n\n    private fun onConnectStateChanged(state: ActionState<FtpServer, Unit>) {\n        when (state) {\n            is ActionState.Ready, is ActionState.Running -> {\n                val isConnecting = state is ActionState.Running\n                binding.progress.fadeToVisibilityUnsafe(isConnecting)\n                binding.scrollView.fadeToVisibilityUnsafe(!isConnecting)\n                binding.saveOrConnectAndAddButton.isEnabled = !isConnecting\n                binding.removeOrAddButton.isEnabled = !isConnecting\n            }\n            is ActionState.Success -> {\n                Storages.addOrReplace(state.argument)\n                setResult(Activity.RESULT_OK)\n                finish()\n            }\n            is ActionState.Error -> {\n                val throwable = state.throwable\n                throwable.printStackTrace()\n                showToast(throwable.toString())\n                viewModel.finishConnecting()\n            }\n        }\n    }\n\n    private fun remove() {\n        Storages.remove(args.server!!)\n        setResult(Activity.RESULT_OK)\n        finish()\n    }\n\n    private fun getServerOrSetError(): FtpServer? {\n        var errorEdit: TextInputEditText? = null\n        val host = binding.hostEdit.text.toString().takeIfNotEmpty()\n            ?.let { URI::class.canonicalizeHost(it) }\n        if (host == null) {\n            binding.hostLayout.error = getString(R.string.storage_edit_ftp_server_host_error_empty)\n            if (errorEdit == null) {\n                errorEdit = binding.hostEdit\n            }\n        } else if (!URI::class.isValidHost(host)) {\n            binding.hostLayout.error =\n                getString(R.string.storage_edit_ftp_server_host_error_invalid)\n            if (errorEdit == null) {\n                errorEdit = binding.hostEdit\n            }\n        }\n        val port = binding.portEdit.text.toString().takeIfNotEmpty()\n            .let { if (it != null) it.toIntOrNull() else protocol.defaultPort }\n        if (port == null) {\n            binding.portLayout.error =\n                getString(R.string.storage_edit_ftp_server_port_error_invalid)\n            if (errorEdit == null) {\n                errorEdit = binding.portEdit\n            }\n        }\n        val path = binding.pathEdit.text.toString().trim()\n        val name = binding.nameEdit.text.toString().takeIfNotEmpty()\n        val username: String?\n        val password: String\n        when (authenticationType) {\n            AuthenticationType.PASSWORD -> {\n                username = binding.usernameEdit.text.toString().takeIfNotEmpty()\n                if (username == null) {\n                    binding.usernameLayout.error =\n                        getString(R.string.storage_edit_ftp_server_username_error_empty)\n                    if (errorEdit == null) {\n                        errorEdit = binding.usernameEdit\n                    }\n                }\n                password = binding.passwordEdit.text.toString()\n            }\n            AuthenticationType.ANONYMOUS -> {\n                username = Authority.ANONYMOUS_USERNAME\n                password = Authority.ANONYMOUS_PASSWORD\n            }\n        }\n        if (errorEdit != null) {\n            errorEdit.requestFocus()\n            return null\n        }\n        val authority = Authority(protocol, host!!, port!!, username!!, mode, encoding)\n        return FtpServer(args.server?.id, name, authority, password, path)\n    }\n\n    @Parcelize\n    class Args(\n        val server: FtpServer? = null,\n        val host: String? = null\n    ) : ParcelableArgs\n\n    private enum class AuthenticationType {\n        PASSWORD,\n        ANONYMOUS\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/EditFtpServerViewModel.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.asStateFlow\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.runInterruptible\nimport me.zhanghai.android.files.provider.common.newDirectoryStream\nimport me.zhanghai.android.files.util.ActionState\nimport me.zhanghai.android.files.util.isFinished\nimport me.zhanghai.android.files.util.isReady\nimport java.nio.charset.Charset\n\nclass EditFtpServerViewModel : ViewModel() {\n    val charsets = Charset.availableCharsets().values.toList()\n\n    private val _connectState = MutableStateFlow<ActionState<FtpServer, Unit>>(ActionState.Ready())\n    val connectState = _connectState.asStateFlow()\n\n    fun connect(server: FtpServer) {\n        viewModelScope.launch {\n            check(_connectState.value.isReady)\n            _connectState.value = ActionState.Running(server)\n            _connectState.value = try {\n                runInterruptible(Dispatchers.IO) {\n                    FtpServerAuthenticator.addTransientServer(server)\n                    try {\n                        val path = server.path\n                        path.fileSystem.use {\n                            path.newDirectoryStream().toList()\n                        }\n                    } finally {\n                        FtpServerAuthenticator.removeTransientServer(server)\n                    }\n                }\n                ActionState.Success(server, Unit)\n            } catch (e: Exception) {\n                ActionState.Error(server, e)\n            }\n        }\n    }\n\n    fun finishConnecting() {\n        viewModelScope.launch {\n            check(_connectState.value.isFinished)\n            _connectState.value = ActionState.Ready()\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/EditSftpServerActivity.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.os.Bundle\nimport android.view.View\nimport androidx.fragment.app.commit\nimport me.zhanghai.android.files.app.AppActivity\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.putArgs\n\nclass EditSftpServerActivity : AppActivity() {\n    private val args by args<EditSftpServerFragment.Args>()\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        // Calls ensureSubDecor().\n        findViewById<View>(android.R.id.content)\n        if (savedInstanceState == null) {\n            val fragment = EditSftpServerFragment().putArgs(args)\n            supportFragmentManager.commit { add(android.R.id.content, fragment) }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/EditSftpServerFragment.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.os.Bundle\nimport android.text.TextUtils\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.core.view.isVisible\nimport androidx.core.widget.doAfterTextChanged\nimport androidx.fragment.app.Fragment\nimport androidx.lifecycle.lifecycleScope\nimport com.google.android.material.textfield.TextInputEditText\nimport com.hierynomus.sshj.common.KeyDecryptionFailedException\nimport java8.nio.file.Path\nimport kotlinx.coroutines.launch\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.databinding.EditSftpServerFragmentBinding\nimport me.zhanghai.android.files.file.MimeType\nimport me.zhanghai.android.files.filelist.FileListActivity\nimport me.zhanghai.android.files.provider.sftp.client.Authority\nimport me.zhanghai.android.files.provider.sftp.client.PasswordAuthentication\nimport me.zhanghai.android.files.provider.sftp.client.PublicKeyAuthentication\nimport me.zhanghai.android.files.ui.UnfilteredArrayAdapter\nimport me.zhanghai.android.files.util.ActionState\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.fadeToVisibilityUnsafe\nimport me.zhanghai.android.files.util.finish\nimport me.zhanghai.android.files.util.getTextArray\nimport me.zhanghai.android.files.util.hideTextInputLayoutErrorOnTextChange\nimport me.zhanghai.android.files.util.isReady\nimport me.zhanghai.android.files.util.launchSafe\nimport me.zhanghai.android.files.util.showToast\nimport me.zhanghai.android.files.util.takeIfNotEmpty\nimport me.zhanghai.android.files.util.viewModels\nimport java.net.URI\n\nclass EditSftpServerFragment : Fragment() {\n    private val openPrivateKeyFileLauncher = registerForActivityResult(\n        FileListActivity.OpenFileContract(), this::onOpenPrivateKeyFileResult\n    )\n\n    private val args by args<Args>()\n\n    private val viewModel by viewModels { { EditSftpServerViewModel() } }\n\n    private lateinit var binding: EditSftpServerFragmentBinding\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        lifecycleScope.launchWhenStarted {\n            launch {\n                viewModel.readPrivateKeyFileState.collect { onReadPrivateKeyFileStateChanged(it) }\n            }\n            launch { viewModel.connectState.collect { onConnectStateChanged(it) } }\n        }\n    }\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View =\n        EditSftpServerFragmentBinding.inflate(inflater, container, false)\n            .also { binding = it }\n            .root\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n\n        val activity = requireActivity() as AppCompatActivity\n        activity.lifecycleScope.launchWhenCreated {\n            activity.setSupportActionBar(binding.toolbar)\n            activity.supportActionBar!!.setDisplayHomeAsUpEnabled(true)\n            activity.setTitle(\n                if (args.server != null) {\n                    R.string.storage_edit_sftp_server_title_edit\n                } else {\n                    R.string.storage_edit_sftp_server_title_add\n                }\n            )\n        }\n\n        binding.hostEdit.hideTextInputLayoutErrorOnTextChange(binding.hostLayout)\n        binding.hostEdit.doAfterTextChanged { updateNamePlaceholder() }\n        binding.portEdit.hideTextInputLayoutErrorOnTextChange(binding.portLayout)\n        binding.portEdit.doAfterTextChanged { updateNamePlaceholder() }\n        binding.pathEdit.doAfterTextChanged { updateNamePlaceholder() }\n        binding.authenticationTypeEdit.setAdapter(\n            UnfilteredArrayAdapter(\n                binding.authenticationTypeEdit.context, R.layout.dropdown_item,\n                objects = getTextArray(R.array.storage_edit_sftp_server_authentication_type_entries)\n            )\n        )\n        authenticationType = AuthenticationType.PASSWORD\n        binding.authenticationTypeEdit.doAfterTextChanged {\n            onAuthenticationTypeChanged(authenticationType)\n        }\n        binding.usernameEdit.hideTextInputLayoutErrorOnTextChange(binding.usernameLayout)\n        binding.usernameEdit.doAfterTextChanged { updateNamePlaceholder() }\n        binding.privateKeyLayout.setEndIconOnClickListener { onOpenPrivateKeyFile() }\n        binding.privateKeyEdit.hideTextInputLayoutErrorOnTextChange(\n            binding.privateKeyLayout, binding.privateKeyPasswordLayout\n        )\n        binding.privateKeyPasswordEdit.hideTextInputLayoutErrorOnTextChange(\n            binding.privateKeyLayout, binding.privateKeyPasswordLayout\n        )\n        binding.saveOrConnectAndAddButton.setText(\n            if (args.server != null) {\n                R.string.save\n            } else {\n                R.string.storage_edit_sftp_server_connect_and_add\n            }\n        )\n        binding.saveOrConnectAndAddButton.setOnClickListener {\n            if (args.server != null) {\n                saveOrAdd()\n            } else {\n                connectAndAdd()\n            }\n        }\n        binding.cancelButton.setOnClickListener { finish() }\n        binding.removeOrAddButton.setText(\n            if (args.server != null) R.string.remove else R.string.storage_edit_sftp_server_add\n        )\n        binding.removeOrAddButton.setOnClickListener {\n            if (args.server != null) {\n                remove()\n            } else {\n                saveOrAdd()\n            }\n        }\n\n        if (savedInstanceState == null) {\n            val server = args.server\n            if (server != null) {\n                val authority = server.authority\n                binding.hostEdit.setText(authority.host)\n                if (authority.port != Authority.DEFAULT_PORT) {\n                    binding.portEdit.setText(authority.port.toString())\n                }\n                binding.usernameEdit.setText(authority.username)\n                when (val authentication = server.authentication) {\n                    is PasswordAuthentication -> {\n                        authenticationType = AuthenticationType.PASSWORD\n                        binding.passwordEdit.setText(authentication.password)\n                    }\n                    is PublicKeyAuthentication -> {\n                        authenticationType = AuthenticationType.PUBLIC_KEY\n                        binding.privateKeyEdit.setText(authentication.privateKey)\n                        binding.privateKeyPasswordEdit.setText(authentication.privateKeyPassword)\n                    }\n                }\n                binding.pathEdit.setText(server.relativePath)\n                binding.nameEdit.setText(server.customName)\n            }\n        }\n    }\n\n    private fun updateNamePlaceholder() {\n        val host = binding.hostEdit.text.toString().takeIfNotEmpty()\n        val port = binding.portEdit.text.toString().takeIfNotEmpty()?.toIntOrNull()\n            ?: Authority.DEFAULT_PORT\n        val path = binding.pathEdit.text.toString().trim()\n        val username = binding.usernameEdit.text.toString()\n        binding.nameLayout.placeholderText = if (host != null) {\n            val authority = Authority(host, port, username)\n            if (path.isNotEmpty()) \"$authority/$path\" else authority.toString()\n        } else {\n            getString(R.string.storage_edit_sftp_server_name_placeholder)\n        }\n    }\n\n    private var authenticationType: AuthenticationType\n        get() {\n            val adapter = binding.authenticationTypeEdit.adapter\n            val items = List(adapter.count) { adapter.getItem(it) as CharSequence }\n            val selectedItem = binding.authenticationTypeEdit.text\n            val selectedIndex = items.indexOfFirst { TextUtils.equals(it, selectedItem) }\n            return AuthenticationType.entries[selectedIndex]\n        }\n        set(value) {\n            val adapter = binding.authenticationTypeEdit.adapter\n            val item = adapter.getItem(value.ordinal) as CharSequence\n            binding.authenticationTypeEdit.setText(item, false)\n            onAuthenticationTypeChanged(value)\n        }\n\n    private fun onAuthenticationTypeChanged(authenticationType: AuthenticationType) {\n        binding.passwordLayout.isVisible = authenticationType == AuthenticationType.PASSWORD\n        binding.publicKeyAuthenticationLayout.isVisible =\n            authenticationType == AuthenticationType.PUBLIC_KEY\n    }\n\n    private fun onOpenPrivateKeyFile() {\n        if (!viewModel.readPrivateKeyFileState.value.isReady) {\n            return\n        }\n        openPrivateKeyFileLauncher.launchSafe(listOf(MimeType.ANY), this)\n    }\n\n    private fun onOpenPrivateKeyFileResult(result: Path?) {\n        result ?: return\n        viewModel.readPrivateKeyFile(result)\n    }\n\n    private fun onReadPrivateKeyFileStateChanged(state: ActionState<Path, String>) {\n        when (state) {\n            is ActionState.Ready, is ActionState.Running -> {\n                val isReading = state is ActionState.Running\n                binding.privateKeyLayout.placeholderText =\n                    if (isReading) getString(R.string.loading) else null\n                if (isReading) {\n                    binding.privateKeyEdit.text = null\n                }\n            }\n            is ActionState.Success -> {\n                binding.privateKeyEdit.setText(state.result)\n                viewModel.finishReadingPrivateKeyFile()\n            }\n            is ActionState.Error -> {\n                val throwable = state.throwable\n                throwable.printStackTrace()\n                showToast(throwable.toString())\n                viewModel.finishReadingPrivateKeyFile()\n            }\n        }\n    }\n\n    private fun saveOrAdd() {\n        val server = getServerOrSetError() ?: return\n        Storages.addOrReplace(server)\n        finish()\n    }\n\n    private fun connectAndAdd() {\n        if (!viewModel.connectState.value.isReady) {\n            return\n        }\n        val server = getServerOrSetError() ?: return\n        viewModel.connect(server)\n    }\n\n    private fun onConnectStateChanged(state: ActionState<SftpServer, Unit>) {\n        when (state) {\n            is ActionState.Ready, is ActionState.Running -> {\n                val isConnecting = state is ActionState.Running\n                binding.progress.fadeToVisibilityUnsafe(isConnecting)\n                binding.scrollView.fadeToVisibilityUnsafe(!isConnecting)\n                binding.saveOrConnectAndAddButton.isEnabled = !isConnecting\n                binding.removeOrAddButton.isEnabled = !isConnecting\n            }\n            is ActionState.Success -> {\n                Storages.addOrReplace(state.argument)\n                finish()\n            }\n            is ActionState.Error -> {\n                val throwable = state.throwable\n                throwable.printStackTrace()\n                showToast(throwable.toString())\n                viewModel.finishConnecting()\n            }\n        }\n    }\n\n    private fun remove() {\n        Storages.remove(args.server!!)\n        finish()\n    }\n\n    private fun getServerOrSetError(): SftpServer? {\n        var errorEdit: TextInputEditText? = null\n        val host = binding.hostEdit.text.toString().takeIfNotEmpty()\n            ?.let { URI::class.canonicalizeHost(it) }\n        if (host == null) {\n            binding.hostLayout.error =\n                getString(R.string.storage_edit_sftp_server_host_error_empty)\n            if (errorEdit == null) {\n                errorEdit = binding.hostEdit\n            }\n        } else if (!URI::class.isValidHost(host)) {\n            binding.hostLayout.error =\n                getString(R.string.storage_edit_sftp_server_host_error_invalid)\n            if (errorEdit == null) {\n                errorEdit = binding.hostEdit\n            }\n        }\n        val port = binding.portEdit.text.toString().takeIfNotEmpty()\n            .let { if (it != null) it.toIntOrNull() else Authority.DEFAULT_PORT }\n        if (port == null) {\n            binding.portLayout.error = getString(R.string.storage_edit_sftp_server_port_error_invalid)\n            if (errorEdit == null) {\n                errorEdit = binding.portEdit\n            }\n        }\n        val path = binding.pathEdit.text.toString().trim()\n        val name = binding.nameEdit.text.toString().takeIfNotEmpty()\n        val username = binding.usernameEdit.text.toString().takeIfNotEmpty()\n        if (username == null) {\n            binding.usernameLayout.error =\n                getString(R.string.storage_edit_sftp_server_username_error_empty)\n            if (errorEdit == null) {\n                errorEdit = binding.usernameEdit\n            }\n        }\n        val authentication = when (authenticationType) {\n            AuthenticationType.PASSWORD -> {\n                val password = binding.passwordEdit.text.toString()\n                PasswordAuthentication(password)\n            }\n            AuthenticationType.PUBLIC_KEY -> {\n                val privateKey = binding.privateKeyEdit.text.toString().takeIfNotEmpty()\n                val privateKeyPassword =\n                    binding.privateKeyPasswordEdit.text.toString().takeIfNotEmpty()\n                if (privateKey == null) {\n                    binding.privateKeyLayout.error =\n                        getString(R.string.storage_edit_sftp_server_private_key_error_empty)\n                    if (errorEdit == null) {\n                        errorEdit = binding.privateKeyEdit\n                    }\n                } else {\n                    val exception = PublicKeyAuthentication.validate(privateKey, privateKeyPassword)\n                    if (exception != null) {\n                        exception.printStackTrace()\n                        if (exception is KeyDecryptionFailedException) {\n                            binding.privateKeyPasswordLayout.error = getString(\n                                R.string.storage_edit_sftp_server_private_key_password_error_invalid\n                            )\n                            if (errorEdit == null) {\n                                errorEdit = binding.privateKeyPasswordEdit\n                            }\n                        } else {\n                            binding.privateKeyLayout.error = getString(\n                                R.string.storage_edit_sftp_server_private_key_error_invalid\n                            )\n                            if (errorEdit == null) {\n                                errorEdit = binding.privateKeyEdit\n                            }\n                        }\n                    }\n                }\n                if (errorEdit == null) {\n                    PublicKeyAuthentication(privateKey!!, privateKeyPassword)\n                } else {\n                    null\n                }\n            }\n        }\n        if (errorEdit != null) {\n            errorEdit.requestFocus()\n            return null\n        }\n        val authority = Authority(host!!, port!!, username!!)\n        return SftpServer(args.server?.id, name, authority, authentication!!, path)\n    }\n\n    @Parcelize\n    class Args(val server: SftpServer? = null) : ParcelableArgs\n\n    private enum class AuthenticationType {\n        PASSWORD,\n        PUBLIC_KEY\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/EditSftpServerViewModel.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport java8.nio.file.Path\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.asStateFlow\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.runInterruptible\nimport me.zhanghai.android.files.provider.common.newDirectoryStream\nimport me.zhanghai.android.files.provider.common.readAllBytes\nimport me.zhanghai.android.files.provider.common.size\nimport me.zhanghai.android.files.util.ActionState\nimport me.zhanghai.android.files.util.isFinished\nimport me.zhanghai.android.files.util.isReady\nimport java.io.IOException\n\nclass EditSftpServerViewModel : ViewModel() {\n    private val _readPrivateKeyFileState =\n        MutableStateFlow<ActionState<Path, String>>(ActionState.Ready())\n    val readPrivateKeyFileState = _readPrivateKeyFileState.asStateFlow()\n\n    fun readPrivateKeyFile(file: Path) {\n        viewModelScope.launch {\n            check(_readPrivateKeyFileState.value.isReady)\n            _readPrivateKeyFileState.value = ActionState.Running(file)\n            _readPrivateKeyFileState.value = try {\n                val text = runInterruptible(Dispatchers.IO) {\n                    val size = file.size()\n                    if (size > MAX_PRIVATE_KEY_FILE_SIZE) {\n                        throw IOException(\"Private key file size $size is too large\")\n                    }\n                    val bytes = file.readAllBytes()\n                    String(bytes)\n                }\n                ActionState.Success(file, text)\n            } catch (e: Exception) {\n                ActionState.Error(file, e)\n            }\n        }\n    }\n\n    fun finishReadingPrivateKeyFile() {\n        viewModelScope.launch {\n            check(_readPrivateKeyFileState.value.isFinished)\n            _readPrivateKeyFileState.value = ActionState.Ready()\n        }\n    }\n\n    private val _connectState = MutableStateFlow<ActionState<SftpServer, Unit>>(ActionState.Ready())\n    val connectState = _connectState.asStateFlow()\n\n    fun connect(server: SftpServer) {\n        viewModelScope.launch {\n            check(_connectState.value.isReady)\n            _connectState.value = ActionState.Running(server)\n            _connectState.value = try {\n                runInterruptible(Dispatchers.IO) {\n                    SftpServerAuthenticator.addTransientServer(server)\n                    try {\n                        val path = server.path\n                        path.fileSystem.use {\n                            path.newDirectoryStream().toList()\n                        }\n                    } finally {\n                        SftpServerAuthenticator.removeTransientServer(server)\n                    }\n                }\n                ActionState.Success(server, Unit)\n            } catch (e: Exception) {\n                ActionState.Error(server, e)\n            }\n        }\n    }\n\n    fun finishConnecting() {\n        viewModelScope.launch {\n            check(_connectState.value.isFinished)\n            _connectState.value = ActionState.Ready()\n        }\n    }\n\n    companion object {\n        private const val MAX_PRIVATE_KEY_FILE_SIZE = 1024 * 1024.toLong()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/EditSmbServerActivity.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.app.Activity\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Bundle\nimport android.view.View\nimport androidx.activity.result.contract.ActivityResultContract\nimport androidx.fragment.app.commit\nimport me.zhanghai.android.files.app.AppActivity\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.createIntent\nimport me.zhanghai.android.files.util.putArgs\n\nclass EditSmbServerActivity : AppActivity() {\n    private val args by args<EditSmbServerFragment.Args>()\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        // Calls ensureSubDecor().\n        findViewById<View>(android.R.id.content)\n        if (savedInstanceState == null) {\n            val fragment = EditSmbServerFragment().putArgs(args)\n            supportFragmentManager.commit { add(android.R.id.content, fragment) }\n        }\n    }\n\n    class Contract : ActivityResultContract<EditSmbServerFragment.Args, Boolean>() {\n        override fun createIntent(context: Context, input: EditSmbServerFragment.Args): Intent =\n            EditSmbServerActivity::class.createIntent().putArgs(input)\n\n        override fun parseResult(resultCode: Int, intent: Intent?): Boolean =\n            resultCode == Activity.RESULT_OK\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/EditSmbServerFragment.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.app.Activity\nimport android.os.Bundle\nimport android.text.TextUtils\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.core.view.isVisible\nimport androidx.core.widget.doAfterTextChanged\nimport androidx.fragment.app.Fragment\nimport androidx.lifecycle.lifecycleScope\nimport com.google.android.material.textfield.TextInputEditText\nimport com.hierynomus.smbj.auth.AuthenticationContext\nimport kotlinx.coroutines.launch\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.databinding.EditSmbServerFragmentBinding\nimport me.zhanghai.android.files.provider.smb.client.Authority\nimport me.zhanghai.android.files.ui.UnfilteredArrayAdapter\nimport me.zhanghai.android.files.util.ActionState\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.fadeToVisibilityUnsafe\nimport me.zhanghai.android.files.util.finish\nimport me.zhanghai.android.files.util.getTextArray\nimport me.zhanghai.android.files.util.hideTextInputLayoutErrorOnTextChange\nimport me.zhanghai.android.files.util.isReady\nimport me.zhanghai.android.files.util.setResult\nimport me.zhanghai.android.files.util.showToast\nimport me.zhanghai.android.files.util.takeIfNotEmpty\nimport me.zhanghai.android.files.util.viewModels\nimport java.net.URI\n\nclass EditSmbServerFragment : Fragment() {\n    private val args by args<Args>()\n\n    private val viewModel by viewModels { { EditSmbServerViewModel() } }\n\n    private lateinit var binding: EditSmbServerFragmentBinding\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        lifecycleScope.launchWhenStarted {\n            launch { viewModel.connectState.collect { onConnectStateChanged(it) } }\n        }\n    }\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View =\n        EditSmbServerFragmentBinding.inflate(inflater, container, false)\n            .also { binding = it }\n            .root\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n\n        val activity = requireActivity() as AppCompatActivity\n        activity.lifecycleScope.launchWhenCreated {\n            activity.setSupportActionBar(binding.toolbar)\n            activity.supportActionBar!!.setDisplayHomeAsUpEnabled(true)\n            activity.setTitle(\n                if (args.server != null) {\n                    R.string.storage_edit_smb_server_title_edit\n                } else {\n                    R.string.storage_edit_smb_server_title_add\n                }\n            )\n        }\n\n        binding.hostEdit.hideTextInputLayoutErrorOnTextChange(binding.hostLayout)\n        binding.hostEdit.doAfterTextChanged { updateNamePlaceholder() }\n        binding.portEdit.hideTextInputLayoutErrorOnTextChange(binding.portLayout)\n        binding.portEdit.doAfterTextChanged { updateNamePlaceholder() }\n        binding.pathEdit.doAfterTextChanged { updateNamePlaceholder() }\n        binding.authenticationTypeEdit.setAdapter(\n            UnfilteredArrayAdapter(\n                binding.authenticationTypeEdit.context, R.layout.dropdown_item,\n                objects = getTextArray(R.array.storage_edit_smb_server_authentication_type_entries)\n            )\n        )\n        authenticationType = AuthenticationType.PASSWORD\n        binding.authenticationTypeEdit.doAfterTextChanged {\n            onAuthenticationTypeChanged(authenticationType)\n            updateNamePlaceholder()\n        }\n        binding.usernameEdit.hideTextInputLayoutErrorOnTextChange(binding.usernameLayout)\n        binding.usernameEdit.doAfterTextChanged { updateNamePlaceholder() }\n        binding.domainEdit.doAfterTextChanged { updateNamePlaceholder() }\n        binding.saveOrConnectAndAddButton.setText(\n            if (args.server != null) {\n                R.string.save\n            } else {\n                R.string.storage_edit_smb_server_connect_and_add\n            }\n        )\n        binding.saveOrConnectAndAddButton.setOnClickListener {\n            if (args.server != null) {\n                saveOrAdd()\n            } else {\n                connectAndAdd()\n            }\n        }\n        binding.cancelButton.setOnClickListener { finish() }\n        binding.removeOrAddButton.setText(\n            if (args.server != null) R.string.remove else R.string.storage_edit_smb_server_add\n        )\n        binding.removeOrAddButton.setOnClickListener {\n            if (args.server != null) {\n                remove()\n            } else {\n                saveOrAdd()\n            }\n        }\n\n        if (savedInstanceState == null) {\n            val server = args.server\n            if (server != null) {\n                val authority = server.authority\n                binding.hostEdit.setText(authority.host)\n                if (authority.port != Authority.DEFAULT_PORT) {\n                    binding.portEdit.setText(authority.port.toString())\n                }\n                when {\n                    AuthenticationContext.guest().let {\n                        authority.username == it.username && authority.domain == it.domain\n                                && server.password == it.password.concatToString()\n                    } -> authenticationType = AuthenticationType.GUEST\n                    AuthenticationContext.anonymous().let {\n                        authority.username == it.username && authority.domain == it.domain\n                                && server.password == it.password.concatToString()\n                    } -> authenticationType = AuthenticationType.ANONYMOUS\n                    else -> {\n                        authenticationType = AuthenticationType.PASSWORD\n                        binding.usernameEdit.setText(authority.username)\n                        binding.domainEdit.setText(authority.domain)\n                        binding.passwordEdit.setText(server.password)\n                    }\n                }\n                binding.pathEdit.setText(server.relativePath)\n                binding.nameEdit.setText(server.customName)\n            } else {\n                val host = args.host\n                if (host != null) {\n                    binding.hostEdit.setText(host)\n                }\n            }\n        }\n    }\n\n    private fun updateNamePlaceholder() {\n        val host = binding.hostEdit.text.toString().takeIfNotEmpty()\n        val port = binding.portEdit.text.toString().takeIfNotEmpty()?.toIntOrNull()\n            ?: Authority.DEFAULT_PORT\n        val path = binding.pathEdit.text.toString().trim()\n        val username: String\n        val domain: String?\n        when (authenticationType) {\n            AuthenticationType.PASSWORD -> {\n                username = binding.usernameEdit.text.toString()\n                domain = binding.domainEdit.text.toString().takeIfNotEmpty()\n            }\n            AuthenticationType.GUEST -> {\n                AuthenticationContext.guest().let {\n                    username = it.username\n                    domain = it.domain\n                }\n            }\n            AuthenticationType.ANONYMOUS -> {\n                AuthenticationContext.anonymous().let {\n                    username = it.username\n                    domain = it.domain\n                }\n            }\n        }\n        binding.nameLayout.placeholderText = if (host != null) {\n            val authority = Authority(host, port, username, domain)\n            if (path.isNotEmpty()) \"$authority/$path\" else authority.toString()\n        } else {\n            getString(R.string.storage_edit_smb_server_name_placeholder)\n        }\n    }\n\n    private var authenticationType: AuthenticationType\n        get() {\n            val adapter = binding.authenticationTypeEdit.adapter\n            val items = List(adapter.count) { adapter.getItem(it) as CharSequence }\n            val selectedItem = binding.authenticationTypeEdit.text\n            val selectedIndex = items.indexOfFirst { TextUtils.equals(it, selectedItem) }\n            return AuthenticationType.entries[selectedIndex]\n        }\n        set(value) {\n            val adapter = binding.authenticationTypeEdit.adapter\n            val item = adapter.getItem(value.ordinal) as CharSequence\n            binding.authenticationTypeEdit.setText(item, false)\n            onAuthenticationTypeChanged(value)\n        }\n\n    private fun onAuthenticationTypeChanged(authenticationType: AuthenticationType) {\n        binding.passwordAuthenticationLayout.isVisible =\n            authenticationType == AuthenticationType.PASSWORD\n    }\n\n    private fun saveOrAdd() {\n        val server = getServerOrSetError() ?: return\n        Storages.addOrReplace(server)\n        setResult(Activity.RESULT_OK)\n        finish()\n    }\n\n    private fun connectAndAdd() {\n        if (!viewModel.connectState.value.isReady) {\n            return\n        }\n        val server = getServerOrSetError() ?: return\n        viewModel.connect(server)\n    }\n\n    private fun onConnectStateChanged(state: ActionState<SmbServer, Unit>) {\n        when (state) {\n            is ActionState.Ready, is ActionState.Running -> {\n                val isConnecting = state is ActionState.Running\n                binding.progress.fadeToVisibilityUnsafe(isConnecting)\n                binding.scrollView.fadeToVisibilityUnsafe(!isConnecting)\n                binding.saveOrConnectAndAddButton.isEnabled = !isConnecting\n                binding.removeOrAddButton.isEnabled = !isConnecting\n            }\n            is ActionState.Success -> {\n                Storages.addOrReplace(state.argument)\n                setResult(Activity.RESULT_OK)\n                finish()\n            }\n            is ActionState.Error -> {\n                val throwable = state.throwable\n                throwable.printStackTrace()\n                showToast(throwable.toString())\n                viewModel.finishConnecting()\n            }\n        }\n    }\n\n    private fun remove() {\n        Storages.remove(args.server!!)\n        setResult(Activity.RESULT_OK)\n        finish()\n    }\n\n    private fun getServerOrSetError(): SmbServer? {\n        var errorEdit: TextInputEditText? = null\n        val host = binding.hostEdit.text.toString().takeIfNotEmpty()\n            ?.let { URI::class.canonicalizeHost(it) }\n        if (host == null) {\n            binding.hostLayout.error = getString(R.string.storage_edit_smb_server_host_error_empty)\n            if (errorEdit == null) {\n                errorEdit = binding.hostEdit\n            }\n        } else if (!URI::class.isValidHost(host)) {\n            binding.hostLayout.error =\n                getString(R.string.storage_edit_smb_server_host_error_invalid)\n            if (errorEdit == null) {\n                errorEdit = binding.hostEdit\n            }\n        }\n        val port = binding.portEdit.text.toString().takeIfNotEmpty()\n            .let { if (it != null) it.toIntOrNull() else Authority.DEFAULT_PORT }\n        if (port == null) {\n            binding.portLayout.error =\n                getString(R.string.storage_edit_smb_server_port_error_invalid)\n            if (errorEdit == null) {\n                errorEdit = binding.portEdit\n            }\n        }\n        val path = binding.pathEdit.text.toString().trim()\n        val name = binding.nameEdit.text.toString().takeIfNotEmpty()\n        val username: String?\n        val domain: String?\n        val password: String\n        when (authenticationType) {\n            AuthenticationType.PASSWORD -> {\n                username = binding.usernameEdit.text.toString().takeIfNotEmpty()\n                if (username == null) {\n                    binding.usernameLayout.error =\n                        getString(R.string.storage_edit_smb_server_username_error_empty)\n                    if (errorEdit == null) {\n                        errorEdit = binding.usernameEdit\n                    }\n                }\n                domain = binding.domainEdit.text.toString().takeIfNotEmpty()\n                password = binding.passwordEdit.text.toString()\n            }\n            AuthenticationType.GUEST -> {\n                AuthenticationContext.guest().let {\n                    username = it.username\n                    domain = it.domain\n                    password = it.password.concatToString()\n                }\n            }\n            AuthenticationType.ANONYMOUS -> {\n                AuthenticationContext.anonymous().let {\n                    username = it.username\n                    domain = it.domain\n                    password = it.password.concatToString()\n                }\n            }\n        }\n        if (errorEdit != null) {\n            errorEdit.requestFocus()\n            return null\n        }\n        val authority = Authority(host!!, port!!, username!!, domain)\n        return SmbServer(args.server?.id, name, authority, password, path)\n    }\n\n    @Parcelize\n    class Args(\n        val server: SmbServer? = null,\n        val host: String? = null\n    ) : ParcelableArgs\n\n    private enum class AuthenticationType {\n        PASSWORD,\n        GUEST,\n        ANONYMOUS\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/EditSmbServerViewModel.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.asStateFlow\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.runInterruptible\nimport me.zhanghai.android.files.provider.common.newDirectoryStream\nimport me.zhanghai.android.files.util.ActionState\nimport me.zhanghai.android.files.util.isFinished\nimport me.zhanghai.android.files.util.isReady\n\nclass EditSmbServerViewModel : ViewModel() {\n    private val _connectState = MutableStateFlow<ActionState<SmbServer, Unit>>(ActionState.Ready())\n    val connectState = _connectState.asStateFlow()\n\n    fun connect(server: SmbServer) {\n        viewModelScope.launch {\n            check(_connectState.value.isReady)\n            _connectState.value = ActionState.Running(server)\n            _connectState.value = try {\n                runInterruptible(Dispatchers.IO) {\n                    SmbServerAuthenticator.addTransientServer(server)\n                    try {\n                        val path = server.path\n                        path.fileSystem.use {\n                            path.newDirectoryStream().toList()\n                        }\n                    } finally {\n                        SmbServerAuthenticator.removeTransientServer(server)\n                    }\n                }\n                ActionState.Success(server, Unit)\n            } catch (e: Exception) {\n                ActionState.Error(server, e)\n            }\n        }\n    }\n\n    fun finishConnecting() {\n        viewModelScope.launch {\n            check(_connectState.value.isFinished)\n            _connectState.value = ActionState.Ready()\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/EditWebDavServerActivity.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.os.Bundle\nimport android.view.View\nimport androidx.fragment.app.commit\nimport me.zhanghai.android.files.app.AppActivity\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.putArgs\n\nclass EditWebDavServerActivity : AppActivity() {\n    private val args by args<EditWebDavServerFragment.Args>()\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        // Calls ensureSubDecor().\n        findViewById<View>(android.R.id.content)\n        if (savedInstanceState == null) {\n            val fragment = EditWebDavServerFragment().putArgs(args)\n            supportFragmentManager.commit { add(android.R.id.content, fragment) }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/EditWebDavServerFragment.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.app.Activity\nimport android.os.Bundle\nimport android.text.TextUtils\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.core.view.isVisible\nimport androidx.core.widget.doAfterTextChanged\nimport androidx.fragment.app.Fragment\nimport androidx.lifecycle.lifecycleScope\nimport com.google.android.material.textfield.TextInputEditText\nimport kotlinx.coroutines.launch\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.databinding.EditWebdavServerFragmentBinding\nimport me.zhanghai.android.files.provider.webdav.client.AccessTokenAuthentication\nimport me.zhanghai.android.files.provider.webdav.client.Authority\nimport me.zhanghai.android.files.provider.webdav.client.NoneAuthentication\nimport me.zhanghai.android.files.provider.webdav.client.PasswordAuthentication\nimport me.zhanghai.android.files.provider.webdav.client.Protocol\nimport me.zhanghai.android.files.ui.UnfilteredArrayAdapter\nimport me.zhanghai.android.files.util.ActionState\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.fadeToVisibilityUnsafe\nimport me.zhanghai.android.files.util.finish\nimport me.zhanghai.android.files.util.getTextArray\nimport me.zhanghai.android.files.util.hideTextInputLayoutErrorOnTextChange\nimport me.zhanghai.android.files.util.isReady\nimport me.zhanghai.android.files.util.setResult\nimport me.zhanghai.android.files.util.showToast\nimport me.zhanghai.android.files.util.takeIfNotEmpty\nimport me.zhanghai.android.files.util.viewModels\nimport java.net.URI\n\nclass EditWebDavServerFragment : Fragment() {\n    private val args by args<Args>()\n\n    private val viewModel by viewModels { { EditWebDavServerViewModel() } }\n\n    private lateinit var binding: EditWebdavServerFragmentBinding\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        lifecycleScope.launchWhenStarted {\n            launch { viewModel.connectState.collect { onConnectStateChanged(it) } }\n        }\n    }\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View =\n        EditWebdavServerFragmentBinding.inflate(inflater, container, false)\n            .also { binding = it }\n            .root\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n\n        val activity = requireActivity() as AppCompatActivity\n        activity.lifecycleScope.launchWhenCreated {\n            activity.setSupportActionBar(binding.toolbar)\n            activity.supportActionBar!!.setDisplayHomeAsUpEnabled(true)\n            activity.setTitle(\n                if (args.server != null) {\n                    R.string.storage_edit_webdav_server_title_edit\n                } else {\n                    R.string.storage_edit_webdav_server_title_add\n                }\n            )\n        }\n\n        binding.hostEdit.hideTextInputLayoutErrorOnTextChange(binding.hostLayout)\n        binding.hostEdit.doAfterTextChanged { updateNamePlaceholder() }\n        binding.portEdit.hideTextInputLayoutErrorOnTextChange(binding.portLayout)\n        binding.portEdit.doAfterTextChanged { updateNamePlaceholder() }\n        binding.pathEdit.doAfterTextChanged { updateNamePlaceholder() }\n        binding.protocolEdit.setAdapter(\n            UnfilteredArrayAdapter(\n                binding.protocolEdit.context, R.layout.dropdown_item,\n                objects = getTextArray(R.array.storage_edit_webdav_server_protocol_entries)\n            )\n        )\n        protocol = Protocol.DAVS\n        binding.protocolEdit.doAfterTextChanged {\n            updateNamePlaceholder()\n            updatePortPlaceholder()\n        }\n        binding.authenticationTypeEdit.setAdapter(\n            UnfilteredArrayAdapter(\n                binding.authenticationTypeEdit.context, R.layout.dropdown_item,\n                objects =\n                    getTextArray(R.array.storage_edit_webdav_server_authentication_type_entries)\n            )\n        )\n        authenticationType = AuthenticationType.PASSWORD\n        binding.authenticationTypeEdit.doAfterTextChanged {\n            onAuthenticationTypeChanged(authenticationType)\n            updateNamePlaceholder()\n        }\n        binding.usernameEdit.hideTextInputLayoutErrorOnTextChange(binding.usernameLayout)\n        binding.usernameEdit.doAfterTextChanged { updateNamePlaceholder() }\n        binding.saveOrConnectAndAddButton.setText(\n            if (args.server != null) {\n                R.string.save\n            } else {\n                R.string.storage_edit_webdav_server_connect_and_add\n            }\n        )\n        binding.saveOrConnectAndAddButton.setOnClickListener {\n            if (args.server != null) {\n                saveOrAdd()\n            } else {\n                connectAndAdd()\n            }\n        }\n        binding.cancelButton.setOnClickListener { finish() }\n        binding.removeOrAddButton.setText(\n            if (args.server != null) R.string.remove else R.string.storage_edit_webdav_server_add\n        )\n        binding.removeOrAddButton.setOnClickListener {\n            if (args.server != null) {\n                remove()\n            } else {\n                saveOrAdd()\n            }\n        }\n\n        if (savedInstanceState == null) {\n            val server = args.server\n            if (server != null) {\n                val authority = server.authority\n                binding.hostEdit.setText(authority.host)\n                protocol = authority.protocol\n                if (authority.port != protocol.defaultPort) {\n                    binding.portEdit.setText(authority.port.toString())\n                }\n                when (val authentication = server.authentication) {\n                    is PasswordAuthentication -> {\n                        authenticationType = AuthenticationType.PASSWORD\n                        binding.usernameEdit.setText(authority.username)\n                        binding.passwordEdit.setText(authentication.password)\n                    }\n                    is AccessTokenAuthentication -> {\n                        authenticationType = AuthenticationType.ACCESS_TOKEN\n                        binding.accessTokenEdit.setText(authentication.accessToken)\n                    }\n                    is NoneAuthentication -> authenticationType = AuthenticationType.NONE\n                }\n                binding.pathEdit.setText(server.relativePath)\n                binding.nameEdit.setText(server.customName)\n            } else {\n                val host = args.host\n                if (host != null) {\n                    binding.hostEdit.setText(host)\n                }\n            }\n        }\n    }\n\n    private fun updateNamePlaceholder() {\n        val host = binding.hostEdit.text.toString().takeIfNotEmpty()\n        val port = binding.portEdit.text.toString().takeIfNotEmpty()?.toIntOrNull()\n            ?: protocol.defaultPort\n        val path = binding.pathEdit.text.toString().trim()\n        val username = if (authenticationType == AuthenticationType.PASSWORD) {\n            binding.usernameEdit.text.toString()\n        } else {\n            \"\"\n        }\n        binding.nameLayout.placeholderText = if (host != null) {\n            val authority = Authority(protocol, host, port, username)\n            if (path.isNotEmpty()) \"$authority/$path\" else authority.toString()\n        } else {\n            getString(R.string.storage_edit_webdav_server_name_placeholder)\n        }\n    }\n\n    private fun updatePortPlaceholder() {\n        binding.portLayout.placeholderText = protocol.defaultPort.toString()\n    }\n\n    private var protocol: Protocol\n        get() {\n            val adapter = binding.protocolEdit.adapter\n            val items = List(adapter.count) { adapter.getItem(it) as CharSequence }\n            val selectedItem = binding.protocolEdit.text\n            val selectedIndex = items.indexOfFirst { TextUtils.equals(it, selectedItem) }\n            return Protocol.entries[selectedIndex]\n        }\n        set(value) {\n            val adapter = binding.protocolEdit.adapter\n            val item = adapter.getItem(value.ordinal) as CharSequence\n            binding.protocolEdit.setText(item, false)\n        }\n\n    private var authenticationType: AuthenticationType\n        get() {\n            val adapter = binding.authenticationTypeEdit.adapter\n            val items = List(adapter.count) { adapter.getItem(it) as CharSequence }\n            val selectedItem = binding.authenticationTypeEdit.text\n            val selectedIndex = items.indexOfFirst { TextUtils.equals(it, selectedItem) }\n            return AuthenticationType.entries[selectedIndex]\n        }\n        set(value) {\n            val adapter = binding.authenticationTypeEdit.adapter\n            val item = adapter.getItem(value.ordinal) as CharSequence\n            binding.authenticationTypeEdit.setText(item, false)\n            onAuthenticationTypeChanged(value)\n        }\n\n    private fun onAuthenticationTypeChanged(authenticationType: AuthenticationType) {\n        binding.passwordAuthenticationLayout.isVisible =\n            authenticationType == AuthenticationType.PASSWORD\n        binding.accessTokenLayout.isVisible = authenticationType == AuthenticationType.ACCESS_TOKEN\n    }\n\n    private fun saveOrAdd() {\n        val server = getServerOrSetError() ?: return\n        Storages.addOrReplace(server)\n        setResult(Activity.RESULT_OK)\n        finish()\n    }\n\n    private fun connectAndAdd() {\n        if (!viewModel.connectState.value.isReady) {\n            return\n        }\n        val server = getServerOrSetError() ?: return\n        viewModel.connect(server)\n    }\n\n    private fun onConnectStateChanged(state: ActionState<WebDavServer, Unit>) {\n        when (state) {\n            is ActionState.Ready, is ActionState.Running -> {\n                val isConnecting = state is ActionState.Running\n                binding.progress.fadeToVisibilityUnsafe(isConnecting)\n                binding.scrollView.fadeToVisibilityUnsafe(!isConnecting)\n                binding.saveOrConnectAndAddButton.isEnabled = !isConnecting\n                binding.removeOrAddButton.isEnabled = !isConnecting\n            }\n            is ActionState.Success -> {\n                Storages.addOrReplace(state.argument)\n                setResult(Activity.RESULT_OK)\n                finish()\n            }\n            is ActionState.Error -> {\n                val throwable = state.throwable\n                throwable.printStackTrace()\n                showToast(throwable.toString())\n                viewModel.finishConnecting()\n            }\n        }\n    }\n\n    private fun remove() {\n        Storages.remove(args.server!!)\n        setResult(Activity.RESULT_OK)\n        finish()\n    }\n\n    private fun getServerOrSetError(): WebDavServer? {\n        var errorEdit: TextInputEditText? = null\n        val host = binding.hostEdit.text.toString().takeIfNotEmpty()\n            ?.let { URI::class.canonicalizeHost(it) }\n        if (host == null) {\n            binding.hostLayout.error = getString(R.string.storage_edit_webdav_server_host_error_empty)\n            if (errorEdit == null) {\n                errorEdit = binding.hostEdit\n            }\n        } else if (!URI::class.isValidHost(host)) {\n            binding.hostLayout.error =\n                getString(R.string.storage_edit_webdav_server_host_error_invalid)\n            if (errorEdit == null) {\n                errorEdit = binding.hostEdit\n            }\n        }\n        val port = binding.portEdit.text.toString().takeIfNotEmpty()\n            .let { if (it != null) it.toIntOrNull() else protocol.defaultPort }\n        if (port == null) {\n            binding.portLayout.error =\n                getString(R.string.storage_edit_webdav_server_port_error_invalid)\n            if (errorEdit == null) {\n                errorEdit = binding.portEdit\n            }\n        }\n        val path = binding.pathEdit.text.toString().trim()\n        val name = binding.nameEdit.text.toString().takeIfNotEmpty()\n        val (username, authentication) = when (authenticationType) {\n            AuthenticationType.PASSWORD -> {\n                val username = binding.usernameEdit.text.toString().takeIfNotEmpty()\n                if (username == null) {\n                    binding.usernameLayout.error =\n                        getString(R.string.storage_edit_webdav_server_username_error_empty)\n                    if (errorEdit == null) {\n                        errorEdit = binding.usernameEdit\n                    }\n                }\n                val password = binding.passwordEdit.text.toString()\n                username to PasswordAuthentication(password)\n            }\n            AuthenticationType.ACCESS_TOKEN -> {\n                val accessToken = binding.accessTokenEdit.text.toString().takeIfNotEmpty()\n                if (accessToken == null) {\n                    binding.accessTokenLayout.error =\n                        getString(R.string.storage_edit_webdav_server_access_token_error_empty)\n                    if (errorEdit == null) {\n                        errorEdit = binding.accessTokenEdit\n                    }\n                }\n                \"\" to accessToken?.let { AccessTokenAuthentication(it) }\n            }\n            AuthenticationType.NONE -> \"\" to NoneAuthentication\n        }\n        if (errorEdit != null) {\n            errorEdit.requestFocus()\n            return null\n        }\n        val authority = Authority(protocol, host!!, port!!, username!!)\n        return WebDavServer(args.server?.id, name, authority, authentication!!, path)\n    }\n\n    @Parcelize\n    class Args(\n        val server: WebDavServer? = null,\n        val host: String? = null\n    ) : ParcelableArgs\n\n    private enum class AuthenticationType {\n        PASSWORD,\n        ACCESS_TOKEN,\n        NONE\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/EditWebDavServerViewModel.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.asStateFlow\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.runInterruptible\nimport me.zhanghai.android.files.provider.common.newDirectoryStream\nimport me.zhanghai.android.files.util.ActionState\nimport me.zhanghai.android.files.util.isFinished\nimport me.zhanghai.android.files.util.isReady\n\nclass EditWebDavServerViewModel : ViewModel() {\n    private val _connectState =\n        MutableStateFlow<ActionState<WebDavServer, Unit>>(ActionState.Ready())\n    val connectState = _connectState.asStateFlow()\n\n    fun connect(server: WebDavServer) {\n        viewModelScope.launch {\n            check(_connectState.value.isReady)\n            _connectState.value = ActionState.Running(server)\n            _connectState.value = try {\n                runInterruptible(Dispatchers.IO) {\n                    WebDavServerAuthenticator.addTransientServer(server)\n                    try {\n                        val path = server.path\n                        path.fileSystem.use {\n                            path.newDirectoryStream().toList()\n                        }\n                    } finally {\n                        WebDavServerAuthenticator.removeTransientServer(server)\n                    }\n                }\n                ActionState.Success(server, Unit)\n            } catch (e: Exception) {\n                ActionState.Error(server, e)\n            }\n        }\n    }\n\n    fun finishConnecting() {\n        viewModelScope.launch {\n            check(_connectState.value.isFinished)\n            _connectState.value = ActionState.Ready()\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/ExternalStorageShortcut.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.content.Context\nimport android.content.Intent\nimport java8.nio.file.Path\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.file.ExternalStorageUri\nimport me.zhanghai.android.files.file.displayName\nimport me.zhanghai.android.files.util.createDocumentsUiViewDirectoryIntent\nimport me.zhanghai.android.files.util.createIntent\nimport me.zhanghai.android.files.util.putArgs\nimport kotlin.random.Random\n\n@Parcelize\ndata class ExternalStorageShortcut(\n    override val id: Long,\n    override val customName: String?,\n    val uri: ExternalStorageUri\n) : Storage() {\n    constructor(\n        id: Long?,\n        customName: String?,\n        uri: ExternalStorageUri\n    ) : this(id ?: Random.nextLong(), customName, uri)\n\n    override fun getDefaultName(context: Context): String = uri.displayName\n\n    override val description: String\n        get() = uri.value.toString()\n\n    override val path: Path?\n        get() = null\n\n    override fun createIntent(): Intent = uri.value.createDocumentsUiViewDirectoryIntent()\n\n    override fun createEditIntent(): Intent =\n        EditExternalStorageShortcutDialogActivity::class.createIntent()\n            .putArgs(EditExternalStorageShortcutDialogFragment.Args(this))\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/FtpServer.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.content.Context\nimport android.content.Intent\nimport androidx.annotation.DrawableRes\nimport java8.nio.file.Path\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.provider.ftp.client.Authority\nimport me.zhanghai.android.files.provider.ftp.createFtpRootPath\nimport me.zhanghai.android.files.util.createIntent\nimport me.zhanghai.android.files.util.putArgs\nimport kotlin.random.Random\n\n@Parcelize\nclass FtpServer(\n    override val id: Long,\n    override val customName: String?,\n    val authority: Authority,\n    val password: String,\n    val relativePath: String\n) : Storage() {\n    constructor(\n        id: Long?,\n        customName: String?,\n        authority: Authority,\n        password: String,\n        relativePath: String\n    ) : this(id ?: Random.nextLong(), customName, authority, password, relativePath)\n\n    override val iconRes: Int\n        @DrawableRes\n        get() = R.drawable.computer_icon_white_24dp\n\n    override fun getDefaultName(context: Context): String =\n        if (relativePath.isNotEmpty()) \"$authority/$relativePath\" else authority.toString()\n\n    override val description: String\n        get() = authority.toString()\n\n    override val path: Path\n        get() = authority.createFtpRootPath().resolve(relativePath)\n\n    override fun createEditIntent(): Intent =\n        EditFtpServerActivity::class.createIntent().putArgs(EditFtpServerFragment.Args(this))\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/FtpServerAuthenticator.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport me.zhanghai.android.files.provider.ftp.client.Authenticator\nimport me.zhanghai.android.files.provider.ftp.client.Authority\nimport me.zhanghai.android.files.settings.Settings\nimport me.zhanghai.android.files.util.valueCompat\n\nobject FtpServerAuthenticator : Authenticator {\n    private val transientServers = mutableSetOf<FtpServer>()\n\n    override fun getPassword(authority: Authority): String? {\n        val server = synchronized(transientServers) {\n            transientServers.find { it.authority == authority }\n        } ?: Settings.STORAGES.valueCompat.find {\n            it is FtpServer && it.authority == authority\n        } as FtpServer?\n        return server?.password\n    }\n\n    fun addTransientServer(server: FtpServer) {\n        synchronized(transientServers) { transientServers += server }\n    }\n\n    fun removeTransientServer(server: FtpServer) {\n        synchronized(transientServers) { transientServers -= server }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/LanSmbServer.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport java.net.InetAddress\n\ndata class LanSmbServer(\n    val host: String,\n    val address: InetAddress\n) : Comparable<LanSmbServer> {\n    override fun compareTo(other: LanSmbServer): Int =\n        compareValuesBy(this, other, { it.address.hostAddress }, { it.host })\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/LanSmbServerListAdapter.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.view.ViewGroup\nimport androidx.recyclerview.widget.RecyclerView\nimport me.zhanghai.android.files.databinding.LanSmbServerItemBinding\nimport me.zhanghai.android.files.ui.SimpleAdapter\nimport me.zhanghai.android.files.util.layoutInflater\n\nclass LanSmbServerListAdapter(\n    val listener: (LanSmbServer) -> Unit\n) : SimpleAdapter<LanSmbServer, LanSmbServerListAdapter.ViewHolder>() {\n    override val hasStableIds: Boolean\n        get() = true\n\n    override fun getItemId(position: Int): Long = getItem(position).hashCode().toLong()\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder =\n        ViewHolder(LanSmbServerItemBinding.inflate(parent.context.layoutInflater, parent, false))\n\n    override fun onBindViewHolder(holder: ViewHolder, position: Int) {\n        val server = getItem(position)\n        val binding = holder.binding\n        binding.itemLayout.setOnClickListener { listener(server) }\n        binding.hostText.text = server.host\n        binding.addressText.text = server.address.hostAddress\n    }\n\n    class ViewHolder(val binding: LanSmbServerItemBinding) : RecyclerView.ViewHolder(binding.root)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/LanSmbServerListLiveData.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.os.AsyncTask\nimport jcifs.context.SingletonContext\nimport jcifs.smb.SmbException\nimport jcifs.smb.SmbFile\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.asCoroutineDispatcher\nimport kotlinx.coroutines.channels.ReceiveChannel\nimport kotlinx.coroutines.channels.consumeEach\nimport kotlinx.coroutines.channels.produce\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.runBlocking\nimport me.zhanghai.android.files.util.CloseableLiveData\nimport me.zhanghai.android.files.util.Failure\nimport me.zhanghai.android.files.util.Loading\nimport me.zhanghai.android.files.util.Stateful\nimport me.zhanghai.android.files.util.Success\nimport me.zhanghai.android.files.util.getLocalAddress\nimport me.zhanghai.android.files.util.toLinkedSet\nimport me.zhanghai.android.files.util.valueCompat\nimport java.net.Inet4Address\nimport java.net.InetAddress\nimport java.net.UnknownHostException\nimport java.util.concurrent.ExecutorService\nimport java.util.concurrent.Executors\nimport java.util.concurrent.Future\n\nclass LanSmbServerListLiveData : CloseableLiveData<Stateful<List<LanSmbServer>>>() {\n    private var loadFuture: Future<*>? = null\n\n    init {\n        loadValue()\n    }\n\n    fun loadValue() {\n        cancelLoadingValue()\n        value = Loading(value?.value)\n        loadFuture = (AsyncTask.THREAD_POOL_EXECUTOR as ExecutorService).submit {\n            try {\n                val newServerSet = mutableSetOf<LanSmbServer>()\n                Executors.newFixedThreadPool(60).asCoroutineDispatcher().use { dispatcher ->\n                    runBlocking(dispatcher) {\n                        val serverChannel = produce {\n                            launch {\n                                getServersByComputerBrowserService().consumeEach { send(it) }\n                            }\n                            launch {\n                                getServersByScanningSubnet().consumeEach { send(it) }\n                            }\n                        }\n                        serverChannel.consumeEach {\n                            // Use linked set to preserve UI stability.\n                            val serverSet = valueCompat.value?.toLinkedSet() ?: linkedSetOf()\n                            serverSet += it\n                            val servers = serverSet.toList()\n                            postValue(Loading(servers))\n                            newServerSet += it\n                        }\n                    }\n                }\n                // Remove old servers that aren't found any more.\n                val newServers = (valueCompat.value ?: emptyList()).toMutableList()\n                newServers.retainAll(newServerSet)\n                postValue(Success(newServers))\n            } catch (e: Exception) {\n                postValue(Failure(valueCompat.value, e))\n            }\n        }\n    }\n\n    // If a computer running recent Windows 10 is elected the master browser, it won't actually\n    // provide the service to others (jCIFS-NG NetServerEnumIterator gets\n    // ERROR_SERVICE_NOT_INSTALLED), as SMBv1 has been disabled. Windows now uses WS-Discovery, but\n    // it doesn't have a good standalone Java implementation and Samba doesn't support it.\n    // https://social.technet.microsoft.com/Forums/en-US/bd0af6aa-51ec-477a-8c81-888a4e60bd94/master-browser-service-broken-after-creator-update#2c6b9e65-da8a-4e41-a2cb-db086443ef87\n    // https://docs.microsoft.com/en-nz/windows-server/storage/file-server/troubleshoot/smbv1-not-installed-by-default-in-windows\n    private fun CoroutineScope.getServersByComputerBrowserService(\n    ): ReceiveChannel<LanSmbServer> =\n        produce {\n            launch {\n                @Suppress(\"DEPRECATION\")\n                val lan = SmbFile(\"smb://\")\n                val domains = try {\n                    lan.listFiles()\n                } catch (e: SmbException) {\n                    e.printStackTrace()\n                    return@launch\n                }\n                val nameServiceClient = SingletonContext.getInstance().nameServiceClient\n                for (domain in domains) {\n                    launch {\n                        val servers = try {\n                            domain.listFiles()\n                        } catch (e: SmbException) {\n                            e.printStackTrace()\n                            return@launch\n                        }\n                        for (server in servers) {\n                            launch {\n                                // Drop the trailing slash\n                                val host = server.name.dropLast(1)\n                                val address = try {\n                                    nameServiceClient.getByName(host).toInetAddress()\n                                } catch (e: UnknownHostException) {\n                                    e.printStackTrace()\n                                    return@launch\n                                }\n                                send(LanSmbServer(host, address))\n                            }\n                        }\n                    }\n                }\n            }\n        }\n\n    private fun CoroutineScope.getServersByScanningSubnet(): ReceiveChannel<LanSmbServer> =\n        produce {\n            launch {\n                val localAddress = InetAddress::class.getLocalAddress()\n                if (localAddress !is Inet4Address || !localAddress.isSiteLocalAddress) {\n                    return@launch\n                }\n                val nameServiceClient = SingletonContext.getInstance().nameServiceClient\n                for (address in localAddress.getSubnetAddresses()) {\n                    launch {\n                        val nbtAddresses = try {\n                            nameServiceClient.getNbtAllByAddress(address.hostAddress)\n                        } catch (e: UnknownHostException) {\n                            e.printStackTrace()\n                            return@launch\n                        }\n                        val host = nbtAddresses.firstOrNull()?.hostName ?: return@launch\n                        send(LanSmbServer(host, address))\n                    }\n                }\n            }\n        }\n\n    private fun Inet4Address.getSubnetAddresses(): Sequence<Inet4Address> =\n        sequence {\n            val addressBytes = address\n            for (i in 0..99) {\n                for (j in 0..2) {\n                    val lastBit = 100 * j + i\n                    if (lastBit > 255) {\n                        continue\n                    }\n                    addressBytes[3] = lastBit.toByte()\n                    yield(InetAddress.getByAddress(addressBytes) as Inet4Address)\n                }\n            }\n        }\n\n    override fun close() {\n        cancelLoadingValue()\n    }\n\n    private fun cancelLoadingValue() {\n        loadFuture?.cancel(true)\n        loadFuture = null\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/SftpServer.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.content.Context\nimport android.content.Intent\nimport androidx.annotation.DrawableRes\nimport java8.nio.file.Path\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.provider.sftp.client.Authentication\nimport me.zhanghai.android.files.provider.sftp.client.Authority\nimport me.zhanghai.android.files.provider.sftp.createSftpRootPath\nimport me.zhanghai.android.files.util.createIntent\nimport me.zhanghai.android.files.util.putArgs\nimport kotlin.random.Random\n\n@Parcelize\nclass SftpServer(\n    override val id: Long,\n    override val customName: String?,\n    val authority: Authority,\n    val authentication: Authentication,\n    val relativePath: String\n) : Storage() {\n    constructor(\n        id: Long?,\n        customName: String?,\n        authority: Authority,\n        authentication: Authentication,\n        relativePath: String\n    ) : this(id ?: Random.nextLong(), customName, authority, authentication, relativePath)\n\n    override val iconRes: Int\n        @DrawableRes\n        get() = R.drawable.computer_icon_white_24dp\n\n    override fun getDefaultName(context: Context): String =\n        if (relativePath.isNotEmpty()) \"$authority/$relativePath\" else authority.toString()\n\n    override val description: String\n        get() = authority.toString()\n\n    override val path: Path\n        get() = authority.createSftpRootPath().resolve(relativePath)\n\n    override fun createEditIntent(): Intent =\n        EditSftpServerActivity::class.createIntent().putArgs(EditSftpServerFragment.Args(this))\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/SftpServerAuthenticator.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport me.zhanghai.android.files.provider.sftp.client.Authentication\nimport me.zhanghai.android.files.provider.sftp.client.Authenticator\nimport me.zhanghai.android.files.provider.sftp.client.Authority\nimport me.zhanghai.android.files.settings.Settings\nimport me.zhanghai.android.files.util.valueCompat\n\nobject SftpServerAuthenticator : Authenticator {\n    private val transientServers = mutableSetOf<SftpServer>()\n\n    override fun getAuthentication(authority: Authority): Authentication? {\n        val server = synchronized(transientServers) {\n            transientServers.find { it.authority == authority }\n        } ?: Settings.STORAGES.valueCompat.find {\n            it is SftpServer && it.authority == authority\n        } as SftpServer?\n        return server?.authentication\n    }\n\n    fun addTransientServer(server: SftpServer) {\n        synchronized(transientServers) { transientServers += server }\n    }\n\n    fun removeTransientServer(server: SftpServer) {\n        synchronized(transientServers) { transientServers -= server }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/SmbServer.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.content.Context\nimport android.content.Intent\nimport androidx.annotation.DrawableRes\nimport java8.nio.file.Path\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.provider.smb.client.Authority\nimport me.zhanghai.android.files.provider.smb.createSmbRootPath\nimport me.zhanghai.android.files.util.createIntent\nimport me.zhanghai.android.files.util.putArgs\nimport kotlin.random.Random\n\n@Parcelize\nclass SmbServer(\n    override val id: Long,\n    override val customName: String?,\n    val authority: Authority,\n    val password: String,\n    val relativePath: String\n) : Storage() {\n    constructor(\n        id: Long?,\n        customName: String?,\n        authority: Authority,\n        password: String,\n        relativePath: String\n    ) : this(id ?: Random.nextLong(), customName, authority, password, relativePath)\n\n    override val iconRes: Int\n        @DrawableRes\n        get() = R.drawable.computer_icon_white_24dp\n\n    override fun getDefaultName(context: Context): String =\n        if (relativePath.isNotEmpty()) \"$authority/$relativePath\" else authority.toString()\n\n    override val description: String\n        get() = authority.toString()\n\n    override val path: Path\n        get() = authority.createSmbRootPath().resolve(relativePath)\n\n    override fun createEditIntent(): Intent =\n        EditSmbServerActivity::class.createIntent().putArgs(EditSmbServerFragment.Args(this))\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/SmbServerAuthenticator.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport me.zhanghai.android.files.provider.smb.client.Authenticator\nimport me.zhanghai.android.files.provider.smb.client.Authority\nimport me.zhanghai.android.files.settings.Settings\nimport me.zhanghai.android.files.util.valueCompat\n\nobject SmbServerAuthenticator : Authenticator {\n    private val transientServers = mutableSetOf<SmbServer>()\n\n    override fun getPassword(authority: Authority): String? {\n        val server = synchronized(transientServers) {\n            transientServers.find { it.authority == authority }\n        } ?: Settings.STORAGES.valueCompat.find {\n            it is SmbServer && it.authority == authority\n        } as SmbServer?\n        return server?.password\n    }\n\n    fun addTransientServer(server: SmbServer) {\n        synchronized(transientServers) { transientServers += server }\n    }\n\n    fun removeTransientServer(server: SmbServer) {\n        synchronized(transientServers) { transientServers -= server }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/Storage.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Parcelable\nimport androidx.annotation.DrawableRes\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.util.takeIfNotEmpty\n\nabstract class Storage : Parcelable {\n    abstract val id: Long\n\n    open val iconRes: Int\n        @DrawableRes\n        get() = R.drawable.directory_icon_white_24dp\n\n    abstract val customName: String?\n\n    abstract fun getDefaultName(context: Context): String\n\n    fun getName(context: Context): String = customName?.takeIfNotEmpty() ?: getDefaultName(context)\n\n    abstract val description: String\n\n    abstract val path: Path?\n\n    open val linuxPath: String? = null\n\n    open val isVisible: Boolean = true\n\n    open fun createIntent(): Intent? = null\n\n    abstract fun createEditIntent(): Intent\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/StorageListActivity.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.os.Bundle\nimport android.view.View\nimport androidx.fragment.app.add\nimport androidx.fragment.app.commit\nimport me.zhanghai.android.files.app.AppActivity\n\nclass StorageListActivity : AppActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        // Calls ensureSubDecor().\n        findViewById<View>(android.R.id.content)\n        if (savedInstanceState == null) {\n            supportFragmentManager.commit { add<StorageListFragment>(android.R.id.content) }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/StorageListAdapter.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.view.ViewGroup\nimport com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter\nimport com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange\nimport com.h6ah4i.android.widget.advrecyclerview.utils.AbstractDraggableItemViewHolder\nimport me.zhanghai.android.files.compat.foregroundCompat\nimport me.zhanghai.android.files.compat.isTransformedTouchPointInViewCompat\nimport me.zhanghai.android.files.databinding.StorageItemBinding\nimport me.zhanghai.android.files.ui.SimpleAdapter\nimport me.zhanghai.android.files.util.layoutInflater\n\nclass StorageListAdapter(\n    private val listener: Listener\n) : SimpleAdapter<Storage, StorageListAdapter.ViewHolder>(),\n    DraggableItemAdapter<StorageListAdapter.ViewHolder> {\n    override val hasStableIds: Boolean\n        get() = true\n\n    override fun getItemId(position: Int): Long = getItem(position).id\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder =\n        ViewHolder(\n            StorageItemBinding.inflate(parent.context.layoutInflater, parent, false)\n        )\n\n    override fun onBindViewHolder(holder: ViewHolder, position: Int) {\n        val storage = getItem(position)\n        val binding = holder.binding\n        // Need to remove the ripple before it's drawn onto the bitmap for dragging.\n        binding.root.foregroundCompat!!.mutate().setVisible(!holder.dragState.isActive, false)\n        binding.root.setOnClickListener { listener.editStorage(storage) }\n        binding.iconImage.setImageResource(storage.iconRes)\n        binding.nameText.isActivated = storage.isVisible\n        binding.nameText.text = storage.getName(binding.nameText.context)\n        binding.descriptionText.text = storage.description\n    }\n\n    override fun onCheckCanStartDrag(holder: ViewHolder, position: Int, x: Int, y: Int): Boolean =\n        (holder.binding.root as ViewGroup).isTransformedTouchPointInViewCompat(\n            x.toFloat(), y.toFloat(), holder.binding.dragHandleView, null\n        )\n\n    override fun onGetItemDraggableRange(holder: ViewHolder, position: Int): ItemDraggableRange? =\n        null\n\n    override fun onCheckCanDrop(draggingPosition: Int, dropPosition: Int): Boolean = true\n\n    override fun onItemDragStarted(position: Int) {\n        notifyDataSetChanged()\n    }\n\n    override fun onItemDragFinished(fromPosition: Int, toPosition: Int, result: Boolean) {\n        notifyDataSetChanged()\n    }\n\n    override fun onMoveItem(fromPosition: Int, toPosition: Int) {\n        if (fromPosition == toPosition) {\n            return\n        }\n        listener.moveStorage(fromPosition, toPosition)\n    }\n\n    class ViewHolder(val binding: StorageItemBinding) : AbstractDraggableItemViewHolder(\n        binding.root\n    )\n\n    interface Listener {\n        fun editStorage(storage: Storage)\n        fun moveStorage(fromPosition: Int, toPosition: Int)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/StorageListFragment.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.graphics.drawable.NinePatchDrawable\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.fragment.app.Fragment\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport androidx.recyclerview.widget.RecyclerView\nimport com.h6ah4i.android.widget.advrecyclerview.animator.DraggableItemAnimator\nimport com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager\nimport com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils\nimport me.zhanghai.android.files.databinding.StorageListFragmentBinding\nimport me.zhanghai.android.files.settings.Settings\nimport me.zhanghai.android.files.ui.ScrollingViewOnApplyWindowInsetsListener\nimport me.zhanghai.android.files.util.createIntent\nimport me.zhanghai.android.files.util.fadeToVisibilityUnsafe\nimport me.zhanghai.android.files.util.getDrawable\nimport me.zhanghai.android.files.util.startActivitySafe\n\nclass StorageListFragment : Fragment(), StorageListAdapter.Listener {\n    private lateinit var binding: StorageListFragmentBinding\n\n    private lateinit var adapter: StorageListAdapter\n    private lateinit var dragDropManager: RecyclerViewDragDropManager\n    private lateinit var wrappedAdapter: RecyclerView.Adapter<*>\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View =\n        StorageListFragmentBinding.inflate(inflater, container, false)\n            .also { binding = it }\n            .root\n\n    override fun onActivityCreated(savedInstanceState: Bundle?) {\n        super.onActivityCreated(savedInstanceState)\n\n        val activity = requireActivity() as AppCompatActivity\n        activity.setSupportActionBar(binding.toolbar)\n        activity.supportActionBar!!.setDisplayHomeAsUpEnabled(true)\n        binding.recyclerView.layoutManager = LinearLayoutManager(\n            activity, RecyclerView.VERTICAL, false\n        )\n        adapter = StorageListAdapter(this)\n        dragDropManager = RecyclerViewDragDropManager().apply {\n            setDraggingItemShadowDrawable(\n                getDrawable(\n                    com.h6ah4i.android.materialshadowninepatch.R.drawable.ms9_composite_shadow_z2\n                ) as NinePatchDrawable\n            )\n        }\n        wrappedAdapter = dragDropManager.createWrappedAdapter(adapter)\n        binding.recyclerView.adapter = wrappedAdapter\n        binding.recyclerView.itemAnimator = DraggableItemAnimator()\n        dragDropManager.attachRecyclerView(binding.recyclerView)\n        binding.recyclerView.setOnApplyWindowInsetsListener(\n            ScrollingViewOnApplyWindowInsetsListener(binding.recyclerView)\n        )\n        binding.fab.setOnClickListener { onAddStorage() }\n\n        Settings.STORAGES.observe(viewLifecycleOwner) { onStorageListChanged(it) }\n    }\n\n    override fun onPause() {\n        super.onPause()\n\n        dragDropManager.cancelDrag()\n    }\n\n    override fun onDestroyView() {\n        super.onDestroyView()\n\n        dragDropManager.release()\n        WrapperAdapterUtils.releaseAll(wrappedAdapter)\n    }\n\n    private fun onStorageListChanged(storages: List<Storage>) {\n        binding.emptyView.fadeToVisibilityUnsafe(storages.isEmpty())\n        adapter.replace(storages)\n    }\n\n    private fun onAddStorage() {\n        startActivitySafe(AddStorageDialogActivity::class.createIntent())\n    }\n\n    override fun editStorage(storage: Storage) {\n        startActivitySafe(storage.createEditIntent())\n    }\n\n    override fun moveStorage(fromPosition: Int, toPosition: Int) {\n        Storages.move(fromPosition, toPosition)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/StorageVolumeListLiveData.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.content.BroadcastReceiver\nimport android.content.ContentResolver\nimport android.content.Context\nimport android.content.Intent\nimport android.content.IntentFilter\nimport android.os.storage.StorageVolume\nimport androidx.core.content.ContextCompat\nimport androidx.lifecycle.LiveData\nimport me.zhanghai.android.files.app.application\nimport me.zhanghai.android.files.app.storageManager\nimport me.zhanghai.android.files.compat.registerReceiverCompat\nimport me.zhanghai.android.files.compat.storageVolumesCompat\n\nobject StorageVolumeListLiveData : LiveData<List<StorageVolume>>() {\n    init {\n        loadValue()\n        application.registerReceiverCompat(\n            object : BroadcastReceiver() {\n                override fun onReceive(context: Context, intent: Intent) {\n                    loadValue()\n                }\n            }, IntentFilter().apply {\n                // @see android.os.storage.VolumeInfo#sEnvironmentToBroadcast\n                addAction(Intent.ACTION_MEDIA_UNMOUNTED)\n                addAction(Intent.ACTION_MEDIA_CHECKING)\n                addAction(Intent.ACTION_MEDIA_MOUNTED)\n                addAction(Intent.ACTION_MEDIA_EJECT)\n                addAction(Intent.ACTION_MEDIA_UNMOUNTABLE)\n                addAction(Intent.ACTION_MEDIA_REMOVED)\n                addAction(Intent.ACTION_MEDIA_BAD_REMOVAL)\n                // The \"file\" data scheme is required to receive these broadcasts.\n                // @see https://stackoverflow.com/a/7143298\n                addDataScheme(ContentResolver.SCHEME_FILE)\n            }, ContextCompat.RECEIVER_NOT_EXPORTED\n        )\n    }\n\n    private fun loadValue() {\n        value = storageManager.storageVolumesCompat\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/Storages.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport me.zhanghai.android.files.settings.Settings\nimport me.zhanghai.android.files.util.removeFirst\nimport me.zhanghai.android.files.util.valueCompat\n\nobject Storages {\n    fun addOrReplace(storage: Storage) {\n        val storages = Settings.STORAGES.valueCompat.toMutableList().apply {\n            val index = indexOfFirst { it.id == storage.id }\n            if (index != -1) {\n                this[index] = storage\n            } else {\n                this += storage\n            }\n        }\n        Settings.STORAGES.putValue(storages)\n    }\n\n    fun replace(storage: Storage) {\n        val storages = Settings.STORAGES.valueCompat.toMutableList()\n            .apply { this[indexOfFirst { it.id == storage.id }] = storage }\n        Settings.STORAGES.putValue(storages)\n    }\n\n    fun move(fromPosition: Int, toPosition: Int) {\n        val bookmarkDirectories = Settings.STORAGES.valueCompat.toMutableList()\n            .apply { add(toPosition, removeAt(fromPosition)) }\n        Settings.STORAGES.putValue(bookmarkDirectories)\n    }\n\n    fun remove(storage: Storage) {\n        val bookmarkDirectories = Settings.STORAGES.valueCompat.toMutableList()\n            .apply { removeFirst { it.id == storage.id } }\n        Settings.STORAGES.putValue(bookmarkDirectories)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/URIExtensions.kt",
    "content": "package me.zhanghai.android.files.storage\n\nimport java.net.URI\nimport java.net.URISyntaxException\nimport kotlin.reflect.KClass\n\n// @see URI.appendAuthority\nfun KClass<URI>.canonicalizeHost(host: String): String =\n    if (host.contains(':') && !host.startsWith('[') && !host.endsWith(']')) \"[$host]\" else host\n\nfun KClass<URI>.createOrLog(uri: String): URI? =\n    try {\n        URI(uri)\n    } catch (e: URISyntaxException) {\n        e.printStackTrace()\n        null\n    }\n\nfun KClass<URI>.createOrLog(\n    scheme: String?,\n    userInfo: String?,\n    host: String?,\n    port: Int,\n    path: String?,\n    query: String?,\n    fragment: String?\n): URI? =\n    try {\n        URI(scheme, userInfo, host, port, path, query, fragment)\n    } catch (e: URISyntaxException) {\n        e.printStackTrace()\n        null\n    }\n\nfun KClass<URI>.isValidHost(host: String): Boolean =\n    try {\n        URI(null, null, host, -1, null, null, null)\n        true\n    } catch (e: URISyntaxException) {\n        false\n    }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/WebDavServer.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport android.content.Context\nimport android.content.Intent\nimport androidx.annotation.DrawableRes\nimport java8.nio.file.Path\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.provider.webdav.client.Authentication\nimport me.zhanghai.android.files.provider.webdav.client.Authority\nimport me.zhanghai.android.files.provider.webdav.createWebDavRootPath\nimport me.zhanghai.android.files.util.createIntent\nimport me.zhanghai.android.files.util.putArgs\nimport kotlin.random.Random\n\n@Parcelize\nclass WebDavServer(\n    override val id: Long,\n    override val customName: String?,\n    val authority: Authority,\n    val authentication: Authentication,\n    val relativePath: String\n) : Storage() {\n    constructor(\n        id: Long?,\n        customName: String?,\n        authority: Authority,\n        authentication: Authentication,\n        relativePath: String\n    ) : this(id ?: Random.nextLong(), customName, authority, authentication, relativePath)\n\n    override val iconRes: Int\n        @DrawableRes\n        get() = R.drawable.computer_icon_white_24dp\n\n    override fun getDefaultName(context: Context): String =\n        if (relativePath.isNotEmpty()) \"$authority/$relativePath\" else authority.toString()\n\n    override val description: String\n        get() = authority.toString()\n\n    override val path: Path\n        get() = authority.createWebDavRootPath().resolve(relativePath)\n\n    override fun createEditIntent(): Intent =\n        EditWebDavServerActivity::class.createIntent().putArgs(EditWebDavServerFragment.Args(this))\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/storage/WebDavServerAuthenticator.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.storage\n\nimport me.zhanghai.android.files.provider.webdav.client.Authentication\nimport me.zhanghai.android.files.provider.webdav.client.Authenticator\nimport me.zhanghai.android.files.provider.webdav.client.Authority\nimport me.zhanghai.android.files.settings.Settings\nimport me.zhanghai.android.files.util.valueCompat\n\nobject WebDavServerAuthenticator : Authenticator {\n    private val transientServers = mutableSetOf<WebDavServer>()\n\n    override fun getAuthentication(authority: Authority): Authentication? {\n        val server = synchronized(transientServers) {\n            transientServers.find { it.authority == authority }\n        } ?: Settings.STORAGES.valueCompat.find {\n            it is WebDavServer && it.authority == authority\n        } as WebDavServer?\n        return server?.authentication\n    }\n\n    fun addTransientServer(server: WebDavServer) {\n        synchronized(transientServers) { transientServers += server }\n    }\n\n    fun removeTransientServer(server: WebDavServer) {\n        synchronized(transientServers) { transientServers -= server }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/terminal/Terminal.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.terminal\n\nimport android.content.ComponentName\nimport android.content.Context\nimport android.content.Intent\nimport android.net.Uri\nimport me.zhanghai.android.files.app.packageManager\nimport me.zhanghai.android.files.util.startActivitySafe\n\nobject Terminal {\n    fun open(path: String, context: Context) {\n        val componentName =\n            packageManager.queryIntentActivities(Intent(Intent.ACTION_SEND).setType(\"*/*\"), 0)\n                .firstOrNull { it.activityInfo.name.endsWith(\".TermHere\") }?.activityInfo\n                ?.let { ComponentName(it.packageName, it.name) }\n                ?: ComponentName(\"jackpal.androidterm\", \"jackpal.androidterm.TermHere\")\n        val intent = Intent()\n            .setComponent(componentName)\n            .setAction(Intent.ACTION_SEND)\n            .putExtra(Intent.EXTRA_STREAM, Uri.parse(path))\n        context.startActivitySafe(intent)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/theme/custom/CustomThemeHelper.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.theme.custom\n\nimport android.app.Activity\nimport android.app.Application\nimport android.content.Context\nimport android.os.Bundle\nimport androidx.annotation.StyleRes\nimport androidx.appcompat.app.AppCompatActivity\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.compat.recreateCompat\nimport me.zhanghai.android.files.compat.setThemeCompat\nimport me.zhanghai.android.files.compat.themeResIdCompat\nimport me.zhanghai.android.files.settings.Settings\nimport me.zhanghai.android.files.theme.night.NightModeHelper\nimport me.zhanghai.android.files.util.SimpleActivityLifecycleCallbacks\nimport me.zhanghai.android.files.util.valueCompat\n\nobject CustomThemeHelper {\n    private val activityBaseThemes = mutableMapOf<Activity, Int>()\n\n    fun initialize(application: Application) {\n        application.registerActivityLifecycleCallbacks(object : SimpleActivityLifecycleCallbacks {\n            override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {\n                check(activityBaseThemes.containsKey(activity)) {\n                    \"Activity must extend AppActivity: $activity\"\n                }\n            }\n\n            override fun onActivityDestroyed(activity: Activity) {\n                activityBaseThemes.remove(activity)\n            }\n        })\n    }\n\n    fun apply(activity: Activity) {\n        val baseThemeRes = activity.themeResIdCompat\n        activityBaseThemes[activity] = baseThemeRes\n        val customThemeRes = getCustomThemeRes(baseThemeRes, activity)\n        activity.setThemeCompat(customThemeRes)\n    }\n\n    fun sync() {\n        for ((activity, baseThemeRes) in activityBaseThemes) {\n            val currentThemeRes = activity.themeResIdCompat\n            val customThemeRes = getCustomThemeRes(baseThemeRes, activity)\n            if (currentThemeRes != customThemeRes) {\n                // Ignore \".Black\" theme changes when not in night mode.\n                if (!NightModeHelper.isInNightMode(activity as AppCompatActivity)\n                    && isBlackThemeChange(currentThemeRes, customThemeRes, activity)) {\n                    continue\n                }\n                if (activity is OnThemeChangedListener) {\n                    (activity as OnThemeChangedListener).onThemeChanged(customThemeRes)\n                } else {\n                    activity.recreateCompat()\n                }\n            }\n        }\n    }\n\n    private fun getCustomThemeRes(@StyleRes baseThemeRes: Int, context: Context): Int {\n        val resources = context.resources\n        val baseThemeName = resources.getResourceName(baseThemeRes)\n        val customThemeName = if (Settings.MATERIAL_DESIGN_3.valueCompat) {\n            val defaultThemeName = resources.getResourceEntryName(R.style.Theme_MaterialFiles)\n            val material3ThemeName =\n                resources.getResourceEntryName(R.style.Theme_MaterialFiles_Material3)\n            baseThemeName.replace(defaultThemeName, material3ThemeName)\n        } else {\n            val themeColorName =\n                resources.getResourceEntryName(Settings.THEME_COLOR.valueCompat.resourceId)\n            \"$baseThemeName.$themeColorName\"\n        } + if (Settings.BLACK_NIGHT_MODE.valueCompat) \".Black\" else \"\"\n        return resources.getIdentifier(customThemeName, null, null)\n    }\n\n    private fun isBlackThemeChange(\n        @StyleRes themeRes1: Int,\n        @StyleRes themeRes2: Int,\n        context: Context\n    ): Boolean {\n        val resources = context.resources\n        val themeName1 = resources.getResourceName(themeRes1)\n        val themeName2 = resources.getResourceName(themeRes2)\n        return themeName1 == \"$themeName2.Black\" || themeName2 == \"$themeName1.Black\"\n    }\n\n    interface OnThemeChangedListener {\n        fun onThemeChanged(@StyleRes theme: Int)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/theme/custom/ThemeColor.kt",
    "content": "package me.zhanghai.android.files.theme.custom\n\nimport androidx.annotation.ColorRes\nimport me.zhanghai.android.files.R\n\nenum class ThemeColor(@ColorRes val resourceId: Int) {\n    COLOR_PRIMARY(R.color.color_primary),\n    MATERIAL_RED(R.color.material_red),\n    MATERIAL_PINK(R.color.material_pink),\n    MATERIAL_PURPLE(R.color.material_purple),\n    MATERIAL_DEEP_PURPLE(R.color.material_deep_purple),\n    MATERIAL_INDIGO(R.color.material_indigo),\n    MATERIAL_BLUE(R.color.material_blue),\n    MATERIAL_LIGHT_BLUE(R.color.material_light_blue),\n    MATERIAL_CYAN(R.color.material_cyan),\n    MATERIAL_TEAL(R.color.material_teal),\n    MATERIAL_GREEN(R.color.material_green),\n    MATERIAL_LIGHT_GREEN(R.color.material_light_green),\n    MATERIAL_LIME(R.color.material_lime),\n    MATERIAL_YELLOW(R.color.material_yellow),\n    MATERIAL_AMBER(R.color.material_amber),\n    MATERIAL_ORANGE(R.color.material_orange),\n    MATERIAL_DEEP_ORANGE(R.color.material_deep_orange),\n    MATERIAL_BROWN(R.color.material_brown),\n    MATERIAL_GREY(R.color.material_grey),\n    MATERIAL_BLUE_GREY(R.color.material_blue_grey)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/theme/custom/ThemeColorPreference.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.theme.custom\n\nimport android.content.Context\nimport android.content.res.TypedArray\nimport android.util.AttributeSet\nimport androidx.annotation.AttrRes\nimport androidx.annotation.ColorInt\nimport androidx.annotation.StyleRes\nimport com.takisoft.preferencex.PreferenceFragmentCompat\nimport me.zhanghai.android.files.colorpicker.BaseColorPreference\nimport me.zhanghai.android.files.colorpicker.ColorPreferenceDialogFragment\nimport me.zhanghai.android.files.compat.getColorCompat\n\nclass ThemeColorPreference : BaseColorPreference {\n    private lateinit var _stringValue: String\n    var stringValue: String\n        get() = _stringValue\n        set(value) {\n            _stringValue = value\n            persistString(value)\n            notifyChanged()\n        }\n\n    // We can't use lateinit for Int.\n    private var initialValue: Int? = null\n    override var value: Int\n        // Deliberately only bind for the initial value, because we are going to restart the\n        // activity upon change and we want to let the activity animation have the correct visual\n        // appearance.\n        @ColorInt\n        get() {\n            var initialValue = initialValue\n            if (initialValue == null) {\n                initialValue = entryValues[stringValue.toInt()]\n                this.initialValue = initialValue\n            }\n            return initialValue\n        }\n        set(value) {\n            stringValue = entryValues.indexOf(value).toString()\n        }\n\n    private lateinit var defaultStringValue: String\n    override val defaultValue: Int\n        @ColorInt\n        get() = entryValues[defaultStringValue.toInt()]\n\n    override var entryValues: IntArray\n        private set\n\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    constructor(\n        context: Context,\n        attrs: AttributeSet?,\n        @AttrRes defStyleAttr: Int,\n        @StyleRes defStyleRes: Int\n    ) : super(context, attrs, defStyleAttr, defStyleRes)\n\n    init {\n        val context = context\n        entryValues = ThemeColor.entries.map { context.getColorCompat(it.resourceId) }.toIntArray()\n    }\n\n    override fun onGetDefaultValue(a: TypedArray, index: Int): Any? =\n        a.getString(index).also { defaultStringValue = it!! }\n\n    override fun onSetInitialValue(defaultValue: Any?) {\n        stringValue = getPersistedString(defaultValue as String?)\n    }\n\n    companion object {\n        init {\n            PreferenceFragmentCompat.registerPreferenceFragment(\n                ThemeColorPreference::class.java, ColorPreferenceDialogFragment::class.java\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/theme/night/NightMode.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.theme.night\n\nimport androidx.appcompat.app.AppCompatDelegate\n\nenum class NightMode(val value: Int) {\n    FOLLOW_SYSTEM(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM),\n    OFF(AppCompatDelegate.MODE_NIGHT_NO),\n    ON(AppCompatDelegate.MODE_NIGHT_YES),\n    AUTO_TIME(AppCompatDelegate.MODE_NIGHT_AUTO_TIME),\n    AUTO_BATTERY(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/theme/night/NightModeHelper.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.theme.night\n\nimport android.app.Activity\nimport android.app.Application\nimport android.content.res.Configuration\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.appcompat.app.AppCompatDelegate\nimport androidx.appcompat.app.AppCompatDelegateCompat\nimport me.zhanghai.android.files.app.application\nimport me.zhanghai.android.files.settings.Settings\nimport me.zhanghai.android.files.util.SimpleActivityLifecycleCallbacks\nimport me.zhanghai.android.files.util.valueCompat\n\n// We take over the activity creation when setting the default night mode from AppCompat so that:\n// 1. We can recreate all activities upon change, instead of only started activities.\n// 2. We can have custom handling of the change, instead of being forced to either recreate or\n//    update resources configuration which is shared among activities.\nobject NightModeHelper {\n    private val activities = mutableSetOf<AppCompatActivity>()\n\n    fun initialize(application: Application) {\n        application.registerActivityLifecycleCallbacks(object : SimpleActivityLifecycleCallbacks {\n                override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {\n                    check(activity in activities) { \"Activity must extend AppActivity: $activity\" }\n                }\n\n                override fun onActivityDestroyed(activity: Activity) {\n                    activities -= activity as AppCompatActivity\n                }\n            })\n    }\n\n    fun apply(activity: AppCompatActivity) {\n        activities += activity\n        activity.delegate.localNightMode = nightMode\n    }\n\n    fun sync() {\n        for (activity in activities) {\n            val nightMode = nightMode\n            if (activity is OnNightModeChangedListener) {\n                if (getUiModeNight(activity.delegate.localNightMode, activity)\n                    != getUiModeNight(nightMode, activity)) {\n                    activity.onNightModeChangedFromHelper(nightMode)\n                }\n            } else {\n                activity.delegate.localNightMode = nightMode\n            }\n        }\n    }\n\n    private val nightMode: Int\n        get() = Settings.NIGHT_MODE.valueCompat.value\n\n    /*\n     * @see androidx.appcompat.app.AppCompatDelegateImpl#updateForNightMode(int, boolean)\n     */\n    private fun getUiModeNight(nightMode: Int, activity: AppCompatActivity): Int =\n        when (AppCompatDelegateCompat.mapNightMode(activity.delegate, application, nightMode)) {\n            AppCompatDelegate.MODE_NIGHT_YES -> Configuration.UI_MODE_NIGHT_YES\n            AppCompatDelegate.MODE_NIGHT_NO -> Configuration.UI_MODE_NIGHT_NO\n            else ->\n                (activity.applicationContext.resources.configuration.uiMode\n                    and Configuration.UI_MODE_NIGHT_MASK)\n        }\n\n    fun isInNightMode(activity: AppCompatActivity): Boolean =\n        (getUiModeNight(activity.delegate.localNightMode, activity)\n            == Configuration.UI_MODE_NIGHT_YES)\n\n    interface OnNightModeChangedListener {\n        fun onNightModeChangedFromHelper(nightMode: Int)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/AllowSoftInputHackAlertDialogCustomView.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.View\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport androidx.core.view.isGone\n\nclass AllowSoftInputHackAlertDialogCustomView @JvmOverloads constructor(\n    context: Context,\n    attrs: AttributeSet? = null,\n    @AttrRes defStyleAttr: Int = 0,\n    @StyleRes defStyleRes: Int = 0\n) : View(context, attrs, defStyleAttr, defStyleRes) {\n    override fun onCheckIsTextEditor(): Boolean = true\n\n    // Called once during ViewGroup.addView().\n    override fun hasFocus(): Boolean {\n        // Makes hasCustomPanel false in AlertController.setupView().\n        (parent.parent as View).isGone = true\n        return super.hasFocus()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/AnimatedListAdapter.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.os.Handler\nimport android.os.Looper\nimport androidx.recyclerview.widget.DiffUtil\nimport androidx.recyclerview.widget.RecyclerView\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.util.getAnimation\n\nabstract class AnimatedListAdapter<T, VH : RecyclerView.ViewHolder>(\n    callback: DiffUtil.ItemCallback<T>\n) : ListAdapter<T, VH>(callback) {\n    private var isAnimating = false\n\n    private var animationStartOffset = 0\n\n    private val stopAnimationHandler = Handler(Looper.getMainLooper())\n    private val stopAnimationRunnable = Runnable { stopAnimation() }\n\n    private val clearAnimationListener = object : RecyclerView.OnScrollListener() {\n        override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {\n            clearAnimation()\n        }\n    }\n\n    private var recyclerView: RecyclerView? = null\n\n    override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {\n        super.onAttachedToRecyclerView(recyclerView)\n\n        this.recyclerView = recyclerView\n        recyclerView.addOnScrollListener(clearAnimationListener)\n    }\n\n    override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {\n        super.onDetachedFromRecyclerView(recyclerView)\n\n        recyclerView.removeOnScrollListener(clearAnimationListener)\n        this.recyclerView = null\n    }\n\n    override fun refresh() {\n        resetAnimation()\n        super.refresh()\n    }\n\n    override fun replace(list: List<T>, clear: Boolean) {\n        if (clear) {\n            resetAnimation()\n        }\n        super.replace(list, clear)\n    }\n\n    override fun clear() {\n        resetAnimation()\n        super.clear()\n    }\n\n    protected fun bindViewHolderAnimation(holder: VH) {\n        holder.itemView.clearAnimation()\n        if (isAnimating) {\n            val animation = holder.itemView.context.getAnimation(R.anim.list_item)\n                .apply { startOffset = animationStartOffset.toLong() }\n            animationStartOffset += ANIMATION_STAGGER_MILLIS\n            holder.itemView.startAnimation(animation)\n            postStopAnimation()\n        }\n    }\n\n    private fun stopAnimation() {\n        stopAnimationHandler.removeCallbacks(stopAnimationRunnable)\n        isAnimating = false\n        animationStartOffset = 0\n    }\n\n    private fun postStopAnimation() {\n        stopAnimationHandler.removeCallbacks(stopAnimationRunnable)\n        stopAnimationHandler.post(stopAnimationRunnable)\n    }\n\n    private fun clearAnimation() {\n        stopAnimation()\n        recyclerView?.let {\n            for (index in 0..<it.childCount) {\n                it.getChildAt(index).clearAnimation()\n            }\n        }\n    }\n\n    private fun resetAnimation() {\n        clearAnimation()\n        isAnimating = isAnimationEnabled\n    }\n\n    protected open val isAnimationEnabled: Boolean\n        get() = true\n\n    companion object {\n        private const val ANIMATION_STAGGER_MILLIS = 20\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/AppBarLayoutExpandHackListener.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.view.animation.AnimationUtils\nimport androidx.recyclerview.widget.RecyclerView\nimport com.google.android.material.appbar.AppBarLayout\n\nclass AppBarLayoutExpandHackListener(\n    private val recyclerView: RecyclerView\n) : AppBarLayout.OnOffsetChangedListener {\n    private val offsetAnimationMaxEndTime = (AnimationUtils.currentAnimationTimeMillis()\n        + MAX_OFFSET_ANIMATION_DURATION)\n\n    private var lastVerticalOffset: Int? = null\n\n    override fun onOffsetChanged(appBarLayout: AppBarLayout, verticalOffset: Int) {\n        if (verticalOffset == 0\n            || AnimationUtils.currentAnimationTimeMillis() > offsetAnimationMaxEndTime) {\n            // AppBarLayout crashes with IndexOutOfBoundsException when a non-last listener removes\n            // itself, so we have to do the removal asynchronously.\n            appBarLayout.postOnAnimation { appBarLayout.removeOnOffsetChangedListener(this) }\n        }\n        val lastVerticalOffset = lastVerticalOffset\n        this.lastVerticalOffset = verticalOffset\n        if (lastVerticalOffset != null) {\n            recyclerView.scrollBy(0, verticalOffset - lastVerticalOffset)\n        }\n    }\n\n    companion object {\n        // @see AppBarLayout.BaseBehavior.MAX_OFFSET_ANIMATION_DURATION\n        private const val MAX_OFFSET_ANIMATION_DURATION = 600\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/AspectRatioFrameLayout.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.widget.FrameLayout\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.compat.obtainStyledAttributesCompat\nimport me.zhanghai.android.files.compat.use\nimport kotlin.math.roundToInt\n\nclass AspectRatioFrameLayout @JvmOverloads constructor(\n    context: Context,\n    attrs: AttributeSet? = null,\n    @AttrRes defStyleAttr: Int = 0,\n    @StyleRes defStyleRes: Int = 0\n) : FrameLayout(context, attrs, defStyleAttr, defStyleRes) {\n    @SuppressLint(\"RestrictedApi\")\n    var ratio: Float = context.obtainStyledAttributesCompat(\n        attrs, R.styleable.AspectRatioFrameLayout, defStyleAttr, defStyleRes\n    ).use { it.getFloat(R.styleable.AspectRatioFrameLayout_aspectRatio, 0f) }\n        set(value) {\n            if (field == value) {\n                return\n            }\n            field = value\n            requestLayout()\n            invalidate()\n        }\n\n    fun setRatio(width: Float, height: Float) {\n        ratio = width / height\n    }\n\n    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {\n        val newWidthMeasureSpec: Int\n        val newHeightMeasureSpec: Int\n        if (ratio > 0) {\n            val widthMode = MeasureSpec.getMode(widthMeasureSpec)\n            if (widthMode == MeasureSpec.EXACTLY) {\n                val width = MeasureSpec.getSize(widthMeasureSpec)\n                val height = (width / ratio).roundToInt().coerceAtLeast(minimumHeight)\n                newWidthMeasureSpec = widthMeasureSpec\n                newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)\n            } else {\n                val height = MeasureSpec.getSize(heightMeasureSpec)\n                val width = (ratio * height).roundToInt().coerceAtLeast(minimumWidth)\n                newWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY)\n                newHeightMeasureSpec = heightMeasureSpec\n            }\n        } else {\n            newWidthMeasureSpec = widthMeasureSpec\n            newHeightMeasureSpec = heightMeasureSpec\n        }\n        super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/AspectRatioImageView.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport android.util.AttributeSet\nimport androidx.annotation.AttrRes\nimport com.google.android.material.imageview.ShapeableImageView\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.compat.obtainStyledAttributesCompat\nimport me.zhanghai.android.files.compat.use\nimport kotlin.math.roundToInt\n\nopen class AspectRatioImageView @JvmOverloads constructor(\n    context: Context,\n    attrs: AttributeSet? = null,\n    @AttrRes defStyleAttr: Int = 0\n) : ShapeableImageView(context, attrs, defStyleAttr) {\n    @SuppressLint(\"RestrictedApi\")\n    var ratio: Float = context.obtainStyledAttributesCompat(\n        attrs, R.styleable.AspectRatioFrameLayout, defStyleAttr\n    ).use { it.getFloat(R.styleable.AspectRatioFrameLayout_aspectRatio, 0f) }\n        set(value) {\n            if (field == value) {\n                return\n            }\n            field = value\n            requestLayout()\n            invalidate()\n        }\n\n    fun setRatio(width: Float, height: Float) {\n        ratio = width / height\n    }\n\n    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {\n        val newWidthMeasureSpec: Int\n        val newHeightMeasureSpec: Int\n        if (ratio > 0) {\n            val widthMode = MeasureSpec.getMode(widthMeasureSpec)\n            if (widthMode == MeasureSpec.EXACTLY) {\n                val width = MeasureSpec.getSize(widthMeasureSpec)\n                val height = (width / ratio).roundToInt().coerceIn(minimumHeight, maxHeight)\n                newWidthMeasureSpec = widthMeasureSpec\n                newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)\n            } else {\n                val height = MeasureSpec.getSize(heightMeasureSpec)\n                val width = (ratio * height).roundToInt().coerceIn(minimumWidth, maxWidth)\n                newWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY)\n                newHeightMeasureSpec = heightMeasureSpec\n            }\n        } else {\n            newWidthMeasureSpec = widthMeasureSpec\n            newHeightMeasureSpec = heightMeasureSpec\n        }\n        super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/AutoGoneTextView.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport androidx.appcompat.widget.AppCompatTextView\nimport androidx.core.view.isVisible\n\n/**\n * TextView that automatically sets its visibility to View.GONE when empty.\n */\nclass AutoGoneTextView : AppCompatTextView {\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    override fun setText(text: CharSequence?, type: BufferType) {\n        super.setText(text, type)\n\n        isVisible = !text.isNullOrEmpty()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/AutoMirrorDrawable.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.content.res.Resources\nimport android.graphics.Canvas\nimport android.graphics.Rect\nimport android.graphics.drawable.Drawable\nimport android.view.View\nimport androidx.appcompat.graphics.drawable.DrawableWrapperCompat\nimport me.zhanghai.android.files.compat.layoutDirectionCompat\n\nclass AutoMirrorDrawable(drawable: Drawable) : DrawableWrapperCompat(drawable) {\n    override fun draw(canvas: Canvas) {\n        if (needMirroring()) {\n            val centerX = bounds.exactCenterX()\n            canvas.scale(-1f, 1f, centerX, 0f)\n            super.draw(canvas)\n            canvas.scale(-1f, 1f, centerX, 0f)\n        } else {\n            super.draw(canvas)\n        }\n    }\n\n    override fun onLayoutDirectionChanged(layoutDirection: Int): Boolean {\n        super.onLayoutDirectionChanged(layoutDirection)\n\n        return true\n    }\n\n    override fun isAutoMirrored(): Boolean = true\n\n    private fun needMirroring(): Boolean = layoutDirectionCompat == View.LAYOUT_DIRECTION_RTL\n\n    override fun getPadding(padding: Rect): Boolean {\n        val hasPadding = super.getPadding(padding)\n        if (needMirroring()) {\n            val paddingStart = padding.left\n            val paddingEnd = padding.right\n            padding.left = paddingEnd\n            padding.right = paddingStart\n        }\n        return hasPadding\n    }\n\n    override fun getConstantState(): ConstantState? =\n        drawable!!.constantState?.let { DelegateConstantState(it) }\n\n    private class DelegateConstantState(\n        private val constantState: ConstantState\n    ) : ConstantState() {\n        override fun newDrawable(): Drawable = AutoMirrorDrawable(constantState.newDrawable())\n\n        override fun newDrawable(res: Resources?): Drawable =\n            AutoMirrorDrawable(constantState.newDrawable(res))\n\n        override fun newDrawable(res: Resources?, theme: Resources.Theme?): Drawable =\n            AutoMirrorDrawable(constantState.newDrawable(res, theme))\n\n        override fun getChangingConfigurations(): Int = constantState.changingConfigurations\n\n        override fun canApplyTheme(): Boolean = constantState.canApplyTheme()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/BottomBarLayout.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.content.Context\nimport android.content.res.ColorStateList\nimport android.graphics.drawable.ColorDrawable\nimport android.util.AttributeSet\nimport android.widget.FrameLayout\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport com.google.android.material.shape.MaterialShapeDrawable\nimport com.google.android.material.shape.MaterialShapeUtils\n\nclass BottomBarLayout @JvmOverloads constructor(\n    context: Context,\n    attrs: AttributeSet? = null,\n    @AttrRes defStyleAttr: Int = 0,\n    @StyleRes defStyleRes: Int = 0\n) : FrameLayout(context, attrs, defStyleAttr, defStyleRes) {\n    init {\n        val background = background\n        if (background is ColorDrawable) {\n            this.background = MaterialShapeDrawable().apply {\n                fillColor = ColorStateList.valueOf(background.color)\n                initializeElevationOverlay(context)\n                elevation = this@BottomBarLayout.elevation\n            }\n        }\n    }\n\n    override fun onAttachedToWindow() {\n        super.onAttachedToWindow()\n\n        MaterialShapeUtils.setParentAbsoluteElevation(this)\n    }\n\n    override fun setElevation(elevation: Float) {\n        super.setElevation(elevation)\n\n        MaterialShapeUtils.setElevation(this, elevation)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/CheckableForegroundLinearLayout.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.widget.Checkable\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport me.zhanghai.android.foregroundcompat.ForegroundLinearLayout\n\nclass CheckableForegroundLinearLayout : ForegroundLinearLayout, Checkable {\n    private var _isChecked = false\n        set(value) {\n            if (field == value) {\n                return\n            }\n            field = value\n            refreshDrawableState()\n        }\n\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    constructor(\n        context: Context,\n        attrs: AttributeSet?,\n        @AttrRes defStyleAttr: Int,\n        @StyleRes defStyleRes: Int\n    ) : super(context, attrs, defStyleAttr, defStyleRes)\n\n    override fun isChecked(): Boolean = _isChecked\n\n    override fun setChecked(checked: Boolean) {\n        _isChecked = checked\n        refreshDrawableState()\n    }\n\n    override fun toggle() {\n        _isChecked = !_isChecked\n        refreshDrawableState()\n    }\n\n    override fun onCreateDrawableState(extraSpace: Int): IntArray =\n        super.onCreateDrawableState(extraSpace + 1).apply {\n            if (_isChecked) {\n                mergeDrawableStates(this, CHECKED_STATE_SET)\n            }\n        }\n\n    companion object {\n        private val CHECKED_STATE_SET = intArrayOf(android.R.attr.state_checked)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/CheckableFrameLayout.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.widget.Checkable\nimport android.widget.FrameLayout\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\n\nclass CheckableFrameLayout : FrameLayout, Checkable {\n    private var _isChecked = false\n        set(value) {\n            if (field == value) {\n                return\n            }\n            field = value\n            refreshDrawableState()\n        }\n\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    constructor(\n        context: Context,\n        attrs: AttributeSet?,\n        @AttrRes defStyleAttr: Int,\n        @StyleRes defStyleRes: Int\n    ) : super(context, attrs, defStyleAttr, defStyleRes)\n\n    override fun isChecked(): Boolean = _isChecked\n\n    override fun setChecked(checked: Boolean) {\n        _isChecked = checked\n        refreshDrawableState()\n    }\n\n    override fun toggle() {\n        _isChecked = !_isChecked\n        refreshDrawableState()\n    }\n\n    override fun onCreateDrawableState(extraSpace: Int): IntArray =\n        super.onCreateDrawableState(extraSpace + 1).apply {\n            if (_isChecked) {\n                mergeDrawableStates(this, CHECKED_STATE_SET)\n            }\n        }\n\n    companion object {\n        private val CHECKED_STATE_SET = intArrayOf(android.R.attr.state_checked)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/CheckableItemBackground.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport android.graphics.Color\nimport android.graphics.drawable.ColorDrawable\nimport android.graphics.drawable.Drawable\nimport android.graphics.drawable.GradientDrawable\nimport androidx.annotation.Dimension\nimport androidx.appcompat.graphics.drawable.AnimatedStateListDrawableCompat\nimport me.zhanghai.android.files.util.asColor\nimport me.zhanghai.android.files.util.dpToDimension\nimport me.zhanghai.android.files.util.dpToDimensionPixelOffset\nimport me.zhanghai.android.files.util.getColorByAttr\nimport me.zhanghai.android.files.util.shortAnimTime\nimport me.zhanghai.android.files.util.withModulatedAlpha\n\nobject CheckableItemBackground {\n    // We need an <animated-selector> (AnimatedStateListDrawable) with an item drawable referencing\n    // a ColorStateList that adds an alpha to our primary color, which is a theme attribute. We\n    // currently don't have any compat handling for ColorStateList inside drawable on pre-23,\n    // although AppCompatResources do have compat handling for inflating ColorStateList directly.\n    // Note that the <selector>s used in Material Components are color resources, so they are\n    // inflated as ColorStateList instead of StateListDrawable and don't have this problem.\n    @SuppressLint(\"RestrictedApi\")\n    fun create(\n        @Dimension(unit = Dimension.DP) insetDp: Float,\n        @Dimension(unit = Dimension.DP) cornerSizeDp: Float,\n        context: Context\n    ): Drawable =\n        AnimatedStateListDrawableCompat().apply {\n            val shortAnimTime = context.shortAnimTime\n            setEnterFadeDuration(shortAnimTime)\n            setExitFadeDuration(shortAnimTime)\n            val checkedDrawable = GradientDrawable().apply {\n                cornerRadius = context.dpToDimension(cornerSizeDp)\n                val primaryColor = context.getColorByAttr(androidx.appcompat.R.attr.colorPrimary)\n                setColor(primaryColor.asColor().withModulatedAlpha(0.12f).value)\n                setStroke(2 * context.dpToDimensionPixelOffset(insetDp), Color.TRANSPARENT)\n            }\n            addState(intArrayOf(android.R.attr.state_checked), checkedDrawable)\n            addState(intArrayOf(), ColorDrawable(Color.TRANSPARENT))\n        }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/CheckableView.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.View\nimport android.widget.Checkable\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\n\nopen class CheckableView : View, Checkable {\n    private var _isChecked = false\n        set(value) {\n            if (field == value) {\n                return\n            }\n            field = value\n            refreshDrawableState()\n        }\n\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    constructor(\n        context: Context,\n        attrs: AttributeSet?,\n        @AttrRes defStyleAttr: Int,\n        @StyleRes defStyleRes: Int\n    ) : super(context, attrs, defStyleAttr, defStyleRes)\n\n    override fun isChecked(): Boolean = _isChecked\n\n    override fun setChecked(checked: Boolean) {\n        _isChecked = checked\n        refreshDrawableState()\n    }\n\n    override fun toggle() {\n        _isChecked = !_isChecked\n        refreshDrawableState()\n    }\n\n    override fun onCreateDrawableState(extraSpace: Int): IntArray =\n        super.onCreateDrawableState(extraSpace + 1).apply {\n            if (_isChecked) {\n                mergeDrawableStates(this, CHECKED_STATE_SET)\n            }\n        }\n\n    companion object {\n        private val CHECKED_STATE_SET = intArrayOf(android.R.attr.state_checked)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/CoordinatorAppBarLayout.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.content.Context\nimport android.content.res.ColorStateList\nimport android.graphics.Color\nimport android.graphics.Rect\nimport android.os.Build\nimport android.util.AttributeSet\nimport android.view.View\nimport androidx.annotation.AttrRes\nimport androidx.core.graphics.ColorUtils\nimport com.google.android.material.shape.MaterialShapeDrawable\nimport me.zhanghai.android.files.util.activity\n\nclass CoordinatorAppBarLayout : FitsSystemWindowsAppBarLayout {\n    private val syncBackgroundColorViews = mutableListOf<View>()\n\n    private var offset = 0\n    private val tempClipBounds = Rect()\n\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(\n        context: Context,\n        attrs: AttributeSet?,\n        @AttrRes defStyleAttr: Int\n    ) : super(context, attrs, defStyleAttr)\n\n    init {\n        val defaultBackgroundColor = (background as? MaterialShapeDrawable)?.fillColor?.defaultColor\n        if (defaultBackgroundColor != null) {\n            val window = context.activity!!.window\n            val statusBarColor = window.statusBarColor\n            if (defaultBackgroundColor == statusBarColor\n                || defaultBackgroundColor == ColorUtils.setAlphaComponent(statusBarColor, 0xFF)) {\n                window.statusBarColor = Color.TRANSPARENT\n            }\n        }\n\n        addLiftOnScrollListener { _, backgroundColor ->\n            onBackgroundColorChanged(backgroundColor)\n        }\n\n        addOnOffsetChangedListener { _, offset ->\n            this.offset = offset\n            updateFirstChildClipBounds()\n        }\n    }\n\n    override fun onFinishInflate() {\n        super.onFinishInflate()\n\n        getChildAt(0)?.let {\n            it.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->\n                updateFirstChildClipBounds()\n            }\n        }\n    }\n\n    fun syncBackgroundColorTo(view: View) {\n        syncBackgroundColorViews += view\n    }\n\n    private fun onBackgroundColorChanged(backgroundColor: Int) {\n        syncBackgroundColorViews.forEach {\n            (it.background as? MaterialShapeDrawable)?.fillColor =\n                ColorStateList.valueOf(backgroundColor)\n        }\n    }\n\n    private fun updateFirstChildClipBounds() {\n        val firstChild = getChildAt(0) ?: return\n        tempClipBounds.set(0, -offset, firstChild.width, firstChild.height)\n        // Work around a bug before Android N that an empty clip bounds doesn't clip.\n        // Making the clip bounds somewhere outside view bounds doesn't work, so as a hack we just\n        // assume that the first child won't draw anything in its top-left pixel.\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {\n            if (tempClipBounds.isEmpty) {\n                tempClipBounds.set(0, 0, 1, 1)\n            }\n        }\n        firstChild.clipBounds = tempClipBounds\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/CoordinatorScrollingFrameLayout.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport android.os.Build\nimport android.util.AttributeSet\nimport android.view.View\nimport android.view.ViewGroup\nimport android.view.WindowInsets\nimport android.widget.FrameLayout\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport androidx.coordinatorlayout.widget.CoordinatorLayout\nimport androidx.coordinatorlayout.widget.CoordinatorLayout.AttachedBehavior\nimport androidx.core.graphics.Insets\nimport androidx.core.view.ScrollingView\nimport androidx.core.view.WindowInsetsCompat\nimport androidx.core.view.children\nimport androidx.core.view.updateLayoutParams\nimport androidx.core.view.updatePadding\nimport com.google.android.material.appbar.AppBarLayout.ScrollingViewBehavior\nimport me.zhanghai.android.files.util.layoutInNavigation\n\nclass CoordinatorScrollingFrameLayout : FrameLayout, AttachedBehavior {\n    private var bottomInsets: WindowInsets? = null\n\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    constructor(\n        context: Context,\n        attrs: AttributeSet?,\n        @AttrRes defStyleAttr: Int,\n        @StyleRes defStyleRes: Int\n    ) : super(context, attrs, defStyleAttr, defStyleRes)\n\n    init {\n        fitsSystemWindows = true\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {\n            layoutInNavigation = true\n        }\n    }\n\n    override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets {\n        updatePadding(left = insets.systemWindowInsetLeft, right = insets.systemWindowInsetRight)\n        bottomInsets = WindowInsetsCompat.Builder()\n            .setSystemWindowInsets(Insets.of(0, 0, 0, insets.systemWindowInsetBottom))\n            .build()\n            .toWindowInsets()\n        requestLayout()\n        return insets\n    }\n\n    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {\n        val bottomInsets = bottomInsets\n        if (bottomInsets != null) {\n            val scrollingView = findScrollingView()\n            val scrollingChildView = scrollingView?.let { findChildView(it) }\n            for (childView in children) {\n                if (childView == scrollingChildView) {\n                    if (scrollingView.fitsSystemWindows) {\n                        scrollingView.onApplyWindowInsets(bottomInsets)\n                    } else {\n                        scrollingView.updatePadding(bottom = bottomInsets.systemWindowInsetBottom)\n                    }\n                } else {\n                    childView.updateLayoutParams<MarginLayoutParams> {\n                        bottomMargin = bottomInsets.systemWindowInsetBottom\n                    }\n                }\n            }\n        }\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec)\n    }\n\n    private fun findScrollingView(viewGroup: ViewGroup = this): View? {\n        for (index in 0..<viewGroup.childCount) {\n            val view = viewGroup.getChildAt(index)\n            if (view is ScrollingView) {\n                return view\n            }\n            if (view is ViewGroup) {\n                findScrollingView(view)?.let { return it }\n            }\n        }\n        return null\n    }\n\n    private fun findChildView(view: View): View? {\n        var view = view\n        while (true) {\n            val parent = view.parent\n            if (parent == this) {\n                return view\n            }\n            if (parent !is View) {\n                return null\n            }\n            view = parent\n        }\n    }\n\n    override fun getBehavior(): CoordinatorLayout.Behavior<*> = Behavior()\n\n    private class Behavior : ScrollingViewBehavior() {\n        override fun onMeasureChild(\n            parent: CoordinatorLayout,\n            child: View,\n            parentWidthMeasureSpec: Int,\n            widthUsed: Int,\n            parentHeightMeasureSpec: Int,\n            heightUsed: Int\n        ): Boolean {\n            var parentHeightMeasureSpec = parentHeightMeasureSpec\n            @SuppressLint(\"RestrictedApi\")\n            val parentInsets = parent.lastWindowInsets\n            if (parentInsets != null) {\n                val parentHeightSize = (MeasureSpec.getSize(parentHeightMeasureSpec)\n                    - parentInsets.systemWindowInsetTop - parentInsets.systemWindowInsetBottom)\n                val parentHeightMode = MeasureSpec.getMode(parentHeightMeasureSpec)\n                parentHeightMeasureSpec = MeasureSpec.makeMeasureSpec(\n                    parentHeightSize, parentHeightMode\n                )\n            }\n            return super.onMeasureChild(\n                parent, child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec,\n                heightUsed\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/CoordinatorScrollingLinearLayout.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport android.os.Build\nimport android.util.AttributeSet\nimport android.view.View\nimport android.view.ViewGroup\nimport android.view.WindowInsets\nimport android.widget.LinearLayout\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport androidx.coordinatorlayout.widget.CoordinatorLayout\nimport androidx.coordinatorlayout.widget.CoordinatorLayout.AttachedBehavior\nimport androidx.core.graphics.Insets\nimport androidx.core.view.ScrollingView\nimport androidx.core.view.WindowInsetsCompat\nimport androidx.core.view.updateLayoutParams\nimport androidx.core.view.updatePadding\nimport com.google.android.material.appbar.AppBarLayout.ScrollingViewBehavior\nimport me.zhanghai.android.files.util.layoutInNavigation\n\nclass CoordinatorScrollingLinearLayout : LinearLayout, AttachedBehavior {\n    private var bottomInsets: WindowInsets? = null\n\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    constructor(\n        context: Context,\n        attrs: AttributeSet?,\n        @AttrRes defStyleAttr: Int,\n        @StyleRes defStyleRes: Int\n    ) : super(context, attrs, defStyleAttr, defStyleRes)\n\n    init {\n        orientation = VERTICAL\n        fitsSystemWindows = true\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {\n            layoutInNavigation = true\n        }\n    }\n\n    override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets {\n        updatePadding(left = insets.systemWindowInsetLeft, right = insets.systemWindowInsetRight)\n        bottomInsets = WindowInsetsCompat.Builder()\n            .setSystemWindowInsets(Insets.of(0, 0, 0, insets.systemWindowInsetBottom))\n            .build()\n            .toWindowInsets()\n        requestLayout()\n        return insets\n    }\n\n    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {\n        val bottomInsets = bottomInsets\n        if (bottomInsets != null) {\n            val childView = getChildAt(childCount - 1)\n            if (childView != null) {\n                val scrollingView = findScrollingView()\n                val scrollingChildView = scrollingView?.let { findChildView(it) }\n                if (childView == scrollingChildView) {\n                    if (scrollingView.fitsSystemWindows) {\n                        scrollingView.onApplyWindowInsets(bottomInsets)\n                    } else {\n                        scrollingView.updatePadding(bottom = bottomInsets.systemWindowInsetBottom)\n                    }\n                } else {\n                    childView.updateLayoutParams<MarginLayoutParams> {\n                        bottomMargin = bottomInsets.systemWindowInsetBottom\n                    }\n                }\n            }\n        }\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec)\n    }\n\n    private fun findScrollingView(viewGroup: ViewGroup = this): View? {\n        for (index in 0..<viewGroup.childCount) {\n            val view = viewGroup.getChildAt(index)\n            if (view is ScrollingView) {\n                return view\n            }\n            if (view is ViewGroup) {\n                findScrollingView(view)?.let { return it }\n            }\n        }\n        return null\n    }\n\n    private fun findChildView(view: View): View? {\n        var view = view\n        while (true) {\n            val parent = view.parent\n            if (parent == this) {\n                return view\n            }\n            if (parent !is View) {\n                return null\n            }\n            view = parent\n        }\n    }\n\n    override fun getBehavior(): CoordinatorLayout.Behavior<*> = Behavior()\n\n    private class Behavior : ScrollingViewBehavior() {\n        override fun onMeasureChild(\n            parent: CoordinatorLayout,\n            child: View,\n            parentWidthMeasureSpec: Int,\n            widthUsed: Int,\n            parentHeightMeasureSpec: Int,\n            heightUsed: Int\n        ): Boolean {\n            var parentHeightMeasureSpec = parentHeightMeasureSpec\n            @SuppressLint(\"RestrictedApi\")\n            val parentInsets = parent.lastWindowInsets\n            if (parentInsets != null) {\n                val parentHeightSize = (MeasureSpec.getSize(parentHeightMeasureSpec)\n                    - parentInsets.systemWindowInsetTop - parentInsets.systemWindowInsetBottom)\n                val parentHeightMode = MeasureSpec.getMode(parentHeightMeasureSpec)\n                parentHeightMeasureSpec = MeasureSpec.makeMeasureSpec(\n                    parentHeightSize, parentHeightMode\n                )\n            }\n            return super.onMeasureChild(\n                parent, child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec,\n                heightUsed\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/CrossfadeSubtitleToolbar.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.animation.Animator\nimport android.animation.AnimatorListenerAdapter\nimport android.animation.ObjectAnimator\nimport android.animation.ValueAnimator\nimport android.animation.ValueAnimator.AnimatorUpdateListener\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.View\nimport android.widget.TextView\nimport androidx.annotation.AttrRes\nimport androidx.appcompat.widget.Toolbar\nimport androidx.interpolator.view.animation.FastOutSlowInInterpolator\nimport me.zhanghai.android.files.util.shortAnimTime\n\nclass CrossfadeSubtitleToolbar : Toolbar {\n    private val subtitleAnimator = ObjectAnimator.ofFloat(null, View.ALPHA, 1f, 0f, 1f).apply {\n        duration = (2 * context.shortAnimTime).toLong()\n        interpolator = FastOutSlowInInterpolator()\n        val listener = AnimatorListener()\n        addUpdateListener(listener)\n        addListener(listener)\n    }\n\n    private var nextSubtitle: CharSequence? = null\n\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    override fun getSubtitle(): CharSequence? = nextSubtitle ?: super.getSubtitle()\n\n    override fun setSubtitle(subtitle: CharSequence?) {\n        if (getSubtitle() == subtitle) {\n            return\n        }\n        nextSubtitle = subtitle\n        ensureSubtitleAnimatorTarget()\n        if (subtitleAnimator.target == null) {\n            // Subtitle text view not available (yet), just delegate to super.\n            super.setSubtitle(subtitle)\n            return\n        }\n        if (!subtitleAnimator.isRunning) {\n            subtitleAnimator.start()\n        }\n    }\n\n    private fun ensureSubtitleAnimatorTarget() {\n        if (subtitleAnimator.target != null) {\n            return\n        }\n        val subtitleTextView = try {\n            Toolbar::class.java.getDeclaredField(\"mSubtitleTextView\")\n                .apply { isAccessible = true }\n                .get(this) as TextView?\n        } catch (e: Exception) {\n            e.printStackTrace()\n            null\n        } ?: return\n        subtitleAnimator.target = subtitleTextView\n    }\n\n    private inner class AnimatorListener : AnimatorListenerAdapter(), AnimatorUpdateListener {\n        private var isTextUpdated = false\n\n        override fun onAnimationUpdate(animator: ValueAnimator) {\n            if (animator.animatedFraction < 0.5) {\n                isTextUpdated = false\n            } else {\n                ensureTextUpdated()\n            }\n        }\n\n        override fun onAnimationEnd(animator: Animator) {\n            ensureTextUpdated()\n            if (nextSubtitle != null) {\n                isTextUpdated = false\n                animator.start()\n            }\n        }\n\n        private fun ensureTextUpdated() {\n            if (!isTextUpdated) {\n                if (nextSubtitle != null) {\n                    super@CrossfadeSubtitleToolbar.setSubtitle(nextSubtitle)\n                    nextSubtitle = null\n                }\n                isTextUpdated = true\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/DisabledAlphaImageView.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.content.Context\nimport android.graphics.drawable.Drawable\nimport android.util.AttributeSet\nimport androidx.annotation.AttrRes\nimport me.zhanghai.android.files.util.getFloatByAttr\nimport kotlin.math.roundToInt\n\nclass DisabledAlphaImageView @JvmOverloads constructor(\n    context: Context,\n    attrs: AttributeSet? = null,\n    @AttrRes defStyleAttr: Int = 0\n) : AspectRatioImageView(context, attrs, defStyleAttr) {\n    override fun setImageDrawable(drawable: Drawable?) {\n        super.setImageDrawable(drawable)\n\n        updateImageAlpha()\n    }\n\n    override fun drawableStateChanged() {\n        super.drawableStateChanged()\n\n        updateImageAlpha()\n    }\n\n    private fun updateImageAlpha() {\n        var alpha = 0xFF\n        val enabled = android.R.attr.state_enabled in drawableState\n        if (!enabled) {\n            val disabledAlpha = context.getFloatByAttr(android.R.attr.disabledAlpha)\n            alpha = (disabledAlpha * alpha).roundToInt()\n        }\n        imageAlpha = alpha\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/DrawerLayoutOnBackPressedCallback.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.view.View\nimport androidx.activity.OnBackPressedCallback\nimport androidx.core.view.GravityCompat\nimport androidx.drawerlayout.widget.DrawerLayout\nimport androidx.drawerlayout.widget.DrawerLayout.SimpleDrawerListener\n\nclass DrawerLayoutOnBackPressedCallback(\n    private val drawerLayout: DrawerLayout,\n    private val gravity: Int = GravityCompat.START\n) : OnBackPressedCallback(drawerLayout.isDrawerVisibleAndUnlocked(gravity)) {\n    init {\n        drawerLayout.addDrawerListener(\n            object : SimpleDrawerListener() {\n                override fun onDrawerOpened(drawerView: View) {\n                    isEnabled = drawerLayout.isDrawerVisibleAndUnlocked(gravity)\n                }\n\n                override fun onDrawerClosed(drawerView: View) {\n                    isEnabled = drawerLayout.isDrawerVisibleAndUnlocked(gravity)\n                }\n            }\n        )\n    }\n\n    override fun handleOnBackPressed() {\n        drawerLayout.closeDrawer(gravity)\n    }\n}\n\nprivate fun DrawerLayout.isDrawerVisibleAndUnlocked(gravity: Int): Boolean =\n    isDrawerVisible(gravity) && getDrawerLockMode(gravity) == DrawerLayout.LOCK_MODE_UNLOCKED\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/DropDownView.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.content.Context\nimport android.os.Parcelable\nimport android.util.AttributeSet\nimport android.view.View\nimport android.widget.AdapterView\nimport android.widget.ListAdapter\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport androidx.appcompat.widget.FixPaddingListPopupWindow\nimport androidx.appcompat.widget.ListPopupWindow\nimport com.google.android.material.shape.MaterialShapeDrawable\nimport com.google.android.material.shape.ShapeAppearanceModel\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.util.ParcelableState\nimport me.zhanghai.android.files.util.doOnGlobalLayout\nimport me.zhanghai.android.files.util.getBooleanByAttr\nimport me.zhanghai.android.files.util.getDimensionPixelOffset\n\nclass DropDownView @JvmOverloads constructor(\n    context: Context,\n    attrs: AttributeSet? = null,\n    @AttrRes defStyleAttr: Int = 0,\n    @StyleRes defStyleRes: Int = 0\n) : View(context, attrs, defStyleAttr, defStyleRes) {\n    private val popup: ListPopupWindow\n\n    init {\n        visibility = INVISIBLE\n        popup = FixPaddingListPopupWindow(context, attrs).apply {\n            isModal = true\n            anchorView = this@DropDownView\n            inputMethodMode = ListPopupWindow.INPUT_METHOD_NOT_NEEDED\n        }\n        maybeSimulateElevationOverlay()\n    }\n\n    private fun maybeSimulateElevationOverlay() {\n        val context = context\n        val elevationOverlayEnabled =\n            context.getBooleanByAttr(com.google.android.material.R.attr.elevationOverlayEnabled)\n        if (!elevationOverlayEnabled) {\n            return\n        }\n        val elevation = context.getDimensionPixelOffset(\n            com.google.android.material.R.dimen.mtrl_exposed_dropdown_menu_popup_elevation\n        ).toFloat()\n        val background = MaterialShapeDrawable.createWithElevationOverlay(context, elevation)\n            .apply {\n                val cornerSize = context.getDimensionPixelOffset(\n                    com.google.android.material.R.dimen.mtrl_shape_corner_size_small_component\n                ).toFloat()\n                shapeAppearanceModel = ShapeAppearanceModel.builder()\n                    .setAllCornerSizes(cornerSize)\n                    .build()\n            }\n        popup.setBackgroundDrawable(background)\n    }\n\n    override fun onDetachedFromWindow() {\n        super.onDetachedFromWindow()\n\n        if (popup.isShowing) {\n            popup.dismiss()\n        }\n    }\n\n    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {\n        super.onMeasure(widthMeasureSpec, heightMeasureSpec)\n\n        popup.width = measuredWidth\n    }\n\n    override fun onSaveInstanceState(): Parcelable? =\n        State(super.onSaveInstanceState(), popup.isShowing)\n\n    override fun onRestoreInstanceState(state: Parcelable?) {\n        state as State\n        super.onRestoreInstanceState(state.superState)\n        if (state.isShowing) {\n            doOnGlobalLayout {\n                if (!popup.isShowing) {\n                    popup.show()\n                }\n            }\n        }\n    }\n\n    fun setAdapter(adapter: ListAdapter?) {\n        popup.setAdapter(adapter)\n    }\n\n    fun setOnItemClickListener(listener: AdapterView.OnItemClickListener?) {\n        popup.setOnItemClickListener(listener)\n    }\n\n    fun setOnItemClickListener(listener: (AdapterView<*>, View, Int, Long) -> Unit) {\n        popup.setOnItemClickListener(listener)\n    }\n\n    val isShowing: Boolean\n        get() = popup.isShowing\n\n    fun show() {\n        popup.show()\n    }\n\n    fun dismiss() {\n        popup.dismiss()\n    }\n\n    @Parcelize\n    private class State(val superState: Parcelable?, val isShowing: Boolean) : ParcelableState\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/FitsSystemWindowsAppBarLayout.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport androidx.annotation.AttrRes\nimport androidx.core.view.WindowInsetsCompat\nimport androidx.core.view.updatePadding\nimport com.google.android.material.appbar.OnWindowInsetChangedAppBarLayout\n\nopen class FitsSystemWindowsAppBarLayout : OnWindowInsetChangedAppBarLayout {\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(\n        context: Context,\n        attrs: AttributeSet?,\n        @AttrRes defStyleAttr: Int\n    ) : super(context, attrs, defStyleAttr)\n\n    init {\n        fitsSystemWindows = true\n    }\n\n    override fun onWindowInsetChanged(insets: WindowInsetsCompat): WindowInsetsCompat {\n        val windowInsets = insets.toWindowInsets()!!\n        updatePadding(\n            left = windowInsets.systemWindowInsetLeft, right = windowInsets.systemWindowInsetRight\n        )\n        return super.onWindowInsetChanged(insets)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/FixLayoutSearchView.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.View\nimport androidx.annotation.AttrRes\nimport androidx.appcompat.widget.SearchView\nimport androidx.core.view.updateLayoutParams\nimport androidx.core.view.updatePaddingRelative\nimport me.zhanghai.android.files.compat.requireViewByIdCompat\nimport me.zhanghai.android.files.util.dpToDimensionPixelSize\nimport me.zhanghai.android.files.util.getDrawableByAttr\n\nopen class FixLayoutSearchView : SearchView {\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    init {\n        // A negative value won't work here because SearchView will use its preferred width as max\n        // width instead.\n        maxWidth = Int.MAX_VALUE\n        val searchEditFrame = requireViewByIdCompat<View>(androidx.appcompat.R.id.search_edit_frame)\n        searchEditFrame.updateLayoutParams<MarginLayoutParams> {\n            leftMargin = 0\n            rightMargin = 0\n        }\n        val searchSrcText = requireViewByIdCompat<View>(androidx.appcompat.R.id.search_src_text)\n        searchSrcText.updatePaddingRelative(start = 0, end = 0)\n        val searchCloseBtn = requireViewByIdCompat<View>(androidx.appcompat.R.id.search_close_btn)\n        val searchCloseBtnPaddingHorizontal = searchCloseBtn.context.dpToDimensionPixelSize(12)\n        searchCloseBtn.updatePaddingRelative(\n            start = searchCloseBtnPaddingHorizontal, end = searchCloseBtnPaddingHorizontal\n        )\n        searchCloseBtn.background = searchCloseBtn.context\n            .getDrawableByAttr(androidx.appcompat.R.attr.actionBarItemBackground)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/FixQueryChangeSearchView.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport androidx.annotation.AttrRes\n\nclass FixQueryChangeSearchView : FixLayoutSearchView {\n    var shouldIgnoreQueryChange = false\n        private set\n\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    override fun setIconified(iconify: Boolean) {\n        shouldIgnoreQueryChange = true\n        super.setIconified(iconify)\n        shouldIgnoreQueryChange = false\n    }\n\n    override fun onActionViewCollapsed() {\n        shouldIgnoreQueryChange = true\n        super.onActionViewCollapsed()\n        shouldIgnoreQueryChange = false\n    }\n\n    override fun onActionViewExpanded() {\n        shouldIgnoreQueryChange = true\n        super.onActionViewExpanded()\n        shouldIgnoreQueryChange = false\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/IgnoreFitsSystemWindowsFullyDraggableDrawerContentLayout.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.WindowInsets\nimport androidx.annotation.AttrRes\nimport com.drakeet.drawer.FullDraggableContainer\n\nclass IgnoreFitsSystemWindowsFullyDraggableDrawerContentLayout @JvmOverloads constructor(\n    context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr: Int = 0\n) : FullDraggableContainer(context, attrs, defStyleAttr) {\n    override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets = insets\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/LicensesDialogExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.content.Context\nimport android.graphics.Color\nimport android.net.Uri\nimport android.os.Message\nimport android.view.View\nimport android.webkit.WebChromeClient\nimport android.webkit.WebView\nimport android.widget.FrameLayout\nimport androidx.appcompat.app.AlertDialog\nimport androidx.core.graphics.ColorUtils\nimport androidx.core.view.ViewCompat\nimport de.psdev.licensesdialog.model.Notices\nimport me.zhanghai.android.files.compat.scrollIndicatorsCompat\nimport me.zhanghai.android.files.util.createViewIntent\nimport me.zhanghai.android.files.util.getColorByAttr\nimport me.zhanghai.android.files.util.getDimensionPixelSize\nimport me.zhanghai.android.files.util.startActivitySafe\nimport java.nio.charset.StandardCharsets\nimport kotlin.math.roundToInt\n\n/**\n * @see de.psdev.licensesdialog.LicensesDialog\n */\nfun AlertDialog.Builder.setLicensesView(notices: Notices): AlertDialog.Builder {\n    val context = context\n    val html = createHtml(notices, context)\n    return setView(createView(html, context))\n}\n\nprivate fun createHtml(notices: Notices, context: Context): String =\n    StringBuilder().apply {\n        append(\"<!DOCTYPE html><html lang=\\\"en-US\\\"><head><meta charset=\\\"utf-8\\\"><style>\")\n        append(createStyle(context))\n        append(\"</style></head><body><ul>\")\n        for (notice in notices.notices) {\n            append(\"<li><div>\")\n            append(notice.name)\n            val url = notice.url\n            if (!url.isNullOrEmpty()) {\n                append(\" (<a href=\\\"\")\n                append(url)\n                append(\"\\\" target=\\\"_blank\\\">\")\n                append(url)\n                append(\"</a>)\")\n            }\n            append(\"</div><pre>\")\n            val copyright = notice.copyright\n            if (!copyright.isNullOrEmpty()) {\n                append(copyright)\n                append(\"<br><br>\")\n            }\n            val license = notice.license\n            if (license != null) {\n                append(license.getSummaryText(context))\n            }\n            append(\"</pre></li>\")\n        }\n        append(\"</ul></body></html>\")\n    }.toString()\n\nprivate fun createStyle(context: Context): String {\n    val primaryTextColor = context.getColorByAttr(android.R.attr.textColorPrimary).toCssColor()\n    val preformattedTextBackgroundColor = ColorUtils.setAlphaComponent(\n        context.getColorByAttr(com.google.android.material.R.attr.colorOnSurface),\n        (0.08f * 0xFF).roundToInt()\n    ).toCssColor()\n    val linkTextColor = context.getColorByAttr(android.R.attr.textColorLink).toCssColor()\n    val textHighlightColor = context.getColorByAttr(android.R.attr.textColorHighlight).toCssColor()\n    return \"\"\"\n        ::selection {\n            background: $textHighlightColor;\n        }\n        body {\n            color: $primaryTextColor;\n            margin: 0;\n            overflow-wrap: break-word;\n            -webkit-tap-highlight-color: $textHighlightColor;\n        }\n        ul {\n            list-style-type: none;\n            margin: 0;\n            padding: 0;\n        }\n        li {\n            padding: 12px;\n        }\n        div {\n            padding: 0 12px;\n        }\n        pre {\n            background: $preformattedTextBackgroundColor;\n            margin: 12px 0 0 0;\n            padding: 12px;\n            white-space: pre-wrap;\n        }\n        a, a:link, a:visited, a:hover, a:focus, a:active {\n            color: $linkTextColor;\n        }\n    \"\"\".trimIndent()\n}\n\nprivate fun Int.toCssColor(): String =\n    if (Color.alpha(this) == 0xFF) {\n        \"#%06X\".format(this and 0x00FFFFFF)\n    } else {\n        \"rgba(${Color.red(this)}, ${Color.green(this)}, ${Color.blue(this)}, ${\n            Color.alpha(this).toFloat() / 0xFF\n        })\"\n    }\n\nprivate fun createView(html: String, context: Context): View {\n    val webView = WebView(context).apply {\n        scrollIndicatorsCompat = (ViewCompat.SCROLL_INDICATOR_TOP\n            or ViewCompat.SCROLL_INDICATOR_BOTTOM)\n        setBackgroundColor(Color.TRANSPARENT)\n        settings.setSupportMultipleWindows(true)\n        webChromeClient = object : WebChromeClient() {\n            override fun onCreateWindow(\n                view: WebView,\n                isDialog: Boolean,\n                isUserGesture: Boolean,\n                resultMsg: Message\n            ): Boolean {\n                val data = view.hitTestResult.extra\n                if (data != null) {\n                    context.startActivitySafe(Uri.parse(data).createViewIntent())\n                }\n                return false\n            }\n        }\n        loadDataWithBaseURL(null, html, \"text/html\", StandardCharsets.UTF_8.name(), null)\n    }\n    return FrameLayout(context).apply {\n        setPaddingRelative(\n            0, context.getDimensionPixelSize(\n                androidx.appcompat.R.dimen.abc_dialog_title_divider_material\n            ), 0, 0\n        )\n        addView(webView)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/LicensesDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.app.Dialog\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatDialogFragment\nimport androidx.fragment.app.Fragment\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport de.psdev.licensesdialog.LicensesDialog\nimport de.psdev.licensesdialog.NoticesXmlParser\nimport de.psdev.licensesdialog.model.Notices\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.util.ParcelableState\nimport me.zhanghai.android.files.util.getState\nimport me.zhanghai.android.files.util.putState\nimport me.zhanghai.android.files.util.show\n\nclass LicensesDialogFragment : AppCompatDialogFragment() {\n    private lateinit var notices: Notices\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        notices = if (savedInstanceState != null) {\n            savedInstanceState.getState<State>().notices\n        } else {\n            NoticesXmlParser.parse(resources.openRawResource(R.raw.licenses))\n                .apply { addNotice(LicensesDialog.LICENSES_DIALOG_NOTICE) }\n        }\n    }\n\n    override fun onSaveInstanceState(outState: Bundle) {\n        super.onSaveInstanceState(outState)\n\n        outState.putState(State(notices))\n    }\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =\n        MaterialAlertDialogBuilder(requireContext(), theme)\n            .setTitle(R.string.about_licenses_title)\n            .apply { setLicensesView(notices) }\n            .setNegativeButton(R.string.close, null)\n            .create()\n\n    companion object {\n        fun show(fragment: Fragment) {\n            LicensesDialogFragment().show(fragment)\n        }\n    }\n\n    @Parcelize\n    private class State(val notices: Notices) : ParcelableState\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/ListAdapter.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport androidx.recyclerview.widget.AdapterListUpdateCallback\nimport androidx.recyclerview.widget.DiffUtil\nimport androidx.recyclerview.widget.RecyclerView\n\nabstract class ListAdapter<T, VH : RecyclerView.ViewHolder>(\n    callback: DiffUtil.ItemCallback<T>\n) : RecyclerView.Adapter<VH>() {\n    private val listDiffer = ListDiffer(AdapterListUpdateCallback(this), callback)\n\n    val list: List<T>\n        get() = listDiffer.list\n\n    override fun getItemCount(): Int = list.size\n\n    fun getItem(position: Int): T = list[position]\n\n    // Disable stable IDs and only let the list callback instruct animation properly.\n    final override fun getItemId(position: Int): Long = RecyclerView.NO_ID\n\n    open fun refresh() {\n        val list = listDiffer.list\n        listDiffer.list = emptyList()\n        listDiffer.list = list\n    }\n\n    open fun replace(list: List<T>, clear: Boolean) {\n        if (clear) {\n            listDiffer.list = emptyList()\n        }\n        listDiffer.list = list\n    }\n\n    open fun clear() {\n        listDiffer.list = emptyList()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/ListDiffer.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport androidx.recyclerview.widget.DiffUtil\nimport androidx.recyclerview.widget.ListUpdateCallback\n\nclass ListDiffer<T>(\n    private val updateCallback: ListUpdateCallback,\n    private val diffCallback: DiffUtil.ItemCallback<T>\n) {\n    private var _list: List<T> = emptyList()\n    var list: List<T>\n        get() = _list\n        set(newList) {\n            if (newList === _list || newList.isEmpty() && _list.isEmpty()) {\n                return\n            }\n            if (newList.isEmpty()) {\n                val oldListSize = _list.size\n                _list = emptyList()\n                updateCallback.onRemoved(0, oldListSize)\n                return\n            }\n            if (_list.isEmpty()) {\n                _list = newList\n                updateCallback.onInserted(0, newList.size)\n                return\n            }\n            val oldList = _list\n            val result = DiffUtil.calculateDiff(object : DiffUtil.Callback() {\n                override fun getOldListSize(): Int {\n                    return oldList.size\n                }\n\n                override fun getNewListSize(): Int {\n                    return newList.size\n                }\n\n                override fun areItemsTheSame(\n                    oldItemPosition: Int,\n                    newItemPosition: Int\n                ): Boolean {\n                    val oldItem: T? = oldList[oldItemPosition]\n                    val newItem: T? = newList[newItemPosition]\n                    return if (oldItem != null && newItem != null) {\n                        diffCallback.areItemsTheSame(oldItem, newItem)\n                    } else {\n                        oldItem == null && newItem == null\n                    }\n                }\n\n                override fun areContentsTheSame(\n                    oldItemPosition: Int,\n                    newItemPosition: Int\n                ): Boolean {\n                    val oldItem: T? = oldList[oldItemPosition]\n                    val newItem: T? = newList[newItemPosition]\n                    return if (oldItem != null && newItem != null) {\n                        diffCallback.areContentsTheSame(oldItem, newItem)\n                    } else if (oldItem == null && newItem == null) {\n                        true\n                    } else {\n                        throw AssertionError()\n                    }\n                }\n\n                override fun getChangePayload(\n                    oldItemPosition: Int,\n                    newItemPosition: Int\n                ): Any? {\n                    val oldItem: T? = oldList[oldItemPosition]\n                    val newItem: T? = newList[newItemPosition]\n                    return if (oldItem != null && newItem != null) {\n                        diffCallback.getChangePayload(oldItem, newItem)\n                    } else {\n                        throw AssertionError()\n                    }\n                }\n            })\n            _list = newList\n            result.dispatchUpdatesTo(updateCallback)\n        }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/MaterialListPreferenceDialogFragmentCompat.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\npackage me.zhanghai.android.files.ui\n\nimport android.content.DialogInterface\nimport android.os.Bundle\nimport androidx.appcompat.app.AlertDialog\nimport androidx.preference.ListPreference\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.util.ParcelableState\nimport me.zhanghai.android.files.util.getState\nimport me.zhanghai.android.files.util.putState\n\nclass MaterialListPreferenceDialogFragmentCompat : MaterialPreferenceDialogFragmentCompat() {\n    override val preference: ListPreference\n        get() = super.preference as ListPreference\n\n    private lateinit var entries: Array<CharSequence>\n    private lateinit var entryValues: Array<CharSequence>\n\n    private var checkedEntryIndex = 0\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        if (savedInstanceState == null) {\n            val preference = preference\n            entries = preference.entries\n            entryValues = preference.entryValues\n            checkedEntryIndex = preference.findIndexOfValue(preference.value)\n        } else {\n            val state = savedInstanceState.getState<State>()\n            entries = state.entries\n            entryValues = state.entryValues\n            checkedEntryIndex = state.checkedEntryIndex\n        }\n    }\n\n    override fun onSaveInstanceState(outState: Bundle) {\n        super.onSaveInstanceState(outState)\n\n        outState.putState(State(entries, entryValues, checkedEntryIndex))\n    }\n\n    override fun onPrepareDialogBuilder(builder: AlertDialog.Builder) {\n        super.onPrepareDialogBuilder(builder)\n\n        builder.setSingleChoiceItems(entries, checkedEntryIndex) { dialog, which ->\n            checkedEntryIndex = which\n            onClick(dialog, DialogInterface.BUTTON_POSITIVE)\n            dialog.dismiss()\n        }\n        builder.setPositiveButton(null, null)\n    }\n\n    override fun onDialogClosed(positiveResult: Boolean) {\n        if (positiveResult && checkedEntryIndex >= 0) {\n            val value = entryValues[checkedEntryIndex].toString()\n            val preference = preference\n            if (preference.callChangeListener(value)) {\n                preference.value = value\n            }\n        }\n    }\n\n    @Parcelize\n    private class State(\n        val entries: Array<CharSequence>,\n        val entryValues: Array<CharSequence>,\n        val checkedEntryIndex: Int\n    ) : ParcelableState\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/MaterialPreferenceDialogFragmentCompat.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.app.Dialog\nimport android.content.Context\nimport android.content.DialogInterface\nimport android.graphics.Bitmap\nimport android.graphics.drawable.BitmapDrawable\nimport android.os.Bundle\nimport android.text.TextUtils\nimport android.view.View\nimport android.view.WindowManager\nimport android.widget.TextView\nimport androidx.annotation.LayoutRes\nimport androidx.appcompat.app.AlertDialog\nimport androidx.appcompat.app.AppCompatDialogFragment\nimport androidx.preference.DialogPreference\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.util.ParcelableState\nimport me.zhanghai.android.files.util.getState\nimport me.zhanghai.android.files.util.layoutInflater\nimport me.zhanghai.android.files.util.putState\nimport me.zhanghai.android.files.util.toBitmapDrawable\n\n/**\n * @see androidx.preference.PreferenceDialogFragmentCompat\n */\nabstract class MaterialPreferenceDialogFragmentCompat : AppCompatDialogFragment(),\n    DialogInterface.OnClickListener {\n\n    open val preference: DialogPreference by lazy {\n        val fragment = targetFragment\n        check(fragment is DialogPreference.TargetFragment) {\n            \"Target fragment must implement TargetFragment interface\"\n        }\n        val key = requireArguments().getString(ARG_KEY)!!\n        fragment.findPreference(key)!!\n    }\n\n    private var dialogTitle: CharSequence? = null\n    private var positiveButtonText: CharSequence? = null\n    private var negativeButtonText: CharSequence? = null\n    private var dialogMessage: CharSequence? = null\n    @LayoutRes\n    private var dialogLayoutRes: Int = 0\n    private var dialogIcon: BitmapDrawable? = null\n\n    /** Which button was clicked.  */\n    private var whichButtonClicked = 0\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        if (savedInstanceState == null) {\n            dialogTitle = preference.dialogTitle\n            positiveButtonText = preference.positiveButtonText\n            negativeButtonText = preference.negativeButtonText\n            dialogMessage = preference.dialogMessage\n            dialogLayoutRes = preference.dialogLayoutResource\n            dialogIcon = preference.dialogIcon?.toBitmapDrawable(resources)\n        } else {\n            val state = savedInstanceState.getState<State>()\n            dialogTitle = state.dialogTitle\n            positiveButtonText = state.positiveButtonText\n            negativeButtonText = state.negativeButtonText\n            dialogMessage = state.dialogMessage\n            dialogLayoutRes = state.dialogLayoutRes\n            dialogIcon = state.dialogIcon?.let { BitmapDrawable(resources, it) }\n        }\n    }\n\n    override fun onSaveInstanceState(outState: Bundle) {\n        super.onSaveInstanceState(outState)\n\n        outState.putState(\n            State(\n                dialogTitle, positiveButtonText, negativeButtonText, dialogMessage, dialogLayoutRes,\n                dialogIcon?.bitmap\n            )\n        )\n    }\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {\n        whichButtonClicked = DialogInterface.BUTTON_NEGATIVE\n        val dialog = MaterialAlertDialogBuilder(requireContext(), theme)\n            .setTitle(dialogTitle)\n            .setIcon(dialogIcon)\n            .setPositiveButton(positiveButtonText, this)\n            .setNegativeButton(negativeButtonText, this)\n            .apply {\n                val contentView = onCreateDialogView(context)\n                if (contentView != null) {\n                    onBindDialogView(contentView)\n                    setView(contentView)\n                } else {\n                    setMessage(dialogMessage)\n                }\n                onPrepareDialogBuilder(this)\n            }\n            .create()\n        if (needInputMethod()) {\n            requestInputMethod(dialog)\n        }\n        return dialog\n    }\n\n    /**\n     * Prepares the dialog builder to be shown when the preference is clicked.\n     * Use this to set custom properties on the dialog.\n     *\n     *\n     * Do not [AlertDialog.Builder.create] or [AlertDialog.Builder.show].\n     */\n    open fun onPrepareDialogBuilder(builder: AlertDialog.Builder) {}\n\n    /**\n     * Returns whether the preference needs to display a soft input method when the dialog is\n     * displayed. Default is false. Subclasses should override this method if they need the soft\n     * input method brought up automatically.\n     *\n     *\n     * Note: If your application targets P or above, ensure your subclass manually requests\n     * focus (ideally in [.onBindDialogView]) for the input field in order to\n     * correctly attach the input method to the field.\n     *\n     * @hide\n     */\n    open fun needInputMethod(): Boolean = false\n\n    /**\n     * Sets the required flags on the dialog window to enable input method window to show up.\n     */\n    private fun requestInputMethod(dialog: Dialog) {\n        dialog.window!!.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)\n    }\n\n    /**\n     * Creates the content view for the dialog (if a custom content view is required).\n     * By default, it inflates the dialog layout resource if it is set.\n     *\n     * @return The content view for the dialog\n     * @see DialogPreference.setLayoutResource\n     */\n    open fun onCreateDialogView(context: Context): View? =\n        if (dialogLayoutRes != 0) context.layoutInflater.inflate(dialogLayoutRes, null) else null\n\n    /**\n     * Binds views in the content view of the dialog to data.\n     *\n     *\n     * Make sure to call through to the superclass implementation.\n     *\n     * @param view The content view of the dialog, if it is custom\n     */\n    open fun onBindDialogView(view: View) {\n        val dialogMessageView = view.findViewById<View>(android.R.id.message)\n        if (dialogMessageView != null) {\n            var newVisibility = View.GONE\n            if (!TextUtils.isEmpty(dialogMessage)) {\n                if (dialogMessageView is TextView) {\n                    dialogMessageView.text = dialogMessage\n                }\n                newVisibility = View.VISIBLE\n            }\n            if (dialogMessageView.visibility != newVisibility) {\n                dialogMessageView.visibility = newVisibility\n            }\n        }\n    }\n\n    override fun onClick(dialog: DialogInterface, which: Int) {\n        whichButtonClicked = which\n    }\n\n    override fun onDismiss(dialog: DialogInterface) {\n        super.onDismiss(dialog)\n\n        onDialogClosed(whichButtonClicked == DialogInterface.BUTTON_POSITIVE)\n    }\n\n    abstract fun onDialogClosed(positiveResult: Boolean)\n\n    companion object {\n        /*protected */const val ARG_KEY = \"key\"\n    }\n\n    @Parcelize\n    private class State(\n        val dialogTitle: CharSequence?,\n        val positiveButtonText: CharSequence?,\n        val negativeButtonText: CharSequence?,\n        val dialogMessage: CharSequence?,\n        @LayoutRes val dialogLayoutRes: Int,\n        val dialogIcon: Bitmap?\n    ) : ParcelableState\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/NavigationFrameLayout.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.content.Context\nimport android.content.res.ColorStateList\nimport android.graphics.drawable.ColorDrawable\nimport android.util.AttributeSet\nimport android.view.WindowInsets\nimport android.widget.FrameLayout\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport com.google.android.material.shape.MaterialShapeDrawable\nimport com.google.android.material.shape.MaterialShapeUtils\n\nclass NavigationFrameLayout : FrameLayout {\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    constructor(\n        context: Context,\n        attrs: AttributeSet?,\n        @AttrRes defStyleAttr: Int,\n        @StyleRes defStyleRes: Int\n    ) : super(context, attrs, defStyleAttr, defStyleRes)\n\n    init {\n        val background = background\n        if (background is ColorDrawable) {\n            this.background = MaterialShapeDrawable().apply {\n                fillColor = ColorStateList.valueOf(background.color)\n                initializeElevationOverlay(context)\n                elevation = this@NavigationFrameLayout.elevation\n            }\n        }\n    }\n\n    override fun onAttachedToWindow() {\n        super.onAttachedToWindow()\n\n        MaterialShapeUtils.setParentAbsoluteElevation(this)\n    }\n\n    override fun setElevation(elevation: Float) {\n        super.setElevation(elevation)\n\n        MaterialShapeUtils.setElevation(this, elevation)\n    }\n\n    override fun onApplyWindowInsets(insets: WindowInsets): WindowInsets = insets\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/OverlayToolbar.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport androidx.annotation.AttrRes\nimport com.google.android.material.appbar.MaterialToolbar\nimport com.google.android.material.shape.MaterialShapeDrawable\n\nclass OverlayToolbar : MaterialToolbar {\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    override fun onAttachedToWindow() {\n        super.onAttachedToWindow()\n\n        // @see MaterialShapeUtils.setParentAbsoluteElevation\n        val background = background\n        if (background is MaterialShapeDrawable && background.isElevationOverlayEnabled) {\n            // This fix is needed for elevation overlay after activity recreation on S+.\n            background.parentAbsoluteElevation = 0f\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/OverlayToolbarActionMode.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.view.ViewGroup\nimport androidx.appcompat.widget.Toolbar\nimport androidx.core.view.isVisible\nimport me.zhanghai.android.files.util.fadeInUnsafe\nimport me.zhanghai.android.files.util.fadeOutUnsafe\n\nclass OverlayToolbarActionMode(bar: ViewGroup, toolbar: Toolbar) : ToolbarActionMode(bar, toolbar) {\n    constructor(toolbar: Toolbar) : this(toolbar, toolbar)\n\n    init {\n        bar.isVisible = false\n    }\n\n    override fun show(bar: ViewGroup, animate: Boolean) {\n        if (animate) {\n            bar.fadeInUnsafe()\n        } else {\n            bar.isVisible = true\n        }\n    }\n\n    override fun hide(bar: ViewGroup, animate: Boolean) {\n        if (animate) {\n            bar.fadeOutUnsafe()\n        } else {\n            bar.isVisible = false\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/PersistentBarLayout.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.Gravity\nimport android.view.View\nimport android.view.ViewGroup\nimport android.view.WindowInsets\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport androidx.core.content.res.use\nimport androidx.core.view.children\nimport androidx.core.view.isInvisible\nimport androidx.customview.widget.ViewDragHelper\nimport me.zhanghai.android.files.util.layoutInStatusBar\n\n/**\n * @see PersistentDrawerLayout\n */\nclass PersistentBarLayout @JvmOverloads constructor(\n    context: Context,\n    attrs: AttributeSet? = null,\n    @AttrRes defStyleAttr: Int = 0,\n    @StyleRes defStyleRes: Int = 0\n) : ViewGroup(context, attrs, defStyleAttr, defStyleRes) {\n    private val topDragger = ViewDragHelper.create(this, ViewDragCallback(Gravity.TOP))\n    private val bottomDragger = ViewDragHelper.create(this, ViewDragCallback(Gravity.BOTTOM))\n\n    private var lastInsets: WindowInsets? = null\n\n    init {\n        if (fitsSystemWindows) {\n            layoutInStatusBar = true\n        }\n    }\n\n    override fun dispatchApplyWindowInsets(insets: WindowInsets): WindowInsets {\n        if (!fitsSystemWindows) {\n            return insets\n        }\n        for (child in children) {\n            if (isBarView(child)) {\n                if (isTopBarView(child)) {\n                    child.dispatchApplyWindowInsets(\n                        insets.replaceSystemWindowInsets(\n                            insets.systemWindowInsetLeft, insets.systemWindowInsetTop,\n                            insets.systemWindowInsetRight, 0\n                        )\n                    )\n                } else {\n                    child.dispatchApplyWindowInsets(\n                        insets.replaceSystemWindowInsets(\n                            insets.systemWindowInsetLeft, 0, insets.systemWindowInsetRight,\n                            insets.systemWindowInsetBottom\n                        )\n                    )\n                }\n            } else if (isFillView(child)) {\n                child.dispatchApplyWindowInsets(insets)\n            }\n        }\n        lastInsets = insets\n        updateContentViewsWindowInsets()\n        return insets.consumeSystemWindowInsets()\n    }\n\n    private fun updateContentViewsWindowInsets() {\n        var contentInsets = lastInsets ?: return\n        for (child in children) {\n            if (isBarView(child)) {\n                val childLayoutParams = child.layoutParams as LayoutParams\n                val childRange = (childLayoutParams.topMargin + child.measuredHeight\n                    + childLayoutParams.bottomMargin)\n                val childConsumedInset = (childRange * childLayoutParams.offset).toInt()\n                contentInsets = if (isTopBarView(child)) {\n                    contentInsets.replaceSystemWindowInsets(\n                        contentInsets.systemWindowInsetLeft,\n                        (contentInsets.systemWindowInsetTop - childConsumedInset).coerceAtLeast(0),\n                        contentInsets.systemWindowInsetRight,\n                        contentInsets.systemWindowInsetBottom\n                    )\n                } else {\n                    contentInsets.replaceSystemWindowInsets(\n                        contentInsets.systemWindowInsetLeft,\n                        contentInsets.systemWindowInsetTop,\n                        contentInsets.systemWindowInsetRight,\n                        (contentInsets.systemWindowInsetBottom - childConsumedInset)\n                            .coerceAtLeast(0)\n                    )\n                }\n            }\n        }\n        for (child in children) {\n            if (isContentView(child)) {\n                child.dispatchApplyWindowInsets(contentInsets)\n            }\n        }\n    }\n\n    override fun computeScroll() {\n        val topSettling = topDragger.continueSettling(true)\n        val bottomSettling = bottomDragger.continueSettling(true)\n        if (topSettling || bottomSettling) {\n            postInvalidateOnAnimation()\n        }\n    }\n\n    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {\n        val widthMode = MeasureSpec.getMode(widthMeasureSpec)\n        var widthSize = MeasureSpec.getSize(widthMeasureSpec)\n        val heightMode = MeasureSpec.getMode(heightMeasureSpec)\n        var heightSize = MeasureSpec.getSize(heightMeasureSpec)\n        if (widthMode != MeasureSpec.EXACTLY || heightMode != MeasureSpec.EXACTLY) {\n            if (isInEditMode) {\n                if (widthMode == MeasureSpec.UNSPECIFIED) {\n                    widthSize = 300\n                }\n                if (heightMode == MeasureSpec.UNSPECIFIED) {\n                    heightSize = 300\n                }\n            } else {\n                throw IllegalArgumentException(\n                    \"BarLayout must be measured with MeasureSpec.EXACTLY\"\n                )\n            }\n        }\n        setMeasuredDimension(widthSize, heightSize)\n        var hasTopBar = false\n        var hasBottomBar = false\n        for (child in children) {\n            if (child.visibility == View.GONE) {\n                continue\n            }\n            val isBar = isBarView(child)\n            if (isBar || isFillView(child)) {\n                if (isBar) {\n                    val isTopBar = isTopBarView(child)\n                    check(!((isTopBar && hasTopBar) || (!isTopBar && hasBottomBar))) {\n                        (\"Child $child is a second ${if (isTopBar) \"top\" else \"bottom\"} bar\")\n                    }\n                    if (isTopBar) {\n                        hasTopBar = true\n                    } else {\n                        hasBottomBar = true\n                    }\n                }\n                val childLayoutParams = child.layoutParams as LayoutParams\n                val childWidthSpec = getChildMeasureSpec(\n                    widthMeasureSpec, childLayoutParams.leftMargin + childLayoutParams.rightMargin,\n                    childLayoutParams.width\n                )\n                val childHeightSpec = getChildMeasureSpec(\n                    heightMeasureSpec, childLayoutParams.topMargin + childLayoutParams.bottomMargin,\n                    childLayoutParams.height\n                )\n                child.measure(childWidthSpec, childHeightSpec)\n            } else check(isContentView(child)) {\n                (\"Child $child does not have a valid layout_gravity - must be Gravity.LEFT,\"\n                    + \" Gravity.RIGHT, Gravity.NO_GRAVITY or Gravity.FILL\")\n            }\n        }\n        updateContentViewsWindowInsets()\n        measureContentViews()\n    }\n\n    private fun measureContentViews() {\n        val contentWidth = measuredWidth\n        var contentHeight = measuredHeight\n        for (child in children) {\n            if (child.visibility == View.GONE) {\n                continue\n            }\n            if (isBarView(child)) {\n                val childLayoutParams = child.layoutParams as LayoutParams\n                val childRange = (childLayoutParams.topMargin + child.measuredHeight\n                    + childLayoutParams.bottomMargin)\n                contentHeight -= (childRange * childLayoutParams.offset).toInt()\n            }\n        }\n        for (child in children) {\n            if (child.visibility == View.GONE) {\n                continue\n            }\n            if (isContentView(child)) {\n                val childLayoutParams = child.layoutParams as LayoutParams\n                val childWidthSpec = MeasureSpec.makeMeasureSpec(\n                    contentWidth - childLayoutParams.leftMargin - childLayoutParams.rightMargin,\n                    MeasureSpec.EXACTLY\n                )\n                val contentHeightSpec = MeasureSpec.makeMeasureSpec(\n                    contentHeight - childLayoutParams.topMargin - childLayoutParams.bottomMargin,\n                    MeasureSpec.EXACTLY\n                )\n                child.measure(childWidthSpec, contentHeightSpec)\n            }\n        }\n    }\n\n    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {\n        for (child in children) {\n            if (child.visibility == View.GONE) {\n                continue\n            }\n            if (isBarView(child)) {\n                val childWidth = child.measuredWidth\n                val childHeight = child.measuredHeight\n                val childLayoutParams = child.layoutParams as LayoutParams\n                val childTop = computeBarViewTop(child)\n                val childHorizontalGravity = Gravity.getAbsoluteGravity(\n                    childLayoutParams.gravity, layoutDirection\n                ) and Gravity.HORIZONTAL_GRAVITY_MASK\n                val width = right - left\n                when (childHorizontalGravity) {\n                    Gravity.LEFT -> child.layout(\n                        childLayoutParams.leftMargin, childTop,\n                        childLayoutParams.leftMargin + childWidth, childTop + childHeight\n                    )\n                    Gravity.RIGHT -> {\n                        val childRight = width - childLayoutParams.rightMargin\n                        child.layout(\n                            childRight - childWidth, childTop, childRight, childTop + childHeight\n                        )\n                    }\n                    Gravity.CENTER_HORIZONTAL -> {\n                        val childLeft = ((width - childWidth) / 2 + childLayoutParams.leftMargin\n                            - childLayoutParams.rightMargin)\n                        child.layout(\n                            childLeft, childTop, childLeft + childWidth, childTop + childHeight\n                        )\n                    }\n                    else -> child.layout(\n                        childLayoutParams.leftMargin, childTop,\n                        childLayoutParams.leftMargin + childWidth, childTop + childHeight\n                    )\n                }\n                child.isInvisible = childLayoutParams.offset <= 0\n            } else if (isFillView(child)) {\n                val childLayoutParams = child.layoutParams as LayoutParams\n                child.layout(\n                    childLayoutParams.leftMargin, childLayoutParams.topMargin,\n                    childLayoutParams.leftMargin + child.measuredWidth,\n                    childLayoutParams.topMargin + child.measuredHeight\n                )\n            }\n        }\n        layoutContentViews()\n    }\n\n    private fun computeBarViewTop(barView: View): Int {\n        val childLayoutParams = barView.layoutParams as LayoutParams\n        val childRange = (childLayoutParams.topMargin + barView.measuredHeight\n            + childLayoutParams.bottomMargin)\n        return if (isTopBarView(barView)) {\n            (-childRange + (childRange * childLayoutParams.offset).toInt()\n                + childLayoutParams.topMargin)\n        } else {\n            (measuredHeight - (childRange * childLayoutParams.offset).toInt()\n                + childLayoutParams.bottomMargin)\n        }\n    }\n\n    private fun layoutContentViews() {\n        var contentTop = 0\n        for (child in children) {\n            if (child.visibility == View.GONE) {\n                continue\n            }\n            if (isBarView(child)) {\n                if (isTopBarView(child)) {\n                    val childLayoutParams = child.layoutParams as LayoutParams\n                    contentTop = child.bottom + childLayoutParams.bottomMargin\n                }\n            }\n        }\n        for (child in children) {\n            if (child.visibility == View.GONE) {\n                continue\n            }\n            if (isContentView(child)) {\n                val childLayoutParams = child.layoutParams as LayoutParams\n                val childTop = contentTop + childLayoutParams.topMargin\n                child.layout(\n                    childLayoutParams.leftMargin, childTop,\n                    childLayoutParams.leftMargin + child.measuredWidth,\n                    childTop + child.measuredHeight\n                )\n            }\n        }\n    }\n\n    override fun generateLayoutParams(attrs: AttributeSet?): ViewGroup.LayoutParams =\n        LayoutParams(context, attrs)\n\n    override fun generateLayoutParams(\n        layoutParams: ViewGroup.LayoutParams\n    ): ViewGroup.LayoutParams =\n        when (layoutParams) {\n            is LayoutParams -> LayoutParams(layoutParams)\n            is MarginLayoutParams -> LayoutParams(layoutParams)\n            else -> LayoutParams(layoutParams)\n        }\n\n    override fun generateDefaultLayoutParams(): ViewGroup.LayoutParams =\n        LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)\n\n    override fun checkLayoutParams(layoutParams: ViewGroup.LayoutParams): Boolean =\n        layoutParams is LayoutParams && super.checkLayoutParams(layoutParams)\n\n    fun isBarShown(barView: View): Boolean {\n        require(isBarView(barView)) { \"View $barView is not a bar\" }\n        val childLayoutParams = barView.layoutParams as LayoutParams\n        return childLayoutParams.isShown\n    }\n\n    fun isBarShown(gravity: Int): Boolean {\n        val barView = findBarView(gravity)\n            ?: throw IllegalArgumentException(\"No bar view found with gravity $gravity\")\n        return isBarShown(barView)\n    }\n\n    fun showBar(barView: View, animate: Boolean = true) {\n        require(isBarView(barView)) { \"View $barView is not a bar\" }\n        val childLayoutParams = barView.layoutParams as LayoutParams\n        if (childLayoutParams.isShown && childLayoutParams.offset == 1f) {\n            return\n        }\n        childLayoutParams.isShown = true\n        if (!isLaidOut) {\n            childLayoutParams.offset = 1f\n        } else if (animate) {\n            if (isTopBarView(barView)) {\n                topDragger.smoothSlideViewTo(barView, barView.left, 0)\n            } else {\n                bottomDragger.smoothSlideViewTo(\n                    barView, barView.left, height - barView.height - childLayoutParams.bottomMargin\n                )\n            }\n        } else {\n            moveBarToOffset(barView, 1f)\n            updateContentViewsWindowInsets()\n            measureContentViews()\n            layoutContentViews()\n        }\n        invalidate()\n    }\n\n    fun showBar(gravity: Int, animate: Boolean = true) {\n        val barView = findBarView(gravity)\n            ?: throw IllegalArgumentException(\"No bar view found with gravity $gravity\")\n        showBar(barView, animate)\n    }\n\n    fun hideBar(barView: View, animate: Boolean = true) {\n        require(isBarView(barView)) { \"View $barView is not a bar\" }\n        val childLayoutParams = barView.layoutParams as LayoutParams\n        if (!childLayoutParams.isShown && childLayoutParams.offset == 0f) {\n            return\n        }\n        childLayoutParams.isShown = false\n        if (!isLaidOut) {\n            childLayoutParams.offset = 0f\n        } else if (animate) {\n            if (isTopBarView(barView)) {\n                topDragger.smoothSlideViewTo(\n                    barView, barView.left, -barView.height - childLayoutParams.bottomMargin\n                )\n            } else {\n                bottomDragger.smoothSlideViewTo(barView, barView.left, height)\n            }\n        } else {\n            childLayoutParams.offset = 0f\n            moveBarToOffset(barView, 0f)\n            updateContentViewsWindowInsets()\n            measureContentViews()\n            layoutContentViews()\n        }\n        invalidate()\n    }\n\n    fun hideBar(gravity: Int, animate: Boolean = true) {\n        val barView = findBarView(gravity)\n            ?: throw IllegalArgumentException(\"No bar view found with gravity $gravity\")\n        hideBar(barView, animate)\n    }\n\n    private fun moveBarToOffset(barView: View, offset: Float) {\n        val childLayoutParams = barView.layoutParams as LayoutParams\n        if (childLayoutParams.offset == offset) {\n            return\n        }\n        childLayoutParams.offset = offset\n        val oldChildTop = barView.top\n        val newChildTop = computeBarViewTop(barView)\n        barView.offsetTopAndBottom(newChildTop - oldChildTop)\n        barView.isInvisible = offset <= 0\n    }\n\n    fun toggleBar(barView: View) {\n        if (isBarShown(barView)) {\n            hideBar(barView)\n        } else {\n            showBar(barView)\n        }\n    }\n\n    fun toggleBar(gravity: Int) {\n        val barView = findBarView(gravity)\n            ?: throw IllegalArgumentException(\"No bar view found with gravity $gravity\")\n        toggleBar(barView)\n    }\n\n    private fun findBarView(gravity: Int): View? {\n        val verticalGravity = gravity and Gravity.VERTICAL_GRAVITY_MASK\n        for (child in children) {\n            val childVerticalGravity = getChildVerticalGravity(child)\n            if (childVerticalGravity == verticalGravity) {\n                return child\n            }\n        }\n        return null\n    }\n\n    private fun isBarView(child: View): Boolean {\n        val verticalGravity = getChildVerticalGravity(child)\n        return verticalGravity == Gravity.TOP || verticalGravity == Gravity.BOTTOM\n    }\n\n    private fun isTopBarView(barView: View): Boolean {\n        val verticalGravity = getChildVerticalGravity(barView)\n        return verticalGravity == Gravity.TOP\n    }\n\n    private fun isContentView(child: View): Boolean {\n        return getChildGravity(child) == Gravity.NO_GRAVITY\n    }\n\n    private fun isFillView(child: View): Boolean {\n        return getChildGravity(child) == Gravity.FILL\n    }\n\n    private fun getChildGravity(child: View): Int {\n        return (child.layoutParams as LayoutParams).gravity\n    }\n\n    private fun getChildVerticalGravity(child: View): Int {\n        return getChildGravity(child) and Gravity.VERTICAL_GRAVITY_MASK\n    }\n\n    private inner class ViewDragCallback(private val gravity: Int) : ViewDragHelper.Callback() {\n        override fun tryCaptureView(child: View, pointerId: Int): Boolean = false\n\n        override fun onViewPositionChanged(\n            changedView: View, left: Int, top: Int, dx: Int, dy: Int\n        ) {\n            val childRange = getViewVerticalDragRange(changedView)\n            val childLayoutParams = changedView.layoutParams as LayoutParams\n            if (isTopBarView(changedView)) {\n                childLayoutParams.offset = (top - childLayoutParams.topMargin + childRange)\n                    .toFloat() / childRange\n            } else {\n                val height = height\n                childLayoutParams.offset = ((childLayoutParams.topMargin + height - top).toFloat()\n                    / childRange)\n            }\n            changedView.isInvisible = childLayoutParams.offset <= 0\n            updateContentViewsWindowInsets()\n            measureContentViews()\n            layoutContentViews()\n        }\n\n        override fun onViewCaptured(capturedChild: View, activePointerId: Int) {\n            closeOtherBar()\n        }\n\n        private fun closeOtherBar() {\n            val otherGravity = if (gravity == Gravity.TOP) Gravity.BOTTOM else Gravity.TOP\n            val otherBar = findBarView(otherGravity)\n            otherBar?.let { hideBar(it) }\n        }\n\n        override fun getViewVerticalDragRange(child: View): Int {\n            if (!isBarView(child)) {\n                return 0\n            }\n            val childLayoutParams = child.layoutParams as LayoutParams\n            return childLayoutParams.topMargin + child.height + childLayoutParams.bottomMargin\n        }\n\n        override fun clampViewPositionHorizontal(child: View, left: Int, dx: Int): Int = child.left\n\n        override fun clampViewPositionVertical(child: View, top: Int, dy: Int): Int =\n            if (isTopBarView(child)) {\n                top.coerceIn(-getViewVerticalDragRange(child)..0)\n            } else {\n                val height = height\n                top.coerceIn(height - getViewVerticalDragRange(child)..height)\n            }\n    }\n\n    class LayoutParams : MarginLayoutParams {\n        var gravity = Gravity.NO_GRAVITY\n        var offset = 0f\n        var isShown = false\n\n        constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {\n            gravity = context.obtainStyledAttributes(attrs, ATTRS)\n                .use { it.getInt(0, Gravity.NO_GRAVITY) }\n        }\n\n        constructor(width: Int, height: Int) : super(width, height)\n\n        constructor(width: Int, height: Int, gravity: Int) : this(width, height) {\n            this.gravity = gravity\n        }\n\n        constructor(source: LayoutParams) : super(source) {\n            gravity = source.gravity\n        }\n\n        constructor(source: MarginLayoutParams) : super(source)\n\n        constructor(source: ViewGroup.LayoutParams) : super(source)\n\n        companion object {\n            private val ATTRS = intArrayOf(android.R.attr.layout_gravity)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/PersistentBarLayoutToolbarActionMode.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.view.ViewGroup\nimport androidx.appcompat.widget.Toolbar\n\nclass PersistentBarLayoutToolbarActionMode(\n    private val persistentBarLayout: PersistentBarLayout,\n    bar: ViewGroup,\n    toolbar: Toolbar\n) : ToolbarActionMode(bar, toolbar) {\n    override fun show(bar: ViewGroup, animate: Boolean) {\n        persistentBarLayout.showBar(bar, animate)\n    }\n\n    override fun hide(bar: ViewGroup, animate: Boolean) {\n        persistentBarLayout.hideBar(bar, animate)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/PersistentDrawerLayout.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.Gravity\nimport android.view.View\nimport android.view.ViewGroup\nimport android.view.WindowInsets\nimport androidx.annotation.AttrRes\nimport androidx.annotation.StyleRes\nimport androidx.core.content.res.use\nimport androidx.core.view.children\nimport androidx.core.view.isInvisible\nimport androidx.customview.widget.ViewDragHelper\nimport me.zhanghai.android.files.util.layoutInStatusBar\n\nclass PersistentDrawerLayout @JvmOverloads constructor(\n    context: Context,\n    attrs: AttributeSet? = null,\n    @AttrRes defStyleAttr: Int = 0,\n    @StyleRes defStyleRes: Int = 0\n) : ViewGroup(context, attrs, defStyleAttr, defStyleRes) {\n    private val leftDragger = ViewDragHelper.create(this, ViewDragCallback(Gravity.LEFT))\n    private val rightDragger = ViewDragHelper.create(this, ViewDragCallback(Gravity.RIGHT))\n\n    private var lastInsets: WindowInsets? = null\n\n    init {\n        if (fitsSystemWindows) {\n            layoutInStatusBar = true\n        }\n    }\n\n    override fun dispatchApplyWindowInsets(insets: WindowInsets): WindowInsets {\n        if (!fitsSystemWindows) {\n            return insets\n        }\n        for (child in children) {\n            if (isDrawerView(child)) {\n                if (isLeftDrawerView(child)) {\n                    child.dispatchApplyWindowInsets(\n                        insets.replaceSystemWindowInsets(\n                            insets.systemWindowInsetLeft, insets.systemWindowInsetTop, 0,\n                            insets.systemWindowInsetBottom\n                        )\n                    )\n                } else {\n                    child.dispatchApplyWindowInsets(\n                        insets.replaceSystemWindowInsets(\n                            0,\n                            insets.systemWindowInsetTop, insets.systemWindowInsetRight,\n                            insets.systemWindowInsetBottom\n                        )\n                    )\n                }\n            } else if (isFillView(child)) {\n                child.dispatchApplyWindowInsets(insets)\n            }\n        }\n        lastInsets = insets\n        updateContentViewsWindowInsets()\n        return insets.consumeSystemWindowInsets()\n    }\n\n    private fun updateContentViewsWindowInsets() {\n        var contentInsets = lastInsets ?: return\n        for (child in children) {\n            if (isDrawerView(child)) {\n                val childLayoutParams = child.layoutParams as LayoutParams\n                val childRange = (childLayoutParams.leftMargin + child.measuredWidth\n                    + childLayoutParams.rightMargin)\n                val childConsumedInset = (childRange * childLayoutParams.offset).toInt()\n                contentInsets = if (isLeftDrawerView(child)) {\n                    contentInsets.replaceSystemWindowInsets(\n                        (contentInsets.systemWindowInsetLeft - childConsumedInset).coerceAtLeast(0),\n                        contentInsets.systemWindowInsetTop,\n                        contentInsets.systemWindowInsetRight,\n                        contentInsets.systemWindowInsetBottom\n                    )\n                } else {\n                    contentInsets.replaceSystemWindowInsets(\n                        contentInsets.systemWindowInsetLeft,\n                        contentInsets.systemWindowInsetTop,\n                        (contentInsets.systemWindowInsetRight - childConsumedInset)\n                            .coerceAtLeast(0),\n                        contentInsets.systemWindowInsetBottom\n                    )\n                }\n            }\n        }\n        for (child in children) {\n            if (isContentView(child)) {\n                child.dispatchApplyWindowInsets(contentInsets)\n            }\n        }\n    }\n\n    override fun computeScroll() {\n        val leftSettling = leftDragger.continueSettling(true)\n        val rightSettling = rightDragger.continueSettling(true)\n        if (leftSettling || rightSettling) {\n            postInvalidateOnAnimation()\n        }\n    }\n\n    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {\n        val widthMode = MeasureSpec.getMode(widthMeasureSpec)\n        var widthSize = MeasureSpec.getSize(widthMeasureSpec)\n        val heightMode = MeasureSpec.getMode(heightMeasureSpec)\n        var heightSize = MeasureSpec.getSize(heightMeasureSpec)\n        if (widthMode != MeasureSpec.EXACTLY || heightMode != MeasureSpec.EXACTLY) {\n            if (isInEditMode) {\n                if (widthMode == MeasureSpec.UNSPECIFIED) {\n                    widthSize = 300\n                }\n                if (heightMode == MeasureSpec.UNSPECIFIED) {\n                    heightSize = 300\n                }\n            } else {\n                throw IllegalArgumentException(\n                    \"DrawerLayout must be measured with MeasureSpec.EXACTLY\"\n                )\n            }\n        }\n        setMeasuredDimension(widthSize, heightSize)\n        var hasLeftDrawer = false\n        var hasRightDrawer = false\n        for (child in children) {\n            if (child.visibility == View.GONE) {\n                continue\n            }\n            val isDrawer = isDrawerView(child)\n            if (isDrawer || isFillView(child)) {\n                if (isDrawer) {\n                    val isLeftDrawer = isLeftDrawerView(child)\n                    check(!((isLeftDrawer && hasLeftDrawer) || (!isLeftDrawer && hasRightDrawer))) {\n                        (\"Child $child is a second ${if (isLeftDrawer) \"left\" else \"right\"} drawer\")\n                    }\n                    if (isLeftDrawer) {\n                        hasLeftDrawer = true\n                    } else {\n                        hasRightDrawer = true\n                    }\n                }\n                val childLayoutParams = child.layoutParams as LayoutParams\n                val childWidthSpec = getChildMeasureSpec(\n                    widthMeasureSpec, childLayoutParams.leftMargin + childLayoutParams.rightMargin,\n                    childLayoutParams.width\n                )\n                val childHeightSpec = getChildMeasureSpec(\n                    heightMeasureSpec, childLayoutParams.topMargin + childLayoutParams.bottomMargin,\n                    childLayoutParams.height\n                )\n                child.measure(childWidthSpec, childHeightSpec)\n            } else check(isContentView(child)) {\n                (\"Child $child does not have a valid layout_gravity - must be Gravity.LEFT,\"\n                    + \" Gravity.RIGHT, Gravity.NO_GRAVITY or Gravity.FILL\")\n            }\n        }\n        updateContentViewsWindowInsets()\n        measureContentViews()\n    }\n\n    private fun measureContentViews() {\n        var contentWidth = measuredWidth\n        val contentHeight = measuredHeight\n        for (child in children) {\n            if (child.visibility == View.GONE) {\n                continue\n            }\n            if (isDrawerView(child)) {\n                val childLayoutParams = child.layoutParams as LayoutParams\n                val childRange = (childLayoutParams.leftMargin + child.measuredWidth\n                    + childLayoutParams.rightMargin)\n                contentWidth -= (childRange * childLayoutParams.offset).toInt()\n            }\n        }\n        for (child in children) {\n            if (child.visibility == View.GONE) {\n                continue\n            }\n            if (isContentView(child)) {\n                val childLayoutParams = child.layoutParams as LayoutParams\n                val childWidthSpec = MeasureSpec.makeMeasureSpec(\n                    contentWidth - childLayoutParams.leftMargin - childLayoutParams.rightMargin,\n                    MeasureSpec.EXACTLY\n                )\n                val contentHeightSpec = MeasureSpec.makeMeasureSpec(\n                    contentHeight - childLayoutParams.topMargin - childLayoutParams.bottomMargin,\n                    MeasureSpec.EXACTLY\n                )\n                child.measure(childWidthSpec, contentHeightSpec)\n            }\n        }\n    }\n\n    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {\n        for (child in children) {\n            if (child.visibility == View.GONE) {\n                continue\n            }\n            if (isDrawerView(child)) {\n                val childWidth = child.measuredWidth\n                val childHeight = child.measuredHeight\n                val childLayoutParams = child.layoutParams as LayoutParams\n                val childLeft = computeDrawerViewLeft(child)\n                val childVerticalGravity = (childLayoutParams.gravity\n                    and Gravity.VERTICAL_GRAVITY_MASK)\n                val height = bottom - top\n                when (childVerticalGravity) {\n                    Gravity.TOP -> child.layout(\n                        childLeft, childLayoutParams.topMargin, childLeft + childWidth,\n                        childLayoutParams.topMargin + childHeight\n                    )\n                    Gravity.BOTTOM -> {\n                        val childBottom = height - childLayoutParams.bottomMargin\n                        child.layout(\n                            childLeft, childBottom - childHeight, childLeft + childWidth,\n                            childBottom\n                        )\n                    }\n                    Gravity.CENTER_VERTICAL -> {\n                        val childTop = ((height - childHeight) / 2 + childLayoutParams.topMargin\n                            - childLayoutParams.bottomMargin)\n                        child.layout(\n                            childLeft, childTop, childLeft + childWidth, childTop + childHeight\n                        )\n                    }\n                    else -> child.layout(\n                        childLeft, childLayoutParams.topMargin, childLeft + childWidth,\n                        childLayoutParams.topMargin + childHeight\n                    )\n                }\n                child.isInvisible = childLayoutParams.offset <= 0\n            } else if (isFillView(child)) {\n                val childLayoutParams = child.layoutParams as LayoutParams\n                child.layout(\n                    childLayoutParams.leftMargin, childLayoutParams.topMargin,\n                    childLayoutParams.leftMargin + child.measuredWidth,\n                    childLayoutParams.topMargin + child.measuredHeight\n                )\n            }\n        }\n        layoutContentViews()\n    }\n\n    private fun computeDrawerViewLeft(drawerView: View): Int {\n        val childLayoutParams = drawerView.layoutParams as LayoutParams\n        val childRange = (childLayoutParams.leftMargin + drawerView.measuredWidth\n            + childLayoutParams.rightMargin)\n        return if (isLeftDrawerView(drawerView)) {\n            (-childRange + (childRange * childLayoutParams.offset).toInt()\n                + childLayoutParams.leftMargin)\n        } else {\n            (measuredWidth - (childRange * childLayoutParams.offset).toInt()\n                + childLayoutParams.leftMargin)\n        }\n    }\n\n    private fun layoutContentViews() {\n        var contentLeft = 0\n        for (child in children) {\n            if (child.visibility == View.GONE) {\n                continue\n            }\n            if (isDrawerView(child)) {\n                if (isLeftDrawerView(child)) {\n                    val childLayoutParams = child.layoutParams as LayoutParams\n                    contentLeft = child.right + childLayoutParams.rightMargin\n                }\n            }\n        }\n        for (child in children) {\n            if (child.visibility == View.GONE) {\n                continue\n            }\n            if (isContentView(child)) {\n                val childLayoutParams = child.layoutParams as LayoutParams\n                val childLeft = contentLeft + childLayoutParams.leftMargin\n                child.layout(\n                    childLeft, childLayoutParams.topMargin, childLeft + child.measuredWidth,\n                    childLayoutParams.topMargin + child.measuredHeight\n                )\n            }\n        }\n    }\n\n    override fun generateLayoutParams(attrs: AttributeSet?): ViewGroup.LayoutParams =\n        LayoutParams(context, attrs)\n\n    override fun generateLayoutParams(\n        layoutParams: ViewGroup.LayoutParams\n    ): ViewGroup.LayoutParams =\n        when (layoutParams) {\n            is LayoutParams -> LayoutParams(layoutParams)\n            is MarginLayoutParams -> LayoutParams(layoutParams)\n            else -> LayoutParams(layoutParams)\n        }\n\n    override fun generateDefaultLayoutParams(): ViewGroup.LayoutParams =\n        LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)\n\n    override fun checkLayoutParams(layoutParams: ViewGroup.LayoutParams): Boolean =\n        layoutParams is LayoutParams && super.checkLayoutParams(layoutParams)\n\n    fun isDrawerOpen(drawerView: View): Boolean {\n        require(isDrawerView(drawerView)) { \"View $drawerView is not a drawer\" }\n        val childLayoutParams = drawerView.layoutParams as LayoutParams\n        return childLayoutParams.isOpen\n    }\n\n    fun isDrawerOpen(gravity: Int): Boolean {\n        val drawerView = findDrawerView(gravity)\n            ?: throw IllegalArgumentException(\"No drawer view found with gravity $gravity\")\n        return isDrawerOpen(drawerView)\n    }\n\n    fun openDrawer(drawerView: View, animate: Boolean = true) {\n        require(isDrawerView(drawerView)) { \"View $drawerView is not a drawer\" }\n        val childLayoutParams = drawerView.layoutParams as LayoutParams\n        if (childLayoutParams.isOpen && childLayoutParams.offset == 1f) {\n            return\n        }\n        childLayoutParams.isOpen = true\n        if (!isLaidOut) {\n            childLayoutParams.offset = 1f\n        } else if (animate) {\n            if (isLeftDrawerView(drawerView)) {\n                leftDragger.smoothSlideViewTo(drawerView, 0, drawerView.top)\n            } else {\n                rightDragger.smoothSlideViewTo(\n                    drawerView, width - drawerView.width - childLayoutParams.rightMargin,\n                    drawerView.top\n                )\n            }\n        } else {\n            moveDrawerToOffset(drawerView, 1f)\n            updateContentViewsWindowInsets()\n            measureContentViews()\n            layoutContentViews()\n        }\n        invalidate()\n    }\n\n    fun openDrawer(gravity: Int, animate: Boolean = true) {\n        val drawerView = findDrawerView(gravity)\n            ?: throw IllegalArgumentException(\"No drawer view found with gravity $gravity\")\n        openDrawer(drawerView, animate)\n    }\n\n    fun closeDrawer(drawerView: View, animate: Boolean = true) {\n        require(isDrawerView(drawerView)) { \"View $drawerView is not a drawer\" }\n        val childLayoutParams = drawerView.layoutParams as LayoutParams\n        if (!childLayoutParams.isOpen && childLayoutParams.offset == 0f) {\n            return\n        }\n        childLayoutParams.isOpen = false\n        if (!isLaidOut) {\n            childLayoutParams.offset = 0f\n        } else if (animate) {\n            if (isLeftDrawerView(drawerView)) {\n                leftDragger.smoothSlideViewTo(\n                    drawerView, -drawerView.width - childLayoutParams.rightMargin, drawerView.top\n                )\n            } else {\n                rightDragger.smoothSlideViewTo(drawerView, width, drawerView.top)\n            }\n        } else {\n            childLayoutParams.offset = 0f\n            moveDrawerToOffset(drawerView, 0f)\n            updateContentViewsWindowInsets()\n            measureContentViews()\n            layoutContentViews()\n        }\n        invalidate()\n    }\n\n    fun closeDrawer(gravity: Int, animate: Boolean = true) {\n        val drawerView = findDrawerView(gravity)\n            ?: throw IllegalArgumentException(\"No drawer view found with gravity $gravity\")\n        closeDrawer(drawerView, animate)\n    }\n\n    private fun moveDrawerToOffset(drawerView: View, offset: Float) {\n        val childLayoutParams = drawerView.layoutParams as LayoutParams\n        if (childLayoutParams.offset == offset) {\n            return\n        }\n        childLayoutParams.offset = offset\n        val oldChildLeft = drawerView.left\n        val newChildLeft = computeDrawerViewLeft(drawerView)\n        drawerView.offsetLeftAndRight(newChildLeft - oldChildLeft)\n        drawerView.isInvisible = offset <= 0\n    }\n\n    fun toggleDrawer(drawerView: View) {\n        if (isDrawerOpen(drawerView)) {\n            closeDrawer(drawerView)\n        } else {\n            openDrawer(drawerView)\n        }\n    }\n\n    fun toggleDrawer(gravity: Int) {\n        val drawerView = findDrawerView(gravity)\n            ?: throw IllegalArgumentException(\"No drawer view found with gravity $gravity\")\n        toggleDrawer(drawerView)\n    }\n\n    private fun findDrawerView(gravity: Int): View? {\n        val horizontalGravity = (Gravity.getAbsoluteGravity(gravity, layoutDirection)\n            and Gravity.HORIZONTAL_GRAVITY_MASK)\n        for (child in children) {\n            val childHorizontalGravity = getChildAbsoluteHorizontalGravity(child)\n            if (childHorizontalGravity == horizontalGravity) {\n                return child\n            }\n        }\n        return null\n    }\n\n    private fun isDrawerView(child: View): Boolean {\n        val horizontalGravity = getChildAbsoluteHorizontalGravity(child)\n        return horizontalGravity == Gravity.LEFT || horizontalGravity == Gravity.RIGHT\n    }\n\n    private fun isLeftDrawerView(drawerView: View): Boolean {\n        val horizontalGravity = getChildAbsoluteHorizontalGravity(drawerView)\n        return horizontalGravity == Gravity.LEFT\n    }\n\n    private fun isContentView(child: View): Boolean {\n        return getChildGravity(child) == Gravity.NO_GRAVITY\n    }\n\n    private fun isFillView(child: View): Boolean {\n        return getChildGravity(child) == Gravity.FILL\n    }\n\n    private fun getChildGravity(child: View): Int {\n        return (child.layoutParams as LayoutParams).gravity\n    }\n\n    private fun getChildAbsoluteHorizontalGravity(child: View): Int {\n        return (Gravity.getAbsoluteGravity(getChildGravity(child), layoutDirection)\n            and Gravity.HORIZONTAL_GRAVITY_MASK)\n    }\n\n    private inner class ViewDragCallback(private val gravity: Int) : ViewDragHelper.Callback() {\n        override fun tryCaptureView(child: View, pointerId: Int): Boolean = false\n\n        override fun onViewPositionChanged(\n            changedView: View, left: Int, top: Int, dx: Int, dy: Int\n        ) {\n            val childRange = getViewHorizontalDragRange(changedView)\n            val childLayoutParams = changedView.layoutParams as LayoutParams\n            if (isLeftDrawerView(changedView)) {\n                childLayoutParams.offset = (left - childLayoutParams.leftMargin + childRange)\n                    .toFloat() / childRange\n            } else {\n                val width = width\n                childLayoutParams.offset = ((childLayoutParams.leftMargin + width - left).toFloat()\n                    / childRange)\n            }\n            changedView.isInvisible = childLayoutParams.offset <= 0\n            updateContentViewsWindowInsets()\n            measureContentViews()\n            layoutContentViews()\n        }\n\n        override fun onViewCaptured(capturedChild: View, activePointerId: Int) {\n            closeOtherDrawer()\n        }\n\n        private fun closeOtherDrawer() {\n            val otherGravity = if (gravity == Gravity.LEFT) Gravity.RIGHT else Gravity.LEFT\n            val otherDrawer = findDrawerView(otherGravity)\n            otherDrawer?.let { closeDrawer(it) }\n        }\n\n        override fun getViewHorizontalDragRange(child: View): Int {\n            if (!isDrawerView(child)) {\n                return 0\n            }\n            val childLayoutParams = child.layoutParams as LayoutParams\n            return childLayoutParams.leftMargin + child.width + childLayoutParams.rightMargin\n        }\n\n        override fun clampViewPositionHorizontal(child: View, left: Int, dx: Int): Int =\n            if (isLeftDrawerView(child)) {\n                left.coerceIn(-getViewHorizontalDragRange(child)..0)\n            } else {\n                val width = width\n                left.coerceIn(width - getViewHorizontalDragRange(child)..width)\n            }\n\n        override fun clampViewPositionVertical(child: View, top: Int, dy: Int): Int = child.top\n    }\n\n    class LayoutParams : MarginLayoutParams {\n        var gravity = Gravity.NO_GRAVITY\n        var offset = 0f\n        var isOpen = false\n\n        constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {\n            gravity = context.obtainStyledAttributes(attrs, ATTRS)\n                .use { it.getInt(0, Gravity.NO_GRAVITY) }\n        }\n\n        constructor(width: Int, height: Int) : super(width, height)\n\n        constructor(width: Int, height: Int, gravity: Int) : this(width, height) {\n            this.gravity = gravity\n        }\n\n        constructor(source: LayoutParams) : super(source) {\n            gravity = source.gravity\n        }\n\n        constructor(source: MarginLayoutParams) : super(source)\n\n        constructor(source: ViewGroup.LayoutParams) : super(source)\n\n        companion object {\n            private val ATTRS = intArrayOf(android.R.attr.layout_gravity)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/PreferenceFragmentCompat.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.os.Bundle\nimport android.view.View\nimport androidx.preference.ListPreference\nimport androidx.preference.Preference\nimport com.takisoft.preferencex.PreferenceFragmentCompat as TakisoftPreferenceFragmentCompat\n\nabstract class PreferenceFragmentCompat : TakisoftPreferenceFragmentCompat() {\n    // @see https://github.com/Gericop/Android-Support-Preference-V7-Fix/issues/201\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        if (preferenceScreen == null) {\n            val preferenceScreen = preferenceManager.createPreferenceScreen(requireContext())\n            setPreferenceScreen(preferenceScreen)\n        }\n\n        super.onViewCreated(view, savedInstanceState)\n    }\n\n    override fun onDisplayPreferenceDialog(preference: Preference) {\n        if (parentFragmentManager.findFragmentByTag(DIALOG_FRAGMENT_TAG) == null\n            && preference is ListPreference) {\n            displayPreferenceDialog(MaterialListPreferenceDialogFragmentCompat(), preference.key)\n        } else {\n            super.onDisplayPreferenceDialog(preference)\n        }\n    }\n\n    companion object {\n        // @see PreferenceFragmentCompat.DIALOG_FRAGMENT_TAG\n        private const val DIALOG_FRAGMENT_TAG = \"androidx.preference.PreferenceFragment.DIALOG\"\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/ReadOnlyTextInputEditText.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.content.Context\nimport android.graphics.Color\nimport android.graphics.drawable.Drawable\nimport android.graphics.drawable.RippleDrawable\nimport android.os.Build\nimport android.text.method.MovementMethod\nimport android.util.AttributeSet\nimport android.view.View\nimport androidx.annotation.AttrRes\nimport com.google.android.material.shape.MaterialShapeDrawable\nimport com.google.android.material.textfield.TextInputEditText\nimport me.zhanghai.android.files.util.getColorStateListByAttr\n\nclass ReadOnlyTextInputEditText : TextInputEditText {\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    init {\n        setTextIsSelectable(isTextSelectable)\n    }\n\n    override fun getFreezesText(): Boolean = false\n\n    override fun getDefaultEditable(): Boolean = false\n\n    override fun getDefaultMovementMethod(): MovementMethod? = null\n\n    override fun setTextIsSelectable(selectable: Boolean) {\n        super.setTextIsSelectable(selectable)\n\n        if (selectable) {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n                focusable = View.FOCUSABLE_AUTO\n            }\n        } else {\n            isClickable = false\n            isFocusable = false\n        }\n        background = background\n    }\n\n    override fun setBackgroundDrawable(background: Drawable?) {\n        var background = background\n        if (isTextSelectable) {\n            if (background is RippleDrawable) {\n                val content = background.findDrawableByLayerId(0)\n                if (content is MaterialShapeDrawable) {\n                    background = content\n                }\n            }\n        } else {\n            if (background is MaterialShapeDrawable) {\n                background = addRippleEffect(background)\n            }\n        }\n        @Suppress(\"DEPRECATION\")\n        super.setBackgroundDrawable(background)\n    }\n\n    // @see com.google.android.material.textfield.DropdownMenuEndIconDelegate#addRippleEffect(\n    //      AutoCompleteTextView)\n    private fun addRippleEffect(boxBackground: MaterialShapeDrawable): Drawable {\n        val rippleColor =\n            context.getColorStateListByAttr(androidx.appcompat.R.attr.colorControlHighlight)\n        val mask = MaterialShapeDrawable(boxBackground.shapeAppearanceModel)\n            .apply { setTint(Color.WHITE) }\n        return RippleDrawable(rippleColor, boxBackground, mask)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/ReadOnlyTextInputLayout.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.EditText\nimport androidx.annotation.AttrRes\nimport com.google.android.material.textfield.TextInputLayout\nimport me.zhanghai.android.files.compat.getDrawableCompat\n\nclass ReadOnlyTextInputLayout : TextInputLayout {\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    init {\n        isExpandedHintEnabled = false\n    }\n\n    override fun addView(child: View, index: Int, params: ViewGroup.LayoutParams) {\n        super.addView(child, index, params)\n\n        if (child is EditText) {\n            setDropDown(!child.isTextSelectable)\n        }\n    }\n\n    fun setDropDown(dropDown: Boolean) {\n        if (dropDown) {\n            endIconMode = END_ICON_CUSTOM\n            endIconDrawable = context.getDrawableCompat(\n                com.google.android.material.R.drawable.mtrl_ic_arrow_drop_down\n            )\n        } else {\n            endIconMode = END_ICON_NONE\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/SaveStateSubsamplingScaleImageView.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.content.Context\nimport android.os.Parcelable\nimport android.util.AttributeSet\nimport com.davemorrissey.labs.subscaleview.ImageSource\nimport com.davemorrissey.labs.subscaleview.ImageViewState\nimport com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.util.ParcelableState\n\nclass SaveStateSubsamplingScaleImageView : SubsamplingScaleImageView {\n    private var pendingState: ImageViewState? = null\n\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attr: AttributeSet?) : super(context, attr)\n\n    fun setImageRestoringSavedState(imageSource: ImageSource) {\n        setImage(imageSource, pendingState)\n        pendingState = null\n    }\n\n    override fun onSaveInstanceState(): Parcelable = State(super.onSaveInstanceState(), state)\n\n    override fun onRestoreInstanceState(state: Parcelable) {\n        state as State\n        super.onRestoreInstanceState(state.superState)\n        pendingState = state.state\n    }\n\n    @Parcelize\n    private class State(val superState: Parcelable?, val state: ImageViewState?) : ParcelableState\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/ScrollingChildEditText.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport androidx.appcompat.widget.AppCompatEditText\n\nclass ScrollingChildEditText : AppCompatEditText {\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    // onMeasure() calls registerForPreDraw() and onPreDraw() calls bringPointIntoView(), which\n    // results in unwanted scroll when IME is toggled.\n    override fun onPreDraw(): Boolean = true\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/ScrollingViewOnApplyWindowInsetsListener.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.graphics.Rect\nimport android.view.View\nimport android.view.WindowInsets\nimport me.zhanghai.android.fastscroll.FastScroller\n\nclass ScrollingViewOnApplyWindowInsetsListener(\n    view: View,\n    private val fastScroller: FastScroller? = null\n) : View.OnApplyWindowInsetsListener {\n    private val initialPadding =\n        Rect(view.paddingLeft, view.paddingTop, view.paddingRight, view.paddingBottom)\n\n    init {\n        fastScroller?.setPadding(0, 0, 0, 0)\n    }\n\n    override fun onApplyWindowInsets(view: View, insets: WindowInsets): WindowInsets {\n        view.setPadding(\n            initialPadding.left, initialPadding.top, initialPadding.right,\n            initialPadding.bottom + insets.systemWindowInsetBottom\n        )\n        fastScroller?.setPadding(0, 0, 0, insets.systemWindowInsetBottom)\n        return insets\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/SimpleAdapter.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport androidx.recyclerview.widget.RecyclerView\n\nabstract class SimpleAdapter<T, VH : RecyclerView.ViewHolder> : RecyclerView.Adapter<VH>() {\n    private val _list = mutableListOf<T>()\n    val list: List<T>\n        get() = _list\n\n    protected abstract val hasStableIds: Boolean\n\n    init {\n        setHasStableIds(hasStableIds)\n    }\n\n    fun addAll(collection: Collection<T>) {\n        val oldSize = _list.size\n        _list.addAll(collection)\n        notifyItemRangeInserted(oldSize, collection.size)\n    }\n\n    fun replace(collection: Collection<T>) {\n        _list.clear()\n        _list.addAll(collection)\n        notifyDataSetChanged()\n    }\n\n    fun add(position: Int, item: T) {\n        _list.add(position, item)\n        notifyItemInserted(position)\n    }\n\n    fun add(item: T) {\n        add(_list.size, item)\n    }\n\n    operator fun set(position: Int, item: T) {\n        _list[position] = item\n        notifyItemChanged(position)\n    }\n\n    fun remove(position: Int): T {\n        val item = _list.removeAt(position)\n        notifyItemRemoved(position)\n        return item\n    }\n\n    fun clear() {\n        val oldSize = _list.size\n        _list.clear()\n        notifyItemRangeRemoved(0, oldSize)\n    }\n\n    fun findPositionById(id: Long): Int {\n        val count = itemCount\n        for (index in 0..<count) {\n            if (getItemId(index) == id) {\n                return index\n            }\n        }\n        return RecyclerView.NO_POSITION\n    }\n\n    fun notifyItemChangedById(id: Long) {\n        val position = findPositionById(id)\n        if (position != RecyclerView.NO_POSITION) {\n            notifyItemChanged(position)\n        }\n    }\n\n    fun removeById(id: Long): T? {\n        val position = findPositionById(id)\n        return if (position != RecyclerView.NO_POSITION) {\n            remove(position)\n        } else {\n            null\n        }\n    }\n\n    fun getItem(position: Int): T {\n        return _list[position]\n    }\n\n    override fun getItemCount(): Int {\n        return _list.size\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/SpeedDialViewOnBackPressedCallback.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport androidx.activity.OnBackPressedCallback\nimport com.leinardi.android.speeddial.SpeedDialView\n\nclass SpeedDialViewOnBackPressedCallback(\n    private val speedDialView: SpeedDialView\n) : OnBackPressedCallback(speedDialView.isOpen) {\n    init {\n        speedDialView.setOnChangeListener(\n            object : SpeedDialView.OnChangeListener {\n                override fun onMainActionSelected(): Boolean = false\n\n                override fun onToggleChanged(isOpen: Boolean) {\n                    isEnabled = speedDialView.isOpen\n                }\n            }\n        )\n    }\n\n    override fun handleOnBackPressed() {\n        speedDialView.close()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/StaticAdapter.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.annotation.LayoutRes\nimport androidx.recyclerview.widget.RecyclerView\nimport me.zhanghai.android.files.util.layoutInflater\n\nclass StaticAdapter(\n    @LayoutRes val layoutRes: Int,\n    val listener: ((Int) -> Unit)? = null\n) : RecyclerView.Adapter<StaticAdapter.ViewHolder>() {\n    init {\n        setHasStableIds(true)\n    }\n\n    @get:JvmName(\"_getItemCount\")\n    var itemCount: Int = 1\n        set(value) {\n            if (field == value) {\n                return\n            }\n            val oldValue = field\n            field = value\n            if (value < oldValue) {\n                notifyItemRangeRemoved(value, oldValue - value)\n            } else {\n                notifyItemRangeInserted(oldValue, value - oldValue)\n            }\n        }\n\n    override fun getItemCount(): Int = itemCount\n\n    override fun getItemId(position: Int): Long = position.toLong()\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder =\n        ViewHolder(parent.context.layoutInflater.inflate(layoutRes, parent, false))\n\n    override fun onBindViewHolder(holder: ViewHolder, position: Int) {\n        if (listener != null) {\n            // TODO: kotlinc: Reference has a nullable type '((Int) -> Unit)?', use explicit\n            //  '?.invoke()' to make a function-like call instead\n            //holder.itemView.setOnClickListener { listener(position) }\n            holder.itemView.setOnClickListener { listener.invoke(position) }\n        }\n    }\n\n    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/TabFragmentPagerAdapter.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.FragmentManager\nimport androidx.fragment.app.FragmentPagerAdapter\n\nclass TabFragmentPagerAdapter(\n    fragmentManager: FragmentManager,\n    private vararg val tabs: Pair<CharSequence?, () -> Fragment>\n) : FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {\n    override fun getItem(position: Int): Fragment = tabs[position].second()\n\n    override fun getCount(): Int = tabs.size\n\n    override fun getPageTitle(position: Int): CharSequence? = tabs[position].first\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/ThemedFastScroller.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.view.ViewGroup\nimport me.zhanghai.android.fastscroll.FastScroller\nimport me.zhanghai.android.fastscroll.FastScrollerBuilder\n\nobject ThemedFastScroller {\n    fun create(view: ViewGroup): FastScroller = FastScrollerBuilder(view).useMd2Style().build()\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/ThemedSpeedDialView.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.animation.Animator\nimport android.animation.AnimatorListenerAdapter\nimport android.animation.AnimatorSet\nimport android.animation.ObjectAnimator\nimport android.content.Context\nimport android.content.res.ColorStateList\nimport android.graphics.Color\nimport android.graphics.drawable.Drawable\nimport android.graphics.drawable.RotateDrawable\nimport android.os.Bundle\nimport android.os.Parcelable\nimport android.util.AttributeSet\nimport android.util.Property\nimport android.view.View\nimport android.widget.ImageView\nimport android.widget.TextView\nimport androidx.annotation.AttrRes\nimport androidx.core.view.setMargins\nimport androidx.core.view.updateLayoutParams\nimport androidx.interpolator.view.animation.FastOutSlowInInterpolator\nimport com.leinardi.android.speeddial.FabWithLabelView\nimport com.leinardi.android.speeddial.SpeedDialActionItem\nimport com.leinardi.android.speeddial.SpeedDialView\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.compat.createCompat\nimport me.zhanghai.android.files.compat.drawableCompat\nimport me.zhanghai.android.files.compat.foregroundCompat\nimport me.zhanghai.android.files.compat.setTextAppearanceCompat\nimport me.zhanghai.android.files.util.ParcelableState\nimport me.zhanghai.android.files.util.asColor\nimport me.zhanghai.android.files.util.dpToDimensionPixelSize\nimport me.zhanghai.android.files.util.getColorByAttr\nimport me.zhanghai.android.files.util.getParcelableSafe\nimport me.zhanghai.android.files.util.getResourceIdByAttr\nimport me.zhanghai.android.files.util.isMaterial3Theme\nimport me.zhanghai.android.files.util.shortAnimTime\nimport me.zhanghai.android.files.util.withModulatedAlpha\n\nclass ThemedSpeedDialView : SpeedDialView {\n    private var onChangeListener: OnChangeListener? = null\n\n    private var mainFabAnimator: Animator? = null\n\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(\n        context, attrs, defStyleAttr\n    )\n\n    init {\n        // Work around ripple bug on Android 12 when useCompatPadding = true.\n        // @see https://github.com/material-components/material-components-android/issues/2617\n        mainFab.apply {\n            updateLayoutParams<MarginLayoutParams> {\n                setMargins(context.dpToDimensionPixelSize(16))\n            }\n            useCompatPadding = false\n        }\n        val context = context\n        if (context.isMaterial3Theme) {\n            mainFabClosedBackgroundColor =\n                context.getColorByAttr(com.google.android.material.R.attr.colorSecondaryContainer)\n            mainFabClosedIconColor =\n                context.getColorByAttr(com.google.android.material.R.attr.colorOnSecondaryContainer)\n            mainFabOpenedBackgroundColor =\n                context.getColorByAttr(androidx.appcompat.R.attr.colorPrimary)\n            mainFabOpenedIconColor =\n                context.getColorByAttr(com.google.android.material.R.attr.colorOnPrimary)\n        } else {\n            mainFabClosedBackgroundColor =\n                context.getColorByAttr(com.google.android.material.R.attr.colorSecondary)\n            mainFabClosedIconColor =\n                context.getColorByAttr(com.google.android.material.R.attr.colorOnSecondary)\n            mainFabOpenedBackgroundColor = mainFabClosedBackgroundColor\n            mainFabOpenedIconColor = mainFabClosedIconColor\n        }\n        // Always use our own animation to fix the library issue that ripple is rotated as well.\n        val mainFabDrawable = RotateDrawable::class.createCompat().apply {\n            drawableCompat = mainFab.drawable\n            toDegrees = mainFabAnimationRotateAngle\n        }\n        mainFabAnimationRotateAngle = 0f\n        setMainFabClosedDrawable(mainFabDrawable)\n        super.setOnChangeListener(object : OnChangeListener {\n            override fun onMainActionSelected(): Boolean =\n                onChangeListener?.onMainActionSelected() ?: false\n\n            override fun onToggleChanged(isOpen: Boolean) {\n                mainFabAnimator?.cancel()\n                mainFabAnimator = createMainFabAnimator(isOpen).apply {\n                    addListener(object : AnimatorListenerAdapter() {\n                        override fun onAnimationEnd(animation: Animator) {\n                            mainFabAnimator = null\n                        }\n                    })\n                    start()\n                }\n                onChangeListener?.onToggleChanged(isOpen)\n            }\n        })\n    }\n\n    override fun setOnChangeListener(onChangeListener: OnChangeListener?) {\n        this.onChangeListener = onChangeListener\n    }\n\n    private fun createMainFabAnimator(isOpen: Boolean): Animator =\n        AnimatorSet().apply {\n            playTogether(\n                ObjectAnimator.ofArgb(\n                    mainFab, VIEW_PROPERTY_BACKGROUND_TINT,\n                    if (isOpen) mainFabOpenedBackgroundColor else mainFabClosedBackgroundColor\n                ),\n                ObjectAnimator.ofArgb(\n                    mainFab, IMAGE_VIEW_PROPERTY_IMAGE_TINT,\n                    if (isOpen) mainFabOpenedIconColor else mainFabClosedIconColor\n                ),\n                ObjectAnimator.ofInt(\n                    mainFab.drawable, DRAWABLE_PROPERTY_LEVEL, if (isOpen) 10000 else 0\n                )\n            )\n            duration = context.shortAnimTime.toLong()\n            interpolator = FastOutSlowInInterpolator()\n        }\n\n    override fun onAttachedToWindow() {\n        super.onAttachedToWindow()\n\n        val overlayLayout = overlayLayout\n        if (overlayLayout != null) {\n            val surfaceColor =\n                context.getColorByAttr(com.google.android.material.R.attr.colorSurface)\n            val overlayColor = surfaceColor.asColor().withModulatedAlpha(0.87f).value\n            overlayLayout.setBackgroundColor(overlayColor)\n        }\n    }\n\n    override fun addActionItem(\n        actionItem: SpeedDialActionItem,\n        position: Int,\n        animate: Boolean\n    ): FabWithLabelView? {\n        val context = context\n        val isMaterial3Theme = context.isMaterial3Theme\n        val fabImageTintColor = if (isMaterial3Theme) {\n            context.getColorByAttr(androidx.appcompat.R.attr.colorPrimary)\n        } else {\n            context.getColorByAttr(com.google.android.material.R.attr.colorSecondary)\n        }\n        val fabBackgroundColor =\n            context.getColorByAttr(com.google.android.material.R.attr.colorSurface)\n        val labelColor = context.getColorByAttr(android.R.attr.textColorSecondary)\n        val labelBackgroundColor = if (isMaterial3Theme) {\n            Color.TRANSPARENT\n        } else {\n            // Label view doesn't have enough elevation (only 1dp) for elevation overlay to work\n            // well.\n            context.getColorByAttr(androidx.appcompat.R.attr.colorBackgroundFloating)\n        }\n        val actionItem = SpeedDialActionItem.Builder(\n            actionItem.id,\n            // Should not be a resource, pass null to fail fast.\n            actionItem.getFabImageDrawable(null)\n        )\n            .setLabel(actionItem.getLabel(context))\n            .setFabImageTintColor(fabImageTintColor)\n            .setFabBackgroundColor(fabBackgroundColor)\n            .setLabelColor(labelColor)\n            .setLabelBackgroundColor(labelBackgroundColor)\n            .setLabelClickable(actionItem.isLabelClickable)\n            .setTheme(actionItem.theme)\n            .create()\n        return super.addActionItem(actionItem, position, animate)?.apply {\n            fab.apply {\n                updateLayoutParams<MarginLayoutParams> {\n                    val horizontalMargin = context.dpToDimensionPixelSize(20)\n                    setMargins(horizontalMargin, 0, horizontalMargin, 0)\n                }\n                useCompatPadding = false\n            }\n            if (isMaterial3Theme) {\n                labelBackground.apply {\n                    useCompatPadding = false\n                    setContentPadding(0, 0, 0, 0)\n                    foregroundCompat = null\n                    (getChildAt(0) as TextView).apply {\n                        setTextAppearanceCompat(\n                            context.getResourceIdByAttr(\n                                com.google.android.material.R.attr.textAppearanceLabelLarge\n                            )\n                        )\n                    }\n                }\n            }\n        }\n    }\n\n    override fun onSaveInstanceState(): Parcelable {\n        val superState = (super.onSaveInstanceState() as Bundle)\n            .getParcelableSafe<Parcelable>(\"superState\")\n        return State(superState, isOpen)\n    }\n\n    override fun onRestoreInstanceState(state: Parcelable) {\n        state as State\n        super.onRestoreInstanceState(state.superState)\n        if (state.isOpen) {\n            toggle(false)\n        }\n    }\n\n    companion object {\n        private val VIEW_PROPERTY_BACKGROUND_TINT =\n            object : Property<View, Int>(Int::class.java, \"backgroundTint\") {\n                override fun get(view: View): Int? = view.backgroundTintList!!.defaultColor\n\n                override fun set(view: View, value: Int?) {\n                    view.backgroundTintList = ColorStateList.valueOf(value!!)\n                }\n            }\n\n        private val IMAGE_VIEW_PROPERTY_IMAGE_TINT =\n            object : Property<ImageView, Int>(Int::class.java, \"imageTint\") {\n                override fun get(view: ImageView): Int? = view.imageTintList!!.defaultColor\n\n                override fun set(view: ImageView, value: Int?) {\n                    view.imageTintList = ColorStateList.valueOf(value!!)\n                }\n            }\n\n        private val DRAWABLE_PROPERTY_LEVEL =\n            object : Property<Drawable, Int>(Int::class.java, \"level\") {\n                override fun get(drawable: Drawable): Int? = drawable.level\n\n                override fun set(drawable: Drawable, value: Int?) {\n                    drawable.level = value!!\n                }\n            }\n    }\n\n    @Parcelize\n    private class State(val superState: Parcelable?, val isOpen: Boolean) : ParcelableState\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/ToolbarActionMode.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\npackage me.zhanghai.android.files.ui\n\nimport android.graphics.drawable.Drawable\nimport android.view.Menu\nimport android.view.MenuItem\nimport android.view.ViewGroup\nimport androidx.activity.OnBackPressedCallback\nimport androidx.annotation.DrawableRes\nimport androidx.annotation.MenuRes\nimport androidx.annotation.StringRes\nimport androidx.appcompat.widget.Toolbar\n\nabstract class ToolbarActionMode(\n    private val bar: ViewGroup,\n    private val toolbar: Toolbar\n) {\n    @MenuRes\n    private var menuRes = 0\n\n    private var callback: Callback? = null\n\n    init {\n        toolbar.setNavigationOnClickListener { callback?.onToolbarNavigationIconClicked(this) }\n        toolbar.setOnMenuItemClickListener {\n            callback?.onToolbarActionModeMenuItemClicked(this, it) ?: false\n        }\n    }\n\n    val onBackPressedCallback: OnBackPressedCallback = object : OnBackPressedCallback(false) {\n        override fun handleOnBackPressed() {\n            finish()\n        }\n    }\n\n    var navigationIcon: Drawable?\n        get() = toolbar.navigationIcon\n        set(value) {\n            toolbar.navigationIcon = value\n        }\n\n    var navigationContentDescription: CharSequence?\n        get() = toolbar.navigationContentDescription\n        set(value) {\n            toolbar.navigationContentDescription = value\n        }\n\n    fun setNavigationIcon(@DrawableRes iconRes: Int, @StringRes contentDescriptionRes: Int) {\n        toolbar.setNavigationIcon(iconRes)\n        toolbar.setNavigationContentDescription(contentDescriptionRes)\n    }\n\n    var title: CharSequence?\n        get() = toolbar.title\n        set(value) {\n            toolbar.title = value\n        }\n\n    fun setTitle(@StringRes titleRes: Int) {\n        toolbar.setTitle(titleRes)\n    }\n\n    var subtitle: CharSequence?\n        get() = toolbar.subtitle\n        set(value) {\n            toolbar.subtitle = value\n        }\n\n    fun setSubtitle(@StringRes subtitleRes: Int) {\n        toolbar.setSubtitle(subtitleRes)\n    }\n\n    val menu: Menu\n        get() = toolbar.menu\n\n    fun setMenuResource(@MenuRes menuRes: Int) {\n        if (this.menuRes == menuRes) {\n            return\n        }\n        this.menuRes = menuRes\n        toolbar.menu.clear()\n        if (menuRes != 0) {\n            toolbar.inflateMenu(menuRes)\n        }\n    }\n\n    val isActive: Boolean\n        get() = callback != null\n\n    fun start(callback: Callback, animate: Boolean = true) {\n        this.callback = callback\n        onBackPressedCallback.isEnabled = true\n        show(bar, animate)\n        callback.onToolbarActionModeStarted(this)\n    }\n\n    protected abstract fun show(bar: ViewGroup, animate: Boolean)\n\n    fun finish(animate: Boolean = true) {\n        val callback = callback ?: return\n        this.callback = null\n        onBackPressedCallback.isEnabled = false\n        toolbar.menu.close()\n        hide(bar, animate)\n        callback.onToolbarActionModeFinished(this)\n    }\n\n    protected abstract fun hide(bar: ViewGroup, animate: Boolean)\n\n    interface Callback {\n        fun onToolbarActionModeStarted(toolbarActionMode: ToolbarActionMode) {}\n\n        fun onToolbarNavigationIconClicked(toolbarActionMode: ToolbarActionMode) {\n            toolbarActionMode.finish()\n        }\n\n        fun onToolbarActionModeMenuItemClicked(\n            toolbarActionMode: ToolbarActionMode,\n            item: MenuItem\n        ): Boolean\n\n        fun onToolbarActionModeFinished(toolbarActionMode: ToolbarActionMode)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/UnfilteredArrayAdapter.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.content.Context\nimport android.widget.ArrayAdapter\nimport android.widget.Filter\n\nclass UnfilteredArrayAdapter<T> : ArrayAdapter<T> {\n    private val filter = object : Filter() {\n        override fun performFiltering(constraint: CharSequence): FilterResults = FilterResults()\n\n        override fun publishResults(constraint: CharSequence, results: FilterResults) {}\n    }\n\n    constructor(\n        context: Context,\n        resource: Int,\n        textViewResourceId: Int = 0,\n        objects: List<T> = emptyList()\n    ) : super(context, resource, textViewResourceId, objects)\n\n    constructor(\n        context: Context,\n        resource: Int,\n        textViewResourceId: Int = 0,\n        objects: Array<out T>\n    ) : super(context, resource, textViewResourceId, objects)\n\n    override fun getFilter(): Filter = filter\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/ViewPagerAdapter.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.viewpager.widget.PagerAdapter\n\nabstract class ViewPagerAdapter : PagerAdapter() {\n    override fun instantiateItem(container: ViewGroup, position: Int): View =\n        onCreateView(container, position)\n\n    protected abstract fun onCreateView(container: ViewGroup, position: Int): View\n\n    override fun destroyItem(container: ViewGroup, position: Int, item: Any) {\n        onDestroyView(container, position, item as View)\n        container.removeView(item)\n    }\n\n    protected abstract fun onDestroyView(container: ViewGroup, position: Int, view: View)\n\n    override fun isViewFromObject(view: View, item: Any): Boolean = view === item\n\n    override fun getItemPosition(item: Any): Int = getViewPosition(item as View)\n\n    protected open fun getViewPosition(view: View): Int = POSITION_UNCHANGED\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/ViewPagerTransformers.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.view.View\nimport androidx.viewpager2.widget.ViewPager2\nimport kotlin.math.abs\n\n// @see https://developer.android.com/training/animation/screen-slide-2\n\nobject DepthPageTransformer : ViewPager2.PageTransformer {\n    private const val MIN_SCALE = 0.75f\n\n    override fun transformPage(view: View, position: Float) {\n        view.apply {\n            val pageWidth = width\n            when {\n                position < -1 -> { // [-Infinity,-1)\n                    // This page is way off-screen to the left.\n                    alpha = 0f\n                }\n                position <= 0 -> { // [-1,0]\n                    // Use the default slide transition when moving to the left page\n                    alpha = 1f\n                    translationX = 0f\n                    translationZ = 0f\n                    scaleX = 1f\n                    scaleY = 1f\n                }\n                position <= 1 -> { // (0,1]\n                    // Fade the page out.\n                    alpha = 1 - position\n\n                    // Counteract the default slide transition\n                    translationX = pageWidth * -position\n                    // Move it behind the left page\n                    translationZ = -1f\n\n                    // Scale the page down (between MIN_SCALE and 1)\n                    val scaleFactor = (MIN_SCALE + (1 - MIN_SCALE) * (1 - abs(position)))\n                    scaleX = scaleFactor\n                    scaleY = scaleFactor\n                }\n                else -> { // (1,+Infinity]\n                    // This page is way off-screen to the right.\n                    alpha = 0f\n                }\n            }\n        }\n    }\n}\n\nobject ZoomOutPageTransformer : ViewPager2.PageTransformer {\n    private const val MIN_SCALE = 0.85f\n    private const val MIN_ALPHA = 0.5f\n\n    override fun transformPage(view: View, position: Float) {\n        view.apply {\n            val pageWidth = width\n            val pageHeight = height\n            when {\n                position < -1 -> { // [-Infinity,-1)\n                    // This page is way off-screen to the left.\n                    alpha = 0f\n                }\n                position <= 1 -> { // [-1,1]\n                    // Modify the default slide transition to shrink the page as well\n                    val scaleFactor = (1 - abs(position)).coerceAtLeast(MIN_SCALE)\n                    val vertMargin = pageHeight * (1 - scaleFactor) / 2\n                    val horzMargin = pageWidth * (1 - scaleFactor) / 2\n                    translationX = if (position < 0) {\n                        horzMargin - vertMargin / 2\n                    } else {\n                        horzMargin + vertMargin / 2\n                    }\n\n                    // Scale the page down (between MIN_SCALE and 1)\n                    scaleX = scaleFactor\n                    scaleY = scaleFactor\n\n                    // Fade the page relative to its size.\n                    alpha = (MIN_ALPHA +\n                        (((scaleFactor - MIN_SCALE) / (1 - MIN_SCALE)) * (1 - MIN_ALPHA)))\n                }\n                else -> { // (1,+Infinity]\n                    // This page is way off-screen to the right.\n                    alpha = 0f\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/ui/WrapFirstPageContentViewPager.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.ui\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport androidx.viewpager.widget.ViewPager\n\nclass WrapFirstPageContentViewPager : ViewPager {\n    constructor(context: Context) : super(context)\n\n    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)\n\n    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {\n        val heightMode = MeasureSpec.getMode(heightMeasureSpec)\n        val newHeightMeasureSpec = if (heightMode != MeasureSpec.EXACTLY && childCount > 0) {\n            val child = getChildAt(0)\n            child.measure(widthMeasureSpec, heightMeasureSpec)\n            val height = child.measuredHeight\n            MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)\n        } else {\n            heightMeasureSpec\n        }\n        super.onMeasure(widthMeasureSpec, newHeightMeasureSpec)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/AbstractLocalCursor.kt",
    "content": "package me.zhanghai.android.files.util\n\nimport android.content.ContentResolver\nimport android.database.CharArrayBuffer\nimport android.database.ContentObserver\nimport android.database.Cursor\nimport android.database.CursorIndexOutOfBoundsException\nimport android.database.DataSetObserver\nimport android.net.Uri\nimport android.os.Bundle\n\nabstract class AbstractLocalCursor : Cursor {\n    private var _position: Int = -1\n\n    private var _isClosed: Boolean = false\n\n    private var _extras = Bundle.EMPTY\n\n    override fun getPosition(): Int = _position\n\n    override fun move(offset: Int): Boolean = moveToPosition(_position + offset)\n\n    override fun moveToPosition(position: Int): Boolean {\n        val count = count\n        return when {\n            position >= count -> {\n                _position = count\n                false\n            }\n            position < 0 -> {\n                _position = -1\n                false\n            }\n            else -> {\n                _position = position\n                true\n            }\n        }\n    }\n\n    override fun moveToFirst(): Boolean = moveToPosition(0)\n\n    override fun moveToLast(): Boolean = moveToPosition(count - 1)\n\n    override fun moveToNext(): Boolean = moveToPosition(_position + 1)\n\n    override fun moveToPrevious(): Boolean = moveToPosition(_position - 1)\n\n    override fun isFirst(): Boolean {\n        val count = count\n        return count != 0 && _position == 0\n    }\n\n    override fun isLast(): Boolean {\n        val count = count\n        return count != 0 && _position == count - 1\n    }\n\n    override fun isBeforeFirst(): Boolean {\n        val count = count\n        return count == 0 || _position == -1\n    }\n\n    override fun isAfterLast(): Boolean {\n        val count = count\n        return count == 0 || _position == count\n    }\n\n    override fun getColumnIndex(columnName: String): Int {\n        for ((index, columnNameForIndex) in columnNames.withIndex()) {\n            if (columnNameForIndex.equals(columnName, true)) {\n                return index\n            }\n        }\n        return -1\n    }\n\n    override fun getColumnIndexOrThrow(columnName: String): Int =\n        getColumnIndex(columnName).also {\n            require(it != -1) {\n                \"Column '$columnName' does not exist, available columns: ${\n                    columnNames.contentToString()}\"\n            }\n        }\n\n    override fun getColumnName(columnIndex: Int): String = columnNames[columnIndex]\n\n    override fun getColumnCount(): Int = columnNames.size\n\n    private fun getObjectChecked(columnIndex: Int): Any? {\n        val columnCount = columnCount\n        if (columnIndex !in 0..<columnCount) {\n            throw CursorIndexOutOfBoundsException(\n                \"Requested column: $columnIndex, # of columns: $columnCount\"\n            )\n        }\n        val count = count\n        if (_position !in 0..<count) {\n            throw CursorIndexOutOfBoundsException(_position, count)\n        }\n        return getObject(columnIndex)\n    }\n\n    protected abstract fun getObject(columnIndex: Int): Any?\n\n    override fun getBlob(columnIndex: Int): ByteArray? = getObjectChecked(columnIndex) as ByteArray?\n\n    override fun getString(columnIndex: Int): String? = getObjectChecked(columnIndex)?.toString()\n\n    override fun copyStringToBuffer(columnIndex: Int, buffer: CharArrayBuffer) {\n        val value = getString(columnIndex)\n        if (value != null) {\n            val data = buffer.data\n            if (data == null || data.size < value.length) {\n                buffer.data = value.toCharArray()\n            } else {\n                value.toCharArray(data, 0, 0, value.length)\n            }\n            buffer.sizeCopied = value.length\n        } else {\n            buffer.sizeCopied = 0\n        }\n    }\n\n    override fun getShort(columnIndex: Int): Short =\n        when (val value = getObjectChecked(columnIndex)) {\n            null -> 0\n            is Number -> value.toShort()\n            else -> value.toString().toShort()\n        }\n\n    override fun getInt(columnIndex: Int): Int =\n        when (val value = getObjectChecked(columnIndex)) {\n            null -> 0\n            is Number -> value.toInt()\n            else -> value.toString().toInt()\n        }\n\n    override fun getLong(columnIndex: Int): Long =\n        when (val value = getObjectChecked(columnIndex)) {\n            null -> 0\n            is Number -> value.toLong()\n            else -> value.toString().toLong()\n        }\n\n    override fun getFloat(columnIndex: Int): Float =\n        when (val value = getObjectChecked(columnIndex)) {\n            null -> 0f\n            is Number -> value.toFloat()\n            else -> value.toString().toFloat()\n        }\n\n    override fun getDouble(columnIndex: Int): Double =\n        when (val value = getObjectChecked(columnIndex)) {\n            null -> 0.0\n            is Number -> value.toDouble()\n            else -> value.toString().toDouble()\n        }\n\n    override fun getType(columnIndex: Int): Int =\n        when (getObjectChecked(columnIndex)) {\n            null -> Cursor.FIELD_TYPE_NULL\n            is Byte, is Short, is Int, is Long -> Cursor.FIELD_TYPE_INTEGER\n            is Float, is Double -> Cursor.FIELD_TYPE_FLOAT\n            is ByteArray -> Cursor.FIELD_TYPE_BLOB\n            else -> Cursor.FIELD_TYPE_STRING\n        }\n\n    override fun isNull(columnIndex: Int): Boolean = getObjectChecked(columnIndex) == null\n\n    override fun deactivate() {}\n\n    override fun requery(): Boolean = true\n\n    override fun close() {\n        _isClosed = true\n    }\n\n    override fun isClosed(): Boolean = _isClosed\n\n    override fun registerContentObserver(observer: ContentObserver) {}\n\n    override fun unregisterContentObserver(observer: ContentObserver) {}\n\n    override fun registerDataSetObserver(observer: DataSetObserver) {}\n\n    override fun unregisterDataSetObserver(observer: DataSetObserver) {}\n\n    override fun setNotificationUri(resolver: ContentResolver, uri: Uri) {\n        throw UnsupportedOperationException()\n    }\n\n    override fun getNotificationUri(): Uri? = null\n\n    override fun getWantsAllOnMoveCalls(): Boolean = false\n\n    override fun setExtras(extras: Bundle?) {\n        _extras = extras ?: Bundle.EMPTY\n    }\n\n    override fun getExtras(): Bundle = _extras\n\n    override fun respond(extras: Bundle): Bundle = Bundle.EMPTY\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/ActionState.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nsealed class ActionState<A, R> {\n    class Ready<A, R> : ActionState<A, R>() {\n        override fun equals(other: Any?): Boolean {\n            if (this === other) {\n                return true\n            }\n            if (javaClass != other?.javaClass) {\n                return false\n            }\n            return true\n        }\n\n        override fun hashCode(): Int = javaClass.hashCode()\n    }\n\n    data class Running<A, R>(val argument: A) : ActionState<A, R>()\n\n    data class Success<A, R>(val argument: A, val result: R) : ActionState<A, R>()\n\n    data class Error<A, R>(val argument: A, val throwable: Throwable) : ActionState<A, R>()\n}\n\nval ActionState<*, *>.isReady: Boolean\n    get() = this is ActionState.Ready\n\nval ActionState<*, *>.isRunning: Boolean\n    get() = this is ActionState.Running\n\nval ActionState<*, *>.isFinished: Boolean\n    get() = when (this) { is ActionState.Success, is ActionState.Error -> true else -> false }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/ActivityResultLauncherExtensions.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.content.ActivityNotFoundException\nimport android.content.Context\nimport androidx.activity.result.ActivityResultLauncher\nimport androidx.core.app.ActivityOptionsCompat\nimport androidx.fragment.app.Fragment\nimport me.zhanghai.android.files.R\n\nfun <I> ActivityResultLauncher<I>.launchSafe(input: I, context: Context) {\n    try {\n        launch(input)\n    } catch (e: ActivityNotFoundException) {\n        context.showToast(R.string.activity_not_found)\n    }\n}\n\nfun <I> ActivityResultLauncher<I>.launchSafe(input: I, fragment: Fragment) {\n    try {\n        launch(input)\n    } catch (e: ActivityNotFoundException) {\n        fragment.showToast(R.string.activity_not_found)\n    }\n}\n\nfun <I> ActivityResultLauncher<I>.launchSafe(\n    input: I,\n    options: ActivityOptionsCompat?,\n    context: Context\n) {\n    try {\n        launch(input, options)\n    } catch (e: ActivityNotFoundException) {\n        context.showToast(R.string.activity_not_found)\n    }\n}\n\nfun <I> ActivityResultLauncher<I>.launchSafe(\n    input: I,\n    options: ActivityOptionsCompat?,\n    fragment: Fragment\n) {\n    try {\n        launch(input, options)\n    } catch (e: ActivityNotFoundException) {\n        fragment.showToast(R.string.activity_not_found)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/AddressExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.location.Address\n\nval Address.addressLines: Iterable<String?>\n    get() =\n        object : Iterable<String?> {\n            override fun iterator(): Iterator<String?> = object : Iterator<String?> {\n                var index = 0\n\n                override fun hasNext(): Boolean = index <= maxAddressLineIndex\n\n                override fun next(): String? = getAddressLine(index).also { ++index }\n            }\n        }\n\n// @see com.android.documentsui.inspector.MediaView.getAddress\nval Address.userFriendlyString: String?\n    get() =\n        addressLines.joinToString(\"\\n\") { it.orEmpty() }.takeIfNotBlank()\n            ?: locality.takeIfNotBlank() ?: subAdminArea.takeIfNotBlank()\n            ?: adminArea.takeIfNotBlank() ?: countryName.takeIfNotBlank()\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/AnimatorExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.animation.Animator\nimport android.animation.AnimatorListenerAdapter\nimport android.view.ViewPropertyAnimator\nimport kotlinx.coroutines.suspendCancellableCoroutine\nimport kotlin.coroutines.resume\n\nsuspend fun Animator.awaitEnd(): Unit =\n    suspendCancellableCoroutine { continuation ->\n        continuation.invokeOnCancellation { cancel() }\n        addListener(object : AnimatorListenerAdapter() {\n            private var canceled = false\n\n            override fun onAnimationCancel(animation: Animator) {\n                canceled = true\n            }\n\n            override fun onAnimationEnd(animation: Animator) {\n                removeListener(this)\n                if (continuation.isActive) {\n                    if (canceled) {\n                        continuation.cancel()\n                    } else {\n                        continuation.resume(Unit)\n                    }\n                }\n            }\n        })\n    }\n\nsuspend fun ViewPropertyAnimator.awaitEnd(): Unit =\n    suspendCancellableCoroutine { continuation ->\n        continuation.invokeOnCancellation { cancel() }\n        setListener(object : AnimatorListenerAdapter() {\n            private var canceled = false\n\n            override fun onAnimationCancel(animation: Animator) {\n                canceled = true\n            }\n\n            override fun onAnimationEnd(animation: Animator) {\n                setListener(null)\n                if (continuation.isActive) {\n                    if (canceled) {\n                        continuation.cancel()\n                    } else {\n                        continuation.resume(Unit)\n                    }\n                }\n            }\n        })\n    }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/AnyExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nfun Any.hash(vararg values: Any?): Int = values.contentDeepHashCode()\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/AutoCloseableExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nfun AutoCloseable.closeSafe() {\n    try {\n        close()\n    } catch (e: Exception) {\n        e.printStackTrace()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/Base64.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.util.Base64 as AndroidBase64\n\n@JvmInline\nvalue class Base64(val value: String)\n\nfun String.asBase64(): Base64 = Base64(this)\n\nfun Base64.toByteArray(): ByteArray = AndroidBase64.decode(value, AndroidBase64.DEFAULT)\n\nfun ByteArray.toBase64(): Base64 =\n    AndroidBase64.encodeToString(this, AndroidBase64.NO_WRAP).asBase64()\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/BundleExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.os.Bundle\nimport android.os.Parcelable\nimport android.util.SparseArray\nimport me.zhanghai.android.files.app.appClassLoader\n\nfun <T : Parcelable> Bundle.getParcelableSafe(key: String?): T? {\n    classLoader = appClassLoader\n    return getParcelable(key)\n}\n\nfun Bundle.getParcelableArraySafe(key: String?): Array<Parcelable>? {\n    classLoader = appClassLoader\n    return getParcelableArray(key)\n}\n\nfun <T : Parcelable?> Bundle.getParcelableArrayListSafe(key: String?): ArrayList<T>? {\n    classLoader = appClassLoader\n    return getParcelableArrayList(key)\n}\n\nfun <T : Parcelable?> Bundle.getSparseParcelableArraySafe(key: String?): SparseArray<T>? {\n    classLoader = appClassLoader\n    return getSparseParcelableArray(key)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/ByteArrayExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport java.security.MessageDigest\n\nfun ByteArray.sha1Digest(): ByteArray = MessageDigest.getInstance(\"SHA-1\").digest(this)\n\nfun ByteArray.toHexString(): String {\n    val chars = CharArray(2 * size)\n    for (index in indices) {\n        val byte = this[index]\n        chars[2 * index] = ((byte.toInt() ushr 4) and 0xF).toHexChar()\n        chars[2 * index + 1] = (byte.toInt() and 0xF).toHexChar()\n    }\n    return String(chars)\n}\n\nprivate fun Int.toHexChar(): Char = if (this >= 10) 'A' + (this - 10) else '0' + this\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/BytePathName.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport me.zhanghai.android.files.provider.common.ByteString\nimport me.zhanghai.android.files.provider.common.ByteStringBuilder\nimport me.zhanghai.android.files.provider.common.dropLast\nimport me.zhanghai.android.files.provider.common.dropLastWhile\nimport me.zhanghai.android.files.provider.common.substringAfterLast\nimport me.zhanghai.android.files.provider.common.takeIfNotEmpty\n\n@JvmInline\nvalue class BytePathName(val value: ByteString) {\n    val fileName: ByteString?\n        get() = value.substringAfterLast(SEPARATOR).takeIfNotEmpty()\n\n    val directoryName: ByteString?\n        get() = value.substringBeforeLastOrNull(SEPARATOR)?.dropLastWhile { it == SEPARATOR }\n\n    private fun ByteString.substringBeforeLastOrNull(delimiter: Byte): ByteString? {\n        val index = lastIndexOf(delimiter)\n        return if (index != -1) substring(0, index) else null\n    }\n\n    companion object {\n        // Not using File.separatorChar so that behavior is consistent and always ready for URIs.\n        // Anyway we are on Android. If one day we were moved to Windows, fail-fast is also good.\n        const val SEPARATOR = '/'.code.toByte()\n    }\n}\n\nfun ByteString.asPathName(): BytePathName {\n    require(isValidPathName)\n    return BytePathName(this)\n}\n\nfun ByteString.asPathNameOrNull(): BytePathName? = if (isValidPathName) BytePathName(this) else null\n\nprivate val ByteString.isValidPathName: Boolean\n    get() = isNotEmpty() && !contains('\\u0000'.code.toByte())\n\n@JvmInline\nvalue class ByteFileName(val value: ByteString) {\n    val singleExtension: ByteString\n        get() = value.substringAfterLast(EXTENSION_SEPARATOR, ByteString.EMPTY)\n\n    val extensions: ByteString\n        get() {\n            val lastExtension = singleExtension\n            val lastExtensionString = lastExtension.toString()\n            if (DOUBLE_EXTENSIONS.any { lastExtensionString.equals(it, true) }) {\n                val secondLastExtension = value.dropLast(lastExtension.length + 1)\n                    .substringAfterLast(EXTENSION_SEPARATOR, ByteString.EMPTY)\n                if (secondLastExtension.isNotEmpty()) {\n                    return ByteStringBuilder()\n                        .append(secondLastExtension)\n                        .append(EXTENSION_SEPARATOR)\n                        .append(lastExtension)\n                        .toByteString()\n                }\n            }\n            return lastExtension\n        }\n\n    val baseName: ByteString\n        get() {\n            val extensions = extensions\n            return if (extensions.isNotEmpty()) value.dropLast(extensions.length + 1) else value\n        }\n\n    companion object {\n        const val EXTENSION_SEPARATOR = '.'.code.toByte()\n\n        // https://github.com/GNOME/nautilus/blob/c73ad94a72f8e9a989b01858018de74182d17f0e/eel/eel-vfs-extensions.c#L124\n        private val DOUBLE_EXTENSIONS = listOf(\"bz\", \"bz2\", \"gz\", \"sit\", \"xz\", \"Z\")\n    }\n}\n\nfun ByteString.asFileName(): ByteFileName {\n    require(isValidFileName)\n    return ByteFileName(this)\n}\n\nfun ByteString.asFileNameOrNull(): ByteFileName? = if (isValidFileName) ByteFileName(this) else null\n\nprivate val ByteString.isValidFileName: Boolean\n    get() = isNotEmpty() && !contains('\\u0000'.code.toByte()) && !contains(BytePathName.SEPARATOR)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/CancellationSignalExtensions.kt",
    "content": "package me.zhanghai.android.files.util\n\nimport android.os.CancellationSignal\nimport kotlinx.coroutines.DelicateCoroutinesApi\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.GlobalScope\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.suspendCancellableCoroutine\nimport kotlin.coroutines.resume\n\n// @see androidx.room.CoroutinesRoom.execute\nsuspend fun <T> runWithCancellationSignal(block: (CancellationSignal) -> T): T {\n    val signal = CancellationSignal()\n    return suspendCancellableCoroutine { continuation ->\n        @OptIn(DelicateCoroutinesApi::class)\n        val job = GlobalScope.launch(Dispatchers.IO) {\n            continuation.resume(block(signal))\n        }\n        continuation.invokeOnCancellation {\n            signal.cancel()\n            job.cancel()\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/CharSequenceExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nfun <T : CharSequence> T.takeIfNotBlank(): T? = if (isNotBlank()) this else null\n\nfun <T : CharSequence> T.takeIfNotEmpty(): T? = if (isNotEmpty()) this else null\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/ClickableArrowKeyMovementMethod.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.text.NoCopySpan.Concrete\nimport android.text.Selection\nimport android.text.Spannable\nimport android.text.method.ArrowKeyMovementMethod\nimport android.text.method.LinkMovementMethod\nimport android.text.style.ClickableSpan\nimport android.view.KeyEvent\nimport android.view.MotionEvent\nimport android.view.View\nimport android.widget.TextView\nimport kotlin.math.max\nimport kotlin.math.min\n\n/**\n * @see LinkMovementMethod\n * @see ArrowKeyMovementMethod\n * @see ClickableMovementMethod\n */\nobject ClickableArrowKeyMovementMethod : ArrowKeyMovementMethod() {\n    private const val CLICK = 1\n    private const val UP = 2\n    private const val DOWN = 3\n\n    private val FROM_BELOW = Concrete()\n\n    override fun initialize(view: TextView, text: Spannable) {\n        super.initialize(view, text)\n\n        text.removeSpan(FROM_BELOW)\n    }\n\n    override fun onTakeFocus(view: TextView, text: Spannable, direction: Int) {\n        super.onTakeFocus(view, text, direction)\n\n        if (direction.hasBits(View.FOCUS_BACKWARD)) {\n            text.setSpan(FROM_BELOW, 0, 0, Spannable.SPAN_POINT_POINT)\n        } else {\n            text.removeSpan(FROM_BELOW)\n        }\n    }\n\n    override fun handleMovementKey(\n        view: TextView,\n        text: Spannable,\n        keyCode: Int,\n        movementMetaState: Int,\n        event: KeyEvent\n    ): Boolean {\n        when (keyCode) {\n            KeyEvent.KEYCODE_DPAD_CENTER, KeyEvent.KEYCODE_ENTER -> {\n                if (KeyEvent.metaStateHasNoModifiers(movementMetaState)) {\n                    if (event.action == KeyEvent.ACTION_DOWN && event.repeatCount == 0\n                        && action(CLICK, view, text)) {\n                        return true\n                    }\n                }\n            }\n        }\n        return super.handleMovementKey(view, text, keyCode, movementMetaState, event)\n    }\n\n    override fun up(view: TextView, text: Spannable): Boolean {\n        if (action(UP, view, text)) {\n            return true\n        }\n        return super.up(view, text)\n    }\n\n    override fun down(view: TextView, text: Spannable): Boolean {\n        if (action(DOWN, view, text)) {\n            return true\n        }\n        return super.down(view, text)\n    }\n\n    override fun left(view: TextView, text: Spannable): Boolean {\n        if (action(UP, view, text)) {\n            return true\n        }\n        return super.left(view, text)\n    }\n\n    override fun right(view: TextView, text: Spannable): Boolean {\n        if (action(DOWN, view, text)) {\n            return true\n        }\n        return super.right(view, text)\n    }\n\n    private fun action(what: Int, view: TextView, text: Spannable): Boolean {\n        val layout = view.layout\n        val padding = view.totalPaddingTop + view.totalPaddingBottom\n        val areaTop = view.scrollY\n        val areaBottom = areaTop + view.height - padding\n        val lineTop = layout.getLineForVertical(areaTop)\n        val lineBottom = layout.getLineForVertical(areaBottom)\n        val first = layout.getLineStart(lineTop)\n        val last = layout.getLineEnd(lineBottom)\n        val candidates = text.getSpans(first, last, ClickableSpan::class.java)\n        val a = Selection.getSelectionStart(text)\n        val b = Selection.getSelectionEnd(text)\n        var selectionStart = min(a, b)\n        var selectionEnd = max(a, b)\n        if (selectionStart < 0) {\n            if (text.getSpanStart(FROM_BELOW) >= 0) {\n                selectionEnd = text.length\n                selectionStart = selectionEnd\n            }\n        }\n        if (selectionStart > last) {\n            selectionEnd = Int.MAX_VALUE\n            selectionStart = selectionEnd\n        }\n        if (selectionEnd < first) {\n            selectionEnd = -1\n            selectionStart = selectionEnd\n        }\n        when (what) {\n            CLICK -> {\n                if (selectionStart == selectionEnd) {\n                    return false\n                }\n                val span = text.getSpans(selectionStart, selectionEnd, ClickableSpan::class.java)\n                    .singleOrNull() ?: return false\n                span.onClick(view)\n            }\n            UP -> {\n                var bestStart = -1\n                var bestEnd = -1\n                for (candidate in candidates) {\n                    val end = text.getSpanEnd(candidate)\n                    if (end < selectionEnd || selectionStart == selectionEnd) {\n                        if (end > bestEnd) {\n                            bestStart = text.getSpanStart(candidate)\n                            bestEnd = end\n                        }\n                    }\n                }\n                if (bestStart >= 0) {\n                    Selection.setSelection(text, bestEnd, bestStart)\n                    return true\n                }\n            }\n            DOWN -> {\n                var bestStart = Int.MAX_VALUE\n                var bestEnd = Int.MAX_VALUE\n                for (candidate in candidates) {\n                    val start = text.getSpanStart(candidate)\n                    if (start > selectionStart || selectionStart == selectionEnd) {\n                        if (start < bestStart) {\n                            bestStart = start\n                            bestEnd = text.getSpanEnd(candidate)\n                        }\n                    }\n                }\n                if (bestEnd < Int.MAX_VALUE) {\n                    Selection.setSelection(text, bestStart, bestEnd)\n                    return true\n                }\n            }\n        }\n        return false\n    }\n\n    override fun onTouchEvent(view: TextView, text: Spannable, event: MotionEvent): Boolean {\n        when (val action = event.actionMasked) {\n            MotionEvent.ACTION_DOWN, MotionEvent.ACTION_UP -> {\n                val x = event.x.toInt() - view.totalPaddingLeft + view.scrollX\n                val y = event.y.toInt() - view.totalPaddingTop + view.scrollY\n                val layout = view.layout\n                val span = if (y < 0 || y > layout.height) {\n                    null\n                } else {\n                    val line = layout.getLineForVertical(y)\n                    if (x < layout.getLineLeft(line) || x > layout.getLineRight(line)) {\n                        null\n                    } else {\n                        val off = layout.getOffsetForHorizontal(line, x.toFloat())\n                        text.getSpans(off, off, ClickableSpan::class.java).firstOrNull()\n                    }\n                }\n                if (span != null) {\n                    if (action == MotionEvent.ACTION_DOWN) {\n                        Selection.setSelection(text, text.getSpanStart(span), text.getSpanEnd(span))\n                    } else {\n                        span.onClick(view)\n                    }\n                    return true\n                }\n                // Removed\n                //else {\n                //    Selection.removeSelection(text)\n                //}\n            }\n        }\n        return super.onTouchEvent(view, text, event)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/ClickableMovementMethod.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.text.Selection\nimport android.text.Spannable\nimport android.text.method.BaseMovementMethod\nimport android.text.style.ClickableSpan\nimport android.view.MotionEvent\nimport android.widget.TextView\n\n/**\n * A movement method that traverses links in the text buffer and fires clicks. Unlike\n * [android.text.method.LinkMovementMethod], this will not consume touch events outside\n * [ClickableSpan]s.\n */\nobject ClickableMovementMethod : BaseMovementMethod() {\n    override fun initialize(view: TextView, text: Spannable) {\n        Selection.removeSelection(text)\n    }\n\n    override fun onTouchEvent(view: TextView, text: Spannable, event: MotionEvent): Boolean {\n        when (val action = event.actionMasked) {\n            MotionEvent.ACTION_DOWN, MotionEvent.ACTION_UP -> {\n                val x = event.x.toInt() - view.totalPaddingLeft + view.scrollX\n                val y = event.y.toInt() - view.totalPaddingTop + view.scrollY\n                val layout = view.layout\n                val span = if (y < 0 || y > layout.height) {\n                    null\n                } else {\n                    val line = layout.getLineForVertical(y)\n                    if (x < layout.getLineLeft(line) || x > layout.getLineRight(line)) {\n                        null\n                    } else {\n                        val off = layout.getOffsetForHorizontal(line, x.toFloat())\n                        text.getSpans(off, off, ClickableSpan::class.java).firstOrNull()\n                    }\n                }\n                if (span != null) {\n                    if (action == MotionEvent.ACTION_DOWN) {\n                        Selection.setSelection(text, text.getSpanStart(span), text.getSpanEnd(span))\n                    } else {\n                        span.onClick(view)\n                    }\n                    return true\n                } else {\n                    Selection.removeSelection(text)\n                }\n            }\n        }\n        return false\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/ClipDataExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.content.ClipData\nimport kotlin.reflect.KClass\n\nfun ClipData.firstOrNull(): ClipData.Item? = if (itemCount > 0) getItemAt(0) else null\n\nfun KClass<ClipData>.create(\n    label: CharSequence?,\n    mimeTypes: List<String>,\n    items: List<ClipData.Item>\n): ClipData =\n    ClipData(label, mimeTypes.toTypedArray(), items[0])\n        .apply { items.asSequence().drop(1).forEach { addItem(it) } }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/ClipboardManagerExtensions.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.content.ClipData\nimport android.content.ClipboardManager\nimport android.content.Context\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.app.application\n\nvar ClipboardManager.primaryText: CharSequence\n    get() = primaryClip?.firstOrNull()?.coerceToText(application)!!\n    set(value) {\n        setPrimaryClip(ClipData.newPlainText(null, value))\n    }\n\nprivate const val TOAST_COPIED_TEXT_MAX_LENGTH = 40\n\nfun ClipboardManager.copyText(text: CharSequence, context: Context) {\n    primaryText = text\n    var copiedText = text\n    var ellipsized = false\n    if (copiedText.length > TOAST_COPIED_TEXT_MAX_LENGTH) {\n        copiedText = copiedText.subSequence(0, TOAST_COPIED_TEXT_MAX_LENGTH)\n        ellipsized = true\n    }\n    val indexOfFirstNewline = copiedText.indexOf('\\n')\n    if (indexOfFirstNewline != -1) {\n        val indexOfSecondNewline = copiedText.indexOf('\\n', indexOfFirstNewline + 1)\n        if (indexOfSecondNewline != -1) {\n            copiedText = copiedText.subSequence(0, indexOfSecondNewline)\n            ellipsized = true\n        }\n    }\n    if (ellipsized) {\n        copiedText = \"$copiedText…\"\n    }\n    context.showToast(context.getString(R.string.copied_to_clipboard_format, copiedText))\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/CloseableLiveData.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport androidx.lifecycle.LiveData\nimport java.io.Closeable\n\nabstract class CloseableLiveData<T> : LiveData<T>, Closeable {\n    constructor(value: T) : super(value)\n\n    constructor()\n\n    abstract override fun close()\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/CollectionExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport java.util.EnumSet\n\ninline fun <reified T : Enum<T>> enumSetOf(): EnumSet<T> = EnumSet.noneOf(T::class.java)\n\nfun <T : Enum<T>> enumSetOf(element: T): EnumSet<T> = EnumSet.of(element)\n\nfun <T : Enum<T>> enumSetOf(element1: T, element2: T): EnumSet<T> = EnumSet.of(element1, element2)\n\nfun <T : Enum<T>> enumSetOf(element1: T, element2: T, element3: T): EnumSet<T> =\n    EnumSet.of(element1, element2, element3)\n\nfun <T : Enum<T>> enumSetOf(element1: T, element2: T, element3: T, element4: T): EnumSet<T> =\n    EnumSet.of(element1, element2, element3, element4)\n\nfun <T : Enum<T>> enumSetOf(\n    element1: T,\n    element2: T,\n    element3: T,\n    element4: T,\n    element5: T\n): EnumSet<T> = EnumSet.of(element1, element2, element3, element4, element5)\n\nfun <T : Enum<T>> enumSetOf(first: T, vararg rest: T): EnumSet<T> = EnumSet.of(first, *rest)\n\nfun <T> Iterable<T>.toLinkedSet(): LinkedHashSet<T> = toCollection(LinkedHashSet())\n\ninline fun <reified T : Enum<T>> Collection<T>.toEnumSet(): EnumSet<T> =\n    if (isNotEmpty()) EnumSet.copyOf(this) else EnumSet.noneOf(T::class.java)\n\nfun <T : Collection<*>> T.takeIfNotEmpty(): T? = if (isNotEmpty()) this else null\n\nfun <T> MutableCollection<T>.removeFirst(): T {\n    val iterator = iterator()\n    val element = iterator.next()\n    iterator.remove()\n    return element\n}\n\nfun <K, V> MutableMap<K, V>.removeFirst(): Map.Entry<K, V> {\n    val iterator = iterator()\n    val element = iterator.next()\n    iterator.remove()\n    return element\n}\n\nfun <T> MutableCollection<T>.removeFirst(predicate: (T) -> Boolean): T? {\n    val iterator = iterator()\n    while (iterator.hasNext()) {\n        val element = iterator.next()\n        if (predicate(element)) {\n            iterator.remove()\n            return element\n        }\n    }\n    return null\n}\n\nfun <K, V> MutableMap<K, V>.removeFirst(predicate: (Map.Entry<K, V>) -> Boolean): Map.Entry<K, V>? =\n    entries.removeFirst(predicate)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/Color.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport androidx.annotation.ColorInt\nimport androidx.annotation.FloatRange\nimport androidx.annotation.IntRange\nimport androidx.core.graphics.ColorUtils\nimport kotlin.math.roundToInt\nimport android.graphics.Color as AndroidColor\n\n@JvmInline\nvalue class Color(@ColorInt val value: Int)\n\nfun Int.asColor(): Color = Color(this)\n\nval Color.alpha: Int\n    @IntRange(from = 0, to = 255)\n    get() = AndroidColor.alpha(value)\n\nfun Color.withAlpha(@IntRange(from = 0, to = 255) alpha: Int): Color =\n    ColorUtils.setAlphaComponent(value, alpha).asColor()\n\nfun Color.withModulatedAlpha(@FloatRange(from = 0.0, to = 1.0) alphaModulation: Float): Color {\n    val alpha = (alpha * alphaModulation).roundToInt()\n    return ((alpha shl 24) or (value and 0x00FFFFFF)).asColor()\n}\n\nval Color.red: Int\n    @IntRange(from = 0, to = 255)\n    get() = AndroidColor.red(value)\n\nval Color.green: Int\n    @IntRange(from = 0, to = 255)\n    get() = AndroidColor.green(value)\n\nval Color.blue: Int\n    @IntRange(from = 0, to = 255)\n    get() = AndroidColor.blue(value)\n\nfun Color.compositeOver(background: Color): Color =\n    ColorUtils.compositeColors(value, background.value).asColor()\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/ContextExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.annotation.SuppressLint\nimport android.app.Activity\nimport android.content.ActivityNotFoundException\nimport android.content.Context\nimport android.content.ContextWrapper\nimport android.content.Intent\nimport android.content.res.ColorStateList\nimport android.content.res.Configuration\nimport android.content.res.Resources\nimport android.graphics.drawable.Drawable\nimport android.os.Bundle\nimport android.os.Looper\nimport android.util.TypedValue\nimport android.view.LayoutInflater\nimport android.view.animation.Animation\nimport android.view.animation.AnimationUtils\nimport android.view.animation.Interpolator\nimport android.widget.Toast\nimport androidx.annotation.AnimRes\nimport androidx.annotation.AnyRes\nimport androidx.annotation.ArrayRes\nimport androidx.annotation.AttrRes\nimport androidx.annotation.BoolRes\nimport androidx.annotation.ColorInt\nimport androidx.annotation.DimenRes\nimport androidx.annotation.Dimension\nimport androidx.annotation.IntegerRes\nimport androidx.annotation.InterpolatorRes\nimport androidx.annotation.PluralsRes\nimport androidx.annotation.StyleRes\nimport androidx.appcompat.view.ContextThemeWrapper\nimport androidx.core.content.res.ResourcesCompat\nimport androidx.core.util.TypedValueCompat\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.compat.complexUnitCompat\nimport me.zhanghai.android.files.compat.getFloatCompat\nimport me.zhanghai.android.files.compat.mainExecutorCompat\nimport me.zhanghai.android.files.compat.obtainStyledAttributesCompat\nimport me.zhanghai.android.files.compat.use\n\nval Context.activity: Activity?\n    get() {\n        var context = this\n        while (true) {\n            when (context) {\n                is Activity -> return context\n                is ContextWrapper -> context = context.baseContext\n                else -> return null\n            }\n        }\n    }\n\nfun Context.getAnimation(@AnimRes id: Int): Animation = AnimationUtils.loadAnimation(this, id)\n\nfun Context.getBoolean(@BoolRes id: Int): Boolean = resources.getBoolean(id)\n\n@Dimension\nfun Context.getDimension(@DimenRes id: Int): Float = resources.getDimension(id)\n\n@Dimension(unit = Dimension.DP)\nfun Context.getDimensionDp(@DimenRes id: Int): Float {\n    TypedValue::class.useTemp { value ->\n        resources.getValue(id, value, true)\n        if (value.type != TypedValue.TYPE_DIMENSION) {\n            throw Resources.NotFoundException(\n                \"Resource ID #0x${Integer.toHexString(id)} type #0x${\n                    Integer.toHexString(value.type)\n                } is not valid\"\n            )\n        }\n        if (value.complexUnitCompat == TypedValue.COMPLEX_UNIT_DIP) {\n            return TypedValue.complexToFloat(value.data)\n        }\n        return dimensionToDp(TypedValue.complexToDimension(value.data, resources.displayMetrics))\n    }\n}\n\n@Dimension\nfun Context.getDimensionPixelOffset(@DimenRes id: Int): Int = resources.getDimensionPixelOffset(id)\n\n@Dimension\nfun Context.getDimensionPixelSize(@DimenRes id: Int): Int = resources.getDimensionPixelSize(id)\n\nfun Context.getFloat(@DimenRes id: Int): Float = resources.getFloatCompat(id)\n\nfun Context.getInteger(@IntegerRes id: Int): Int = resources.getInteger(id)\n\nfun Context.getInterpolator(@InterpolatorRes id: Int): Interpolator =\n    AnimationUtils.loadInterpolator(this, id)\n\nfun Context.getQuantityString(@PluralsRes id: Int, quantity: Int): String =\n    resources.getQuantityString(id, quantity)\n\nfun Context.getQuantityString(@PluralsRes id: Int, quantity: Int, vararg formatArgs: Any?): String =\n    resources.getQuantityString(id, quantity, *formatArgs)\n\nfun Context.getQuantityText(@PluralsRes id: Int, quantity: Int): CharSequence =\n    resources.getQuantityText(id, quantity)\n\nfun Context.getStringArray(@ArrayRes id: Int): Array<String> = resources.getStringArray(id)\n\nfun Context.getTextArray(@ArrayRes id: Int): Array<CharSequence> = resources.getTextArray(id)\n\n@SuppressLint(\"RestrictedApi\")\nfun Context.getBooleanByAttr(@AttrRes attr: Int): Boolean =\n    obtainStyledAttributesCompat(attrs = intArrayOf(attr)).use { it.getBoolean(0, false) }\n\n@ColorInt\nfun Context.getColorByAttr(@AttrRes attr: Int): Int =\n    getColorStateListByAttr(attr).defaultColor\n\n@SuppressLint(\"RestrictedApi\")\nfun Context.getColorStateListByAttr(@AttrRes attr: Int): ColorStateList =\n    obtainStyledAttributesCompat(attrs = intArrayOf(attr)).use { it.getColorStateList(0) }\n\n@SuppressLint(\"RestrictedApi\")\nfun Context.getDimensionByAttr(@AttrRes attr: Int): Float =\n    obtainStyledAttributesCompat(attrs = intArrayOf(attr)).use { it.getDimension(0, 0f) }\n\n@SuppressLint(\"RestrictedApi\")\nfun Context.getDimensionPixelOffsetByAttr(@AttrRes attr: Int): Int =\n    obtainStyledAttributesCompat(attrs = intArrayOf(attr)).use {\n        it.getDimensionPixelOffset(0, 0)\n    }\n\n@SuppressLint(\"RestrictedApi\")\nfun Context.getDimensionPixelSizeByAttr(@AttrRes attr: Int): Int =\n    obtainStyledAttributesCompat(attrs = intArrayOf(attr)).use { it.getDimensionPixelSize(0, 0) }\n\n@SuppressLint(\"RestrictedApi\")\nfun Context.getDrawableByAttr(@AttrRes attr: Int): Drawable =\n    obtainStyledAttributesCompat(attrs = intArrayOf(attr)).use { it.getDrawable(0) }\n\n@SuppressLint(\"RestrictedApi\")\nfun Context.getFloatByAttr(@AttrRes attr: Int): Float =\n    obtainStyledAttributesCompat(attrs = intArrayOf(attr)).use { it.getFloat(0, 0f) }\n\n@AnyRes\n@SuppressLint(\"RestrictedApi\")\nfun Context.getResourceIdByAttr(@AttrRes attr: Int): Int =\n    obtainStyledAttributesCompat(attrs = intArrayOf(attr)).use {\n        it.getResourceId(0, ResourcesCompat.ID_NULL)\n    }\n\nval Context.displayWidth: Int\n    get() = resources.displayMetrics.widthPixels\n\nval Context.displayHeight: Int\n    get() = resources.displayMetrics.heightPixels\n\n@Dimension\nfun Context.dpToDimension(@Dimension(unit = Dimension.DP) dp: Float): Float =\n    TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resources.displayMetrics)\n\n@Dimension\nfun Context.dpToDimension(@Dimension(unit = Dimension.DP) dp: Int) = dpToDimension(dp.toFloat())\n\n@Dimension\nfun Context.dpToDimensionPixelOffset(@Dimension(unit = Dimension.DP) dp: Float): Int =\n    dpToDimension(dp).toInt()\n\n@Dimension\nfun Context.dpToDimensionPixelOffset(@Dimension(unit = Dimension.DP) dp: Int) =\n    dpToDimensionPixelOffset(dp.toFloat())\n\n@Dimension\nfun Context.dpToDimensionPixelSize(@Dimension(unit = Dimension.DP) dp: Float): Int {\n    val value = dpToDimension(dp)\n    val size = (if (value >= 0) value + 0.5f else value - 0.5f).toInt()\n    return when {\n        size != 0 -> size\n        value == 0f -> 0\n        value > 0 -> 1\n        else -> -1\n    }\n}\n\n@Dimension\nfun Context.dpToDimensionPixelSize(@Dimension(unit = Dimension.DP) dp: Int) =\n    dpToDimensionPixelSize(dp.toFloat())\n\n@Dimension(unit = Dimension.DP)\nfun Context.dimensionToDp(@Dimension dimension: Float): Float =\n    TypedValueCompat.pxToDp(dimension, resources.displayMetrics)\n\n@Dimension(unit = Dimension.DP)\nfun Context.dimensionToDp(@Dimension dimension: Int): Float = dimensionToDp(dimension.toFloat())\n\nfun Context.hasSwDp(@Dimension(unit = Dimension.DP) dp: Int): Boolean =\n    resources.configuration.smallestScreenWidthDp >= dp\n\nval Context.hasSw600Dp: Boolean\n    get() = hasSwDp(600)\n\nfun Context.hasWDp(@Dimension(unit = Dimension.DP) dp: Int): Boolean =\n    resources.configuration.screenWidthDp >= dp\n\nval Context.hasW600Dp: Boolean\n    get() = hasWDp(600)\n\nval Context.hasW960Dp: Boolean\n    get() = hasWDp(960)\n\nval Context.isLightTheme: Boolean\n    get() = getBooleanByAttr(androidx.appcompat.R.attr.isLightTheme)\n\nval Context.isMaterial3Theme: Boolean\n    @SuppressLint(\"PrivateResource\")\n    get() = getBooleanByAttr(com.google.android.material.R.attr.isMaterial3Theme)\n\nval Context.isOrientationLandscape: Boolean\n    get() = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE\n\nval Context.isOrientationPortrait: Boolean\n    get() = resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT\n\nval Context.layoutInflater: LayoutInflater\n    get() = LayoutInflater.from(this)\n\nval Context.shortAnimTime: Int\n    get() = getInteger(android.R.integer.config_shortAnimTime)\n\nval Context.mediumAnimTime: Int\n    get() = getInteger(android.R.integer.config_mediumAnimTime)\n\nval Context.longAnimTime: Int\n    get() = getInteger(android.R.integer.config_longAnimTime)\n\nfun Context.showToast(textRes: Int, duration: Int = Toast.LENGTH_SHORT) {\n    if (Looper.myLooper() != Looper.getMainLooper()) {\n        mainExecutorCompat.execute { showToast(textRes, duration) }\n        return\n    }\n    Toast.makeText(this, textRes, duration).show()\n}\n\nfun Context.showToast(text: CharSequence, duration: Int = Toast.LENGTH_SHORT) {\n    if (Looper.myLooper() != Looper.getMainLooper()) {\n        mainExecutorCompat.execute { showToast(text, duration) }\n        return\n    }\n    Toast.makeText(this, text, duration).show()\n}\n\nfun Context.startActivitySafe(intent: Intent, options: Bundle? = null) {\n    try {\n        startActivity(intent, options)\n    } catch (e: ActivityNotFoundException) {\n        showToast(R.string.activity_not_found)\n    }\n}\n\nfun Context.withTheme(@StyleRes themeRes: Int): Context =\n    if (themeRes != 0) ContextThemeWrapper(this, themeRes) else this\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/DataState.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nsealed class DataState<T> {\n    abstract val data: T?\n\n    data class Loading<T>(override val data: T? = null) : DataState<T>()\n\n    data class Success<T>(override val data: T) : DataState<T>()\n\n    data class Error<T>(override val data: T?, val throwable: Throwable) : DataState<T>()\n}\n\nfun <T> DataState<T>.toLoading(): DataState.Loading<T> =\n    this as? DataState.Loading ?: DataState.Loading(data)\n\nfun <T> DataState<T>.toError(throwable: Throwable): DataState.Error<T> =\n    DataState.Error(data, throwable)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/DebouncedRunnable.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.os.Handler\n\nclass DebouncedRunnable(\n    private val handler: Handler,\n    private val intervalMillis: Long,\n    block: () -> Unit\n) : () -> Unit {\n    private val lock = Any()\n\n    private val runnable = Runnable(block)\n\n    override operator fun invoke() {\n        synchronized(lock) {\n            handler.removeCallbacks(runnable)\n            handler.postDelayed(runnable, intervalMillis)\n        }\n    }\n\n    fun cancel() {\n        synchronized(lock) { handler.removeCallbacks(runnable) }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/DialogFragmentExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport androidx.fragment.app.DialogFragment\nimport androidx.fragment.app.Fragment\n\nfun DialogFragment.show(fragment: Fragment) {\n    show(fragment.childFragmentManager, null)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/DrawableExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.content.res.Resources\nimport android.graphics.Bitmap\nimport android.graphics.Canvas\nimport android.graphics.drawable.BitmapDrawable\nimport android.graphics.drawable.Drawable\n\nfun Drawable.toBitmapDrawable(resources: Resources): BitmapDrawable =\n    if (this is BitmapDrawable) {\n        this\n    } else {\n        val bitmap = Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, Bitmap.Config.ARGB_8888)\n        val canvas = Canvas(bitmap)\n        setBounds(0, 0, canvas.width, canvas.height)\n        draw(canvas)\n        BitmapDrawable(resources, bitmap)\n    }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/EditTextExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.widget.EditText\n\nfun EditText.setTextWithSelection(text: CharSequence?) {\n    setText(text)\n    setSelection(0, this.text.length)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/EnvironmentExtensions.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.content.Intent\nimport android.net.Uri\nimport android.os.Build\nimport android.os.Environment\nimport android.provider.Settings\nimport androidx.annotation.ChecksSdkIntAtLeast\nimport androidx.annotation.RequiresApi\nimport me.zhanghai.android.files.app.application\nimport me.zhanghai.android.files.app.packageManager\nimport kotlin.reflect.KClass\n\n// TvSettings didn't have \"All files access\" page until Android 13.\n@ChecksSdkIntAtLeast(Build.VERSION_CODES.R)\nfun KClass<Environment>.supportsExternalStorageManager(): Boolean =\n    when {\n        Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> true\n        Build.VERSION.SDK_INT >= Build.VERSION_CODES.R ->\n            isManageAppAllFilesAccessPermissionIntentResolved\n        else -> false\n    }\n\n@RequiresApi(Build.VERSION_CODES.R)\nfun KClass<Environment>.createManageAppAllFilesAccessPermissionIntent(packageName: String): Intent =\n    Intent(\n        Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION,\n        Uri.fromParts(\"package\", packageName, null)\n    )\n\n@delegate:RequiresApi(Build.VERSION_CODES.R)\nprivate val isManageAppAllFilesAccessPermissionIntentResolved: Boolean\n    by lazy(LazyThreadSafetyMode.NONE) {\n        Environment::class.createManageAppAllFilesAccessPermissionIntent(application.packageName)\n            .resolveActivity(packageManager) != null\n    }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/ForegroundNotificationManager.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.annotation.SuppressLint\nimport android.app.Notification\nimport android.app.Service\nimport androidx.core.app.ServiceCompat\nimport me.zhanghai.android.files.app.notificationManager\nimport me.zhanghai.android.files.compat.stopForegroundCompat\n\nclass ForegroundNotificationManager(private val service: Service) {\n    private val notifications = mutableMapOf<Int, Notification>()\n\n    private var foregroundId = 0\n\n    @SuppressLint(\"MissingPermission\")\n    fun notify(id: Int, notification: Notification) {\n        synchronized(notifications) {\n            if (notifications.isEmpty()) {\n                service.startForeground(id, notification)\n                notifications[id] = notification\n                foregroundId = id\n            } else {\n                if (id == foregroundId) {\n                    service.startForeground(id, notification)\n                } else {\n                    notificationManager.notify(id, notification)\n                }\n                notifications[id] = notification\n            }\n        }\n    }\n\n    fun cancel(id: Int) {\n        synchronized(notifications) {\n            if (id !in notifications) {\n                return\n            }\n            if (id == foregroundId) {\n                if (notifications.size == 1) {\n                    service.stopForegroundCompat(ServiceCompat.STOP_FOREGROUND_REMOVE)\n                    notifications -= id\n                    foregroundId = 0\n                } else {\n                    notifications.entries.find { it.key != id }!!.let {\n                        service.startForeground(it.key, it.value)\n                        foregroundId = it.key\n                    }\n                    notificationManager.cancel(id)\n                    notifications -= id\n                }\n            } else {\n                notificationManager.cancel(id)\n                notifications -= id\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/FragmentExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.content.ActivityNotFoundException\nimport android.content.Intent\nimport android.content.res.ColorStateList\nimport android.graphics.drawable.Drawable\nimport android.os.Bundle\nimport android.view.animation.Animation\nimport android.view.animation.Interpolator\nimport android.widget.Toast\nimport androidx.activity.OnBackPressedCallback\nimport androidx.annotation.AnimRes\nimport androidx.annotation.AnyRes\nimport androidx.annotation.ArrayRes\nimport androidx.annotation.AttrRes\nimport androidx.annotation.BoolRes\nimport androidx.annotation.ColorInt\nimport androidx.annotation.ColorRes\nimport androidx.annotation.DimenRes\nimport androidx.annotation.Dimension\nimport androidx.annotation.DrawableRes\nimport androidx.annotation.IntegerRes\nimport androidx.annotation.InterpolatorRes\nimport androidx.annotation.PluralsRes\nimport androidx.fragment.app.Fragment\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.compat.checkSelfPermissionCompat\nimport me.zhanghai.android.files.compat.getColorCompat\nimport me.zhanghai.android.files.compat.getColorStateListCompat\nimport me.zhanghai.android.files.compat.getDrawableCompat\n\nfun Fragment.addOnBackPressedCallback(callback: OnBackPressedCallback) {\n    requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, callback)\n}\n\nfun Fragment.checkSelfPermission(permission: String): Int =\n    requireContext().checkSelfPermissionCompat(permission)\n\nfun Fragment.finish() {\n    requireActivity().finish()\n}\n\nfun Fragment.getAnimation(@AnimRes id: Int): Animation = requireContext().getAnimation(id)\n\nfun Fragment.getBoolean(@BoolRes id: Int): Boolean = requireContext().getBoolean(id)\n\n@ColorInt\nfun Fragment.getColor(@ColorRes id: Int): Int = requireContext().getColorCompat(id)\n\nfun Fragment.getColorStateList(@ColorRes id: Int): ColorStateList =\n    requireContext().getColorStateListCompat(id)\n\n@Dimension\nfun Fragment.getDimension(@DimenRes id: Int): Float = requireContext().getDimension(id)\n\n@Dimension(unit = Dimension.DP)\nfun Fragment.getDimensionDp(@DimenRes id: Int): Float = requireContext().getDimensionDp(id)\n\n@Dimension\nfun Fragment.getDimensionPixelOffset(@DimenRes id: Int): Int =\n    requireContext().getDimensionPixelOffset(id)\n\n@Dimension\nfun Fragment.getDimensionPixelSize(@DimenRes id: Int): Int =\n    requireContext().getDimensionPixelSize(id)\n\nfun Fragment.getDrawable(@DrawableRes id: Int): Drawable = requireContext().getDrawableCompat(id)\n\nfun Fragment.getFloat(@DimenRes id: Int): Float = requireContext().getFloat(id)\n\nfun Fragment.getInteger(@IntegerRes id: Int): Int = requireContext().getInteger(id)\n\nfun Fragment.getInterpolator(@InterpolatorRes id: Int): Interpolator =\n    requireContext().getInterpolator(id)\n\nfun Fragment.getQuantityString(@PluralsRes id: Int, quantity: Int): String =\n    requireContext().getQuantityString(id, quantity)\n\nfun Fragment.getQuantityString(\n    @PluralsRes id: Int,\n    quantity: Int,\n    vararg formatArgs: Any?\n): String = requireContext().getQuantityString(id, quantity, *formatArgs)\n\nfun Fragment.getQuantityText(@PluralsRes id: Int, quantity: Int): CharSequence =\n    requireContext().getQuantityText(id, quantity)\n\nfun Fragment.getStringArray(@ArrayRes id: Int) = requireContext().getStringArray(id)\n\nfun Fragment.getTextArray(@ArrayRes id: Int): Array<CharSequence> =\n    requireContext().getTextArray(id)\n\nfun Fragment.getBooleanByAttr(@AttrRes attr: Int) = requireContext().getBooleanByAttr(attr)\n\n@ColorInt\nfun Fragment.getColorByAttr(@AttrRes attr: Int) = requireContext().getColorByAttr(attr)\n\nfun Fragment.getColorStateListByAttr(@AttrRes attr: Int) =\n    requireContext().getColorStateListByAttr(attr)\n\nfun Fragment.getDimensionByAttr(@AttrRes attr: Int) = requireContext().getDimensionByAttr(attr)\n\nfun Fragment.getDimensionPixelOffsetByAttr(@AttrRes attr: Int) =\n    requireContext().getDimensionPixelOffsetByAttr(attr)\n\nfun Fragment.getDimensionPixelSizeByAttr(@AttrRes attr: Int): Int =\n    requireContext().getDimensionPixelSizeByAttr(attr)\n\nfun Fragment.getDrawableByAttr(@AttrRes attr: Int) = requireContext().getDrawableByAttr(attr)\n\nfun Fragment.getFloatByAttr(@AttrRes attr: Int) = requireContext().getFloatByAttr(attr)\n\n@AnyRes\nfun Fragment.getResourceIdByAttr(@AttrRes attr: Int): Int =\n    requireContext().getResourceIdByAttr(attr)\n\n@Dimension\nfun Fragment.dpToDimension(@Dimension(unit = Dimension.DP) dp: Float) =\n    requireContext().dpToDimension(dp)\n\n@Dimension\nfun Fragment.dpToDimension(@Dimension(unit = Dimension.DP) dp: Int) =\n    requireContext().dpToDimension(dp)\n\n@Dimension\nfun Fragment.dpToDimensionPixelOffset(@Dimension(unit = Dimension.DP) dp: Float) =\n    requireContext().dpToDimensionPixelOffset(dp)\n\n@Dimension\nfun Fragment.dpToDimensionPixelOffset(@Dimension(unit = Dimension.DP) dp: Int) =\n    requireContext().dpToDimensionPixelOffset(dp)\n\n@Dimension\nfun Fragment.dpToDimensionPixelSize(@Dimension(unit = Dimension.DP) dp: Float) =\n    requireContext().dpToDimensionPixelSize(dp)\n\n@Dimension\nfun Fragment.dpToDimensionPixelSize(@Dimension(unit = Dimension.DP) dp: Int) =\n    requireContext().dpToDimensionPixelSize(dp)\n\n@Dimension(unit = Dimension.DP)\nfun Fragment.dimensionToDp(@Dimension dimension: Float): Float =\n    requireContext().dimensionToDp(dimension)\n\n@Dimension(unit = Dimension.DP)\nfun Fragment.dimensionToDp(@Dimension dimension: Int): Float =\n    requireContext().dimensionToDp(dimension)\n\nfun Fragment.setResult(resultCode: Int, resultData: Intent? = null) =\n    requireActivity().setResult(resultCode, resultData)\n\nval Fragment.shortAnimTime\n    get() = requireContext().shortAnimTime\n\nval Fragment.mediumAnimTime\n    get() = requireContext().mediumAnimTime\n\nval Fragment.longAnimTime\n    get() = requireContext().longAnimTime\n\nfun Fragment.showToast(textRes: Int, duration: Int = Toast.LENGTH_SHORT) {\n    requireContext().showToast(textRes, duration)\n}\n\nfun Fragment.showToast(text: CharSequence, duration: Int = Toast.LENGTH_SHORT) {\n    requireContext().showToast(text, duration)\n}\n\nfun Fragment.startActivitySafe(intent: Intent, options: Bundle? = null) {\n    try {\n        startActivity(intent, options)\n    } catch (e: ActivityNotFoundException) {\n        showToast(R.string.activity_not_found)\n    }\n}\n\nfun Fragment.startActivityForResultSafe(intent: Intent, requestCode: Int, options: Bundle? = null) {\n    try {\n        startActivityForResult(intent, requestCode, options)\n    } catch (e: ActivityNotFoundException) {\n        showToast(R.string.activity_not_found)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/FragmentViewModelLazy.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.viewModels\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.lifecycle.ViewModelStoreOwner\nimport androidx.lifecycle.viewmodel.CreationExtras\n\ninline fun <reified VM : ViewModel> Fragment.viewModels(\n    noinline ownerProducer: () -> ViewModelStoreOwner = { this },\n    noinline extrasProducer: (() -> CreationExtras)? = null,\n    noinline factoryProducer: (() -> () -> VM)? = null\n) = viewModels<VM>(\n    ownerProducer,\n    extrasProducer,\n    factoryProducer?.let {\n        {\n            val factory = it()\n            object : ViewModelProvider.Factory {\n                @Suppress(\"UNCHECKED_CAST\")\n                override fun <T : ViewModel> create(modelClass: Class<T>) = factory() as T\n            }\n        }\n    }\n)\n\ninline fun <reified VM : ViewModel> Fragment.activityViewModels(\n    noinline extrasProducer: (() -> CreationExtras)? = null,\n    noinline factoryProducer: (() -> () -> VM)? = null\n) = viewModels(::requireActivity, extrasProducer, factoryProducer)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/GeocoderExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.location.Address\nimport android.location.Geocoder\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.withContext\nimport java.io.IOException\n\nval isGeocoderPresent by lazy { Geocoder.isPresent() }\n\n@Throws(IOException::class)\nsuspend fun Geocoder.awaitGetFromLocation(\n    latitude: Double,\n    longitude: Double,\n    maxResults: Int\n): List<Address> =\n    withContext(Dispatchers.IO) {\n        getFromLocation(latitude, longitude, maxResults)\n            ?: throw IOException(NullPointerException())\n    }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/InetAddressExtensions.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport me.zhanghai.android.files.app.wifiManager\nimport java.net.InetAddress\nimport java.net.NetworkInterface\nimport java.net.SocketException\nimport java.net.UnknownHostException\nimport kotlin.reflect.KClass\n\nfun KClass<InetAddress>.getLocalAddress(): InetAddress? {\n    val wifiInfo = wifiManager.connectionInfo\n    if (wifiInfo != null) {\n        // WifiStateMachine doesn't support IPv6 as of P, so no need to get the original\n        // InetAddress object with reflection.\n        val address = wifiInfo.ipAddress\n        if (address != 0) {\n            return intToInetAddress(address)\n        }\n    }\n    try {\n        for (networkInterface in NetworkInterface.getNetworkInterfaces()) {\n            if (!networkInterface.isUp || networkInterface.isLoopback) {\n                continue\n            }\n            for (inetAddress in networkInterface.inetAddresses) {\n                // Works for consumer IPv4 addresses.\n                if (!inetAddress.isSiteLocalAddress) {\n                    continue\n                }\n                return inetAddress\n            }\n        }\n    } catch (e: SocketException) {\n        e.printStackTrace()\n    }\n    return null\n}\n\n/*\n * @see android.net.NetworkUtils#intToInetAddress(int)\n */\nprivate fun intToInetAddress(hostAddress: Int): InetAddress {\n    val addressBytes = byteArrayOf(\n        (0xff and hostAddress).toByte(),\n        (0xff and (hostAddress shr 8)).toByte(),\n        (0xff and (hostAddress shr 16)).toByte(),\n        (0xff and (hostAddress shr 24)).toByte()\n    )\n    return try {\n        InetAddress.getByAddress(addressBytes)\n    } catch (e: UnknownHostException) {\n        throw AssertionError()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/IntExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nfun Int.hasBits(bits: Int): Boolean = this and bits == bits\n\ninfix fun Int.andInv(other: Int): Int = this and other.inv()\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/IntentExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.content.Context\nimport android.content.Intent\nimport android.net.Uri\nimport android.os.Parcelable\nimport android.provider.MediaStore\nimport android.provider.Settings\nimport androidx.core.app.ShareCompat\nimport me.zhanghai.android.files.app.appClassLoader\nimport me.zhanghai.android.files.app.application\nimport me.zhanghai.android.files.app.packageManager\nimport me.zhanghai.android.files.compat.DocumentsContractCompat\nimport me.zhanghai.android.files.compat.removeFlagsCompat\nimport me.zhanghai.android.files.file.MimeType\nimport me.zhanghai.android.files.file.intentType\nimport kotlin.reflect.KClass\n\nfun <T : Context> KClass<T>.createIntent(): Intent = Intent(application, java)\n\nfun CharSequence.createSendTextIntent(htmlText: String? = null): Intent =\n    // The context parameter here is only used for passing calling activity information and starting\n    // chooser activity, neither of which we care about.\n    ShareCompat.IntentBuilder(application)\n        .setType(MimeType.TEXT_PLAIN.value)\n        .setText(this)\n        .apply { htmlText?.let { setHtmlText(it) } }\n        .intent\n        // FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET is unnecessarily added by ShareCompat.IntentBuilder.\n        .apply {\n            @Suppress(\"DEPRECATION\")\n            removeFlagsCompat(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET)\n        }\n\nfun KClass<Intent>.createLaunchApp(packageName: String): Intent? =\n    packageManager.getLaunchIntentForPackage(packageName)\n\nfun KClass<Intent>.createPickImage(allowMultiple: Boolean = false): Intent =\n    MimeType.IMAGE_ANY.createPickFileIntent(allowMultiple)\n\nfun KClass<Intent>.createPickOrCaptureImageWithChooser(\n    allowPickMultiple: Boolean = false,\n    captureOutputUri: Uri\n): Intent =\n    createPickImage(allowPickMultiple)\n        .withChooser(captureOutputUri.createCaptureImage())\n\nfun KClass<Intent>.createSyncSettings(\n    authorities: Array<out String>? = null,\n    accountTypes: Array<out String>? = null\n): Intent =\n    Intent(Settings.ACTION_SYNC_SETTINGS).apply {\n        if (!authorities.isNullOrEmpty()) {\n            putExtra(Settings.EXTRA_AUTHORITIES, authorities)\n        }\n        if (!accountTypes.isNullOrEmpty()) {\n            putExtra(Settings.EXTRA_ACCOUNT_TYPES, accountTypes)\n        }\n    }\n\nfun KClass<Intent>.createSyncSettingsWithAuthorities(vararg authorities: String) =\n    createSyncSettings(authorities = authorities)\n\nfun KClass<Intent>.createSyncSettingsWithAccountType(vararg accountTypes: String) =\n    createSyncSettings(accountTypes = accountTypes)\n\nfun KClass<Intent>.createViewAppInMarket(packageName: String): Intent =\n    Uri.parse(\"market://details?id=$packageName\").createViewIntent()\n\n// @see com.android.documentsui.inspector.InspectorController.createGeoIntent\n// @see https://developer.android.com/guide/components/intents-common.html#Maps\nfun KClass<Intent>.createViewLocation(latitude: Float, longitude: Float, label: String): Intent =\n    Uri.parse(\"geo:0,0?q=$latitude,$longitude(${Uri.encode(label)})\").createViewIntent()\n\nfun <T : Parcelable> Intent.getParcelableExtraSafe(key: String?): T? {\n    setExtrasClassLoader(appClassLoader)\n    return getParcelableExtra(key)\n}\n\nfun Intent.getParcelableArrayExtraSafe(key: String?): Array<Parcelable>? {\n    setExtrasClassLoader(appClassLoader)\n    return getParcelableArrayExtra(key)\n}\n\nfun <T : Parcelable?> Intent.getParcelableArrayListExtraSafe(key: String?): ArrayList<T>? {\n    setExtrasClassLoader(appClassLoader)\n    return getParcelableArrayListExtra(key)\n}\n\nfun Intent.withChooser(title: CharSequence? = null, vararg initialIntents: Intent): Intent =\n    Intent.createChooser(this, title).apply {\n        putExtra(Intent.EXTRA_INITIAL_INTENTS, initialIntents)\n    }\n\nfun Intent.withChooser(vararg initialIntents: Intent) = withChooser(null, *initialIntents)\n\nfun Uri.createEditIntent(mimeType: MimeType): Intent =\n    Intent(Intent.ACTION_EDIT)\n        // Calling setType() will clear data.\n        .setDataAndType(this, mimeType.intentType)\n        .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)\n\nfun MimeType.createPickFileIntent(allowMultiple: Boolean = false) =\n    Intent(Intent.ACTION_OPEN_DOCUMENT)\n        .addCategory(Intent.CATEGORY_OPENABLE)\n        .setType(value)\n        .apply {\n            if (allowMultiple) {\n                putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)\n            }\n        }\n\nfun Collection<MimeType>.createPickFileIntent(allowMultiple: Boolean = false): Intent =\n    (singleOrNull() ?: MimeType.ANY).createPickFileIntent(allowMultiple)\n        .apply {\n            if (size > 1) {\n                putExtra(Intent.EXTRA_MIME_TYPES, map { it.value }.toTypedArray())\n            }\n        }\n\nfun Uri.createSendImageIntent(text: CharSequence? = null): Intent =\n    createSendStreamIntent(MimeType.IMAGE_ANY).apply {\n        text?.let {\n            // For maximum compatibility.\n            putExtra(Intent.EXTRA_TEXT, it)\n            putExtra(Intent.EXTRA_TITLE, it)\n            putExtra(Intent.EXTRA_SUBJECT, it)\n            // HACK: WeChat moments respects this extra only.\n            putExtra(\"Kdescription\", it)\n        }\n    }\n\nfun Uri.createSendStreamIntent(mimeType: MimeType): Intent =\n    listOf(this).createSendStreamIntent(listOf(mimeType))\n\nfun Collection<Uri>.createSendStreamIntent(mimeTypes: Collection<MimeType>): Intent =\n    // Use ShareCompat.IntentBuilder for its migrateExtraStreamToClipData() because\n    // Intent.migrateExtraStreamToClipData() won't promote child ClipData and flags to the chooser\n    // intent, breaking third party share sheets.\n    // The context parameter here is only used for passing calling activity information and starting\n    // chooser activity, neither of which we care about.\n    ShareCompat.IntentBuilder(application)\n        .setType(mimeTypes.intentType)\n        .apply { forEach { addStream(it) } }\n        .intent\n        // FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET is unnecessarily added by ShareCompat.IntentBuilder.\n        .apply {\n            @Suppress(\"DEPRECATION\")\n            removeFlagsCompat(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET)\n        }\n\nfun Uri.createDocumentsUiViewDirectoryIntent(): Intent =\n    createViewIntent(MimeType.DIRECTORY)\n        .apply { DocumentsContractCompat.getDocumentsUiPackage()?.let { setPackage(it) } }\n\nfun Uri.createViewIntent(): Intent = Intent(Intent.ACTION_VIEW, this)\n\nfun Uri.createViewIntent(mimeType: MimeType): Intent =\n    Intent(Intent.ACTION_VIEW)\n        // Calling setType() will clear data.\n        .setDataAndType(this, mimeType.intentType)\n        .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)\n\n@Suppress(\"DEPRECATION\")\nfun Uri.createInstallPackageIntent(): Intent =\n    Intent(Intent.ACTION_INSTALL_PACKAGE)\n        .setDataAndType(this, MimeType.APK.value)\n        .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)\n\nfun Uri.createCaptureImage(): Intent =\n    Intent(MediaStore.ACTION_IMAGE_CAPTURE)\n        .putExtra(MediaStore.EXTRA_OUTPUT, this)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/IntentPathExtensions.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.content.ContentResolver\nimport android.content.Intent\nimport android.net.Uri\nimport java8.nio.file.Path\nimport java8.nio.file.Paths\nimport me.zhanghai.android.files.BuildConfig\nimport me.zhanghai.android.files.compat.DocumentsContractCompat\nimport me.zhanghai.android.files.storage.createOrLog\nimport java.io.Serializable\nimport java.net.URI\n\nprivate const val EXTRA_PATH_URI = \"${BuildConfig.APPLICATION_ID}.extra.PATH_URI\"\n\nvar Intent.extraPath: Path?\n    get() {\n        val extraPathUri = getStringExtra(EXTRA_PATH_URI)\n        extraPathUri?.let { URI::class.createOrLog(it) }?.let { return Paths.get(it) }\n        data?.toPathOrNull()?.let { return it }\n        val extraInitialUri = getParcelableExtraSafe<Uri>(DocumentsContractCompat.EXTRA_INITIAL_URI)\n        extraInitialUri?.toPathOrNull()?.let { return it }\n        val extraAbsolutePath = getStringExtra(\"org.openintents.extra.ABSOLUTE_PATH\")\n            ?.takeIfNotEmpty()\n        extraAbsolutePath?.let { return Paths.get(it) }\n        return null\n    }\n    set(value) {\n        // We cannot put Path into intent here, otherwise we will crash other apps unmarshalling it.\n        // We cannot put URI into intent here either, because ShortcutInfo uses PersistableBundle\n        // which doesn't support Serializable.\n        putExtra(EXTRA_PATH_URI, value?.toUri()?.toString())\n    }\n\nval Intent.saveAsPath: Path?\n    get() {\n        val uri =\n            when (action) {\n                Intent.ACTION_VIEW -> data\n                Intent.ACTION_SEND -> getParcelableExtraSafe(Intent.EXTRA_STREAM) as? Uri\n                else -> null\n            }\n        return uri?.toPathOrNull()\n    }\n\nprivate fun Uri.toPathOrNull(): Path? =\n    when (scheme) {\n        ContentResolver.SCHEME_FILE, null -> path?.takeIfNotEmpty()?.let { Paths.get(it) }\n        ContentResolver.SCHEME_CONTENT -> {\n            val uri = URI::class.createOrLog(toString())\n                // Some people use Uri.parse() without encoding their path. Let's try saving\n                // them by calling the other URI constructor that encodes everything.\n                ?: URI::class.createOrLog(scheme, userInfo, host, port, path, query, fragment)\n            uri?.let { Paths.get(it) }\n        }\n        else -> null\n    }\n\nprivate const val EXTRA_PATH_URI_LIST = \"${BuildConfig.APPLICATION_ID}.extra.PATH_URI_LIST\"\n\nvar Intent.extraPathList: List<Path>\n    get() {\n        @Suppress(\"UNCHECKED_CAST\")\n        val extraPathUris = (getSerializableExtra(EXTRA_PATH_URI_LIST) as List<URI>?)\n            ?.takeIfNotEmpty()\n        extraPathUris?.let { return it.map { uri -> Paths.get(uri) } }\n        return listOfNotNull(extraPath)\n    }\n    set(value) {\n        // We cannot put Path into intent here, otherwise we will crash other apps unmarshalling it.\n        val pathUris = value.map { it.toUri() }\n        putExtra(EXTRA_PATH_URI_LIST, pathUris as Serializable)\n    }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/LateInitMutableStateFlow.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport kotlinx.coroutines.flow.MutableStateFlow\n\n@Suppress(\"FunctionName\", \"UNCHECKED_CAST\")\nfun <T : Any> LateInitMutableStateFlow(): MutableStateFlow<T> =\n    MutableStateFlow<T?>(null) as MutableStateFlow<T>\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/LazyReflection.kt",
    "content": "package me.zhanghai.android.files.util\n\nimport java.lang.reflect.Constructor\nimport java.lang.reflect.Field\nimport java.lang.reflect.Method\n\n@Throws(ReflectiveOperationException::class)\nfun lazyReflectedClass(className: String): Lazy<Class<*>> = lazy { getReflectedClass(className) }\n\n@Throws(ReflectiveOperationException::class)\nprivate fun getReflectedClass(className: String): Class<*> = Class.forName(className)\n\n@Throws(ReflectiveOperationException::class)\nfun lazyReflectedField(declaringClass: Class<*>, fieldName: String): Lazy<Field> = lazy {\n    getReflectedField(declaringClass, fieldName)\n}\n\n@Throws(ReflectiveOperationException::class)\nfun lazyReflectedField(declaringClassName: String, fieldName: String): Lazy<Field> = lazy {\n    getReflectedField(getReflectedClass(declaringClassName), fieldName)\n}\n\n@Throws(ReflectiveOperationException::class)\nprivate fun getReflectedField(declaringClass: Class<*>, fieldName: String) =\n    declaringClass.getDeclaredField(fieldName).also { it.isAccessible = true }\n\n@Throws(ReflectiveOperationException::class)\nfun <T> lazyReflectedConstructor(\n    declaringClass: Class<T>,\n    vararg parameterTypes: Any\n): Lazy<Constructor<T>> = lazy {\n    getReflectedConstructor(declaringClass, *getParameterTypes(parameterTypes))\n}\n\n@Throws(ReflectiveOperationException::class)\nfun lazyReflectedConstructor(\n    declaringClassName: String,\n    vararg parameterTypes: Any\n): Lazy<Constructor<*>> = lazy {\n    getReflectedConstructor(\n        getReflectedClass(declaringClassName), *getParameterTypes(parameterTypes)\n    )\n}\n\n@Throws(ReflectiveOperationException::class)\nprivate fun <T> getReflectedConstructor(declaringClass: Class<T>, vararg parameterTypes: Class<*>) =\n    declaringClass.getDeclaredConstructor(*parameterTypes).also { it.isAccessible = true }\n\n@Throws(ReflectiveOperationException::class)\nfun lazyReflectedMethod(\n    declaringClass: Class<*>,\n    methodName: String,\n    vararg parameterTypes: Any\n): Lazy<Method> = lazy {\n    getReflectedMethod(declaringClass, methodName, *getParameterTypes(parameterTypes))\n}\n\n@Throws(ReflectiveOperationException::class)\nfun lazyReflectedMethod(\n    declaringClassName: String,\n    methodName: String,\n    vararg parameterTypes: Any\n): Lazy<Method> = lazy {\n    getReflectedMethod(\n        getReflectedClass(declaringClassName), methodName, *getParameterTypes(parameterTypes)\n    )\n}\n\n@Throws(ReflectiveOperationException::class)\nprivate fun getReflectedMethod(\n    declaringClass: Class<*>,\n    methodName: String,\n    vararg parameterTypes: Class<*>\n) = declaringClass.getDeclaredMethod(methodName, *parameterTypes).also { it.isAccessible = true }\n\n@Throws(ReflectiveOperationException::class)\nprivate fun getParameterTypes(parameterTypes: Array<out Any>): Array<Class<*>> =\n    Array(parameterTypes.size) {\n        when (val parameterType = parameterTypes[it]) {\n            is Class<*> -> parameterType\n            is String -> getReflectedClass(parameterType)\n            else -> throw IllegalArgumentException(parameterType.toString())\n        }\n    }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/ListExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nfun <T> List<T>.startsWith(prefix: List<T>): Boolean =\n    size >= prefix.size && subList(0, prefix.size) == prefix\n\nfun <T> List<T>.endsWith(suffix: List<T>): Boolean =\n    size >= suffix.size && subList(size - suffix.size, size) == suffix\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/LiveDataExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport androidx.lifecycle.LiveData\n\n@Suppress(\"UNCHECKED_CAST\")\nval <T> LiveData<T>.valueCompat: T\n    get() = value as T\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/LocaleListCompatExtensions.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport androidx.core.os.LocaleListCompat\nimport java.util.Locale\n\nfun LocaleListCompat.toList(): List<Locale> = List(size()) { this[it]!! }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/LongExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nfun Long.hasBits(bits: Long): Boolean = this and bits == bits\n\ninfix fun Long.andInv(other: Long): Long = this and other.inv()\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/MapSet.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nopen class MapSet<K, V> : AbstractMutableSet<V> {\n    private val keyExtractor: (V) -> K\n\n    private val map: MutableMap<K, V>\n\n    constructor(keyExtractor: (V) -> K) : this(keyExtractor, false)\n\n    protected constructor(keyExtractor: (V) -> K, isLinked: Boolean) : super() {\n        this.keyExtractor = keyExtractor\n        this.map = if (isLinked) linkedMapOf() else hashMapOf()\n    }\n\n    override fun iterator(): MutableIterator<V> = map.values.iterator()\n\n    override val size: Int\n        get() = map.size\n\n    override fun isEmpty(): Boolean = map.isEmpty()\n\n    override fun contains(element: V): Boolean = map.containsKey(keyExtractor(element))\n\n    override fun add(element: V): Boolean = map.put(keyExtractor(element), element) == null\n\n    override fun remove(element: V): Boolean = map.remove(keyExtractor(element)) != null\n\n    override fun clear() {\n        map.clear()\n    }\n}\n\nopen class LinkedMapSet<K, V>(keyExtractor: (V) -> K) : MapSet<K, V>(keyExtractor, true)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/MediaMetadataRetrieverPathExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.media.MediaDataSource\nimport android.media.MediaMetadataRetriever\nimport android.os.Build\nimport androidx.annotation.RequiresApi\nimport java8.nio.channels.SeekableByteChannel\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.provider.common.newByteChannel\nimport me.zhanghai.android.files.provider.document.isDocumentPath\nimport me.zhanghai.android.files.provider.document.resolver.DocumentResolver\nimport me.zhanghai.android.files.provider.ftp.isFtpPath\nimport me.zhanghai.android.files.provider.linux.isLinuxPath\nimport java.io.IOException\nimport java.nio.ByteBuffer\n\nval Path.isMediaMetadataRetrieverCompatible: Boolean\n    get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n        !isFtpPath\n    } else {\n        isLinuxPath || isDocumentPath\n    }\n\nfun MediaMetadataRetriever.setDataSource(path: Path) {\n    when {\n        path.isLinuxPath -> setDataSource(path.toFile().path)\n        path.isDocumentPath ->\n            DocumentResolver.openParcelFileDescriptor(path as DocumentResolver.Path, \"r\")\n                .use { pfd -> setDataSource(pfd.fileDescriptor) }\n        Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> {\n            val channel = try {\n                path.newByteChannel()\n            } catch (e: IOException) {\n                throw IllegalArgumentException(e)\n            }\n            setDataSource(PathMediaDataSource(channel))\n        }\n        else -> throw IllegalArgumentException(path.toString())\n    }\n}\n\n@RequiresApi(Build.VERSION_CODES.M)\nprivate class PathMediaDataSource(private val channel: SeekableByteChannel) : MediaDataSource() {\n    @Throws(IOException::class)\n    override fun readAt(position: Long, buffer: ByteArray, offset: Int, size: Int): Int {\n        channel.position(position)\n        return channel.read(ByteBuffer.wrap(buffer, offset, size))\n    }\n\n    @Throws(IOException::class)\n    override fun getSize(): Long {\n        return channel.size()\n    }\n\n    @Throws(IOException::class)\n    override fun close() {\n        channel.close()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/NotificationTemplate.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.app.NotificationChannel\nimport android.content.Context\nimport android.media.AudioAttributes\nimport android.net.Uri\nimport android.os.Build\nimport androidx.annotation.ColorRes\nimport androidx.annotation.DrawableRes\nimport androidx.annotation.RequiresApi\nimport androidx.annotation.StringRes\nimport androidx.core.app.NotificationCompat\nimport me.zhanghai.android.files.compat.getColorCompat\n\nclass NotificationTemplate(\n    val channelTemplate: NotificationChannelTemplate,\n    @ColorRes val colorRes: Int? = null,\n    @DrawableRes val smallIcon: Int? = null,\n    @StringRes val contentTitleRes: Int? = null,\n    @StringRes val contentTextRes: Int? = null,\n    val ongoing: Boolean? = null,\n    val onlyAlertOnce: Boolean? = null,\n    val autoCancel: Boolean? = null,\n    val category: String? = null,\n    val priority: Int? = null\n) {\n    fun createBuilder(context: Context): NotificationCompat.Builder =\n        NotificationCompat.Builder(context, channelTemplate.id).apply {\n            colorRes?.let { setColor(context.getColorCompat(it)) }\n            smallIcon?.let { setSmallIcon(it) }\n            contentTitleRes?.let { setContentTitle(context.getText(contentTitleRes)) }\n            contentTextRes?.let { setContentText(context.getText(contentTextRes)) }\n            ongoing?.let { setOngoing(it) }\n            onlyAlertOnce?.let { setOnlyAlertOnce(it) }\n            autoCancel?.let { setAutoCancel(it) }\n            category?.let { setCategory(it) }\n            this@NotificationTemplate.priority?.let { priority = it }\n        }\n}\n\nclass NotificationChannelTemplate(\n    val id: String,\n    @StringRes val nameRes: Int,\n    val importance: Int,\n    @StringRes val descriptionRes: Int? = null,\n    val group: String? = null,\n    val showBadge: Boolean? = null,\n    val sound: Pair<Uri, AudioAttributes>? = null,\n    val lightEnabled: Boolean? = null,\n    val lightColor: Int? = null,\n    val vibrationEnabled: Boolean? = null,\n    val vibrationPattern: LongArray? = null,\n    val bypassDnd: Boolean? = null,\n    val lockscreenVisibility: Int? = null,\n    val allowBubbles: Boolean? = null\n) {\n    @RequiresApi(Build.VERSION_CODES.O)\n    fun create(context: Context): NotificationChannel =\n        NotificationChannel(id, context.getString(nameRes), importance).apply {\n            descriptionRes?.let { description = context.getString(it) }\n            this@NotificationChannelTemplate.group?.let { group = it }\n            showBadge?.let { setShowBadge(it) }\n            this@NotificationChannelTemplate.sound?.let { setSound(it.first, it.second) }\n            lightEnabled?.let { enableLights(it) }\n            this@NotificationChannelTemplate.lightColor?.let { lightColor = it }\n            vibrationEnabled?.let { enableVibration(vibrationEnabled) }\n            this@NotificationChannelTemplate.vibrationPattern?.let { vibrationPattern = it }\n            bypassDnd?.let { setBypassDnd(it) }\n            this@NotificationChannelTemplate.lockscreenVisibility?.let { lockscreenVisibility = it }\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n                allowBubbles?.let { setAllowBubbles(it) }\n            }\n        }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/PackageManagerExtensions.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.content.pm.PackageInfo\nimport android.content.pm.PackageManager\nimport android.content.pm.PermissionInfo\nimport kotlin.contracts.ExperimentalContracts\nimport kotlin.contracts.InvocationKind\nimport kotlin.contracts.contract\n\nfun PackageManager.getPackageInfoOrNull(packageName: String, flags: Int): PackageInfo? =\n    getPackageManagerInfoOrNull { getPackageInfo(packageName, flags) }\n\nfun PackageManager.getPermissionInfoOrNull(permissionName: String, flags: Int): PermissionInfo? =\n    getPackageManagerInfoOrNull { getPermissionInfo(permissionName, flags) }\n\n@OptIn(ExperimentalContracts::class)\nprivate inline fun <T> getPackageManagerInfoOrNull(block: () -> T): T? {\n    contract {\n        callsInPlace(block, InvocationKind.EXACTLY_ONCE)\n    }\n    return try {\n        block()\n    } catch (e: PackageManager.NameNotFoundException) {\n        e.printStackTrace()\n        null\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/PackageManagerPathExtensions.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.content.pm.PackageInfo\nimport android.content.pm.PackageManager\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.compat.getPackageArchiveInfoCompat\nimport me.zhanghai.android.files.provider.document.isDocumentPath\nimport me.zhanghai.android.files.provider.document.resolver.DocumentResolver\nimport me.zhanghai.android.files.provider.linux.isLinuxPath\nimport java.io.Closeable\n\nval Path.isGetPackageArchiveInfoCompatible: Boolean\n    get() = isLinuxPath || isDocumentPath\n\nfun PackageManager.getPackageArchiveInfoCompat(\n    path: Path,\n    flags: Int\n): Pair<PackageInfo?, Closeable?> {\n    val archiveFilePath: String\n    val closeable: Closeable?\n    when {\n        path.isLinuxPath -> {\n            archiveFilePath = path.toFile().path\n            closeable = null\n        }\n        path.isDocumentPath -> {\n            val pfd = DocumentResolver.openParcelFileDescriptor(path as DocumentResolver.Path, \"r\")\n            archiveFilePath = \"/proc/self/fd/${pfd.fd}\"\n            closeable = pfd\n        }\n        else -> throw IllegalArgumentException(path.toString())\n    }\n    var successful = false\n    val packageInfo: PackageInfo?\n    try {\n        packageInfo = getPackageArchiveInfoCompat(archiveFilePath, flags)?.apply {\n            applicationInfo?.apply {\n                sourceDir = archiveFilePath\n                publicSourceDir = archiveFilePath\n            }\n        }\n        successful = true\n    } finally {\n        if (!successful) {\n            closeable?.close()\n        }\n    }\n    return packageInfo to closeable\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/ParcelExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport kotlin.contracts.ExperimentalContracts\nimport kotlin.contracts.InvocationKind\nimport kotlin.contracts.contract\nimport me.zhanghai.android.files.app.appClassLoader\nimport me.zhanghai.android.files.compat.readParcelableListCompat\n\n@Suppress(\"DEPRECATION\")\nfun <T : Parcelable> Parcel.readParcelable(): T? = readParcelable(appClassLoader)\n\nfun <T : Parcelable?> Parcel.readParcelableListCompat(classLoader: ClassLoader?): List<T> =\n    readParcelableListCompat(mutableListOf(), classLoader)\n\nfun <E : Parcelable?, L : MutableList<E>> Parcel.readParcelableListCompat(list: L): L =\n    readParcelableListCompat(list, appClassLoader)\n\nfun <T : Parcelable?> Parcel.readParcelableListCompat(): List<T> =\n    readParcelableListCompat(mutableListOf())\n\n@Suppress(\"UNCHECKED_CAST\") fun <T> Parcel.readValue(): T? = readValue(appClassLoader) as T?\n\n@OptIn(ExperimentalContracts::class)\ninline fun <R> Parcel.use(block: (Parcel) -> R): R {\n    contract {\n        callsInPlace(block, InvocationKind.EXACTLY_ONCE)\n    }\n    return try {\n        block(this)\n    } finally {\n        recycle()\n    }\n}\n\n@OptIn(ExperimentalContracts::class)\ninline fun <R> Parcel.withPosition(position: Int, block: Parcel.() -> R): R {\n    contract {\n        callsInPlace(block, InvocationKind.EXACTLY_ONCE)\n    }\n    val savedPosition = dataPosition()\n    setDataPosition(position)\n    return try {\n        block(this)\n    } finally {\n        setDataPosition(savedPosition)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/ParcelSlicedList.kt",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.os.Binder\nimport android.os.Parcel\nimport android.os.Parcelable\n\n// @see android.content.pm.ParceledListSlice\nclass ParcelSlicedList<out T> : Parcelable {\n    val list: List<T>\n\n    constructor(list: List<T>) {\n        this.list = list\n    }\n\n    private constructor(source: Parcel) {\n        val size = source.readInt()\n        val list = ArrayList<T>(size)\n        readSliceFromParcel(list, source)\n        if (list.size < size) {\n            val binder = source.readStrongBinder()\n            do {\n                Parcel.obtain().use { reply ->\n                    Parcel.obtain().use { data ->\n                        binder.transact(Binder.FIRST_CALL_TRANSACTION, data, reply, 0)\n                    }\n                    readSliceFromParcel(list, reply)\n                }\n            } while (list.size < size)\n        }\n        this.list = list\n    }\n\n    private fun readSliceFromParcel(list: MutableList<T>, source: Parcel) {\n        val size = source.readInt()\n        repeat(size) {\n            @Suppress(\"UNCHECKED_CAST\") val element = source.readValue<T>() as T\n            list += element\n        }\n    }\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        val size = list.size\n        dest.writeInt(size)\n        val iterator = list.iterator()\n        writeSliceToParcel(iterator, dest)\n        if (iterator.hasNext()) {\n            dest.writeStrongBinder(object : Binder() {\n                override fun onTransact(\n                    code: Int,\n                    data: Parcel,\n                    reply: Parcel?,\n                    flags: Int\n                ): Boolean =\n                    when (code) {\n                        FIRST_CALL_TRANSACTION -> {\n                            if (reply != null) {\n                                writeSliceToParcel(iterator, reply)\n                            }\n                            true\n                        }\n                        else -> super.onTransact(code, data, reply, flags)\n                    }\n            })\n        }\n    }\n\n    private fun writeSliceToParcel(iterator: Iterator<T>, dest: Parcel) {\n        val startPosition = dest.dataPosition()\n        dest.writeInt(0)\n        var size = 0\n        while (iterator.hasNext() && dest.dataSize() < MAX_IPC_SIZE) {\n            val element = iterator.next()\n            dest.writeValue(element)\n            ++size\n        }\n        val endPosition = dest.dataPosition()\n        dest.setDataPosition(startPosition)\n        dest.writeInt(size)\n        dest.setDataPosition(endPosition)\n    }\n\n    companion object {\n        // @see IBinder.MAX_IPC_SIZE\n        private const val MAX_IPC_SIZE = 64 * 1024\n\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<ParcelSlicedList<Parcelable?>> {\n            override fun createFromParcel(source: Parcel): ParcelSlicedList<Parcelable?> =\n                ParcelSlicedList(source)\n\n            override fun newArray(size: Int): Array<ParcelSlicedList<Parcelable?>?> =\n                arrayOfNulls(size)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/ParcelableArgs.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.app.Activity\nimport android.content.Intent\nimport android.os.Bundle\nimport android.os.Parcelable\nimport androidx.annotation.MainThread\nimport androidx.fragment.app.Fragment\nimport kotlin.reflect.KClass\n\ninterface ParcelableArgs : Parcelable\n\nfun <Args : ParcelableArgs> Bundle.putArgs(args: Args, argsClass: KClass<Args>): Bundle {\n    putParcelable(argsClass.java.name, args)\n    return this\n}\n\ninline fun <reified Args : ParcelableArgs> Bundle.putArgs(args: Args) = putArgs(args, Args::class)\n\nfun <Args : ParcelableArgs> Args.toBundle(argsClass: KClass<Args>): Bundle =\n    Bundle().apply { putArgs(this@toBundle, argsClass) }\n\ninline fun <reified Args : ParcelableArgs> Args.toBundle(): Bundle = toBundle(Args::class)\n\nfun <F : Fragment, Args : ParcelableArgs> F.putArgs(args: Args, argsClass: KClass<Args>): F {\n    val arguments = arguments\n    if (arguments != null) {\n        arguments.putArgs(args, argsClass)\n    } else {\n        this.arguments = args.toBundle(argsClass)\n    }\n    return this\n}\n\ninline fun <F : Fragment, reified Args : ParcelableArgs> F.putArgs(args: Args): F =\n    putArgs(args, Args::class)\n\nfun <Args : ParcelableArgs> Intent.putArgs(args: Args, argsClass: KClass<Args>): Intent =\n    putExtra(argsClass.java.name, args)\n\ninline fun <reified Args : ParcelableArgs> Intent.putArgs(args: Args) = putArgs(args, Args::class)\n\nfun <Args : ParcelableArgs> Bundle.getArgs(argsClass: KClass<Args>): Args =\n    getArgsOrNull(argsClass)!!\n\ninline fun <reified Args : ParcelableArgs> Bundle.getArgs() = getArgs(Args::class)\n\nfun <Args : ParcelableArgs> Bundle.getArgsOrNull(argsClass: KClass<Args>): Args? =\n    getParcelableSafe(argsClass.java.name)\n\ninline fun <reified Args : ParcelableArgs> Bundle.getArgsOrNull() = getArgsOrNull(Args::class)\n\n@MainThread\ninline fun <reified Args : ParcelableArgs> Activity.args() = BundleArgsLazy(Args::class) {\n    intent.extras ?: throw IllegalStateException(\"Activity $this has null intent extras\")\n}\n\n@MainThread\ninline fun <reified Args : ParcelableArgs> Fragment.args() = BundleArgsLazy(Args::class) {\n    arguments ?: throw IllegalStateException(\"Fragment $this has null arguments\")\n}\n\nclass BundleArgsLazy<Args : ParcelableArgs>(\n    private val argsClass: KClass<Args>,\n    private val argumentsProducer: () -> Bundle\n) : Lazy<Args> {\n    private var cached: Args? = null\n\n    override val value: Args\n        get() {\n            var args = cached\n            if (args == null) {\n                args = argumentsProducer().getArgs(argsClass)\n                cached = args\n            }\n            return args\n        }\n\n    override fun isInitialized() = cached != null\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/ParcelableState.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.os.Bundle\nimport android.os.Parcelable\nimport kotlin.reflect.KClass\n\ninterface ParcelableState : Parcelable\n\nfun <State : ParcelableState> Bundle.putState(state: State) =\n    putParcelable(state.javaClass.name, state)\n\nfun <State : ParcelableState> Bundle.getState(stateClass: KClass<State>): State =\n    getParcelableSafe(stateClass.java.name)!!\n\ninline fun <reified State : ParcelableState> Bundle.getState() = getState(State::class)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/Parcelers.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.os.Bundle\nimport android.os.Parcel\nimport android.os.Parcelable\nimport kotlinx.parcelize.Parceler\nimport me.zhanghai.android.files.app.appClassLoader\nimport me.zhanghai.android.files.compat.writeParcelableListCompat\n\nobject BundleParceler : Parceler<Bundle?> {\n    override fun create(parcel: Parcel): Bundle? = parcel.readBundle(appClassLoader)\n\n    override fun Bundle?.write(parcel: Parcel, flags: Int) {\n        parcel.writeBundle(this)\n    }\n}\n\nobject ParcelableParceler : Parceler<Any?> {\n    override fun create(parcel: Parcel): Any? = parcel.readParcelable(appClassLoader)\n\n    override fun Any?.write(parcel: Parcel, flags: Int) {\n        parcel.writeParcelable(this as Parcelable?, flags)\n    }\n}\n\nobject ParcelableListParceler : Parceler<List<Any?>> {\n    override fun create(parcel: Parcel): List<Any?> =\n        parcel.readParcelableListCompat(appClassLoader)\n\n    override fun List<Any?>.write(parcel: Parcel, flags: Int) {\n        @Suppress(\"UNCHECKED_CAST\")\n        parcel.writeParcelableListCompat(this as List<Parcelable?>, flags)\n    }\n}\n\nobject ValueParceler : Parceler<Any?> {\n    override fun create(parcel: Parcel): Any? = parcel.readValue(appClassLoader)\n\n    override fun Any?.write(parcel: Parcel, flags: Int) {\n        parcel.writeValue(this)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/PathName.kt",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\n@JvmInline\nvalue class PathName(val value: String) {\n    val fileName: String?\n        get() = value.substringAfterLast(SEPARATOR).takeIfNotEmpty()\n\n    val directoryName: String?\n        get() = value.substringBeforeLastOrNull(SEPARATOR)?.dropLastWhile { it == SEPARATOR }\n\n    private fun String.substringBeforeLastOrNull(delimiter: Char): String? {\n        val index = lastIndexOf(delimiter)\n        return if (index != -1) substring(0, index) else null\n    }\n\n    companion object {\n        // Not using File.separatorChar so that behavior is consistent and always ready for URIs.\n        // Anyway we are on Android. If one day we were moved to Windows, fail-fast is also good.\n        const val SEPARATOR = '/'\n    }\n}\n\nfun String.asPathName(): PathName {\n    require(isValidPathName)\n    return PathName(this)\n}\n\nfun String.asPathNameOrNull(): PathName? = if (isValidPathName) PathName(this) else null\n\nprivate val String.isValidPathName: Boolean\n    get() = isNotEmpty() && !contains('\\u0000')\n\n@JvmInline\nvalue class FileName(val value: String) {\n    val singleExtension: String\n        get() = value.substringAfterLast(EXTENSION_SEPARATOR, \"\")\n\n    val extensions: String\n        get() {\n            val lastExtension = singleExtension\n            if (DOUBLE_EXTENSIONS.any { lastExtension.equals(it, true) }) {\n                val secondLastExtension = value.dropLast(lastExtension.length + 1)\n                    .substringAfterLast(EXTENSION_SEPARATOR, \"\")\n                if (secondLastExtension.isNotEmpty()) {\n                    return \"$secondLastExtension$EXTENSION_SEPARATOR$lastExtension\"\n                }\n            }\n            return lastExtension\n        }\n\n    val baseName: String\n        get() {\n            val extensions = extensions\n            return if (extensions.isNotEmpty()) value.dropLast(extensions.length + 1) else value\n        }\n\n    companion object {\n        const val EXTENSION_SEPARATOR = '.'\n\n        // https://github.com/GNOME/nautilus/blob/c73ad94a72f8e9a989b01858018de74182d17f0e/eel/eel-vfs-extensions.c#L124\n        private val DOUBLE_EXTENSIONS = listOf(\"bz\", \"bz2\", \"gz\", \"sit\", \"xz\", \"Z\")\n    }\n}\n\nfun String.asFileName(): FileName {\n    require(isValidFileName)\n    return FileName(this)\n}\n\nfun String.asFileNameOrNull(): FileName? = if (isValidFileName) FileName(this) else null\n\nprivate val String.isValidFileName: Boolean\n    get() = isNotEmpty() && !contains('\\u0000') && !contains(PathName.SEPARATOR)\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/RecyclerViewExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport androidx.recyclerview.widget.RecyclerView\n\ninline fun RecyclerView.addOnScrollListener(\n    crossinline onScrollStateChanged: (recyclerView: RecyclerView, newState: Int) -> Unit =\n        { _, _ -> },\n    crossinline onScrolled: (recyclerView: RecyclerView, dx: Int, dy: Int) -> Unit = { _, _, _ -> },\n): RecyclerView.OnScrollListener {\n    val listener = object : RecyclerView.OnScrollListener() {\n        override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {\n            onScrollStateChanged(recyclerView, newState)\n        }\n\n        override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {\n            onScrolled(recyclerView, dx, dy)\n        }\n    }\n    addOnScrollListener(listener)\n    return listener\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/RemoteCallback.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.os.Bundle\nimport android.os.Parcel\nimport android.os.Parcelable\nimport android.os.RemoteException\n\nclass RemoteCallback : Parcelable {\n    private val localCallback: ((Bundle) -> Unit)?\n    private val remoteCallback: IRemoteCallback?\n\n    constructor(callback: (Bundle) -> Unit) {\n        localCallback = callback\n        remoteCallback = null\n    }\n\n    fun sendResult(result: Bundle) {\n        if (remoteCallback != null) {\n            try {\n                remoteCallback.sendResult(result)\n            } catch (e: RemoteException) {\n                e.printStackTrace()\n            }\n        } else {\n            localCallback!!(result)\n        }\n    }\n\n    private inner class Stub : IRemoteCallback.Stub() {\n        override fun sendResult(result: Bundle) {\n            this@RemoteCallback.sendResult(result)\n        }\n    }\n\n    private constructor(source: Parcel) {\n        localCallback = null\n        remoteCallback = IRemoteCallback.Stub.asInterface(source.readStrongBinder())\n    }\n\n    override fun describeContents(): Int = 0\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        dest.writeStrongBinder(Stub().asBinder())\n    }\n\n    companion object {\n        @JvmField\n        val CREATOR = object : Parcelable.Creator<RemoteCallback> {\n            override fun createFromParcel(source: Parcel): RemoteCallback = RemoteCallback(source)\n\n            override fun newArray(size: Int): Array<RemoteCallback?> = arrayOfNulls(size)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/RuntimeBroadcastReceiver.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.content.BroadcastReceiver\nimport android.content.Context\nimport android.content.IntentFilter\nimport androidx.core.content.ContextCompat\nimport me.zhanghai.android.files.compat.registerReceiverCompat\n\nclass RuntimeBroadcastReceiver(\n    private val filter: IntentFilter,\n    private val receiver: BroadcastReceiver,\n    private val context: Context,\n    private val flags: Int = ContextCompat.RECEIVER_NOT_EXPORTED\n) {\n    fun register() {\n        context.registerReceiverCompat(receiver, filter, flags)\n    }\n\n    fun unregister() {\n        context.unregisterReceiver(receiver)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/SelectionLiveData.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport androidx.lifecycle.LifecycleOwner\nimport androidx.lifecycle.MutableLiveData\nimport androidx.recyclerview.widget.RecyclerView\n\nclass SelectionLiveData<Key> : MutableLiveData<Key>() {\n    fun observe(owner: LifecycleOwner, adapter: RecyclerView.Adapter<*>) {\n        observe(owner) {\n            adapter.notifyItemRangeChanged(0, adapter.itemCount, PAYLOAD_SELECTION_CHANGED)\n        }\n    }\n\n    companion object {\n        val PAYLOAD_SELECTION_CHANGED = Any()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/SimpleActivityLifecycleCallbacks.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.app.Activity\nimport android.app.Application\nimport android.os.Bundle\n\ninterface SimpleActivityLifecycleCallbacks : Application.ActivityLifecycleCallbacks {\n    override fun onActivityPaused(activity: Activity) {}\n\n    override fun onActivityStarted(activity: Activity) {}\n\n    override fun onActivityDestroyed(activity: Activity) {}\n\n    override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}\n\n    override fun onActivityStopped(activity: Activity) {}\n\n    override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}\n\n    override fun onActivityResumed(activity: Activity) {}\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/StableUriParceler.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.net.Uri\nimport android.os.Build\nimport android.os.Parcel\nimport androidx.annotation.RequiresApi\nimport kotlinx.parcelize.Parceler\nimport me.zhanghai.android.files.hiddenapi.RestrictedHiddenApi\n\n// The built-in parceling of Uri isn't guaranteed to be stable and has changed 2 times, so we should\n// always use our own parceling for persistence.\n// The reading code here is backwards compatible in that it also handles built-in parceling of Uri,\n// including the changes in\n// https://android.googlesource.com/platform/frameworks/base/+/97f621d81fc51de240ba73bc008d997e0eea7939 ,\n// and the String8 change in API 30.\nobject StableUriParceler : Parceler<Uri?> {\n    private const val NULL_TYPE_ID = 0\n    private const val STRING_URI_TYPE_ID = 1\n    private const val OPAQUE_URI_TYPE_ID = 2\n    private const val HIERARCHICAL_URI_TYPE_ID = 3\n\n    private const val REPRESENTATION_BOTH = 0\n    private const val REPRESENTATION_ENCODED = 1\n    private const val REPRESENTATION_DECODED = 2\n\n    @get:RequiresApi(Build.VERSION_CODES.R)\n    @RestrictedHiddenApi\n    private val parcelReadString8Method by lazyReflectedMethod(Parcel::class.java, \"readString8\")\n\n    override fun create(parcel: Parcel): Uri? {\n        val uriString = parcel.readString() ?: return null\n        // Parcel.readParcelableCreator()\n        return if (uriString.startsWith(Uri::class.java.name)) {\n            readUri(parcel)\n        } else {\n            Uri.parse(uriString)\n        }\n    }\n\n    // Uri.CREATOR.createFromParcel()\n    private fun readUri(parcel: Parcel): Uri? {\n        val uriString = when (val typeId = parcel.readInt()) {\n            NULL_TYPE_ID -> return null\n            // Uri.StringUri.readFrom()\n            STRING_URI_TYPE_ID -> parcel.readUriString()\n            OPAQUE_URI_TYPE_ID -> {\n                // Uri.OpaqueUri.readFrom()\n                val scheme = parcel.readUriString()!!\n                // Assume that we never persist a Uri with only a scheme.\n                if (scheme.contains(':')) {\n                    scheme\n                } else {\n                    val encodedSsp = readEncodedPart(parcel)\n                    val encodedFragment = readEncodedPart(parcel)\n                    // Uri.OpaqueUri.toString()\n                    buildString {\n                        append(scheme)\n                        append(':')\n                        append(encodedSsp)\n                        if (!encodedFragment.isNullOrEmpty()) {\n                            append('#')\n                            append(encodedFragment)\n                        }\n                    }\n                }\n            }\n            HIERARCHICAL_URI_TYPE_ID -> {\n                // Uri.HierarchicalUri.readFrom()\n                // Scheme can be null for HierarchicalUri.\n                val scheme = parcel.readUriString()\n                // Assume that we never persist a Uri with only a scheme.\n                if (scheme != null && scheme.contains(':')) {\n                    scheme\n                } else {\n                    val encodedAuthority = readEncodedPart(parcel)\n                    val hasSchemeOrAuthority = !scheme.isNullOrEmpty() ||\n                        !encodedAuthority.isNullOrEmpty()\n                    val encodedPath = readEncodedPathPart(hasSchemeOrAuthority, parcel)\n                    val encodedQuery = readEncodedPart(parcel)\n                    val encodedFragment = readEncodedPart(parcel)\n                    // Uri.HierarchicalUri.toString()\n                    buildString {\n                        if (scheme != null) {\n                            append(scheme)\n                            append(':')\n                        }\n                        if (encodedAuthority != null) {\n                            append(\"//\")\n                            append(encodedAuthority)\n                        }\n                        if (encodedPath != null) {\n                            append(encodedPath)\n                        }\n                        if (!encodedQuery.isNullOrEmpty()) {\n                            append('?')\n                            append(encodedQuery)\n                        }\n                        if (!encodedFragment.isNullOrEmpty()) {\n                            append('#')\n                            append(encodedFragment)\n                        }\n                    }\n                }\n            }\n            else -> error(\"Unknown type ID $typeId\")\n        }\n        return Uri.parse(uriString)\n    }\n\n    // Uri.Part.readFrom()\n    private fun readEncodedPart(parcel: Parcel): String? =\n        when (val representation = parcel.readInt()) {\n            REPRESENTATION_BOTH -> parcel.readUriString().also { parcel.readUriString() }\n            REPRESENTATION_ENCODED -> parcel.readUriString()\n            REPRESENTATION_DECODED -> Uri.encode(parcel.readUriString())\n            else -> error(\"Unknown representation $representation\")\n        }\n\n    // Uri.PathPart.readFrom()\n    private fun readEncodedPathPart(hasSchemeOrAuthority: Boolean, parcel: Parcel): String? {\n        val encodedPathPart = when (val representation = parcel.readInt()) {\n            REPRESENTATION_BOTH -> parcel.readUriString().also { parcel.readUriString() }\n            REPRESENTATION_ENCODED -> parcel.readUriString()\n            REPRESENTATION_DECODED -> Uri.encode(parcel.readUriString(), \"/\")\n            else -> error(\"Unknown representation $representation\")\n        }\n        return if (hasSchemeOrAuthority) {\n            makeEncodedPathPartAbsolute(encodedPathPart)\n        } else {\n            encodedPathPart\n        }\n    }\n\n    // Uri.PathPart.makeAbsolute()\n    private fun makeEncodedPathPartAbsolute(encodedPathPart: String?): String? =\n        if (encodedPathPart.isNullOrEmpty() || encodedPathPart.startsWith(\"/\")) {\n            encodedPathPart\n        } else {\n            \"/${encodedPathPart}\"\n        }\n\n    private fun Parcel.readUriString(): String? =\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {\n            parcelReadString8Method.invoke(this) as String?\n        } else {\n            readString()\n        }\n\n    override fun Uri?.write(parcel: Parcel, flags: Int) {\n        parcel.writeString(this?.toString())\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/Stateful.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nsealed class Stateful<T> {\n    abstract val value: T?\n}\n\ndata class Loading<T>(override val value: T?) : Stateful<T>()\n\ndata class Failure<T>(override val value: T?, val throwable: Throwable) : Stateful<T>()\n\ndata class Success<T>(override val value: T) : Stateful<T>()\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/StatefulLiveData.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport androidx.lifecycle.LiveData\n\nabstract class StatefulLiveData<T : Any> : LiveData<Stateful<T>>() {\n    init {\n        value = Loading(null)\n    }\n\n    val isReady: Boolean\n        get() = valueCompat.let { it is Loading && it.value == null }\n\n    fun reset() {\n        check(!(valueCompat.let { it is Loading && it.value != null }))\n        value = Loading(null)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/StatusBarManagerExtensions.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.app.StatusBarManager\nimport android.content.ComponentName\nimport android.content.pm.PackageManager\nimport android.graphics.drawable.Icon\nimport android.os.Build\nimport android.service.quicksettings.TileService\nimport androidx.annotation.RequiresApi\nimport me.zhanghai.android.files.app.application\nimport me.zhanghai.android.files.app.packageManager\nimport java.util.concurrent.Executor\n\n@RequiresApi(Build.VERSION_CODES.TIRAMISU)\nfun StatusBarManager.requestAddTileService(\n    serviceClass: Class<out TileService>,\n    resultExecutor: Executor,\n    resultCallback: (Int) -> Unit\n) {\n    val application = application\n    val componentName = ComponentName(application, serviceClass)\n    val packageManager = packageManager\n    val serviceInfo = packageManager.getServiceInfo(\n        componentName,\n        PackageManager.MATCH_DIRECT_BOOT_AWARE or PackageManager.MATCH_DIRECT_BOOT_UNAWARE\n    )\n    val label = serviceInfo.loadLabel(packageManager)\n    val icon = Icon.createWithResource(application, serviceInfo.iconResource)\n    requestAddTileService(componentName, label, icon, resultExecutor, resultCallback)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/StorageVolumeExtensions.kt",
    "content": "package me.zhanghai.android.files.util\n\nimport android.os.storage.StorageVolume\nimport me.zhanghai.android.files.compat.directoryCompat\n\nval StorageVolume.isMounted: Boolean\n    get() = directoryCompat != null\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/StrictModeExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.os.StrictMode\nimport kotlin.reflect.KClass\n\nfun <R> KClass<StrictMode>.withoutPenaltyDeathOnNetwork(block: () -> R): R {\n    val oldThreadPolicy = StrictMode.getThreadPolicy()\n    val newThreadPolicy = StrictMode.ThreadPolicy.Builder(oldThreadPolicy)\n        // There's no API to disable penaltyDeathOnNetwork() but still detect it.\n        .permitNetwork()\n        .build()\n    StrictMode.setThreadPolicy(newThreadPolicy)\n    return try {\n        block()\n    } finally {\n        StrictMode.setThreadPolicy(oldThreadPolicy)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/TextViewExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.graphics.Typeface\nimport android.view.KeyEvent\nimport android.view.inputmethod.EditorInfo\nimport android.widget.TextView\nimport androidx.core.widget.doAfterTextChanged\nimport com.google.android.material.textfield.TextInputLayout\n\nfun TextView.hideTextInputLayoutErrorOnTextChange(vararg textInputLayouts: TextInputLayout) {\n    doAfterTextChanged { textInputLayouts.forEach { it.error = null } }\n}\n\nvar TextView.isBold: Boolean\n    get() = typeface.isBold\n    set(value) {\n        val typeface = typeface\n        if (typeface.isBold == value) {\n            return\n        }\n        val style = if (value) {\n            typeface.style or Typeface.BOLD\n        } else {\n            typeface.style andInv Typeface.BOLD\n        }\n        // Workaround insane behavior in TextView#setTypeface(Typeface, int).\n        if (style > 0) {\n            setTypeface(typeface, style)\n        } else {\n            setTypeface(Typeface.create(typeface, style), style)\n        }\n    }\n\nvar TextView.isItalic: Boolean\n    get() = typeface.isItalic\n    set(value) {\n        val typeface = typeface\n        if (typeface.isItalic == value) {\n            return\n        }\n        val style = if (value) {\n            typeface.style or Typeface.ITALIC\n        } else {\n            typeface.style andInv Typeface.ITALIC\n        }\n        // Workaround insane behavior in TextView#setTypeface(Typeface, int).\n        if (style > 0) {\n            setTypeface(typeface, style)\n        } else {\n            setTypeface(Typeface.create(typeface, style), style)\n        }\n    }\n\n/** @see com.android.keyguard.KeyguardPasswordView#onEditorAction */\nfun TextView.setOnEditorConfirmActionListener(listener: (TextView) -> Unit) {\n    setOnEditorActionListener { view, actionId, event ->\n        val isConfirmAction = if (event != null) {\n            when (event.keyCode) {\n                KeyEvent.KEYCODE_DPAD_CENTER, KeyEvent.KEYCODE_ENTER,\n                KeyEvent.KEYCODE_NUMPAD_ENTER -> true\n                else -> false\n            } && event.action == KeyEvent.ACTION_DOWN\n        } else {\n            when (actionId) {\n                EditorInfo.IME_NULL, EditorInfo.IME_ACTION_DONE, EditorInfo.IME_ACTION_NEXT -> true\n                else -> false\n            }\n        }\n        if (isConfirmAction) {\n            listener(view)\n            true\n        } else {\n            false\n        }\n    }\n}\n\nfun TextView.setSpanClickable() {\n    val wasClickable = isClickable\n    val wasLongClickable = isLongClickable\n    movementMethod = ClickableMovementMethod\n    // Reset for TextView.fixFocusableAndClickableSettings(). We don't want View.onTouchEvent()\n    // to consume touch events.\n    isClickable = wasClickable\n    isLongClickable = wasLongClickable\n}\n\nfun TextView.setSpanClickableAndTextSelectable() {\n    setTextIsSelectable(true)\n    movementMethod = ClickableArrowKeyMovementMethod\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/ThrottledRunnable.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.os.Handler\nimport android.os.SystemClock\n\nclass ThrottledRunnable(\n    private val handler: Handler,\n    private val intervalMillis: Long,\n    block: () -> Unit\n) : () -> Unit {\n    private val lock = Any()\n\n    private val runnable = Runnable(block)\n\n    private var scheduledUptimeMillis = 0L\n\n    override operator fun invoke() {\n        synchronized(lock) {\n            val currentUptimeMillis = SystemClock.uptimeMillis()\n            if (scheduledUptimeMillis + intervalMillis < currentUptimeMillis) {\n                scheduledUptimeMillis = 0\n            }\n            when {\n                scheduledUptimeMillis == 0L -> {\n                    scheduledUptimeMillis = currentUptimeMillis\n                    handler.post(runnable)\n                }\n                scheduledUptimeMillis <= currentUptimeMillis -> {\n                    scheduledUptimeMillis += intervalMillis\n                    handler.postAtTime(runnable, scheduledUptimeMillis)\n                }\n                // We've been scheduled, nothing to do now.\n                else -> {}\n            }\n        }\n    }\n\n    fun cancel() {\n        synchronized(lock) {\n            scheduledUptimeMillis = 0\n            handler.removeCallbacks(runnable)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/ThrowableExtensions.kt",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\ninline fun <reified T : Throwable> Throwable.findCauseByClass(): T? {\n    var current: Throwable? = this\n    do {\n        if (current is T) {\n            return current\n        }\n        current = current!!.cause\n    } while (current != null)\n    return null\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/TypedValueExtensions.kt",
    "content": "/*\n * Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.util.TypedValue\nimport java.util.concurrent.atomic.AtomicReference\nimport kotlin.reflect.KClass\n\ninline fun <T> KClass<TypedValue>.useTemp(block: (TypedValue) -> T): T {\n    val temp = TypedValue::class.obtainTemp()\n    return try {\n        block(temp)\n    } finally {\n        temp.releaseTemp()\n    }\n}\n\nprivate val tempTypedValue = AtomicReference(TypedValue())\n\nfun KClass<TypedValue>.obtainTemp(): TypedValue = tempTypedValue.getAndSet(null) ?: TypedValue()\n\nfun TypedValue.releaseTemp() {\n    tempTypedValue.compareAndSet(null, this)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/UriExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.net.Uri\nimport me.zhanghai.android.files.app.contentResolver\n\nfun Uri.takePersistablePermission(modeFlags: Int): Boolean =\n    try {\n        contentResolver.takePersistableUriPermission(this, modeFlags)\n        true\n    } catch (e: SecurityException) {\n        e.printStackTrace()\n        false\n    }\n\nfun Uri.releasePersistablePermission(modeFlags: Int): Boolean =\n    try {\n        contentResolver.releasePersistableUriPermission(this, modeFlags)\n        true\n    } catch (e: SecurityException) {\n        e.printStackTrace()\n        false\n    }\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/ViewExtensions.kt",
    "content": "/*\n * Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.annotation.SuppressLint\nimport android.view.Gravity\nimport android.view.View\nimport android.view.ViewGroup\nimport android.view.ViewTreeObserver\nimport android.view.inputmethod.InputMethodManager\nimport androidx.core.view.children\nimport androidx.core.view.isGone\nimport androidx.core.view.isInvisible\nimport androidx.core.view.isVisible\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.GlobalScope\nimport kotlinx.coroutines.launch\nimport me.zhanghai.android.files.app.inputMethodManager\n\nfun View.doOnGlobalLayout(block: () -> Unit): OneShotGlobalLayoutListener =\n    OneShotGlobalLayoutListener.add(this, block)\n\n/** @see androidx.core.view.OneShotPreDrawListener */\nclass OneShotGlobalLayoutListener private constructor(\n    private val view: View,\n    private val block: () -> Unit\n) : ViewTreeObserver.OnPreDrawListener, View.OnAttachStateChangeListener {\n    private var viewTreeObserver = view.viewTreeObserver\n\n    override fun onPreDraw(): Boolean {\n        removeListener()\n        block()\n        return true\n    }\n\n    override fun onViewAttachedToWindow(view: View) {\n        viewTreeObserver = view.viewTreeObserver\n    }\n\n    override fun onViewDetachedFromWindow(view: View) {\n        removeListener()\n    }\n\n    fun removeListener() {\n        if (viewTreeObserver.isAlive) {\n            viewTreeObserver.removeOnPreDrawListener(this)\n        } else {\n            view.viewTreeObserver.removeOnPreDrawListener(this)\n        }\n        view.removeOnAttachStateChangeListener(this)\n    }\n\n    companion object {\n        fun add(view: View, block: () -> Unit): OneShotGlobalLayoutListener =\n            OneShotGlobalLayoutListener(view, block).also {\n                view.viewTreeObserver.addOnPreDrawListener(it)\n                view.addOnAttachStateChangeListener(it)\n            }\n    }\n}\n\ninline fun <reified T : View> View.findViewByClass(): T? = findViewByClass(T::class.java)\n\nfun <T : View> View.findViewByClass(clazz: Class<T>): T? {\n    if (clazz.isInstance(this)) {\n        @Suppress(\"UNCHECKED_CAST\")\n        return this as T\n    }\n    if (this is ViewGroup) {\n        children.forEach {\n            it.findViewByClass(clazz)?.let { return it }\n        }\n    }\n    return null\n}\n\nval View.isLayoutDirectionRtl: Boolean\n    get() = layoutDirection == View.LAYOUT_DIRECTION_RTL\n\nvar View.layoutInStatusBar: Boolean\n    get() = systemUiVisibility.hasBits(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)\n    set(value) {\n        systemUiVisibility = if (value) {\n            systemUiVisibility or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or\n                View.SYSTEM_UI_FLAG_LAYOUT_STABLE\n        } else {\n            systemUiVisibility andInv View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN\n        }\n    }\n\nvar View.layoutInNavigation: Boolean\n    get() = systemUiVisibility.hasBits(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)\n    set(value) {\n        systemUiVisibility = if (value) {\n            systemUiVisibility or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or\n                View.SYSTEM_UI_FLAG_LAYOUT_STABLE\n        } else {\n            systemUiVisibility andInv View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION\n        }\n    }\n\nsuspend fun View.fadeIn(force: Boolean = false) {\n    if (!isVisible) {\n        alpha = 0f\n        isVisible = true\n    }\n    animate().run {\n        alpha(1f)\n        if (!(isLaidOut || force) || (isVisible && alpha == 1f)) {\n            duration = 0\n        } else {\n            duration = context.shortAnimTime.toLong()\n            interpolator = context.getInterpolator(android.R.interpolator.fast_out_slow_in)\n        }\n        start()\n        awaitEnd()\n    }\n}\n\nfun View.fadeInUnsafe(force: Boolean = false) {\n    GlobalScope.launch(Dispatchers.Main.immediate) { fadeIn(force) }\n}\n\nsuspend fun View.fadeOut(force: Boolean = false, gone: Boolean = false) {\n    animate().run {\n        alpha(0f)\n        if (!(isLaidOut || force) || (!isVisible || alpha == 0f)) {\n            duration = 0\n        } else {\n            duration = context.shortAnimTime.toLong()\n            interpolator = context.getInterpolator(android.R.interpolator.fast_out_linear_in)\n        }\n        start()\n        awaitEnd()\n    }\n    if (gone) {\n        isGone = true\n    } else {\n        isInvisible = true\n    }\n}\n\nfun View.fadeOutUnsafe(force: Boolean = false, gone: Boolean = false) {\n    GlobalScope.launch(Dispatchers.Main.immediate) { fadeOut(force, gone) }\n}\n\nsuspend fun View.fadeToVisibility(visible: Boolean, force: Boolean = false, gone: Boolean = false) {\n    if (visible) {\n        fadeIn(force)\n    } else {\n        fadeOut(force, gone)\n    }\n}\n\nfun View.fadeToVisibilityUnsafe(visible: Boolean, force: Boolean = false, gone: Boolean = false) {\n    GlobalScope.launch(Dispatchers.Main.immediate) { fadeToVisibility(visible, force, gone) }\n}\n\n@SuppressLint(\"RtlHardcoded\")\nsuspend fun View.slideIn(gravity: Int, force: Boolean = false) {\n    isVisible = true\n    animate().run {\n        when (Gravity.getAbsoluteGravity(gravity, layoutDirection)) {\n            Gravity.LEFT, Gravity.RIGHT -> translationX(0f)\n            Gravity.TOP, Gravity.BOTTOM -> translationY(0f)\n        }\n        if (!(isLaidOut || force)) {\n            duration = 0\n        } else {\n            duration = context.shortAnimTime.toLong()\n            interpolator = context.getInterpolator(android.R.interpolator.fast_out_slow_in)\n        }\n        start()\n        awaitEnd()\n    }\n}\n\nsuspend fun View.slideInUnsafe(gravity: Int, force: Boolean = false) {\n    GlobalScope.launch(Dispatchers.Main.immediate) { slideIn(gravity, force) }\n}\n\n@SuppressLint(\"RtlHardcoded\")\nsuspend fun View.slideOut(gravity: Int, force: Boolean = false, gone: Boolean = false) {\n    animate().run {\n        when (Gravity.getAbsoluteGravity(gravity, layoutDirection)) {\n            Gravity.LEFT -> translationX((-right).toFloat())\n            Gravity.RIGHT -> translationX(((parent as View).width - left).toFloat())\n            Gravity.TOP -> translationY((-bottom).toFloat())\n            Gravity.BOTTOM -> translationY(((parent as View).height - top).toFloat())\n        }\n        if (!(isLaidOut || force)) {\n            duration = 0\n        } else {\n            duration = context.shortAnimTime.toLong()\n            interpolator = context.getInterpolator(android.R.interpolator.fast_out_linear_in)\n        }\n        start()\n        awaitEnd()\n    }\n    if (gone) {\n        isGone = true\n    } else {\n        isInvisible = true\n    }\n}\n\nfun View.slideOutUnsafe(gravity: Int, force: Boolean = false, gone: Boolean = false) {\n    GlobalScope.launch(Dispatchers.Main.immediate) { slideOut(gravity, force, gone) }\n}\n\nsuspend fun View.slideToVisibility(\n    gravity: Int,\n    visible: Boolean,\n    force: Boolean = false,\n    gone: Boolean = false\n) {\n    if (visible) {\n        slideIn(gravity, force)\n    } else {\n        slideOut(gravity, force, gone)\n    }\n}\n\nfun View.slideToVisibilityUnsafe(\n    gravity: Int,\n    visible: Boolean,\n    force: Boolean = false,\n    gone: Boolean = false\n) {\n    GlobalScope.launch(Dispatchers.Main.immediate) {\n        slideToVisibility(gravity, visible, force, gone)\n    }\n}\n\nfun View.showSoftInput() {\n    inputMethodManager.showSoftInput(this, 0)\n}\n\nfun View.hideSoftInput() {\n    inputMethodManager.hideSoftInputFromWindow(windowToken, InputMethodManager.HIDE_NOT_ALWAYS)\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/util/WakeWifiLock.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.util\n\nimport android.net.wifi.WifiManager\nimport android.os.PowerManager\nimport me.zhanghai.android.files.app.powerManager\nimport me.zhanghai.android.files.app.wifiManager\n\nclass WakeWifiLock(tag: String) {\n    private val wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, tag)\n        .apply { setReferenceCounted(false) }\n    private val wifiLock =\n        wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, tag)\n            .apply { setReferenceCounted(false) }\n\n    var isAcquired: Boolean = false\n        set(value) {\n            if (field == value) {\n                return\n            }\n            if (value) {\n                wakeLock.acquire()\n                wifiLock.acquire()\n            } else {\n                wifiLock.release()\n                wakeLock.release()\n            }\n            field = value\n        }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/viewer/image/ConfirmDeleteDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.viewer.image\n\nimport android.app.Dialog\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatDialogFragment\nimport androidx.fragment.app.Fragment\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport java8.nio.file.Path\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.ParcelableParceler\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.putArgs\nimport me.zhanghai.android.files.util.show\n\nclass ConfirmDeleteDialogFragment : AppCompatDialogFragment() {\n    private val args by args<Args>()\n\n    private val listener: Listener\n        get() = requireParentFragment() as Listener\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {\n        return MaterialAlertDialogBuilder(requireContext(), theme)\n            .setMessage(getString(R.string.image_viewer_delete_message_format, args.path.fileName))\n            .setPositiveButton(android.R.string.ok) { _, _ -> listener.delete(args.path) }\n            .setNegativeButton(android.R.string.cancel, null)\n            .create()\n    }\n\n    companion object {\n        fun show(path: Path, fragment: Fragment) {\n            ConfirmDeleteDialogFragment().putArgs(Args(path)).show(fragment)\n        }\n    }\n\n    @Parcelize\n    class Args(val path: @WriteWith<ParcelableParceler> Path) : ParcelableArgs\n\n    interface Listener {\n        fun delete(path: Path)\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/viewer/image/ImageViewerActivity.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.viewer.image\n\nimport android.content.Intent\nimport android.os.Bundle\nimport android.view.View\nimport androidx.fragment.app.commit\nimport java8.nio.file.Path\nimport me.zhanghai.android.files.app.AppActivity\nimport me.zhanghai.android.files.util.extraPathList\nimport me.zhanghai.android.files.util.putArgs\n\nclass ImageViewerActivity : AppActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        // Calls ensureSubDecor().\n        findViewById<View>(android.R.id.content)\n        if (savedInstanceState == null) {\n            val intent = intent\n            val position = intent.getIntExtra(EXTRA_POSITION, 0)\n            val fragment = ImageViewerFragment()\n                .putArgs(ImageViewerFragment.Args(intent, position))\n            supportFragmentManager.commit { add(android.R.id.content, fragment) }\n        }\n    }\n\n    companion object {\n        private val EXTRA_POSITION = \"${ImageViewerActivity::class.java.name}.extra.POSITION\"\n\n        fun putExtras(intent: Intent, paths: List<Path>, position: Int) {\n            // All extra put here must be framework classes, or we may crash the resolver activity.\n            intent.extraPathList = paths\n            intent.putExtra(EXTRA_POSITION, position)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/viewer/image/ImageViewerAdapter.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.viewer.image\n\nimport android.graphics.BitmapFactory\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.core.view.isVisible\nimport androidx.lifecycle.LifecycleOwner\nimport androidx.lifecycle.lifecycleScope\nimport androidx.recyclerview.widget.RecyclerView\nimport coil.dispose\nimport coil.load\nimport coil.size.Size\nimport com.davemorrissey.labs.subscaleview.ImageSource\nimport com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView\nimport com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.DefaultOnImageEventListener\nimport java8.nio.file.Path\nimport java8.nio.file.attribute.BasicFileAttributes\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\nimport me.zhanghai.android.files.coil.fadeIn\nimport me.zhanghai.android.files.databinding.ImageViewerItemBinding\nimport me.zhanghai.android.files.file.MimeType\nimport me.zhanghai.android.files.file.asMimeType\nimport me.zhanghai.android.files.file.asMimeTypeOrNull\nimport me.zhanghai.android.files.file.fileProviderUri\nimport me.zhanghai.android.files.provider.common.AndroidFileTypeDetector\nimport me.zhanghai.android.files.provider.common.newInputStream\nimport me.zhanghai.android.files.provider.common.readAttributes\nimport me.zhanghai.android.files.ui.SimpleAdapter\nimport me.zhanghai.android.files.util.fadeInUnsafe\nimport me.zhanghai.android.files.util.fadeOutUnsafe\nimport me.zhanghai.android.files.util.layoutInflater\nimport me.zhanghai.android.files.util.shortAnimTime\nimport kotlin.math.max\n\nclass ImageViewerAdapter(\n    private val lifecycleOwner: LifecycleOwner,\n    private val listener: (View) -> Unit\n) : SimpleAdapter<Path, ImageViewerAdapter.ViewHolder>() {\n    override val hasStableIds: Boolean\n        get() = true\n\n    override fun getItemId(position: Int): Long = getItem(position).hashCode().toLong()\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder =\n        ViewHolder(ImageViewerItemBinding.inflate(parent.context.layoutInflater, parent, false))\n\n    override fun onBindViewHolder(holder: ViewHolder, position: Int) {\n        val path = getItem(position)\n        val binding = holder.binding\n        binding.image.setOnPhotoTapListener { view, _, _ -> listener(view) }\n        binding.largeImage.setOnClickListener(listener)\n        loadImage(binding, path)\n    }\n\n    override fun onViewRecycled(holder: ViewHolder) {\n        super.onViewRecycled(holder)\n\n        val binding = holder.binding\n        binding.image.dispose()\n        binding.largeImage.recycle()\n    }\n\n    private fun loadImage(binding: ImageViewerItemBinding, path: Path) {\n        binding.progress.fadeInUnsafe(true)\n        binding.errorText.fadeOutUnsafe()\n        binding.image.isVisible = false\n        binding.largeImage.isVisible = false\n        lifecycleOwner.lifecycleScope.launch {\n            val imageInfo = try {\n                withContext(Dispatchers.IO) { path.loadImageInfo() }\n            } catch (e: Exception) {\n                e.printStackTrace()\n                showError(binding, e)\n                return@launch\n            }\n            loadImageWithInfo(binding, path, imageInfo)\n        }\n    }\n\n    private fun Path.loadImageInfo(): ImageInfo {\n        val attributes = readAttributes(BasicFileAttributes::class.java)\n        val mimeType = AndroidFileTypeDetector.getMimeType(this, attributes).asMimeType()\n        val bitmapOptions = BitmapFactory.Options().apply { inJustDecodeBounds = true }\n        newInputStream().use { BitmapFactory.decodeStream(it, null, bitmapOptions) }\n        return ImageInfo(\n            attributes, bitmapOptions.outWidth, bitmapOptions.outHeight,\n            bitmapOptions.outMimeType?.asMimeTypeOrNull() ?: mimeType\n        )\n    }\n\n    private fun loadImageWithInfo(\n        binding: ImageViewerItemBinding,\n        path: Path,\n        imageInfo: ImageInfo\n    ) {\n        if (!imageInfo.shouldUseLargeImageView) {\n            binding.image.apply {\n                isVisible = true\n                load(path to imageInfo.attributes) {\n                    size(Size.ORIGINAL)\n                    fadeIn(context.shortAnimTime)\n                    listener(\n                        onSuccess = { _, _ -> binding.progress.fadeOutUnsafe() },\n                        onError = { _, result -> showError(binding, result.throwable) }\n                    )\n                }\n            }\n        } else {\n            binding.largeImage.apply {\n                setDoubleTapZoomDuration(300)\n                orientation = SubsamplingScaleImageView.ORIENTATION_USE_EXIF\n                // Otherwise OnImageEventListener.onReady() is never called.\n                isVisible = true\n                alpha = 0f\n                setOnImageEventListener(object : DefaultOnImageEventListener() {\n                    override fun onReady() {\n                        setDoubleTapZoomScale(binding.largeImage.cropScale)\n                        binding.progress.fadeOutUnsafe()\n                        binding.largeImage.fadeInUnsafe(true)\n                    }\n\n                    override fun onImageLoadError(e: Exception) {\n                        e.printStackTrace()\n                        showError(binding, e)\n                    }\n                })\n                setImageRestoringSavedState(ImageSource.uri(path.fileProviderUri))\n            }\n        }\n    }\n\n    private val ImageInfo.shouldUseLargeImageView: Boolean\n        get() {\n            // See BitmapFactory.cpp encodedFormatToString()\n            if (mimeType == MimeType.IMAGE_GIF) {\n                return false\n            }\n            if (width <= 0 || height <= 0) {\n                return false\n            }\n            // 4 bytes per pixel for ARGB_8888.\n            if (width * height * 4 > MAX_BITMAP_SIZE) {\n                return true\n            }\n            if (width > 2048 || height > 2048) {\n                val ratio = width.toFloat() / height\n                if (ratio < 0.5 || ratio > 2) {\n                    return true\n                }\n            }\n            return false\n        }\n\n    private val SubsamplingScaleImageView.cropScale: Float\n        get() {\n            val viewWidth = (width - paddingLeft - paddingRight)\n            val viewHeight = (height - paddingTop - paddingBottom)\n            val orientation = appliedOrientation\n            val rotated90Or270 = orientation == SubsamplingScaleImageView.ORIENTATION_90\n                || orientation == SubsamplingScaleImageView.ORIENTATION_270\n            val imageWidth = if (rotated90Or270) sHeight else sWidth\n            val imageHeight = if (rotated90Or270) sWidth else sHeight\n            return max(viewWidth.toFloat() / imageWidth, viewHeight.toFloat() / imageHeight)\n        }\n\n    private fun showError(binding: ImageViewerItemBinding, throwable: Throwable) {\n        binding.progress.fadeOutUnsafe()\n        binding.errorText.text = throwable.toString()\n        binding.errorText.fadeInUnsafe(true)\n        binding.image.isVisible = false\n        binding.largeImage.isVisible = false\n    }\n\n    companion object {\n        // @see android.graphics.RecordingCanvas#MAX_BITMAP_SIZE\n        private const val MAX_BITMAP_SIZE = 100 * 1024 * 1024\n    }\n\n    class ViewHolder(val binding: ImageViewerItemBinding) : RecyclerView.ViewHolder(binding.root)\n\n    private class ImageInfo(\n        val attributes: BasicFileAttributes,\n        val width: Int,\n        val height: Int,\n        val mimeType: MimeType\n    )\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/viewer/image/ImageViewerFragment.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.viewer.image\n\nimport android.content.Intent\nimport android.graphics.Color\nimport android.os.Bundle\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 androidx.appcompat.app.AppCompatActivity\nimport androidx.core.view.doOnPreDraw\nimport androidx.fragment.app.Fragment\nimport androidx.interpolator.view.animation.FastOutSlowInInterpolator\nimport androidx.viewpager2.widget.ViewPager2\nimport dev.chrisbanes.insetter.applySystemWindowInsetsToPadding\nimport java8.nio.file.Path\nimport kotlinx.parcelize.Parcelize\nimport kotlinx.parcelize.WriteWith\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.databinding.ImageViewerFragmentBinding\nimport me.zhanghai.android.files.file.fileProviderUri\nimport me.zhanghai.android.files.provider.common.delete\nimport me.zhanghai.android.files.ui.DepthPageTransformer\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.ParcelableListParceler\nimport me.zhanghai.android.files.util.ParcelableState\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.createSendImageIntent\nimport me.zhanghai.android.files.util.extraPath\nimport me.zhanghai.android.files.util.extraPathList\nimport me.zhanghai.android.files.util.finish\nimport me.zhanghai.android.files.util.getState\nimport me.zhanghai.android.files.util.mediumAnimTime\nimport me.zhanghai.android.files.util.putState\nimport me.zhanghai.android.files.util.showToast\nimport me.zhanghai.android.files.util.startActivitySafe\nimport me.zhanghai.android.files.util.withChooser\nimport me.zhanghai.android.systemuihelper.SystemUiHelper\nimport java.io.IOException\n\nclass ImageViewerFragment : Fragment(), ConfirmDeleteDialogFragment.Listener {\n    private val args by args<Args>()\n    private val argsPaths by lazy { args.intent.extraPathList }\n\n    private lateinit var paths: MutableList<Path>\n\n    private lateinit var binding: ImageViewerFragmentBinding\n\n    private lateinit var systemUiHelper: SystemUiHelper\n\n    private lateinit var adapter: ImageViewerAdapter\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        paths = (savedInstanceState?.getState<State>()?.paths ?: argsPaths).toMutableList()\n\n        setHasOptionsMenu(true)\n    }\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View =\n        ImageViewerFragmentBinding.inflate(inflater, container, false)\n            .also { binding = it }\n            .root\n\n    override fun onActivityCreated(savedInstanceState: Bundle?) {\n        super.onActivityCreated(savedInstanceState)\n\n        if (paths.isEmpty()) {\n            // TODO: Show a toast.\n            finish()\n            return\n        }\n\n        val activity = activity as AppCompatActivity\n        activity.setSupportActionBar(binding.toolbar)\n        activity.supportActionBar!!.setDisplayHomeAsUpEnabled(true)\n        // Our app bar will draw the status bar background.\n        activity.window.statusBarColor = Color.TRANSPARENT\n        binding.appBarLayout.applySystemWindowInsetsToPadding(left = true, top = true, right = true)\n        systemUiHelper = SystemUiHelper(\n            activity, SystemUiHelper.LEVEL_IMMERSIVE, SystemUiHelper.FLAG_IMMERSIVE_STICKY\n        ) { visible: Boolean ->\n            binding.appBarLayout.animate()\n                .alpha(if (visible) 1f else 0f)\n                .translationY(if (visible) 0f else -binding.appBarLayout.bottom.toFloat())\n                .setDuration(mediumAnimTime.toLong())\n                .setInterpolator(FastOutSlowInInterpolator())\n                .start()\n        }\n        // This will set up window flags.\n        systemUiHelper.show()\n        adapter = ImageViewerAdapter(viewLifecycleOwner) { systemUiHelper.toggle() }.apply {\n            replace(paths)\n        }\n        binding.viewPager.apply {\n            // 1 is the default for the old androidx.viewpager.widget.ViewPager.\n            offscreenPageLimit = 1\n            adapter = this@ImageViewerFragment.adapter\n            // ViewPager saves its position and will restore it later.\n            setCurrentItem(args.position, false)\n            setPageTransformer(DepthPageTransformer)\n            registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {\n                override fun onPageSelected(position: Int) {\n                    updateTitle()\n                }\n            })\n        }\n    }\n\n    override fun onViewStateRestored(savedInstanceState: Bundle?) {\n        super.onViewStateRestored(savedInstanceState)\n\n        if (paths.isEmpty()) {\n            // We did finish the activity in onActivityCreated(), however we will still be called\n            // here before the activity is actually finished.\n            return\n        }\n\n        updateTitle()\n    }\n\n    override fun onSaveInstanceState(outState: Bundle) {\n        super.onSaveInstanceState(outState)\n\n        outState.putState(State(paths))\n    }\n\n    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {\n        super.onCreateOptionsMenu(menu, inflater)\n\n        inflater.inflate(R.menu.image_viewer, menu)\n    }\n\n    override fun onOptionsItemSelected(item: MenuItem): Boolean =\n        when (item.itemId) {\n            R.id.action_delete -> {\n                confirmDelete()\n                true\n            }\n            R.id.action_share -> {\n                share()\n                true\n            }\n            else -> super.onOptionsItemSelected(item)\n        }\n\n    private fun confirmDelete() {\n        ConfirmDeleteDialogFragment.show(currentPath, this)\n    }\n\n    override fun delete(path: Path) {\n        try {\n            path.delete()\n        } catch (e: IOException) {\n            e.printStackTrace()\n            showToast(e.toString())\n            return\n        }\n        paths.removeAll(listOf(path))\n        if (paths.isEmpty()) {\n            finish()\n            return\n        }\n        adapter.replace(paths)\n        // ViewPager only asynchronously sets current item to 0, which isn't a desirable behavior\n        // for us and will make updateTitle() crash for index out of bounds.\n        if (binding.viewPager.currentItem > paths.lastIndex) {\n            binding.viewPager.currentItem = paths.lastIndex\n        }\n        updateTitle()\n        // Work around blank screen due to ViewPager2.PageTransformer not being called (and thus the\n        // next item keeps its 0 alpha) when we have offscreenPageLimit = 1.\n        binding.viewPager.doOnPreDraw { binding.viewPager.requestTransform() }\n    }\n\n    private fun updateTitle() {\n        val path = currentPath\n        requireActivity().title = path.fileName.toString()\n        val size = paths.size\n        binding.toolbar.subtitle = if (size > 1) {\n            getString(\n                R.string.image_viewer_subtitle_format, binding.viewPager.currentItem + 1, size\n            )\n        } else {\n            null\n        }\n    }\n\n    private fun share() {\n        val path = currentPath\n        val intent = path.fileProviderUri.createSendImageIntent()\n            .apply { extraPath = path }\n            .withChooser()\n        startActivitySafe(intent)\n    }\n\n    private val currentPath: Path\n        get() = paths[binding.viewPager.currentItem]\n\n    @Parcelize\n    class Args(val intent: Intent, val position: Int) : ParcelableArgs\n\n    @Parcelize\n    private class State(val paths: @WriteWith<ParcelableListParceler> List<Path>) : ParcelableState\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/viewer/saveas/SaveAsActivity.kt",
    "content": "/*\n * Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.viewer.saveas\n\nimport android.os.Bundle\nimport android.os.Environment\nimport java8.nio.file.Path\nimport java8.nio.file.Paths\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.app.AppActivity\nimport me.zhanghai.android.files.file.MimeType\nimport me.zhanghai.android.files.file.asMimeTypeOrNull\nimport me.zhanghai.android.files.filejob.FileJobService\nimport me.zhanghai.android.files.filelist.FileListActivity\nimport me.zhanghai.android.files.util.saveAsPath\nimport me.zhanghai.android.files.util.showToast\n\nclass SaveAsActivity : AppActivity() {\n    private val createFileLauncher =\n        registerForActivityResult(FileListActivity.CreateFileContract(), ::onCreateFileResult)\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        val intent = intent\n        val mimeType = intent.type?.asMimeTypeOrNull() ?: MimeType.ANY\n        val path = intent.saveAsPath\n        if (path == null) {\n            showToast(R.string.save_as_error)\n            finish()\n            return\n        }\n        val title = path.fileName.toString()\n        val initialPath =\n            Paths.get(\n                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).path\n            )\n        createFileLauncher.launch(Triple(mimeType, title, initialPath))\n    }\n\n    private fun onCreateFileResult(result: Path?) {\n        if (result == null) {\n            finish()\n            return\n        }\n        FileJobService.save(intent.saveAsPath!!, result, this)\n        finish()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/viewer/text/ConfirmCloseDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.viewer.text\n\nimport android.app.Dialog\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatDialogFragment\nimport androidx.fragment.app.Fragment\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.util.show\n\nclass ConfirmCloseDialogFragment : AppCompatDialogFragment() {\n    private val listener: Listener\n        get() = requireParentFragment() as Listener\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {\n        return MaterialAlertDialogBuilder(requireContext(), theme)\n            .setMessage(R.string.text_editor_close_message)\n            .setPositiveButton(R.string.keep_editing, null)\n            .setNegativeButton(R.string.discard) { _, _ -> listener.finish() }\n            .create()\n    }\n\n    companion object {\n        fun show(fragment: Fragment) {\n            ConfirmCloseDialogFragment().show(fragment)\n        }\n    }\n\n    interface Listener {\n        fun finish()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/viewer/text/ConfirmReloadDialogFragment.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.viewer.text\n\nimport android.app.Dialog\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatDialogFragment\nimport androidx.fragment.app.Fragment\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.util.show\n\nclass ConfirmReloadDialogFragment : AppCompatDialogFragment() {\n    private val listener: Listener\n        get() = requireParentFragment() as Listener\n\n    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {\n        return MaterialAlertDialogBuilder(requireContext(), theme)\n            .setMessage(R.string.text_editor_reload_message)\n            .setPositiveButton(R.string.keep_editing, null)\n            .setNegativeButton(R.string.reload) { _, _ -> listener.reload() }\n            .create()\n    }\n\n    companion object {\n        fun show(fragment: Fragment) {\n            ConfirmReloadDialogFragment().show(fragment)\n        }\n    }\n\n    interface Listener {\n        fun reload()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/viewer/text/TextEditorActivity.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.viewer.text\n\nimport android.os.Bundle\nimport android.view.View\nimport androidx.fragment.app.commit\nimport me.zhanghai.android.files.app.AppActivity\nimport me.zhanghai.android.files.util.putArgs\n\nclass TextEditorActivity : AppActivity() {\n    private lateinit var fragment: TextEditorFragment\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        // Calls ensureSubDecor().\n        findViewById<View>(android.R.id.content)\n        if (savedInstanceState == null) {\n            fragment = TextEditorFragment().putArgs(TextEditorFragment.Args(intent))\n            supportFragmentManager.commit { add(android.R.id.content, fragment) }\n        } else {\n            fragment = supportFragmentManager.findFragmentById(android.R.id.content)\n                as TextEditorFragment\n        }\n    }\n\n    override fun onSupportNavigateUp(): Boolean {\n        if (fragment.onSupportNavigateUp()) {\n            return true\n        }\n        return super.onSupportNavigateUp()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/viewer/text/TextEditorFragment.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.viewer.text\n\nimport android.content.Intent\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.Menu\nimport android.view.MenuInflater\nimport android.view.MenuItem\nimport android.view.SubMenu\nimport android.view.View\nimport android.view.ViewGroup\nimport androidx.activity.OnBackPressedCallback\nimport androidx.appcompat.app.AppCompatActivity\nimport androidx.core.view.children\nimport androidx.core.widget.doAfterTextChanged\nimport androidx.fragment.app.Fragment\nimport androidx.lifecycle.lifecycleScope\nimport java8.nio.file.Path\nimport kotlinx.coroutines.launch\nimport kotlinx.parcelize.Parcelize\nimport me.zhanghai.android.files.R\nimport me.zhanghai.android.files.databinding.TextEditorFragmentBinding\nimport me.zhanghai.android.files.ui.ThemedFastScroller\nimport me.zhanghai.android.files.util.ActionState\nimport me.zhanghai.android.files.util.DataState\nimport me.zhanghai.android.files.util.ParcelableArgs\nimport me.zhanghai.android.files.util.addOnBackPressedCallback\nimport me.zhanghai.android.files.util.args\nimport me.zhanghai.android.files.util.extraPath\nimport me.zhanghai.android.files.util.fadeInUnsafe\nimport me.zhanghai.android.files.util.fadeOutUnsafe\nimport me.zhanghai.android.files.util.isReady\nimport me.zhanghai.android.files.util.showToast\nimport me.zhanghai.android.files.util.viewModels\nimport java.nio.charset.Charset\n\nclass TextEditorFragment : Fragment(), ConfirmReloadDialogFragment.Listener,\n    ConfirmCloseDialogFragment.Listener {\n    private val args by args<Args>()\n    private lateinit var argsFile: Path\n\n    private lateinit var binding: TextEditorFragmentBinding\n\n    private lateinit var menuBinding: MenuBinding\n\n    private val viewModel by viewModels { { TextEditorViewModel(argsFile) } }\n\n    private lateinit var onBackPressedCallback: OnBackPressedCallback\n\n    private var isSettingText = false\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        setHasOptionsMenu(true)\n\n        lifecycleScope.launchWhenStarted {\n            onBackPressedCallback = object : OnBackPressedCallback(false) {\n                override fun handleOnBackPressed() {\n                    ConfirmCloseDialogFragment.show(this@TextEditorFragment)\n                }\n            }\n            launch {\n                viewModel.isTextChanged.collect {\n                    onBackPressedCallback.isEnabled = viewModel.isTextChanged.value\n                }\n            }\n            addOnBackPressedCallback(onBackPressedCallback)\n\n            launch { viewModel.encoding.collect { onEncodingChanged(it) } }\n            launch { viewModel.textState.collect { onTextStateChanged(it) } }\n            launch { viewModel.isTextChanged.collect { onIsTextChangedChanged(it) } }\n            launch { viewModel.writeFileState.collect { onWriteFileStateChanged(it) } }\n        }\n    }\n\n    override fun onCreateView(\n        inflater: LayoutInflater,\n        container: ViewGroup?,\n        savedInstanceState: Bundle?\n    ): View =\n        TextEditorFragmentBinding.inflate(inflater, container, false)\n            .also { binding = it }\n            .root\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n\n        val argsFile = args.intent.extraPath\n        if (argsFile == null) {\n            // TODO: Show a toast.\n            finish()\n            return\n        }\n        this.argsFile = argsFile\n\n        val activity = requireActivity() as AppCompatActivity\n        activity.lifecycleScope.launchWhenCreated {\n            activity.setSupportActionBar(binding.toolbar)\n            activity.supportActionBar!!.setDisplayHomeAsUpEnabled(true)\n        }\n\n        // TODO: Move reload-prevent here so that we can also handle save-as, etc. Or maybe just get\n        //  rid of the mPathLiveData in TextEditorViewModel.\n        ThemedFastScroller.create(binding.scrollView)\n        // Manually save and restore state in view model to avoid TransactionTooLargeException.\n        binding.textEdit.isSaveEnabled = false\n        val textEditSavedState = viewModel.removeEditTextSavedState()\n        if (textEditSavedState != null) {\n            binding.textEdit.onRestoreInstanceState(textEditSavedState)\n        }\n        binding.textEdit.doAfterTextChanged {\n            if (isSettingText) {\n                return@doAfterTextChanged\n            }\n            // Might happen if the animation is running and user is quick enough.\n            if (viewModel.textState.value !is DataState.Success) {\n                return@doAfterTextChanged\n            }\n            viewModel.isTextChanged.value = true\n        }\n\n        // TODO: Request storage permission if not granted.\n    }\n\n    override fun onSaveInstanceState(outState: Bundle) {\n        super.onSaveInstanceState(outState)\n\n        viewModel.setEditTextSavedState(binding.textEdit.onSaveInstanceState())\n    }\n\n    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {\n        super.onCreateOptionsMenu(menu, inflater)\n\n        menuBinding = MenuBinding.inflate(menu, inflater)\n    }\n\n    override fun onPrepareOptionsMenu(menu: Menu) {\n        super.onPrepareOptionsMenu(menu)\n\n        updateSaveMenuItem()\n        updateEncodingMenuItems()\n    }\n\n    override fun onOptionsItemSelected(item: MenuItem): Boolean =\n        when (item.itemId) {\n            R.id.action_save -> {\n                save()\n                true\n            }\n            R.id.action_reload -> {\n                onReload()\n                true\n            }\n            Menu.FIRST -> {\n                viewModel.encoding.value = Charset.forName(item.titleCondensed!!.toString())\n                true\n            }\n            else -> super.onOptionsItemSelected(item)\n        }\n\n    fun onSupportNavigateUp(): Boolean {\n        if (onBackPressedCallback.isEnabled) {\n            onBackPressedCallback.handleOnBackPressed()\n            return true\n        }\n        return false\n    }\n\n    override fun finish() {\n        requireActivity().finish()\n    }\n\n    private fun onEncodingChanged(encoding: Charset) {\n        updateEncodingMenuItems()\n    }\n\n    private fun updateEncodingMenuItems() {\n        if (!this::menuBinding.isInitialized) {\n            return\n        }\n        val charsetName = viewModel.encoding.value.name()\n        val charsetItem = menuBinding.encodingSubMenu.children\n            .find { it.titleCondensed == charsetName }!!\n        charsetItem.isChecked = true\n    }\n\n    private fun onTextStateChanged(state: DataState<String>) {\n        updateTitle()\n        when (state) {\n            is DataState.Loading -> {\n                binding.progress.fadeInUnsafe()\n                binding.errorText.fadeOutUnsafe()\n                binding.textEdit.fadeOutUnsafe()\n            }\n            is DataState.Success -> {\n                binding.progress.fadeOutUnsafe()\n                binding.errorText.fadeOutUnsafe()\n                binding.textEdit.fadeInUnsafe()\n                if (!viewModel.isTextChanged.value) {\n                    setText(state.data)\n                }\n            }\n            is DataState.Error -> {\n                state.throwable.printStackTrace()\n                binding.progress.fadeOutUnsafe()\n                binding.errorText.fadeInUnsafe()\n                binding.errorText.text = state.throwable.toString()\n                binding.textEdit.fadeOutUnsafe()\n            }\n        }\n    }\n\n    private fun setText(text: String?) {\n        isSettingText = true\n        binding.textEdit.setText(text)\n        isSettingText = false\n        viewModel.isTextChanged.value = false\n    }\n\n    private fun onIsTextChangedChanged(changed: Boolean) {\n        updateTitle()\n    }\n\n    private fun updateTitle() {\n        val fileName = viewModel.file.value.fileName.toString()\n        val changed = viewModel.isTextChanged.value\n        requireActivity().title = getString(\n            if (changed) {\n                R.string.text_editor_title_changed_format\n            } else {\n                R.string.text_editor_title_format\n            }, fileName\n        )\n    }\n\n    private fun onReload() {\n        if (viewModel.isTextChanged.value) {\n            ConfirmReloadDialogFragment.show(this)\n        } else {\n            reload()\n        }\n    }\n\n    override fun reload() {\n        viewModel.isTextChanged.value = false\n        viewModel.reload()\n    }\n\n    private fun save() {\n        val text = binding.textEdit.text.toString()\n        viewModel.writeFile(argsFile, text, requireContext())\n    }\n\n    private fun onWriteFileStateChanged(state: ActionState<Pair<Path, String>, Unit>) {\n        when (state) {\n            is ActionState.Ready, is ActionState.Running -> updateSaveMenuItem()\n            is ActionState.Success -> {\n                showToast(R.string.text_editor_save_success)\n                viewModel.finishWritingFile()\n                viewModel.isTextChanged.value = false\n            }\n            // The error will be toasted by service so we should never show it in UI.\n            is ActionState.Error -> viewModel.finishWritingFile()\n        }\n    }\n\n    private fun updateSaveMenuItem() {\n        if (!this::menuBinding.isInitialized) {\n            return\n        }\n        menuBinding.saveItem.isEnabled = viewModel.writeFileState.value.isReady\n    }\n\n    @Parcelize\n    class Args(val intent: Intent) : ParcelableArgs\n\n    private class MenuBinding private constructor(\n        val menu: Menu,\n        val saveItem: MenuItem,\n        val encodingSubMenu: SubMenu\n    ) {\n        companion object {\n            fun inflate(menu: Menu, inflater: MenuInflater): MenuBinding {\n                inflater.inflate(R.menu.text_editor, menu)\n                val encodingSubMenu = menu.findItem(R.id.action_encoding).subMenu!!\n                for ((charsetName, charset) in Charset.availableCharsets()) {\n                    // HACK: Use titleCondensed to store charset name.\n                    encodingSubMenu.add(Menu.NONE, Menu.FIRST, Menu.NONE, charset.displayName())\n                        .titleCondensed = charsetName\n                }\n                encodingSubMenu.setGroupCheckable(Menu.NONE, true, true)\n                return MenuBinding(menu, menu.findItem(R.id.action_save), encodingSubMenu)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/me/zhanghai/android/files/viewer/text/TextEditorViewModel.kt",
    "content": "/*\n * Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage me.zhanghai.android.files.viewer.text\n\nimport android.content.Context\nimport android.os.Parcelable\nimport androidx.lifecycle.ViewModel\nimport androidx.lifecycle.viewModelScope\nimport java8.nio.file.Path\nimport kotlinx.coroutines.CancellationException\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.Job\nimport kotlinx.coroutines.currentCoroutineContext\nimport kotlinx.coroutines.ensureActive\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.asStateFlow\nimport kotlinx.coroutines.flow.collectLatest\nimport kotlinx.coroutines.flow.combine\nimport kotlinx.coroutines.isActive\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.runInterruptible\nimport kotlinx.coroutines.withContext\nimport me.zhanghai.android.files.filejob.FileJobService\nimport me.zhanghai.android.files.provider.common.readAllBytes\nimport me.zhanghai.android.files.provider.common.size\nimport me.zhanghai.android.files.util.ActionState\nimport me.zhanghai.android.files.util.DataState\nimport me.zhanghai.android.files.util.isFinished\nimport me.zhanghai.android.files.util.isReady\nimport me.zhanghai.android.files.util.toError\nimport me.zhanghai.android.files.util.toLoading\nimport java.io.IOException\nimport java.nio.charset.StandardCharsets\n\nclass TextEditorViewModel(file: Path) : ViewModel() {\n    private val _file = MutableStateFlow(file)\n    val file = _file.asStateFlow()\n\n    private val _bytesState = MutableStateFlow<DataState<ByteArray>>(DataState.Loading())\n\n    private var loadJob: Job? = null\n    private var reloadJob: Job? = null\n\n    init {\n        viewModelScope.launch {\n            _file.collectLatest {\n                loadJob?.cancel()?.also { loadJob = null }\n                reloadJob?.cancel()?.also { reloadJob = null }\n                loadJob = launch {\n                    mapFileToBytesState(it)\n                    if (isActive) {\n                        loadJob = null\n                    }\n                }\n            }\n        }\n    }\n\n    fun reload() {\n        viewModelScope.launch {\n            loadJob?.cancel()?.also { loadJob = null }\n            reloadJob?.cancel()?.also { reloadJob = null }\n            reloadJob = launch {\n                mapFileToBytesState(_file.value)\n                if (isActive) {\n                    reloadJob = null\n                }\n            }\n        }\n    }\n\n    private suspend fun mapFileToBytesState(file: Path) {\n        _bytesState.value = _bytesState.value.toLoading()\n        try {\n            val bytes = runInterruptible(Dispatchers.IO) {\n                val size = file.size()\n                if (size > MAX_FILE_SIZE) {\n                    throw IOException(\"File size $size is too large\")\n                }\n                file.readAllBytes()\n            }\n            currentCoroutineContext().ensureActive()\n            _bytesState.value = DataState.Success(bytes)\n        } catch (e: CancellationException) {\n            e.printStackTrace()\n        } catch (e: Exception) {\n            _bytesState.value = _bytesState.value.toError(e)\n        }\n    }\n\n    val encoding = MutableStateFlow(StandardCharsets.UTF_8)\n\n    private val _textState = MutableStateFlow<DataState<String>>(DataState.Loading())\n    val textState = _textState.asStateFlow()\n\n    init {\n        viewModelScope.launch {\n            _bytesState.combine(encoding) { bytesState, encoding -> bytesState to encoding }\n                .collectLatest { (bytesState, encoding) ->\n                    when (bytesState) {\n                        is DataState.Loading -> _textState.value = _textState.value.toLoading()\n                        is DataState.Success -> {\n                            _textState.value = _textState.value.toLoading()\n                            try {\n                                val text = withContext(Dispatchers.Default) {\n                                    String(bytesState.data, encoding)\n                                }\n                                currentCoroutineContext().ensureActive()\n                                _textState.value = DataState.Success(text)\n                            } catch (e: CancellationException) {\n                                e.printStackTrace()\n                            } catch (e: Exception) {\n                                _textState.value = _textState.value.toError(e)\n                            }\n                        }\n                        is DataState.Error ->\n                            _textState.value = _textState.value.toError(bytesState.throwable)\n                    }\n                }\n        }\n    }\n\n    val isTextChanged = MutableStateFlow(false)\n\n    private val _writeFileState =\n        MutableStateFlow<ActionState<Pair<Path, String>, Unit>>(ActionState.Ready())\n    val writeFileState = _writeFileState.asStateFlow()\n\n    fun writeFile(path: Path, text: String, context: Context) {\n        viewModelScope.launch {\n            check(_writeFileState.value.isReady)\n            val argument = path to text\n            _writeFileState.value = ActionState.Running(argument)\n            val bytes = withContext(Dispatchers.Default) {\n                text.toByteArray(encoding.value)\n            }\n            FileJobService.write(path, bytes, context) { successful ->\n                if (successful) {\n                    loadJob?.cancel()?.also { loadJob = null }\n                    reloadJob?.cancel()?.also { reloadJob = null }\n                    _bytesState.value = DataState.Success(bytes)\n                }\n                _writeFileState.value = if (successful) {\n                    ActionState.Success(argument, Unit)\n                } else {\n                    // The error will be toasted by service so we should never show it in UI, but we\n                    // need a non-null value here.\n                    ActionState.Error(argument, Throwable())\n                }\n            }\n        }\n    }\n\n    fun finishWritingFile() {\n        viewModelScope.launch {\n            check(_writeFileState.value.isFinished)\n            _writeFileState.value = ActionState.Ready()\n        }\n    }\n\n    private var editTextSavedState: Parcelable? = null\n\n    fun setEditTextSavedState(editTextSavedState: Parcelable?) {\n        this.editTextSavedState = editTextSavedState\n    }\n\n    fun removeEditTextSavedState(): Parcelable? {\n        val savedState = editTextSavedState\n        editTextSavedState = null\n        return savedState\n    }\n\n    companion object {\n        private const val MAX_FILE_SIZE = 1024 * 1024.toLong()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/net/schmizz/sshj/sftp/RemoteFileAccessor.java",
    "content": "/*\n * Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\npackage net.schmizz.sshj.sftp;\n\nimport net.schmizz.concurrent.Promise;\n\nimport java.io.IOException;\n\nimport androidx.annotation.NonNull;\n\npublic class RemoteFileAccessor {\n    private RemoteFileAccessor() {}\n\n    @NonNull\n    public static Promise<Response, SFTPException> asyncRead(@NonNull RemoteFile file, long offset,\n                                                             int length) throws IOException {\n        return file.asyncRead(offset, length);\n    }\n\n    @NonNull\n    public static SFTPEngine getRequester(@NonNull RemoteFile file) {\n        return file.requester;\n    }\n}\n"
  },
  {
    "path": "app/src/main/jni/hiddenapi.c",
    "content": "/*\n * Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\n#include <jni.h>\n\nJNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {\n    void *vmEnv = NULL;\n    if ((*vm)->GetEnv(vm, &vmEnv, JNI_VERSION_1_6) != JNI_OK) {\n        return JNI_ERR;\n    }\n    JNIEnv *env = vmEnv;\n    jclass vmRuntimeClass = (*env)->FindClass(env, \"dalvik/system/VMRuntime\");\n    if (!vmRuntimeClass) {\n        return JNI_ERR;\n    }\n    jmethodID vmRuntimeGetRuntimeMethod = (*env)->GetStaticMethodID(env, vmRuntimeClass,\n            \"getRuntime\", \"()Ldalvik/system/VMRuntime;\");\n    if (!vmRuntimeGetRuntimeMethod) {\n        (*env)->DeleteLocalRef(env, vmRuntimeClass);\n        return JNI_ERR;\n    }\n    jobject vmRuntime = (*env)->CallStaticObjectMethod(env, vmRuntimeClass,\n            vmRuntimeGetRuntimeMethod);\n    if (!vmRuntime) {\n        (*env)->DeleteLocalRef(env, vmRuntimeClass);\n        return JNI_ERR;\n    }\n    jmethodID vmRuntimeSetHiddenApiExemptionsMethod = (*env)->GetMethodID(env, vmRuntimeClass,\n            \"setHiddenApiExemptions\", \"([Ljava/lang/String;)V\");\n    if (!vmRuntimeSetHiddenApiExemptionsMethod) {\n        (*env)->DeleteLocalRef(env, vmRuntime);\n        (*env)->DeleteLocalRef(env, vmRuntimeClass);\n        return JNI_ERR;\n    }\n    jclass stringClass = (*env)->FindClass(env, \"java/lang/String\");\n    if (!stringClass) {\n        (*env)->DeleteLocalRef(env, vmRuntime);\n        (*env)->DeleteLocalRef(env, vmRuntimeClass);\n        return JNI_ERR;\n    }\n    jstring signaturePrefix = (*env)->NewStringUTF(env, \"\");\n    if (!signaturePrefix) {\n        (*env)->DeleteLocalRef(env, stringClass);\n        (*env)->DeleteLocalRef(env, vmRuntime);\n        (*env)->DeleteLocalRef(env, vmRuntimeClass);\n        return JNI_ERR;\n    }\n    jobjectArray signaturePrefixes = (*env)->NewObjectArray(env, 1, stringClass, signaturePrefix);\n    (*env)->DeleteLocalRef(env, signaturePrefix);\n    (*env)->DeleteLocalRef(env, stringClass);\n    if (!signaturePrefixes) {\n        (*env)->DeleteLocalRef(env, vmRuntime);\n        (*env)->DeleteLocalRef(env, vmRuntimeClass);\n    }\n    (*env)->CallVoidMethod(env, vmRuntime, vmRuntimeSetHiddenApiExemptionsMethod,\n                           signaturePrefixes);\n    (*env)->DeleteLocalRef(env, signaturePrefixes);\n    (*env)->DeleteLocalRef(env, vmRuntime);\n    (*env)->DeleteLocalRef(env, vmRuntimeClass);\n    return JNI_VERSION_1_6;\n}\n"
  },
  {
    "path": "app/src/main/jni/syscall.c",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\n#include <errno.h>\n#include <stdbool.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include <dirent.h>\n#include <fcntl.h>\n#include <grp.h>\n#include <mntent.h>\n#include <pwd.h>\n#include <sys/inotify.h>\n#include <sys/mount.h>\n#include <sys/sendfile.h>\n#include <sys/stat.h>\n#include <sys/statvfs.h>\n#include <sys/types.h>\n#include <sys/xattr.h>\n#include <unistd.h>\n\n#include <jni.h>\n\n#include <android/log.h>\n\n#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)\n#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)\n#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)\n#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)\n#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)\n\n#define LOG_TAG \"syscall\"\n\n#undef TEMP_FAILURE_RETRY\n// Checks errno when return value is -1.\n#define TEMP_FAILURE_RETRY(exp) ({ \\\n    __typeof__(exp) _rc; \\\n    do { \\\n        errno = 0; \\\n        _rc = (exp); \\\n    } while (_rc == -1 && errno == EINTR); \\\n    if (_rc != -1) { \\\n        errno = 0; \\\n    } \\\n    _rc; })\n\n// Checks return value as errno.\n#define TEMP_FAILURE_RETRY_E(exp) ({ \\\n    __typeof__(exp) _rc; \\\n    do { \\\n        _rc = (exp); \\\n    } while (_rc == EINTR); \\\n    _rc; })\n\n// Checks errno when return value is NULL.\n#define TEMP_FAILURE_RETRY_N(exp) ({ \\\n    __typeof__(exp) _rc; \\\n    do { \\\n        errno = 0; \\\n        _rc = (exp); \\\n    } while (!_rc && errno == EINTR); \\\n    if (_rc) { \\\n        errno = 0; \\\n    } \\\n    _rc; })\n\n// Always checks errno and ignores return value.\n#define TEMP_FAILURE_RETRY_V(exp) ({ \\\n    do { \\\n        errno = 0; \\\n        (exp); \\\n    } while (errno == EINTR); })\n\nstatic jclass findClass(JNIEnv *env, const char *name) {\n    jclass localClass = (*env)->FindClass(env, name);\n    if (!localClass) {\n        ALOGE(\"Failed to find class '%s'\", name);\n        abort();\n    }\n    jclass globalClass = (*env)->NewGlobalRef(env, localClass);\n    (*env)->DeleteLocalRef(env, localClass);\n    if (!globalClass) {\n        ALOGE(\"Failed to create a global reference for '%s'\", name);\n        abort();\n    }\n    return globalClass;\n}\n\nstatic jfieldID findField(JNIEnv *env, jclass clazz, const char *name, const char *signature) {\n    jfieldID field = (*env)->GetFieldID(env, clazz, name, signature);\n    if (!field) {\n        ALOGE(\"Failed to find field '%s' '%s'\", name, signature);\n        abort();\n    }\n    return field;\n}\n\nstatic jmethodID findMethod(JNIEnv *env, jclass clazz, const char *name, const char *signature) {\n    jmethodID method = (*env)->GetMethodID(env, clazz, name, signature);\n    if (!method) {\n        ALOGE(\"Failed to find method '%s' '%s'\", name, signature);\n        abort();\n    }\n    return method;\n}\n\nstatic jclass getSyscallExceptionClass(JNIEnv *env) {\n    static jclass syscallExceptionClass = NULL;\n    if (!syscallExceptionClass) {\n        syscallExceptionClass = findClass(env,\n                \"me/zhanghai/android/files/provider/linux/syscall/SyscallException\");\n    }\n    return syscallExceptionClass;\n}\n\nstatic jclass getByteStringClass(JNIEnv *env) {\n    static jclass byteStringClass = NULL;\n    if (!byteStringClass) {\n        byteStringClass = findClass(env, \"me/zhanghai/android/files/provider/common/ByteString\");\n    }\n    return byteStringClass;\n}\n\nstatic jfieldID getByteStringBytesField(JNIEnv *env) {\n    static jfieldID byteStringBytesField = NULL;\n    if (!byteStringBytesField) {\n        byteStringBytesField = findField(env, getByteStringClass(env), \"bytes\", \"[B\");\n    }\n    return byteStringBytesField;\n}\n\nstatic jclass getFileDescriptorClass(JNIEnv *env) {\n    static jclass fileDescriptorClass = NULL;\n    if (!fileDescriptorClass) {\n        fileDescriptorClass = findClass(env, \"java/io/FileDescriptor\");\n    }\n    return fileDescriptorClass;\n}\n\nstatic jfieldID getFileDescriptorDescriptorField(JNIEnv *env) {\n    static jfieldID fileDescriptorDescriptorField = NULL;\n    if (!fileDescriptorDescriptorField) {\n        fileDescriptorDescriptorField = findField(env, getFileDescriptorClass(env), \"descriptor\",\n                                                  \"I\");\n    }\n    return fileDescriptorDescriptorField;\n}\n\nstatic jclass getInt32RefClass(JNIEnv *env) {\n    static jclass int32RefClass = NULL;\n    if (!int32RefClass) {\n        int32RefClass = findClass(env, \"me/zhanghai/android/files/provider/linux/syscall/Int32Ref\");\n    }\n    return int32RefClass;\n}\n\nstatic jfieldID getInt32RefValueField(JNIEnv *env) {\n    static jfieldID int32RefValueField = NULL;\n    if (!int32RefValueField) {\n        int32RefValueField = findField(env, getInt32RefClass(env), \"value\", \"I\");\n    }\n    return int32RefValueField;\n}\n\nstatic jclass getInt64RefClass(JNIEnv *env) {\n    static jclass int64RefClass = NULL;\n    if (!int64RefClass) {\n        int64RefClass = findClass(env, \"android/system/Int64Ref\");\n    }\n    return int64RefClass;\n}\n\nstatic jfieldID getInt64RefValueField(JNIEnv *env) {\n    static jfieldID int64RefValueField = NULL;\n    if (!int64RefValueField) {\n        int64RefValueField = findField(env, getInt64RefClass(env), \"value\", \"J\");\n    }\n    return int64RefValueField;\n}\n\nstatic jclass getStructDirentClass(JNIEnv *env) {\n    static jclass structStatClass = NULL;\n    if (!structStatClass) {\n        structStatClass = findClass(env,\n                \"me/zhanghai/android/files/provider/linux/syscall/StructDirent\");\n    }\n    return structStatClass;\n}\n\nstatic jclass getStructGroupClass(JNIEnv *env) {\n    static jclass structGroupClass = NULL;\n    if (!structGroupClass) {\n        structGroupClass = findClass(env,\n                \"me/zhanghai/android/files/provider/linux/syscall/StructGroup\");\n    }\n    return structGroupClass;\n}\n\nstatic jclass getStructInotifyEventClass(JNIEnv *env) {\n    static jclass structInotifyEventClass = NULL;\n    if (!structInotifyEventClass) {\n        structInotifyEventClass = findClass(env,\n                \"me/zhanghai/android/files/provider/linux/syscall/StructInotifyEvent\");\n    }\n    return structInotifyEventClass;\n}\n\nstatic jclass getStructMntentClass(JNIEnv *env) {\n    static jclass structMntentClass = NULL;\n    if (!structMntentClass) {\n        structMntentClass = findClass(env,\n                \"me/zhanghai/android/files/provider/linux/syscall/StructMntent\");\n    }\n    return structMntentClass;\n}\n\nstatic jfieldID getStructMntentMntOptsField(JNIEnv *env) {\n    static jfieldID structMntentMntOptsField = NULL;\n    if (!structMntentMntOptsField) {\n        structMntentMntOptsField = findField(env, getStructMntentClass(env), \"mnt_opts\",\n                \"Lme/zhanghai/android/files/provider/common/ByteString;\");\n    }\n    return structMntentMntOptsField;\n}\n\nstatic jclass getStructPasswdClass(JNIEnv *env) {\n    static jclass structPasswdClass = NULL;\n    if (!structPasswdClass) {\n        structPasswdClass = findClass(env,\n                \"me/zhanghai/android/files/provider/linux/syscall/StructPasswd\");\n    }\n    return structPasswdClass;\n}\n\nstatic jclass getStructStatClass(JNIEnv *env) {\n    static jclass structStatClass = NULL;\n    if (!structStatClass) {\n        structStatClass = findClass(env,\n                \"me/zhanghai/android/files/provider/linux/syscall/StructStat\");\n    }\n    return structStatClass;\n}\n\nstatic jclass getStructStatVfsClass(JNIEnv *env) {\n    static jclass structStatVfsClass = NULL;\n    if (!structStatVfsClass) {\n        structStatVfsClass = findClass(env, \"android/system/StructStatVfs\");\n    }\n    return structStatVfsClass;\n}\n\nstatic jclass getStructTimespecClass(JNIEnv *env) {\n    static jclass structTimespecClass = NULL;\n    if (!structTimespecClass) {\n        structTimespecClass = findClass(env,\n                \"me/zhanghai/android/files/provider/linux/syscall/StructTimespec\");\n    }\n    return structTimespecClass;\n}\n\nstatic jfieldID getStructTimespecTvSecField(JNIEnv *env) {\n    static jfieldID structTimespecTvSecField = NULL;\n    if (!structTimespecTvSecField) {\n        structTimespecTvSecField = findField(env, getStructTimespecClass(env), \"tv_sec\", \"J\");\n    }\n    return structTimespecTvSecField;\n}\n\nstatic jfieldID getStructTimespecTvNsecField(JNIEnv *env) {\n    static jfieldID structTimespecTvNsecField = NULL;\n    if (!structTimespecTvNsecField) {\n        structTimespecTvNsecField = findField(env, getStructTimespecClass(env), \"tv_nsec\", \"J\");\n    }\n    return structTimespecTvNsecField;\n}\n\nstatic void throwException(JNIEnv *env, jclass exceptionClass, jmethodID constructor3,\n                           jmethodID constructor2, const char *functionName, int error) {\n    jthrowable cause = NULL;\n    if ((*env)->ExceptionCheck(env)) {\n        cause = (*env)->ExceptionOccurred(env);\n        (*env)->ExceptionClear(env);\n    }\n    jstring detailMessage = (*env)->NewStringUTF(env, functionName);\n    if (!detailMessage) {\n        // Not really much we can do here. We're probably dead in the water,\n        // but let's try to stumble on...\n        (*env)->ExceptionClear(env);\n    }\n    jobject exception;\n    if (cause) {\n        exception = (*env)->NewObject(env, exceptionClass, constructor3, detailMessage, error,\n                cause);\n    } else {\n        exception = (*env)->NewObject(env, exceptionClass, constructor2, detailMessage, error);\n    }\n    (*env)->Throw(env, exception);\n    if (detailMessage) {\n        (*env)->DeleteLocalRef(env, detailMessage);\n    }\n}\n\nstatic void throwSyscallException(JNIEnv* env, const char* functionName) {\n    int error = errno;\n    static jmethodID constructor3 = NULL;\n    if (!constructor3) {\n        constructor3 = findMethod(env, getSyscallExceptionClass(env), \"<init>\",\n                \"(Ljava/lang/String;ILjava/lang/Throwable;)V\");\n    }\n    static jmethodID constructor2 = NULL;\n    if (!constructor2) {\n        constructor2 = findMethod(env, getSyscallExceptionClass(env), \"<init>\",\n                \"(Ljava/lang/String;I)V\");\n    }\n    throwException(env, getSyscallExceptionClass(env), constructor3, constructor2, functionName,\n                   error);\n}\n\nstatic char *mallocStringFromByteString(JNIEnv *env, jobject javaByteString) {\n    jbyteArray javaBytes = (*env)->GetObjectField(env, javaByteString, getByteStringBytesField(\n            env));\n    void *bytes = (*env)->GetByteArrayElements(env, javaBytes, NULL);\n    jsize javaLength = (*env)->GetArrayLength(env, javaBytes);\n    size_t length = (size_t) javaLength;\n    char *string = malloc(length + 1);\n    memcpy(string, bytes, length);\n    (*env)->ReleaseByteArrayElements(env, javaBytes, bytes, JNI_ABORT);\n    (*env)->DeleteLocalRef(env, javaBytes);\n    string[length] = '\\0';\n    return string;\n}\n\nstatic jobject newByteString(JNIEnv *env, const void *bytes, size_t length) {\n    static jmethodID constructor = NULL;\n    if (!constructor) {\n        constructor = findMethod(env, getByteStringClass(env), \"<init>\", \"([B)V\");\n    }\n    jsize javaLength = (jsize) length;\n    jbyteArray javaBytes = (*env)->NewByteArray(env, javaLength);\n    if (!javaBytes) {\n        return NULL;\n    }\n    (*env)->SetByteArrayRegion(env, javaBytes, 0, javaLength, bytes);\n    jobject javaByteString = (*env)->NewObject(env, getByteStringClass(env), constructor,\n            javaBytes);\n    (*env)->DeleteLocalRef(env, javaBytes);\n    return javaByteString;\n}\n\nstatic jobject newByteStringFromString(JNIEnv *env, const char *string) {\n    return newByteString(env, string, strlen(string));\n}\n\nstatic int getFdFromFileDescriptor(JNIEnv *env, jobject javaFileDescriptor) {\n    return (*env)->GetIntField(env, javaFileDescriptor, getFileDescriptorDescriptorField(env));\n}\n\nstatic jobject newFileDescriptor(JNIEnv *env, int fd) {\n    static jmethodID constructor = NULL;\n    if (!constructor) {\n        constructor = findMethod(env, getFileDescriptorClass(env), \"<init>\", \"()V\");\n    }\n    jobject javaFileDescriptor = (*env)->NewObject(env, getFileDescriptorClass(env), constructor);\n    if (!javaFileDescriptor) {\n        return NULL;\n    }\n    (*env)->SetIntField(env, javaFileDescriptor, getFileDescriptorDescriptorField(env), fd);\n    return javaFileDescriptor;\n}\n\nJNIEXPORT jboolean JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_access(\n        JNIEnv *env, jclass clazz, jobject javaPath, jint javaMode) {\n    char *path = mallocStringFromByteString(env, javaPath);\n    int mode = javaMode;\n    int result = TEMP_FAILURE_RETRY(access(path, mode));\n    free(path);\n    if (errno) {\n        throwSyscallException(env, \"access\");\n        return JNI_FALSE;\n    }\n    bool accessible = result == 0;\n    return (jboolean) accessible;\n}\n\nJNIEXPORT void JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_chmod(\n        JNIEnv *env, jclass clazz, jobject javaPath, jint javaMode) {\n    char *path = mallocStringFromByteString(env, javaPath);\n    mode_t mode = (mode_t) javaMode;\n    TEMP_FAILURE_RETRY(chmod(path, mode));\n    free(path);\n    if (errno) {\n        throwSyscallException(env, \"chmod\");\n    }\n}\n\nJNIEXPORT void JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_chown(\n        JNIEnv *env, jclass clazz, jobject javaPath, jint javaUid, jint javaGid) {\n    char *path = mallocStringFromByteString(env, javaPath);\n    uid_t uid = (uid_t) javaUid;\n    gid_t gid = (gid_t) javaGid;\n    TEMP_FAILURE_RETRY(chown(path, uid, gid));\n    free(path);\n    if (errno) {\n        throwSyscallException(env, \"chown\");\n    }\n}\n\nJNIEXPORT void JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_closedir(\n        JNIEnv *env, jclass clazz, jlong javaDir) {\n    DIR *dir = (DIR *) javaDir;\n    TEMP_FAILURE_RETRY(closedir(dir));\n    if (errno) {\n        throwSyscallException(env, \"closedir\");\n    }\n}\n\nJNIEXPORT void JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_endmntent(\n        JNIEnv *env, jclass clazz, jlong javaFile) {\n    FILE *file = (FILE *) javaFile;\n    // The endmntent() function always returns 1.\n    TEMP_FAILURE_RETRY_V(endmntent(file));\n    if (errno) {\n        throwSyscallException(env, \"endmntent\");\n    }\n}\n\n#define AID_APP_START 10000\n\n#if __ANDROID_API__ < __ANDROID_API_O__\n\nstatic __thread gid_t getgrentGid = AID_APP_START;\n\nvoid setgrent() {\n    getgrentGid = 0;\n}\n\nstruct group *getgrent() {\n    while (getgrentGid < AID_APP_START) {\n        struct group *group = getgrgid(getgrentGid);\n        ++getgrentGid;\n        errno = 0;\n        if (group) {\n            return group;\n        }\n    }\n    return NULL;\n}\n\nvoid endgrent() {\n    setgrent();\n}\n\n#endif\n\nJNIEXPORT void JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_endgrent(JNIEnv *env, jclass clazz) {\n    TEMP_FAILURE_RETRY_V(endgrent());\n    if (errno) {\n        throwSyscallException(env, \"endgrent\");\n    }\n}\n\n#if __ANDROID_API__ < __ANDROID_API_O__\n\nstatic __thread uid_t getpwentUid = AID_APP_START;\n\nvoid setpwent() {\n    getpwentUid = 0;\n}\n\nstruct passwd *getpwent() {\n    while (getpwentUid < AID_APP_START) {\n        struct passwd *passwd = getpwuid(getpwentUid);\n        ++getpwentUid;\n        errno = 0;\n        if (passwd) {\n            return passwd;\n        }\n    }\n    return NULL;\n}\n\nvoid endpwent() {\n    setpwent();\n}\n\n#endif\n\nJNIEXPORT void JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_endpwent(JNIEnv *env, jclass clazz) {\n    TEMP_FAILURE_RETRY_V(endpwent());\n    if (errno) {\n        throwSyscallException(env, \"endpwent\");\n    }\n}\n\nJNIEXPORT jint JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_errno(\n        JNIEnv *env, jclass clazz) {\n    return errno;\n}\n\nJNIEXPORT jint JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_fcntl_1int(\n        JNIEnv *env, jclass clazz, jobject javaFd, jint javaCmd, jint javaArg) {\n    int fd = getFdFromFileDescriptor(env, javaFd);\n    int cmd = javaCmd;\n    int arg = javaArg;\n    int result = TEMP_FAILURE_RETRY(fcntl(fd, cmd, arg));\n    if (errno) {\n        throwSyscallException(env, \"fcntl\");\n        return 0;\n    }\n    return result;\n}\n\nJNIEXPORT jint JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_fcntl_1void(\n        JNIEnv *env, jclass clazz, jobject javaFd, jint javaCmd) {\n    int fd = getFdFromFileDescriptor(env, javaFd);\n    int cmd = javaCmd;\n    int result = TEMP_FAILURE_RETRY(fcntl(fd, cmd));\n    if (errno) {\n        throwSyscallException(env, \"fcntl\");\n        return 0;\n    }\n    return result;\n}\n\nstatic jobject newStructGroup(JNIEnv *env, const struct group *group) {\n    static jmethodID constructor = NULL;\n    if (!constructor) {\n        constructor = findMethod(env, getStructGroupClass(env), \"<init>\",\n                                 \"(Lme/zhanghai/android/files/provider/common/ByteString;\"\n                                 \"Lme/zhanghai/android/files/provider/common/ByteString;I\"\n                                 \"[Lme/zhanghai/android/files/provider/common/ByteString;)V\");\n    }\n    jobject gr_name;\n    if (group->gr_name) {\n        gr_name = newByteStringFromString(env, group->gr_name);\n        if (!gr_name) {\n            return NULL;\n        }\n    } else {\n        gr_name = NULL;\n    }\n    jobject gr_passwd;\n    if (group->gr_passwd) {\n        gr_passwd = newByteStringFromString(env, group->gr_passwd);\n        if (!gr_passwd) {\n            return NULL;\n        }\n    } else {\n        gr_passwd = NULL;\n    }\n    jint gr_gid = (jint) group->gr_gid;\n    jobjectArray gr_mem;\n    if (group->gr_mem) {\n        jsize gr_memLength = 0;\n        for (char **gr_memIterator = group->gr_mem; *gr_memIterator; ++gr_memIterator) {\n            ++gr_memLength;\n        }\n        gr_mem = (*env)->NewObjectArray(env, gr_memLength, getByteStringClass(env), NULL);\n        if (!gr_mem) {\n            return NULL;\n        }\n        jsize gr_memIndex = 0;\n        for (char **gr_memIterator = group->gr_mem; *gr_memIterator; ++gr_memIterator,\n                ++gr_memIndex) {\n            jobject gr_memElement = newByteStringFromString(env, *gr_memIterator);\n            if (!gr_memElement) {\n                return NULL;\n            }\n            (*env)->SetObjectArrayElement(env, gr_mem, gr_memIndex, gr_memElement);\n            (*env)->DeleteLocalRef(env, gr_memElement);\n        }\n    } else {\n        gr_mem = NULL;\n    }\n    return (*env)->NewObject(env, getStructGroupClass(env), constructor, gr_name, gr_passwd, gr_gid,\n                             gr_mem);\n}\n\nJNIEXPORT jobject JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_getgrent(JNIEnv *env, jclass clazz) {\n    while (true) {\n        // getgrent() in bionic is thread safe.\n        struct group *group = TEMP_FAILURE_RETRY_N(getgrent());\n        if (errno) {\n            throwSyscallException(env, \"getgrent\");\n            return NULL;\n        }\n        if (!group) {\n            return NULL;\n        }\n        if (group->gr_name[0] == 'o' && group->gr_name[1] == 'e' && group->gr_name[2] == 'm'\n            && group->gr_name[3] == '_') {\n            continue;\n        }\n        if (group->gr_name[0] == 'u' && (group->gr_name[1] >= '0' && group->gr_name[1] <= '9')) {\n            return NULL;\n        }\n        if (group->gr_name[0] == 'a' && group->gr_name[1] == 'l' && group->gr_name[2] == 'l'\n            && group->gr_name[3] == '_' && group->gr_name[4] == 'a'\n            && (group->gr_name[5] >= '0' && group->gr_name[5] <= '9')) {\n            return NULL;\n        }\n        return newStructGroup(env, group);\n    }\n}\n\nJNIEXPORT jobject JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_getgrgid(\n        JNIEnv *env, jclass clazz, jint javaGid) {\n#if __ANDROID_API__ >= __ANDROID_API_N__\n    gid_t gid = (gid_t) javaGid;\n    size_t bufferSize = (size_t) sysconf(_SC_GETGR_R_SIZE_MAX);\n    if (bufferSize == -1) {\n        // See `man 3 getpwnam`\n        bufferSize = 16384;\n    }\n    //char buffer[bufferSize] = {};\n    char buffer[bufferSize];\n    struct group group = {};\n    struct group *result = NULL;\n    errno = TEMP_FAILURE_RETRY_E(getgrgid_r(gid, &group, buffer, bufferSize, &result));\n    if (errno) {\n        throwSyscallException(env, \"getgrgid_r\");\n        return NULL;\n    }\n    if (!result) {\n        return NULL;\n    }\n    return newStructGroup(env, result);\n#else\n    gid_t gid = (gid_t) javaGid;\n    struct group *result = TEMP_FAILURE_RETRY_N(getgrgid(gid));\n    if (errno) {\n        throwSyscallException(env, \"getgrgid\");\n        return NULL;\n    }\n    if (!result) {\n        return NULL;\n    }\n    return newStructGroup(env, result);\n#endif\n}\n\nJNIEXPORT jobject JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_getgrnam(\n        JNIEnv *env, jclass clazz, jobject javaName) {\n#if __ANDROID_API__ >= __ANDROID_API_N__\n    char *name = mallocStringFromByteString(env, javaName);\n    size_t bufferSize = (size_t) sysconf(_SC_GETGR_R_SIZE_MAX);\n    if (bufferSize == -1) {\n        // See `man 3 getpwnam`\n        bufferSize = 16384;\n    }\n    //char buffer[bufferSize] = {};\n    char buffer[bufferSize];\n    struct group group = {};\n    struct group *result = NULL;\n    errno = TEMP_FAILURE_RETRY_E(getgrnam_r(name, &group, buffer, bufferSize, &result));\n    free(name);\n    if (errno) {\n        throwSyscallException(env, \"getgrnam_r\");\n        return NULL;\n    }\n    if (!result) {\n        return NULL;\n    }\n    return newStructGroup(env, result);\n#else\n    char *name = mallocStringFromByteString(env, javaName);\n    struct group *result = TEMP_FAILURE_RETRY_N(getgrnam(name));\n    free(name);\n    if (errno) {\n        throwSyscallException(env, \"getgrnam\");\n        return NULL;\n    }\n    if (!result) {\n        return NULL;\n    }\n    return newStructGroup(env, result);\n#endif\n}\n\n#if __ANDROID_API__ < __ANDROID_API_L_MR1__\n// https://android.googlesource.com/platform/bionic/+/master/libc/bionic/mntent.cpp\nstatic struct mntent* _getmntent_r(FILE* fp, struct mntent* e, char* buf, int buf_len) {\n    memset(e, 0, sizeof(*e));\n    while (fgets(buf, buf_len, fp) != NULL) {\n        // Entries look like \"proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0\".\n        // That is: mnt_fsname mnt_dir mnt_type mnt_opts 0 0.\n        int fsname0, fsname1, dir0, dir1, type0, type1, opts0, opts1;\n        if (sscanf(buf, \" %n%*s%n %n%*s%n %n%*s%n %n%*s%n %d %d\",\n                   &fsname0, &fsname1, &dir0, &dir1, &type0, &type1, &opts0, &opts1,\n                   &e->mnt_freq, &e->mnt_passno) == 2) {\n            e->mnt_fsname = &buf[fsname0];\n            buf[fsname1] = '\\0';\n            e->mnt_dir = &buf[dir0];\n            buf[dir1] = '\\0';\n            e->mnt_type = &buf[type0];\n            buf[type1] = '\\0';\n            e->mnt_opts = &buf[opts0];\n            buf[opts1] = '\\0';\n            return e;\n        }\n    }\n    return NULL;\n}\n#endif\n\nstatic jobject newStructMntent(JNIEnv *env, const struct mntent *mntent) {\n    static jmethodID constructor = NULL;\n    if (!constructor) {\n        constructor = findMethod(env, getStructMntentClass(env), \"<init>\",\n                                 \"(Lme/zhanghai/android/files/provider/common/ByteString;\"\n                                 \"Lme/zhanghai/android/files/provider/common/ByteString;\"\n                                 \"Lme/zhanghai/android/files/provider/common/ByteString;\"\n                                 \"Lme/zhanghai/android/files/provider/common/ByteString;II)V\");\n    }\n    jobject mnt_fsname = newByteStringFromString(env, mntent->mnt_fsname);\n    if (!mnt_fsname) {\n        return NULL;\n    }\n    jobject mnt_dir = newByteStringFromString(env, mntent->mnt_dir);\n    if (!mnt_dir) {\n        return NULL;\n    }\n    jobject mnt_type = newByteStringFromString(env, mntent->mnt_type);\n    if (!mnt_type) {\n        return NULL;\n    }\n    jobject mnt_opts = newByteStringFromString(env, mntent->mnt_opts);\n    if (!mnt_opts) {\n        return NULL;\n    }\n    jint mnt_freq = mntent->mnt_freq;\n    jint mnt_passno = mntent->mnt_passno;\n    return (*env)->NewObject(env, getStructMntentClass(env), constructor, mnt_fsname, mnt_dir,\n            mnt_type, mnt_opts, mnt_freq, mnt_passno);\n}\n\nJNIEXPORT jobject JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_getmntent(\n        JNIEnv *env, jclass clazz, jlong javaFile) {\n    FILE *file = (FILE *) javaFile;\n#if __ANDROID_API__ >= __ANDROID_API_L_MR1__\n    // getmntent() in bionic is thread safe.\n    struct mntent *mntent = TEMP_FAILURE_RETRY_N(getmntent(file));\n#else\n    // getmntent() in bionic is a stub until API 22.\n    struct mntent entryBuffer = {};\n    char stringsBuffer[BUFSIZ] = {};\n    struct mntent *mntent = TEMP_FAILURE_RETRY_N(_getmntent_r(file, &entryBuffer, stringsBuffer,\n            sizeof(stringsBuffer)));\n#endif\n    if (errno) {\n        throwSyscallException(env, \"getmntent\");\n        return NULL;\n    }\n    if (!mntent) {\n        return NULL;\n    }\n    return newStructMntent(env, mntent);\n}\n\nstatic jobject newStructPasswd(JNIEnv *env, const struct passwd *passwd) {\n    static jmethodID constructor = NULL;\n    if (!constructor) {\n        constructor = findMethod(env, getStructPasswdClass(env), \"<init>\",\n                \"(Lme/zhanghai/android/files/provider/common/ByteString;II\"\n                \"Lme/zhanghai/android/files/provider/common/ByteString;\"\n                \"Lme/zhanghai/android/files/provider/common/ByteString;\"\n                \"Lme/zhanghai/android/files/provider/common/ByteString;)V\");\n    }\n    jobject pw_name;\n    if (passwd->pw_name) {\n        pw_name = newByteStringFromString(env, passwd->pw_name);\n        if (!pw_name) {\n            return NULL;\n        }\n    } else {\n        pw_name = NULL;\n    }\n    jint pw_uid = (jint) passwd->pw_uid;\n    jint pw_gid = (jint) passwd->pw_gid;\n#ifdef __LP64__\n    jobject pw_gecos;\n    if (passwd->pw_gecos) {\n        pw_gecos = newByteStringFromString(env, passwd->pw_gecos);\n        if (!pw_gecos) {\n            return NULL;\n        }\n    } else {\n        pw_gecos = NULL;\n    }\n#else\n    jobject pw_gecos = NULL;\n#endif\n    jobject pw_dir;\n    if (passwd->pw_dir) {\n        pw_dir = newByteStringFromString(env, passwd->pw_dir);\n        if (!pw_dir) {\n            return NULL;\n        }\n    } else {\n        pw_dir = NULL;\n    }\n    jobject pw_shell;\n    if (passwd->pw_shell) {\n        pw_shell = newByteStringFromString(env, passwd->pw_shell);\n        if (!pw_shell) {\n            return NULL;\n        }\n    } else {\n        pw_shell = NULL;\n    }\n    return (*env)->NewObject(env, getStructPasswdClass(env), constructor, pw_name, pw_uid, pw_gid,\n                             pw_gecos, pw_dir, pw_shell);\n}\n\nJNIEXPORT jobject JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_getpwent(JNIEnv *env, jclass clazz) {\n    while (true) {\n        // getpwent() in bionic is thread safe.\n        struct passwd *passwd = TEMP_FAILURE_RETRY_N(getpwent());\n        if (errno) {\n            throwSyscallException(env, \"getpwent\");\n            return NULL;\n        }\n        if (!passwd) {\n            return NULL;\n        }\n        if (passwd->pw_name[0] == 'o' && passwd->pw_name[1] == 'e' && passwd->pw_name[2] == 'm'\n            && passwd->pw_name[3] == '_') {\n            continue;\n        }\n        if (passwd->pw_name[0] == 'u' && passwd->pw_name[1] >= '0' && passwd->pw_name[1] <= '9') {\n            return NULL;\n        }\n        return newStructPasswd(env, passwd);\n    }\n}\n\nJNIEXPORT jobject JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_getpwnam(\n        JNIEnv *env, jclass clazz, jobject javaName) {\n    char *name = mallocStringFromByteString(env, javaName);\n    size_t bufferSize = (size_t) sysconf(_SC_GETPW_R_SIZE_MAX);\n    if (bufferSize == -1) {\n        // See `man 3 getpwnam`\n        bufferSize = 16384;\n    }\n    //char buffer[bufferSize] = {};\n    char buffer[bufferSize];\n    struct passwd passwd = {};\n    struct passwd *result = NULL;\n    errno = TEMP_FAILURE_RETRY_E(getpwnam_r(name, &passwd, buffer, bufferSize, &result));\n    free(name);\n    if (errno) {\n        throwSyscallException(env, \"getpwnam_r\");\n        return NULL;\n    }\n    if (!result) {\n        return NULL;\n    }\n    return newStructPasswd(env, result);\n}\n\nJNIEXPORT jobject JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_getpwuid(\n        JNIEnv *env, jclass clazz, jint javaUid) {\n    uid_t uid = (uid_t) javaUid;\n    size_t bufferSize = (size_t) sysconf(_SC_GETPW_R_SIZE_MAX);\n    if (bufferSize == -1) {\n        // See `man 3 getpwuid`\n        bufferSize = 16384;\n    }\n    //char buffer[bufferSize] = {};\n    char buffer[bufferSize];\n    struct passwd passwd = {};\n    struct passwd *result = NULL;\n    errno = TEMP_FAILURE_RETRY_E(getpwuid_r(uid, &passwd, buffer, bufferSize, &result));\n    if (errno) {\n        throwSyscallException(env, \"getpwnam_r\");\n        return NULL;\n    }\n    if (!result) {\n        return NULL;\n    }\n    return newStructPasswd(env, result);\n}\n\nstatic char *mallocMntOptsFromStructMntent(JNIEnv *env, jobject javaMntent) {\n    jobject javaOpts = (*env)->GetObjectField(env, javaMntent, getStructMntentMntOptsField(env));\n    return mallocStringFromByteString(env, javaOpts);\n}\n\n#if __ANDROID_API__ < __ANDROID_API_O__\nstatic char* _hasmntopt(const struct mntent* mnt, const char* opt) {\n    char* token = mnt->mnt_opts;\n    char* const end = mnt->mnt_opts + strlen(mnt->mnt_opts);\n    const size_t optLen = strlen(opt);\n    while (token) {\n        char* const tokenEnd = token + optLen;\n        if (tokenEnd > end) break;\n        if (memcmp(token, opt, optLen) == 0 &&\n            (*tokenEnd == '\\0' || *tokenEnd == ',' || *tokenEnd == '=')) {\n            return token;\n        }\n        token = strchr(token, ',');\n        if (token) token++;\n    }\n    return NULL;\n}\n#endif\n\nJNIEXPORT jboolean JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_hasmntopt(\n        JNIEnv *env, jclass clazz, jobject javaMntent, jobject javaOption) {\n    struct mntent mntent = {};\n    mntent.mnt_opts = mallocMntOptsFromStructMntent(env, javaMntent);\n    char *option = mallocStringFromByteString(env, javaOption);\n#if __ANDROID_API__ >= __ANDROID_API_O__\n    char *match = hasmntopt(&mntent, option);\n#else\n    char *match = _hasmntopt(&mntent, option);\n#endif\n    free(mntent.mnt_opts);\n    free(option);\n    bool hasOption = match != NULL;\n    return (jboolean) hasOption;\n}\n\nJNIEXPORT jint JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_inotify_1add_1watch(\n        JNIEnv *env, jclass clazz, jobject javaFd, jobject javaPath, jint javaMask) {\n    int fd = getFdFromFileDescriptor(env, javaFd);\n    char *path = mallocStringFromByteString(env, javaPath);\n    uint32_t mask = (uint32_t) javaMask;\n    int wd = TEMP_FAILURE_RETRY(inotify_add_watch(fd, path, mask));\n    free(path);\n    if (errno) {\n        throwSyscallException(env, \"inotify_add_watch\");\n        return 0;\n    }\n    return wd;\n}\n\nJNIEXPORT jobject JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_inotify_1init1(\n        JNIEnv *env, jclass clazz, jint javaFlags) {\n    int flags = javaFlags;\n    int fd = TEMP_FAILURE_RETRY(inotify_init1(flags));\n    if (errno) {\n        throwSyscallException(env, \"inotify_init1\");\n        return NULL;\n    }\n    return newFileDescriptor(env, fd);\n}\n\nstatic jobject newStructInotifyEvent(JNIEnv *env, const struct inotify_event *event) {\n    static jmethodID constructor = NULL;\n    if (!constructor) {\n        constructor = findMethod(env, getStructInotifyEventClass(env), \"<init>\",\n                                 \"(IIILme/zhanghai/android/files/provider/common/ByteString;)V\");\n    }\n    jint wd = event->wd;\n    jint mask = (jint) event->mask;\n    jint cookie = (jint) event->cookie;\n    jobject name;\n    size_t nameLength = strlen(event->name);\n    if (nameLength) {\n        name = newByteString(env, event->name, nameLength);\n        if (!name) {\n            return NULL;\n        }\n    } else {\n        name = NULL;\n    }\n    return (*env)->NewObject(env, getStructInotifyEventClass(env), constructor, wd, mask, cookie,\n            name);\n}\n\nJNIEXPORT jobjectArray JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_inotify_1get_1events(\n        JNIEnv *env, jclass clazz, jbyteArray javaBuffer, jint javaOffset, jint javaLength) {\n    void *buffer = (*env)->GetByteArrayElements(env, javaBuffer, NULL);\n    size_t offset = (size_t) javaOffset;\n    size_t length = (size_t) javaLength;\n    char *bufferStart = (char *) buffer + offset;\n    char *bufferEnd = bufferStart + length;\n    jsize javaEventsLength = 0;\n    for (char *eventStart = bufferStart; eventStart < bufferEnd; ) {\n        struct inotify_event *event = (struct inotify_event *) eventStart;\n        ++javaEventsLength;\n        eventStart += sizeof(struct inotify_event) + event->len;\n    }\n    jobjectArray javaEvents = (*env)->NewObjectArray(env, javaEventsLength,\n            getStructInotifyEventClass(env), NULL);\n    if (!javaEvents) {\n        (*env)->ReleaseByteArrayElements(env, javaBuffer, buffer, JNI_ABORT);\n        return NULL;\n    }\n    jsize javaIndex = 0;\n    for (char *eventStart = bufferStart; eventStart < bufferEnd; ) {\n        struct inotify_event *event = (struct inotify_event *) eventStart;\n        jobject javaEvent = newStructInotifyEvent(env, event);\n        if (!javaEvent) {\n            (*env)->DeleteLocalRef(env, javaEvents);\n            (*env)->ReleaseByteArrayElements(env, javaBuffer, buffer, JNI_ABORT);\n            return NULL;\n        }\n        (*env)->SetObjectArrayElement(env, javaEvents, javaIndex, javaEvent);\n        (*env)->DeleteLocalRef(env, javaEvent);\n        ++javaIndex;\n        eventStart += sizeof(struct inotify_event) + event->len;\n    }\n    (*env)->ReleaseByteArrayElements(env, javaBuffer, buffer, JNI_ABORT);\n    return javaEvents;\n}\n\nJNIEXPORT void JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_inotify_1rm_1watch(\n        JNIEnv *env, jclass clazz, jobject javaFd, jint javaWd) {\n    int fd = getFdFromFileDescriptor(env, javaFd);\n    uint32_t wd = (uint32_t) javaWd;\n    TEMP_FAILURE_RETRY(inotify_rm_watch(fd, wd));\n    if (errno) {\n        throwSyscallException(env, \"inotify_rm_watch\");\n    }\n}\n\nJNIEXPORT jint JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_ioctl_1int(\n        JNIEnv* env, jclass clazz, jobject javaFd, jint javaRequest, jobject javaArgument) {\n    int fd = getFdFromFileDescriptor(env, javaFd);\n    int request = javaRequest;\n    int argument = 0;\n    int* argumentPointer = NULL;\n    if (javaArgument) {\n        argument = (*env)->GetIntField(env, javaArgument, getInt32RefValueField(env));\n        argumentPointer = &argument;\n    }\n    int result = TEMP_FAILURE_RETRY(ioctl(fd, request, argumentPointer));\n    if (errno) {\n        throwSyscallException(env, \"ioctl\");\n        return 0;\n    }\n    if (javaArgument) {\n        (*env)->SetIntField(env, javaArgument, getInt32RefValueField(env), argument);\n    }\n    return result;\n}\n\nJNIEXPORT void JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_lchown(\n        JNIEnv *env, jclass clazz, jobject javaPath, jint javaUid, jint javaGid) {\n    char *path = mallocStringFromByteString(env, javaPath);\n    uid_t uid = (uid_t) javaUid;\n    gid_t gid = (gid_t) javaGid;\n    TEMP_FAILURE_RETRY(lchown(path, uid, gid));\n    free(path);\n    if (errno) {\n        throwSyscallException(env, \"lchown\");\n    }\n}\n\nJNIEXPORT jbyteArray JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_lgetxattr(\n        JNIEnv *env, jclass clazz, jobject javaPath, jobject javaName) {\n    char *path = mallocStringFromByteString(env, javaPath);\n    char *name = mallocStringFromByteString(env, javaName);\n    jbyteArray javaValue = NULL;\n    while (true) {\n        size_t size = (size_t) TEMP_FAILURE_RETRY(lgetxattr(path, name, NULL, 0));\n        if (errno) {\n            break;\n        }\n        //char value[size] = {};\n        char value[size];\n        TEMP_FAILURE_RETRY(lgetxattr(path, name, value, size));\n        if (errno) {\n            if (errno == ERANGE) {\n                // Attribute value changed since our last call to lgetxattr(), try again.\n                continue;\n            }\n            break;\n        }\n        jsize javaValueLength = (jsize) size;\n        javaValue = (*env)->NewByteArray(env, javaValueLength);\n        if (!javaValue) {\n            break;\n        }\n        const jbyte *javaValueBuffer = (const jbyte *) value;\n        (*env)->SetByteArrayRegion(env, javaValue, 0, javaValueLength, javaValueBuffer);\n        break;\n    }\n    free(path);\n    free(name);\n    if (errno) {\n        throwSyscallException(env, \"lgetxattr\");\n        return NULL;\n    }\n    return javaValue;\n}\n\nJNIEXPORT void JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_link(\n        JNIEnv *env, jclass clazz, jobject javaOldPath, jobject javaNewPath) {\n    char *oldPath = mallocStringFromByteString(env, javaOldPath);\n    char *newPath = mallocStringFromByteString(env, javaNewPath);\n    TEMP_FAILURE_RETRY(link(oldPath, newPath));\n    free(oldPath);\n    free(newPath);\n    if (errno) {\n        throwSyscallException(env, \"link\");\n    }\n}\n\nJNIEXPORT jobjectArray JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_llistxattr(\n        JNIEnv *env, jclass clazz, jobject javaPath) {\n    char *path = mallocStringFromByteString(env, javaPath);\n    jobjectArray javaNames = NULL;\n    while (true) {\n        size_t size = (size_t) TEMP_FAILURE_RETRY(llistxattr(path, NULL, 0));\n        if (errno) {\n            break;\n        }\n        //char names[size] = {};\n        char names[size];\n        TEMP_FAILURE_RETRY(llistxattr(path, names, size));\n        if (errno) {\n            if (errno == ERANGE) {\n                // Attribute value changed since our last call to llistxattr(), try again.\n                continue;\n            }\n            break;\n        }\n        jsize javaNamesLength = 0;\n        for (char *nameStart = names, *namesEnd = names + size; ; ) {\n            char *nameEnd = memchr(nameStart, '\\0', namesEnd - nameStart);\n            if (!nameEnd) {\n                break;\n            }\n            ++javaNamesLength;\n            nameStart = nameEnd + 1;\n        }\n        javaNames = (*env)->NewObjectArray(env, javaNamesLength, getByteStringClass(env), NULL);\n        if (!javaNames) {\n            break;\n        }\n        jsize nameIndex = 0;\n        for (char *nameStart = names, *namesEnd = names + size; ; ++nameIndex) {\n            char *nameEnd = memchr(nameStart, '\\0', namesEnd - nameStart);\n            if (!nameEnd) {\n                break;\n            }\n            jobject javaName = newByteStringFromString(env, nameStart);\n            if (!javaName) {\n                (*env)->DeleteLocalRef(env, javaNames);\n                javaNames = NULL;\n                break;\n            }\n            (*env)->SetObjectArrayElement(env, javaNames, nameIndex, javaName);\n            (*env)->DeleteLocalRef(env, javaName);\n            nameStart = nameEnd + 1;\n        }\n        break;\n    }\n    free(path);\n    if (errno) {\n        throwSyscallException(env, \"llistxattr\");\n        return NULL;\n    }\n    return javaNames;\n}\n\nJNIEXPORT void JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_lsetxattr(\n        JNIEnv *env, jclass clazz, jobject javaPath, jobject javaName, jbyteArray javaValue,\n        jint javaFlags) {\n    char *path = mallocStringFromByteString(env, javaPath);\n    char *name = mallocStringFromByteString(env, javaName);\n    void *value = (*env)->GetByteArrayElements(env, javaValue, NULL);\n    size_t size = (size_t) (*env)->GetArrayLength(env, javaValue);\n    int flags = javaFlags;\n    TEMP_FAILURE_RETRY(lsetxattr(path, name, value, size, flags));\n    free(path);\n    free(name);\n    (*env)->ReleaseByteArrayElements(env, javaValue, value, JNI_ABORT);\n    if (errno) {\n        throwSyscallException(env, \"lsetxattr\");\n    }\n}\n\nstatic jobject newStructTimespec(JNIEnv *env, const struct timespec *timespec) {\n    static jmethodID constructor = NULL;\n    if (!constructor) {\n        constructor = findMethod(env, getStructTimespecClass(env), \"<init>\", \"(JJ)V\");\n    }\n    jlong tv_sec = timespec->tv_sec;\n    jlong tv_nsec = timespec->tv_nsec;\n    return (*env)->NewObject(env, getStructTimespecClass(env), constructor, tv_sec, tv_nsec);\n}\n\nstatic jobject newStructStat(JNIEnv *env, const struct stat64 *stat) {\n    static jmethodID constructor = NULL;\n    if (!constructor) {\n        constructor = findMethod(env, getStructStatClass(env), \"<init>\", \"(JJIJIIJJJJ\"\n                \"Lme/zhanghai/android/files/provider/linux/syscall/StructTimespec;\"\n                \"Lme/zhanghai/android/files/provider/linux/syscall/StructTimespec;\"\n                \"Lme/zhanghai/android/files/provider/linux/syscall/StructTimespec;)V\");\n    }\n    jlong st_dev = (jlong) stat->st_dev;\n    jlong st_ino = (jlong) stat->st_ino;\n    jint st_mode = (jint) stat->st_mode;\n    jlong st_nlink = stat->st_nlink;\n    jint st_uid = (jint) stat->st_uid;\n    jint st_gid = (jint) stat->st_gid;\n    jlong st_rdev = (jlong) stat->st_rdev;\n    jlong st_size = stat->st_size;\n    jlong st_blksize = stat->st_blksize;\n    jlong st_blocks = (jlong) stat->st_blocks;\n    jobject st_atim = newStructTimespec(env, &stat->st_atim);\n    if (!st_atim) {\n        return NULL;\n    }\n    jobject st_mtim = newStructTimespec(env, &stat->st_mtim);\n    if (!st_mtim) {\n        return NULL;\n    }\n    jobject st_ctim = newStructTimespec(env, &stat->st_ctim);\n    if (!st_ctim) {\n        return NULL;\n    }\n    return (*env)->NewObject(env, getStructStatClass(env), constructor, st_dev, st_ino, st_mode,\n                             st_nlink, st_uid, st_gid, st_rdev, st_size, st_blksize, st_blocks,\n                             st_atim, st_mtim, st_ctim);\n}\n\nstatic jobject doStat(JNIEnv *env, jobject javaPath, bool isLstat) {\n    char *path = mallocStringFromByteString(env, javaPath);\n    struct stat64 stat = {};\n    TEMP_FAILURE_RETRY((isLstat ? lstat64 : stat64)(path, &stat));\n    free(path);\n    if (errno) {\n        throwSyscallException(env, isLstat ? \"lstat64\" : \"stat64\");\n        return NULL;\n    }\n    return newStructStat(env, &stat);\n}\n\nJNIEXPORT jobject JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_lstat(\n        JNIEnv *env, jclass clazz, jobject javaPath) {\n    return doStat(env, javaPath, true);\n}\n\nstatic void readStructTimespec(JNIEnv *env, jobject javaTime, struct timespec *time) {\n    time->tv_sec = (time_t) (*env)->GetLongField(env, javaTime, getStructTimespecTvSecField(env));\n    time->tv_nsec = (long) (*env)->GetLongField(env, javaTime, getStructTimespecTvNsecField(env));\n}\n\nJNIEXPORT void JNICALL\ndoUtimens(JNIEnv *env, jobject javaPath, jobjectArray javaTimes, bool isLutimens) {\n    char *path = mallocStringFromByteString(env, javaPath);\n    size_t timesSize = (size_t) (*env)->GetArrayLength(env, javaTimes);\n    //struct timespec times[timesSize] = {};\n    struct timespec times[timesSize];\n    for (size_t i = 0; i < timesSize; ++i) {\n        jsize javaTimeIndex = (jsize) i;\n        jobject javaTime = (*env)->GetObjectArrayElement(env, javaTimes, javaTimeIndex);\n        readStructTimespec(env, javaTime, &times[i]);\n        (*env)->DeleteLocalRef(env, javaTime);\n    }\n    TEMP_FAILURE_RETRY(utimensat(AT_FDCWD, path, times, isLutimens ? AT_SYMLINK_NOFOLLOW : 0));\n    free(path);\n    if (errno) {\n        throwSyscallException(env, \"utimensat\");\n    }\n}\n\nJNIEXPORT void JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_lutimens(\n        JNIEnv *env, jclass clazz, jobject javaPath, jobjectArray javaTimes) {\n    doUtimens(env, javaPath, javaTimes, true);\n}\n\nJNIEXPORT void JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_mkdir(\n        JNIEnv *env, jclass clazz, jobject javaPath, jint javaMode) {\n    char *path = mallocStringFromByteString(env, javaPath);\n    mode_t mode = (mode_t) javaMode;\n    TEMP_FAILURE_RETRY(mkdir(path, mode));\n    free(path);\n    if (errno) {\n        throwSyscallException(env, \"mkdir\");\n    }\n}\n\nJNIEXPORT jint JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_mount(\n        JNIEnv *env, jclass clazz, jobject javaSource, jobject javaTarget,\n        jobject javaFileSystemType, jlong javaMountFlags, jbyteArray javaData) {\n    if (geteuid() != 0) {\n        // Avoid getting killed by seccomp.\n        errno = EPERM;\n        throwSyscallException(env, \"mount\");\n        return 0;\n    }\n    char *source = javaSource ? mallocStringFromByteString(env, javaSource) : NULL;\n    char *target = mallocStringFromByteString(env, javaTarget);\n    char *fileSystemType = javaFileSystemType ? mallocStringFromByteString(env, javaFileSystemType)\n            : NULL;\n    unsigned long mountFlags = (unsigned long) javaMountFlags;\n    void *data = javaData ? (*env)->GetByteArrayElements(env, javaData, NULL) : NULL;\n    int result = TEMP_FAILURE_RETRY(mount(source, target, fileSystemType, mountFlags, data));\n    if (javaSource) {\n        free(source);\n    }\n    free(target);\n    if (javaFileSystemType) {\n        free(fileSystemType);\n    }\n    if (javaData) {\n        (*env)->ReleaseByteArrayElements(env, javaData, data, JNI_ABORT);\n    }\n    if (errno) {\n        throwSyscallException(env, \"mount\");\n        return 0;\n    }\n    return result;\n}\n\nJNIEXPORT jobject JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_open(\n        JNIEnv *env, jclass clazz, jobject javaPath, jint javaFlags, jint javaMode) {\n    char *path = mallocStringFromByteString(env, javaPath);\n    int flags = javaFlags;\n    mode_t mode = (mode_t) javaMode;\n    int fd = TEMP_FAILURE_RETRY(open(path, flags, mode));\n    free(path);\n    if (errno) {\n        throwSyscallException(env, \"open\");\n        return NULL;\n    }\n    return newFileDescriptor(env, fd);\n}\n\nJNIEXPORT jlong JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_opendir(\n        JNIEnv *env, jclass clazz, jobject javaPath) {\n    char *path = mallocStringFromByteString(env, javaPath);\n    DIR *dir = TEMP_FAILURE_RETRY_N(opendir(path));\n    free(path);\n    if (errno) {\n        throwSyscallException(env, \"opendir\");\n        return (jlong) NULL;\n    }\n    return (jlong) dir;\n}\n\nstatic jobject newStructDirent(JNIEnv *env, const struct dirent64 *dirent) {\n    static jmethodID constructor = NULL;\n    if (!constructor) {\n        constructor = findMethod(env, getStructDirentClass(env), \"<init>\",\n                                 \"(JJIILme/zhanghai/android/files/provider/common/ByteString;)V\");\n    }\n    jlong d_ino = (jlong) dirent->d_ino;\n    jlong d_off = dirent->d_off;\n    jint d_reclen = dirent->d_reclen;\n    jint d_type = dirent->d_type;\n    jobject d_name = newByteStringFromString(env, dirent->d_name);\n    if (!d_name) {\n        return NULL;\n    }\n    return (*env)->NewObject(env, getStructDirentClass(env), constructor, d_ino, d_off, d_reclen,\n                             d_type, d_name);\n}\n\nJNIEXPORT jobject JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_readdir(\n        JNIEnv *env, jclass clazz, jlong javaDir) {\n    DIR *dir = (DIR *) javaDir;\n    struct dirent64 *dirent = TEMP_FAILURE_RETRY_N(readdir64(dir));\n    if (errno) {\n        throwSyscallException(env, \"readdir64\");\n        return NULL;\n    }\n    if (!dirent) {\n        return NULL;\n    }\n    return newStructDirent(env, dirent);\n}\n\nJNIEXPORT jobject JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_readlink(\n        JNIEnv *env, jclass clazz, jobject javaPath) {\n    char *path = mallocStringFromByteString(env, javaPath);\n    size_t maxSize = PATH_MAX;\n    jobject javaTarget = NULL;\n    while (true) {\n        //char target[maxSize] = {};\n        char target[maxSize];\n        size_t size = (size_t) TEMP_FAILURE_RETRY(readlink(path, target, maxSize));\n        if (errno) {\n            break;\n        }\n        if (size >= maxSize) {\n            maxSize *= 2;\n            continue;\n        }\n        javaTarget = newByteString(env, target, size);\n        break;\n    }\n    free(path);\n    if (errno) {\n        throwSyscallException(env, \"readlink\");\n        return NULL;\n    }\n    return javaTarget;\n}\n\nJNIEXPORT jobject JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_realpath(\n        JNIEnv *env, jclass clazz, jobject javaPath) {\n    char *path = mallocStringFromByteString(env, javaPath);\n    char resolvedPath[PATH_MAX] = {};\n    TEMP_FAILURE_RETRY_N(realpath(path, resolvedPath));\n    free(path);\n    if (errno) {\n        throwSyscallException(env, \"realpath\");\n        return NULL;\n    }\n    return newByteStringFromString(env, resolvedPath);\n}\n\nJNIEXPORT void JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_remove(\n        JNIEnv *env, jclass clazz, jobject javaPath) {\n    char *path = mallocStringFromByteString(env, javaPath);\n    int result = TEMP_FAILURE_RETRY(remove(path));\n    free(path);\n    // This is a libc function and doesn't clear errno properly.\n    //if (errno) {\n    if (result) {\n        throwSyscallException(env, \"remove\");\n    }\n}\n\nJNIEXPORT void JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_rename(\n        JNIEnv *env, jclass clazz, jobject javaOldPath, jobject javaNewPath) {\n    char *oldPath = mallocStringFromByteString(env, javaOldPath);\n    char *newPath = mallocStringFromByteString(env, javaNewPath);\n    TEMP_FAILURE_RETRY(rename(oldPath, newPath));\n    free(oldPath);\n    free(newPath);\n    if (errno) {\n        throwSyscallException(env, \"rename\");\n    }\n}\n\nJNIEXPORT jlong JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_sendfile(\n        JNIEnv* env, jclass clazz, jobject javaOutFd, jobject javaInFd, jobject javaOffset,\n        jlong javaCount) {\n    int outFd = getFdFromFileDescriptor(env, javaOutFd);\n    int inFd = getFdFromFileDescriptor(env, javaInFd);\n    off64_t offset = 0;\n    off64_t* offsetPointer = NULL;\n    if (javaOffset) {\n        offset = (*env)->GetLongField(env, javaOffset, getInt64RefValueField(env));\n        offsetPointer = &offset;\n    }\n    size_t count = (size_t) javaCount;\n    long result = TEMP_FAILURE_RETRY(sendfile64(outFd, inFd, offsetPointer, count));\n    if (errno) {\n        throwSyscallException(env, \"sendfile64\");\n        return 0;\n    }\n    if (javaOffset) {\n        (*env)->SetLongField(env, javaOffset, getInt64RefValueField(env), offset);\n    }\n    return result;\n}\n\nJNIEXPORT void JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_setgrent(JNIEnv *env, jclass clazz) {\n    TEMP_FAILURE_RETRY_V(setgrent());\n    if (errno) {\n        throwSyscallException(env, \"setgrent\");\n    }\n}\n\nJNIEXPORT jlong JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_setmntent(\n        JNIEnv *env, jclass clazz, jobject javaPath, jobject javaMode) {\n    char *path = mallocStringFromByteString(env, javaPath);\n    char *mode = mallocStringFromByteString(env, javaMode);\n    FILE *file = TEMP_FAILURE_RETRY_N(setmntent(path, mode));\n    free(path);\n    free(mode);\n    if (errno) {\n        throwSyscallException(env, \"setmntent\");\n        return (jlong) NULL;\n    }\n    return (jlong) file;\n}\n\nJNIEXPORT void JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_setpwent(JNIEnv *env, jclass clazz) {\n    TEMP_FAILURE_RETRY_V(setpwent());\n    if (errno) {\n        throwSyscallException(env, \"setpwent\");\n    }\n}\n\nJNIEXPORT jobject JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_stat(\n        JNIEnv *env, jclass clazz, jobject javaPath) {\n    return doStat(env, javaPath, false);\n}\n\nstatic jobject newStructStatVfs(JNIEnv *env, const struct statvfs64 *statvfs) {\n    static jmethodID constructor = NULL;\n    if (!constructor) {\n        constructor = findMethod(env, getStructStatVfsClass(env), \"<init>\", \"(JJJJJJJJJJJ)V\");\n    }\n    jlong f_bsize = statvfs->f_bsize;\n    jlong f_frsize = statvfs->f_frsize;\n    jlong f_blocks = statvfs->f_blocks;\n    jlong f_bfree = statvfs->f_bfree;\n    jlong f_bavail = statvfs->f_bavail;\n    jlong f_files = statvfs->f_files;\n    jlong f_ffree = statvfs->f_ffree;\n    jlong f_favail = statvfs->f_favail;\n    jlong f_fsid = statvfs->f_fsid;\n    jlong f_flag = statvfs->f_flag;\n    jlong f_namemax = statvfs->f_namemax;\n    return (*env)->NewObject(env, getStructStatVfsClass(env), constructor, f_bsize, f_frsize,\n                             f_blocks, f_bfree, f_bavail, f_files, f_ffree, f_favail, f_fsid,\n                             f_flag, f_namemax);\n}\n\nJNIEXPORT jobject JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_statvfs(\n        JNIEnv *env, jclass clazz, jobject javaPath) {\n    char *path = mallocStringFromByteString(env, javaPath);\n    struct statvfs64 statvfs = {};\n    TEMP_FAILURE_RETRY(statvfs64(path, &statvfs));\n    free(path);\n    if (errno) {\n        throwSyscallException(env, \"statvfs64\");\n        return NULL;\n    }\n    return newStructStatVfs(env, &statvfs);\n}\n\nJNIEXPORT void JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_symlink(\n        JNIEnv *env, jclass clazz, jobject javaTarget, jobject javaLinkPath) {\n    char *target = mallocStringFromByteString(env, javaTarget);\n    char *linkPath = mallocStringFromByteString(env, javaLinkPath);\n    TEMP_FAILURE_RETRY(symlink(target, linkPath));\n    free(target);\n    free(linkPath);\n    if (errno) {\n        throwSyscallException(env, \"symlink\");\n    }\n}\n\nJNIEXPORT void JNICALL\nJava_me_zhanghai_android_files_provider_linux_syscall_Syscall_utimens(\n        JNIEnv *env, jclass clazz, jobject javaPath, jobjectArray javaTimes) {\n    doUtimens(env, javaPath, javaTimes, false);\n}\n"
  },
  {
    "path": "app/src/main/res/anim/list_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<set\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:interpolator=\"@android:interpolator/linear_out_slow_in\">\n\n    <alpha\n        android:duration=\"@android:integer/config_mediumAnimTime\"\n        android:fromAlpha=\"0\"\n        android:toAlpha=\"1\" />\n\n    <translate\n        android:duration=\"@android:integer/config_longAnimTime\"\n        android:fromYDelta=\"25%\"\n        android:toYDelta=\"0%\" />\n</set>\n"
  },
  {
    "path": "app/src/main/res/color/m3_dark_default_color_secondary_text.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<selector\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:ignore=\"PrivateResource\">\n    <item android:state_enabled=\"false\" android:alpha=\"@dimen/material_emphasis_disabled\" android:color=\"@color/m3_sys_color_dark_on_surface\" />\n    <item android:color=\"@color/m3_sys_color_dark_outline\" />\n</selector>\n"
  },
  {
    "path": "app/src/main/res/color/m3_default_color_secondary_text.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<selector\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:ignore=\"PrivateResource\">\n    <item android:state_enabled=\"false\" android:alpha=\"@dimen/material_emphasis_disabled\" android:color=\"@color/m3_sys_color_light_on_surface\" />\n    <item android:color=\"@color/m3_sys_color_light_outline\" />\n</selector>\n"
  },
  {
    "path": "app/src/main/res/color/navigation_item_subtitle_text_color.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"?colorPrimary\" android:state_checked=\"true\" />\n    <item android:alpha=\"0.38\" android:color=\"?android:textColorSecondary\" android:state_enabled=\"false\" />\n    <item android:color=\"?android:textColorSecondary\" />\n</selector>\n"
  },
  {
    "path": "app/src/main/res/color/navigation_item_subtitle_text_color_material3.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<selector\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:ignore=\"PrivateResource\">\n    <item android:alpha=\"@dimen/material_emphasis_disabled\" android:color=\"?colorOutline\" android:state_enabled=\"false\" />\n    <item android:color=\"?colorOnSecondaryContainer\" android:state_checked=\"true\" />\n    <item android:color=\"?colorOutline\" />\n</selector>\n"
  },
  {
    "path": "app/src/main/res/color/storage_name.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"?android:textColorPrimary\" android:state_activated=\"true\" />\n    <item android:color=\"?android:textColorSecondary\" />\n</selector>\n"
  },
  {
    "path": "app/src/main/res/color/switch_thumb_tint_dark_material3.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:alpha=\"?android:disabledAlpha\" android:color=\"?colorOutline\" android:state_checked=\"false\" android:state_enabled=\"false\" />\n    <item android:alpha=\"?android:disabledAlpha\" android:color=\"?colorPrimary\" android:state_enabled=\"false\" />\n    <item android:color=\"?colorOutline\" android:state_checked=\"false\" />\n    <item android:color=\"?colorPrimary\" />\n</selector>\n"
  },
  {
    "path": "app/src/main/res/color/switch_thumb_tint_light_material3.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:alpha=\"?android:disabledAlpha\" android:color=\"?colorSurfaceVariant\" android:state_checked=\"false\" android:state_enabled=\"false\" />\n    <item android:alpha=\"?android:disabledAlpha\" android:color=\"?colorPrimaryContainer\" android:state_enabled=\"false\" />\n    <item android:color=\"?colorSurfaceVariant\" android:state_checked=\"false\" />\n    <item android:color=\"?colorPrimaryContainer\" />\n</selector>\n"
  },
  {
    "path": "app/src/main/res/color/switch_track_tint_dark_material3.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:alpha=\"?android:disabledAlpha\" android:color=\"?colorSurfaceVariant\" android:state_checked=\"false\" android:state_enabled=\"false\" />\n    <item android:alpha=\"?android:disabledAlpha\" android:color=\"?colorPrimaryContainer\" android:state_enabled=\"false\" />\n    <item android:color=\"?colorSurfaceVariant\" android:state_checked=\"false\" />\n    <item android:color=\"?colorPrimaryContainer\" />\n</selector>\n"
  },
  {
    "path": "app/src/main/res/color/switch_track_tint_light_material3.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:alpha=\"?android:disabledAlpha\" android:color=\"?colorOutline\" android:state_checked=\"false\" android:state_enabled=\"false\" />\n    <item android:alpha=\"?android:disabledAlpha\" android:color=\"?colorPrimary\" android:state_enabled=\"false\" />\n    <item android:color=\"?colorOutline\" android:state_checked=\"false\" />\n    <item android:color=\"?colorPrimary\" />\n</selector>\n"
  },
  {
    "path": "app/src/main/res/color-v31/m3_dynamic_dark_default_color_secondary_text.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<selector\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:ignore=\"PrivateResource\">\n    <item android:state_enabled=\"false\" android:alpha=\"@dimen/material_emphasis_disabled\" android:color=\"@color/m3_sys_color_dynamic_dark_on_surface\" />\n    <item android:color=\"@color/m3_sys_color_dynamic_dark_outline\" />\n</selector>\n"
  },
  {
    "path": "app/src/main/res/color-v31/m3_dynamic_default_color_secondary_text.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<selector\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:ignore=\"PrivateResource\">\n    <item android:state_enabled=\"false\" android:alpha=\"@dimen/material_emphasis_disabled\" android:color=\"@color/m3_sys_color_dynamic_light_on_surface\"/>\n    <item android:color=\"@color/m3_sys_color_dynamic_light_outline\"/>\n</selector>\n"
  },
  {
    "path": "app/src/main/res/drawable/about_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M11 18h2v-2h-2v2zm1-16C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm0-14c-2.21 0-4 1.79-4 4h2c0-1.1 0.9-2 2-2s2 0.9 2 2c0 2-3 1.75-3 5h2c0-2.25 3-2.5 3-5 0-2.21-1.79-4-4-4z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/add_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/alarm_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M12.5 8H11v6l4.75 2.85 0.75-1.23-4-2.37zm4.837-6.19l4.607 3.845-1.28 1.535-4.61-3.843zm-10.674 0l1.282 1.536L3.337 7.19l-1.28-1.536zM12 4c-4.97 0-9 4.03-9 9s4.03 9 9 9 9-4.03 9-9-4.03-9-9-9zm0 16c-3.86 0-7-3.14-7-7s3.14-7 7-7 7 3.14 7 7-3.14 7-7 7z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/arrow_end_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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:autoMirrored=\"true\">\n\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/audio_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M12 3v10.55c-0.59-0.34-1.27-0.55-2-0.55-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4V7h4V3h-6zm-2 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/badge_background_18dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<shape\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"oval\">\n    <size android:width=\"18dp\" android:height=\"18dp\" />\n    <solid android:color=\"?colorSurface\" />\n</shape>\n"
  },
  {
    "path": "app/src/main/res/drawable/breadcrumb_item_foreground.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<ripple\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:color=\"?colorControlHighlight\">\n\n    <!-- android:gravity is unavailable until API 23. -->\n    <item\n        android:id=\"@android:id/mask\"\n        android:top=\"4dp\"\n        android:bottom=\"4dp\">\n        <shape>\n            <corners android:radius=\"20dp\" />\n            <size android:height=\"40dp\" />\n            <solid android:color=\"@android:color/white\" />\n        </shape>\n    </item>\n</ripple>\n"
  },
  {
    "path": "app/src/main/res/drawable/camera_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M20 4h-3.17L15 2H9L7.17 4H4c-1.1 0-2 0.9-2 2v12c0 1.1 0.9 2 2 2h16c1.1 0 2-0.9 2-2V6c0-1.1-0.9-2-2-2zm0 14H4V6h4.05l1.83-2h4.24l1.83 2H20v12zM12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zm0 8c-1.65 0-3-1.35-3-3s1.35-3 3-3 3 1.35 3 3-1.35 3-3 3z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/check_icon_control_normal_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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=\"?colorControlNormal\">\n\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/check_icon_on_primary_36dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"36dp\"\n    android:height=\"36dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?colorOnPrimary\">\n\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/checkable_badge_18dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<animated-selector\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:enterFadeDuration=\"@android:integer/config_shortAnimTime\"\n    android:exitFadeDuration=\"@android:integer/config_shortAnimTime\">\n\n    <item android:state_checked=\"true\" android:drawable=\"@drawable/checkable_badge_icon_18dp\" />\n\n    <item android:drawable=\"@android:color/transparent\" />\n</animated-selector>\n"
  },
  {
    "path": "app/src/main/res/drawable/checkable_badge_icon_18dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"18dp\"\n    android:height=\"18dp\"\n    android:viewportWidth=\"18\"\n    android:viewportHeight=\"18\">\n\n    <path\n        android:fillColor=\"?colorPrimary\"\n        android:pathData=\"M 9,0 C 4.032,0 0,4.032 0,9 c 0,4.968 4.032,9 9,9 4.968,0 9,-4.032 9,-9 C 18,4.032 13.968,0 9,0 Z\" />\n\n    <path\n        android:fillColor=\"?colorOnPrimary\"\n        android:pathData=\"M 13.959,4.207 L 7.199,10.965 4.041,7.811 2.627,9.225 7.201,13.793 15.373,5.621 Z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/close_icon_control_normal_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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=\"?colorControlNormal\">\n\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 12 19 6.41z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/close_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\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 12 19 6.41z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/color_preference_widget_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<shape\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"oval\">\n\n    <solid android:color=\"@android:color/white\" />\n</shape>\n"
  },
  {
    "path": "app/src/main/res/drawable/color_swatch_view_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item>\n        <shape android:shape=\"oval\">\n            <size android:width=\"48dp\" android:height=\"48dp\" />\n            <solid android:color=\"@android:color/white\" />\n        </shape>\n    </item>\n\n    <!-- An outline - we don't seem to need this though.\n    <item>\n        <shape android:shape=\"oval\">\n            <size android:width=\"48dp\" android:height=\"48dp\" />\n            <stroke android:color=\"?colorControlHighlight\" android:width=\"2dp\" />\n        </shape>\n    </item>\n    -->\n\n    <!-- android:gravity is only available on API 23+.\n    <item android:gravity=\"center\">\n    -->\n    <item\n        android:left=\"6dp\"\n        android:right=\"6dp\"\n        android:top=\"6dp\"\n        android:bottom=\"6dp\">\n        <animated-selector\n            android:enterFadeDuration=\"@android:integer/config_shortAnimTime\"\n            android:exitFadeDuration=\"@android:integer/config_shortAnimTime\">\n            <item android:state_checked=\"true\" android:drawable=\"@drawable/check_icon_on_primary_36dp\" />\n            <item>\n                <shape android:shape=\"oval\">\n                    <size android:width=\"36dp\" android:height=\"36dp\" />\n                </shape>\n            </item>\n        </animated-selector>\n    </item>\n\n    <item>\n        <ripple android:color=\"@color/mtrl_btn_ripple_color\">\n            <item android:id=\"@android:id/mask\">\n                <shape android:shape=\"oval\">\n                    <solid android:color=\"@android:color/white\" />\n                </shape>\n            </item>\n        </ripple>\n    </item>\n</layer-list>\n"
  },
  {
    "path": "app/src/main/res/drawable/computer_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M20 18c1.1 0 1.99-0.9 1.99-2L22 6c0-1.1-0.9-2-2-2H4c-1.1 0-2 0.9-2 2v10c0 1.1 0.9 2 2 2M4 6h16v10H4V6zm0.67 13H1v2h22v-2h-3.67\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/copy_icon_control_normal_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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=\"?colorControlNormal\">\n\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M16 1H4c-1.1 0-2 0.9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 0.9-2 2v14c0 1.1 0.9 2 2 2h11c1.1 0 2-0.9 2-2V7c0-1.1-0.9-2-2-2zm0 16H8V7h11v14z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/cut_icon_control_normal_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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=\"?colorControlNormal\">\n\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M9.64 7.64c0.23-0.5 0.36-1.05 0.36-1.64 0-2.21-1.79-4-4-4S2 3.79 2 6s1.79 4 4 4c0.59 0 1.14-0.13 1.64-0.36L10 12l-2.36 2.36C7.14 14.13 6.59 14 6 14c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4c0-0.59-0.13-1.14-0.36-1.64L12 14l7 7h3v-1L9.64 7.64zM6 8c-1.1 0-2-0.89-2-2s0.9-2 2-2 2 0.89 2 2-0.9 2-2 2zm0 12c-1.1 0-2-0.89-2-2s0.9-2 2-2 2 0.89 2 2-0.9 2-2 2zm6-7.5c-0.28 0-0.5-0.22-0.5-0.5s0.22-0.5 0.5-0.5 0.5 0.22 0.5 0.5-0.22 0.5-0.5 0.5zM19 3l-6 6 2 2 7-7V3h-3z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/delete_icon_control_normal_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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=\"?colorControlNormal\">\n\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M 17,8 V 20 H 7 V 8 H 17 M 14.5,1 h -5 l -1,1 H 4 V 4 H 20 V 2 H 15.5 Z M 19,6 H 5 v 14 c 0,1.1 0.9,2 2,2 h 10 c 1.1,0 2,-0.9 2,-2 z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/device_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<!-- phone_icon_white_24dp.xml -->\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M17 1.01L7 1c-1.1 0-2 0.9-2 2v18c0 1.1 0.9 2 2 2h10c1.1 0 2-0.9 2-2V3c0-1.1-0.9-1.99-2-1.99zM17 19H7V5h10v14z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/directory_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M9.17 6l2 2H20v10H4V6h5.17M10 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/directory_shortcut_icon_foreground.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n\n    <group\n        android:scaleX=\"1.8\"\n        android:scaleY=\"1.8\"\n        android:translateX=\"32.4\"\n        android:translateY=\"32.4\">\n        <path\n            android:fillColor=\"@color/color_primary_light\"\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    </group>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/document_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M19,5v14H5V5H19 M19,3H5C3.9,3,3,3.9,3,5v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V5C21,3.9,20.1,3,19,3L19,3zM14,17H7v-2h7V17z M17,13H7v-2h10V13z M17,9H7V7h10V9z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/download_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M19 12v7H5v-7H3v7c0 1.1 0.9 2 2 2h14c1.1 0 2-0.9 2-2v-7h-2zm-6 0.67l2.59-2.58L17 11.5l-5 5-5-5 1.41-1.41L11 12.67V3h2v9.67z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/downloads_shortcut_icon_foreground.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n\n    <group\n        android:scaleX=\"1.8\"\n        android:scaleY=\"1.8\"\n        android:translateX=\"32.4\"\n        android:translateY=\"32.4\">\n        <path\n            android:fillColor=\"@color/color_primary_light\"\n            android:pathData=\"M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z\" />\n    </group>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/drag_handle_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M20 9H4v2h16V9zM4 15h16v-2H4v2z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/edit_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@color/activity_icon_tint\"\n        android:pathData=\"M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.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/encrypted_badge_icon_18dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"18dp\"\n    android:height=\"18dp\"\n    android:viewportWidth=\"18\"\n    android:viewportHeight=\"18\">\n\n    <path\n        android:fillColor=\"@color/file_icon_grey\"\n        android:pathData=\"M9,0a9,9 0 0,0 0,18 9,9 0 0,0 0,-18zm0,2a7,7 0 0,1 0,14 7,7 0 0,1 0,-14zM12,7h-0.5v-1a2.5,2.5 0 0,0 -5,0v1h-0.5a1,1 0 0,0 -1,1v4.5a1,1 0 0,0 1,1h6a1,1 0 0,0 1,-1v-4.5a1,1 0 0,0 -1,-1zm-2,0h-2v-1a1,1 0 0,1 2,0zm-1,4.25a1,1 0 0,1 0,-2 1,1 0 0,1 0,2z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/error_badge_icon_18dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"18dp\"\n    android:height=\"18dp\"\n    android:viewportWidth=\"18\"\n    android:viewportHeight=\"18\">\n\n    <path\n        android:fillColor=\"@color/file_icon_grey\"\n        android:pathData=\"M 9 0 C 4.04 0 0 4.04 0 9 C 0 13.96 4.04 18 9 18 C 13.96 18 18 13.96 18 9 C 18 4.04 13.96 0 9 0 Z M 9 2 C 12.88 2 16 5.12 16 9 C 16 12.88 12.88 16 9 16 C 5.12 16 2 12.88 2 9 C 2 5.12 5.12 2 9 2 z M 5.89 4.47 L 4.47 5.89 L 7.59 9 L 4.47 12.11 L 5.89 13.53 L 9 10.41 L 12.11 13.53 L 13.53 12.11 L 10.41 9 L 13.53 5.89 L 12.11 4.47 L 9 7.59 L 5.89 4.47 Z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/error_icon_240dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"240dp\"\n    android:height=\"240dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n\n    <path\n        android:fillColor=\"?colorControlNormal\"\n        android:pathData=\"M11 15h2v2h-2v-2zm0-8h2v6h-2V7zm0.99-5C6.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/extract_icon_control_normal_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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=\"?colorControlNormal\">\n\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.27zM6.24 5h11.52l0.83 1H5.42l0.82-1zM5 19V8h14v11H5zm3-5h2.55v3h2.9v-3H16l-4-4z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/file_apk_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@color/file_icon_grey\"\n        android:pathData=\"M9 13c-0.55 0-1 0.45-1 1s0.45 1 1 1 1-0.45 1-1-0.45-1-1-1zm6 0c-0.55 0-1 0.45-1 1s0.45 1 1 1 1-0.45 1-1-0.45-1-1-1zm5-9H4c-1.1 0-2 0.9-2 2v12c0 1.1 0.9 2 2 2h16c1.1 0 2-0.9 2-2V6c0-1.1-0.9-2-2-2zm0 14H4v-2c0-2.55 1.19-4.81 3.05-6.27L5.15 7.2c-0.25-0.33-0.18-0.8 0.15 -1.05 0.33 -0.25 0.8 -0.18 1.05 0.15 l1.96 2.61C9.41 8.33 10.67 8 12 8s2.59 0.33 3.69 0.91 l1.96-2.61c0.25-0.33 0.72 -0.4 1.05-0.15 0.33 0.25 0.4 0.72 0.15 1.05l-1.89 2.53C18.81 11.19 20 13.45 20 16v2z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/file_archive_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@color/file_icon_grey\"\n        android:pathData=\"M19 3H5c-1.1 0,-2 0.9,-2 2v14c0 1.1 0.9 2 2 2h14c1.1 0 2,-0.9 2,-2V5c0,-1.1,-0.9,-2,-2,-2zm-5 6h-2v2h2v2h-2v-2h-2V9h2V7h-2V5h2v2h2v2zm0 8h-2v-2h-2v-2h2v2h2v2z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/file_audio_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@color/file_icon_purple\"\n        android:pathData=\"M12,3v10.55C11.41,13.21 10.73,13 10,13c-2.21,0 -4,1.79 -4,4c0,2.21 1.79,4 4,4s4,-1.79 4,-4V7h4V3H12zM10,19c-1.1,0 -2,-0.9 -2,-2c0,-1.1 0.9,-2 2,-2s2,0.9 2,2C12,18.1 11.1,19 10,19z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/file_calendar_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@color/file_icon_grey\"\n        android:pathData=\"M19,4h-1V2h-2v2H8V2H6v2H5C3.89,4,3.01,4.9,3.01,6L3,20c0,1.1,0.89,2,2,2h14c1.1,0,2-0.9,2-2V6C21,4.9,20.1,4,19,4z M19,20 L5,20V10h14V20zM14.5,13L14.5,13c-1.38,0-2.5,1.12-2.5,2.5v0c0,1.38,1.12,2.5,2.5,2.5h0c1.38,0,2.5-1.12,2.5-2.5v0 C17,14.12,15.88,13,14.5,13z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/file_certificate_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@color/file_icon_grey\"\n        android:pathData=\"M17.81 4.47c-0.08 0,-0.16,-0.02,-0.23,-0.06C15.66 3.42 14 3 12.01 3c-1.98 0,-3.86 0.47,-5.57 1.41,-0.24 0.13,-0.54 0.04,-0.68,-0.2,-0.13,-0.24,-0.04,-0.55 0.2,-0.68C7.82 2.52 9.86 2 12.01 2c2.13 0 3.99 0.47 6.03 1.52 0.25 0.13 0.34 0.43 0.21 0.67,-0.09 0.18,-0.26 0.28,-0.44 0.28zM3.5 9.72c-0.1 0,-0.2,-0.03,-0.29,-0.09,-0.23,-0.16,-0.28,-0.47,-0.12,-0.7 0.99,-1.4 2.25,-2.5 3.75,-3.27C9.98 4.04 14 4.03 17.15 5.65c1.5 0.77 2.76 1.86 3.75 3.25 0.16 0.22 0.11 0.54,-0.12 0.7,-0.23 0.16,-0.54 0.11,-0.7,-0.12,-0.9,-1.26,-2.04,-2.25,-3.39,-2.94,-2.87,-1.47,-6.54,-1.47,-9.4 0.01,-1.36 0.7,-2.5 1.7,-3.4 2.96,-0.08 0.14,-0.23 0.21,-0.39 0.21zm6.25 12.07c-0.13 0,-0.26,-0.05,-0.35,-0.15,-0.87,-0.87,-1.34,-1.43,-2.01,-2.64,-0.69,-1.23,-1.05,-2.73,-1.05,-4.34 0,-2.97 2.54,-5.39 5.66,-5.39s5.66 2.42 5.66 5.39c0 0.28,-0.22 0.5,-0.5 0.5s-0.5,-0.22,-0.5,-0.5c0,-2.42,-2.09,-4.39,-4.66,-4.39,-2.57 0,-4.66 1.97,-4.66 4.39 0 1.44 0.32 2.77 0.93 3.85 0.64 1.15 1.08 1.64 1.85 2.42 0.19 0.2 0.19 0.51 0 0.71,-0.11 0.1,-0.24 0.15,-0.37 0.15zm7.17,-1.85c-1.19 0,-2.24,-0.3,-3.1,-0.89,-1.49,-1.01,-2.38,-2.65,-2.38,-4.39 0,-0.28 0.22,-0.5 0.5,-0.5s0.5 0.22 0.5 0.5c0 1.41 0.72 2.74 1.94 3.56 0.71 0.48 1.54 0.71 2.54 0.71 0.24 0 0.64,-0.03 1.04,-0.1 0.27,-0.05 0.53 0.13 0.58 0.41 0.05 0.27,-0.13 0.53,-0.41 0.58,-0.57 0.11,-1.07 0.12,-1.21 0.12zM14.91 22c-0.04 0,-0.09,-0.01,-0.13,-0.02,-1.59,-0.44,-2.63,-1.03,-3.72,-2.1,-1.4,-1.39,-2.17,-3.24,-2.17,-5.22 0,-1.62 1.38,-2.94 3.08,-2.94 1.7 0 3.08 1.32 3.08 2.94 0 1.07 0.93 1.94 2.08 1.94s2.08,-0.87 2.08,-1.94c0,-3.77,-3.25,-6.83,-7.25,-6.83,-2.84 0,-5.44 1.58,-6.61 4.03,-0.39 0.81,-0.59 1.76,-0.59 2.8 0 0.78 0.07 2.01 0.67 3.61 0.1 0.26,-0.03 0.55,-0.29 0.64,-0.26 0.1,-0.55,-0.04,-0.64,-0.29,-0.49,-1.31,-0.73,-2.61,-0.73,-3.96 0,-1.2 0.23,-2.29 0.68,-3.24 1.33,-2.79 4.28,-4.6 7.51,-4.6 4.55 0 8.25 3.51 8.25 7.83 0 1.62,-1.38 2.94,-3.08 2.94s-3.08,-1.32,-3.08,-2.94c0,-1.07,-0.93,-1.94,-2.08,-1.94s-2.08 0.87,-2.08 1.94c0 1.71 0.66 3.31 1.87 4.51 0.95 0.94 1.86 1.46 3.27 1.85 0.27 0.07 0.42 0.35 0.35 0.61,-0.05 0.23,-0.26 0.38,-0.47 0.38z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/file_code_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@color/file_icon_grey\"\n        android:pathData=\"M9.4 16.6L4.8 12l4.6,-4.6L8 6l-6 6 6 6 1.4,-1.4zm5.2 0l4.6,-4.6,-4.6,-4.6L16 6l6 6,-6 6,-1.4,-1.4z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/file_contact_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@color/file_icon_grey\"\n        android:pathData=\"M12 6c1.1 0 2 0.9 2 2s-0.9 2-2 2-2-0.9-2-2 0.9-2 2-2m0 10c2.7 0 5.8 1.29 6 2H6c0.23-0.72 3.31-2 6-2m0-12C9.79 4 8 5.79 8 8s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 10c-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/file_directory_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@color/file_icon_grey\"\n        android:pathData=\"M9.17,6l2,2H20v10L4,18V6H9.17M10,4H4C2.9,4 2.01,4.9 2.01,6L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2h-8L10,4L10,4z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/file_directory_thumbnail.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"20dp\"\n    android:height=\"20dp\"\n    android:viewportWidth=\"20\"\n    android:viewportHeight=\"20\">\n\n    <path\n        android:fillColor=\"?colorControlNormal\"\n        android:pathData=\"M8,2H2C0.9,2 0,2.9 0,4v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6C20,4.9 19.1,4 18,4h-8z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/file_document_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@color/file_icon_blue\"\n        android:pathData=\"M19 3H5c-1.1 0,-2 0.9,-2 2v14c0 1.1 0.9 2 2 2h14c1.1 0 2,-0.9 2,-2V5c0,-1.1,-0.9,-2,-2,-2zm-1.99 6H7V7h10.01v2zm0 4H7v-2h10.01v2zm-3 4H7v-2h7.01v2z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/file_ebook_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@color/file_icon_blue\"\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-2zM9 4h2v5l-1-0.75L9 9V4zm9 16H6V4h1v9l3-2.25L13 13V4h5v16z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/file_email_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@color/file_icon_grey\"\n        android:pathData=\"M22 6c0-1.1-0.9-2-2-2H4c-1.1 0-2 0.9-2 2v12c0 1.1 0.9 2 2 2h16c1.1 0 2-0.9 2-2V6zm-2 0l-8 5-8-5h16zm0 12H4V8l8 5 8-5v10z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/file_excel_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@color/file_icon_green\"\n        android:pathData=\"M19 3H5c-1.1 0,-2 0.9,-2 2v14c0 1.1 0.9 2 2 2h14c1.1 0 2,-0.9 2,-2V5c0,-1.1,-0.9,-2,-2,-2zm-2.8 14h-2L12 13.2 9.8 17h-2l3.2,-5,-3.2,-5h2l2.2 3.8L14.2 7h2L13 12l3.2 5z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/file_font_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@color/file_icon_grey\"\n        android:pathData=\"M9.93 13.5h4.14L12 7.98zM20 2H4c-1.1 0,-2 0.9,-2 2v16c0 1.1 0.9 2 2 2h16c1.1 0 2,-0.9 2,-2V4c0,-1.1,-0.9,-2,-2,-2zm-4.05 16.5l-1.14,-3H9.17l-1.12 3H5.96l5.11,-13h1.86l5.11 13h-2.09z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/file_generic_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@color/file_icon_cyan\"\n        android:pathData=\"M14,2H6c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2H18c1.1,0 2,-0.9 2,-2V8l-6,-6zM6,20V4h7v5h5v11H6z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/file_icon_240dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"240dp\"\n    android:height=\"240dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n\n    <path\n        android:fillColor=\"?colorControlNormal\"\n        android:pathData=\"M14 2H6c-1.1 0-1.99 0.9-1.99 2L4 20c0 1.1 0.89 2 1.99 2H18c1.1 0 2-0.9 2-2V8l-6-6zM6 20V4h7v5h5v11H6z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/file_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M14 2H6c-1.1 0-1.99 0.9-1.99 2L4 20c0 1.1 0.89 2 1.99 2H18c1.1 0 2-0.9 2-2V8l-6-6zM6 20V4h7v5h5v11H6z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/file_image_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@color/file_icon_red\"\n        android:pathData=\"M19,5v14H5V5H19M19,3H5C3.9,3 3,3.9 3,5v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5C21,3.9 20.1,3 19,3L19,3zM14.14,11.86l-3,3.87L9,13.14L6,17h12L14.14,11.86L14.14,11.86z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/file_item_grid_foreground_material3.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<ripple\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:color=\"?colorControlHighlight\">\n\n    <item android:id=\"@android:id/mask\">\n        <shape android:shape=\"rectangle\">\n            <corners android:radius=\"12dp\" />\n            <solid android:color=\"@android:color/white\" />\n            <stroke android:color=\"@android:color/transparent\" android:width=\"8dp\" />\n        </shape>\n    </item>\n</ripple>\n"
  },
  {
    "path": "app/src/main/res/drawable/file_item_grid_thumbnail_outline.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <corners android:radius=\"4dp\" />\n    <stroke\n        android:color=\"@color/material_on_surface_stroke\"\n        android:width=\"1dp\" />\n</shape>\n"
  },
  {
    "path": "app/src/main/res/drawable/file_item_grid_thumbnail_outline_material3.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <corners android:radius=\"8dp\" />\n    <stroke\n        android:color=\"@color/material_on_surface_stroke\"\n        android:width=\"1dp\" />\n</shape>\n"
  },
  {
    "path": "app/src/main/res/drawable/file_pdf_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@color/file_icon_deep_red\"\n        android:pathData=\"M7 11.5h1v-1H7v1zM19 3H5c-1.1 0,-2 0.9,-2 2v14c0 1.1 0.9 2 2 2h14c1.1 0 2,-0.9 2,-2V5c0,-1.1,-0.9,-2,-2,-2zm-9.5 8.5c0 0.83,-0.67 1.5,-1.5 1.5H7v2H5.5V9H8c0.83 0 1.5 0.67 1.5 1.5v1zm10,-1H17v1h1.5V13H17v2h-1.5V9h4v1.5zm-5 3c0 0.83,-0.67 1.5,-1.5 1.5h-2.5V9H13c0.83 0 1.5 0.67 1.5 1.5v3zm-2.5 0h1v-3h-1v3z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/file_powerpoint_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@color/file_icon_orange\"\n        android:pathData=\"M19 3H5c-1.1 0,-2 0.9,-2 2v14c0 1.1 0.9 2 2 2h14c1.1 0 2,-0.9 2,-2V5c0,-1.1,-0.9,-2,-2,-2zM9.8 13.4V17H8V7h4.3c1.53 0 2.15 0.3 2.8 0.89 0.65 0.59 0.9 1.37 0.9 2.34 0 1.02,-0.26 1.8,-0.9 2.35s-1.3 0.82,-2.8 0.82H9.8zm0,-1.4V8.4h2.3c0.66 0 1.17 0.25 1.5 0.6 0.33 0.35 0.5 0.72 0.5 1.25 0 0.55,-0.18 0.95,-0.5 1.25,-0.32 0.31,-0.7 0.5,-1.38 0.5H9.8z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/file_presentation_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@color/file_icon_yellow\"\n        android:pathData=\"M19 3H5c-1.1 0,-1.99 0.9,-1.99 2v14c0 1.1 0.89 2 1.99 2h14c1.1 0 2,-0.9 2,-2V5c0,-1.1,-0.9,-2,-2,-2zm0 13H5V8h14v8z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/file_shortcut_icon_foreground.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n\n    <group\n        android:scaleX=\"1.8\"\n        android:scaleY=\"1.8\"\n        android:translateX=\"32.4\"\n        android:translateY=\"32.4\">\n        <path\n            android:fillColor=\"@color/color_primary_light\"\n            android:pathData=\"M6 2c-1.1 0-1.99 0.9-1.99 2L4 20c0 1.1 0.89 2 1.99 2H18c1.1 0 2-0.9 2-2V8l-6-6H6zm7 7V3.5L18.5 9H13z\" />\n    </group>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/file_spreadsheet_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@color/file_icon_deep_green\"\n        android:pathData=\"M19 3H5c-1.1 0,-1.99 0.9,-1.99 2L3 8v11c0 1.1 0.9 2 2 2h14c1.1 0 2,-0.9 2,-2V5c0,-1.1,-0.9,-2,-2,-2zm0 8h-8v8H9v-8H5V9h4V5h2v4h8v2z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/file_text_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@color/file_icon_cyan\"\n        android:pathData=\"M8,16h8v2L8,18zM8,12h8v2L8,14zM14,2L6,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.89,2 1.99,2L18,22c1.1,0 2,-0.9 2,-2L20,8l-6,-6zM18,20L6,20L6,4h7v5h5v11z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/file_video_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@color/file_icon_light_green\"\n        android:pathData=\"M4,6.47L5.76,10H20v8L4,18V6.47M22,4h-4l2,4h-3l-2,-4h-2l2,4h-3l-2,-4H8l2,4H7L5,4H4C2.9,4 2.01,4.9 2.01,6L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V4L22,4z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/file_word_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@color/file_icon_light_blue\"\n        android:pathData=\"M19 3H5c-1.1 0,-2 0.9,-2 2v14c0 1.1 0.9 2 2 2h14c1.1 0 2,-0.9 2,-2V5c0,-1.1,-0.9,-2,-2,-2zm-3.5 14H14l-2,-7.5,-2 7.5H8.5L6.1 7h1.7l1.54 7.51L11.3 7h1.4l1.97 7.51L16.2 7h1.7l-2.4 10z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/filter_icon_control_normal_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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=\"?colorControlNormal\">\n\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ftp_server_shortcut_icon_foreground.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n\n    <group\n        android:scaleX=\"1.8\"\n        android:scaleY=\"1.8\"\n        android:translateX=\"32.4\"\n        android:translateY=\"32.4\">\n        <path\n            android:fillColor=\"@color/color_primary_light\"\n            android:pathData=\"M20 6h-8l-2-2H4c-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-2zm-5 3c1.1 0 2 0.9 2 2s-0.9 2-2 2-2-0.9-2-2 0.9-2 2-2zm4 8h-8v-1c0-1.33 2.67-2 4-2s4 0.67 4 2v1z\" />\n    </group>\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/github_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M12,2A10,10 0 0,0 2,12C2,16.42 4.87,20.17 8.84,21.5C9.34,21.58 9.5,21.27 9.5,21C9.5,20.77 9.5,20.14 9.5,19.31C6.73,19.91 6.14,17.97 6.14,17.97C5.68,16.81 5.03,16.5 5.03,16.5C4.12,15.88 5.1,15.9 5.1,15.9C6.1,15.97 6.63,16.93 6.63,16.93C7.5,18.45 8.97,18 9.54,17.76C9.63,17.11 9.89,16.67 10.17,16.42C7.95,16.17 5.62,15.31 5.62,11.5C5.62,10.39 6,9.5 6.65,8.79C6.55,8.54 6.2,7.5 6.75,6.15C6.75,6.15 7.59,5.88 9.5,7.17C10.29,6.95 11.15,6.84 12,6.84C12.85,6.84 13.71,6.95 14.5,7.17C16.41,5.88 17.25,6.15 17.25,6.15C17.8,7.5 17.45,8.54 17.35,8.79C18,9.5 18.38,10.39 18.38,11.5C18.38,15.32 16.04,16.16 13.81,16.41C14.17,16.72 14.5,17.33 14.5,18.26C14.5,19.6 14.5,20.68 14.5,21C14.5,21.27 14.66,21.59 15.17,21.5C19.14,20.16 22,16.42 22,12A10,10 0 0,0 12,2Z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/image_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M19 5v14H5V5h14m0-2H5c-1.1 0-2 0.9-2 2v14c0 1.1 0.9 2 2 2h14c1.1 0 2-0.9 2-2V5c0-1.1-0.9-2-2-2zm-4.86 8.86l-3 3.87L9 13.14 6 17h12l-3.86-5.14z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/information_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\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 2zm0 18c-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/launcher_icon_monochrome.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"90dp\"\n    android:height=\"90dp\"\n    android:viewportWidth=\"90\"\n    android:viewportHeight=\"90\">\n\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"m 40.472,35.4 3.2,3.2 H 57.8 v 16 H 32.2 V 35.4 h 8.272 M 41.8,32.2 h -9.6 c -1.76,0 -3.184,1.44 -3.184,3.2 L 29,54.6 c 0,1.76 1.44,3.2 3.2,3.2 h 25.6 c 1.76,0 3.2,-1.44 3.2,-3.2 v -16 c 0,-1.76 -1.44,-3.2 -3.2,-3.2 H 45 Z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/lock_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 0.9-2 2v10c0 1.1 0.9 2 2 2h12c1.1 0 2-0.9 2-2V10c0-1.1-0.9-2-2-2zM9 6c0-1.66 1.34-3 3-3s3 1.34 3 3v2H9V6zm9 14H6V10h12v10zm-6-3c1.1 0 2-0.9 2-2s-0.9-2-2-2-2 0.9-2 2 0.9 2 2 2z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/m3_popupmenu_background_overlay.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!--\n  ~ Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <solid android:color=\"?colorSurface\" />\n\n    <corners\n        android:bottomLeftRadius=\"4dp\"\n        android:bottomRightRadius=\"4dp\"\n        android:topLeftRadius=\"4dp\"\n        android:topRightRadius=\"4dp\" />\n</shape>\n"
  },
  {
    "path": "app/src/main/res/drawable/menu_icon_control_normal_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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=\"?colorControlNormal\">\n\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/more_vertical_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\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 2zm0 2c-1.1 0-2 0.9-2 2s0.9 2 2 2 2-0.9 2-2-0.9-2-2-2zm0 6c-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/mtrl_popupmenu_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n  <solid android:color=\"?colorSurface\" />\n\n  <corners\n      android:bottomLeftRadius=\"4dp\"\n      android:bottomRightRadius=\"4dp\"\n      android:topLeftRadius=\"4dp\"\n      android:topRightRadius=\"4dp\" />\n</shape>\n"
  },
  {
    "path": "app/src/main/res/drawable/mtrl_popupmenu_background_overlay.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <solid android:color=\"?colorSurface\" />\n\n    <corners\n        android:bottomLeftRadius=\"4dp\"\n        android:bottomRightRadius=\"4dp\"\n        android:topLeftRadius=\"4dp\"\n        android:topRightRadius=\"4dp\" />\n</shape>\n"
  },
  {
    "path": "app/src/main/res/drawable/notification_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M9.17 6l2 2H20v10H4V6h5.17M10 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/notification_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M12 22c1.1 0 2-0.9 2-2h-4c0 1.1 0.9 2 2 2zm6-6v-5c0-3.07-1.63-5.64-4.5-6.32V4c0-0.83-0.67-1.5-1.5-1.5s-1.5 0.67-1.5 1.5v0.68C7.64 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2zm-2 1H8v-6c0-2.48 1.51-4.5 4-4.5s4 2.02 4 4.5v6z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/open_as_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@color/activity_icon_tint\"\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/paste_icon_control_normal_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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=\"?colorControlNormal\">\n\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M19 2h-4.18C14.4 0.84 13.3 0 12 0S9.6 0.84 9.18 2H5c-1.1 0-2 0.9-2 2v16c0 1.1 0.9 2 2 2h14c1.1 0 2-0.9 2-2V4c0-1.1-0.9-2-2-2zm-7 0c0.55 0 1 0.45 1 1s-0.45 1-1 1-1-0.45-1-1 0.45-1 1-1zm7 18H5V4h2v3h10V4h2v16z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/people_icon_control_normal_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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=\"?colorControlNormal\">\n\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M9 13.75c-2.34 0-7 1.17-7 3.5V19h14v-1.75c0-2.33-4.66-3.5-7-3.5zM4.34 17c0.84-0.58 2.87-1.25 4.66-1.25s3.82 0.67 4.66 1.25H4.34zM9 12c1.93 0 3.5-1.57 3.5-3.5S10.93 5 9 5 5.5 6.57 5.5 8.5 7.07 12 9 12zm0-5c0.83 0 1.5 0.67 1.5 1.5S9.83 10 9 10s-1.5-0.67-1.5-1.5S8.17 7 9 7zm7.04 6.81c1.16 0.84 1.96 1.96 1.96 3.44V19h4v-1.75c0-2.02-3.5-3.17-5.96-3.44zM15 12c1.93 0 3.5-1.57 3.5-3.5S16.93 5 15 5c-0.54 0-1.04 0.13-1.5 0.35 0.63 0.89 1 1.98 1 3.15s-0.37 2.26-1 3.15c0.46 0.22 0.96 0.35 1.5 0.35z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/person_icon_control_normal_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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=\"?colorControlNormal\">\n\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M12 6c1.1 0 2 0.9 2 2s-0.9 2-2 2-2-0.9-2-2 0.9-2 2-2m0 10c2.7 0 5.8 1.29 6 2H6c0.23-0.72 3.31-2 6-2m0-12C9.79 4 8 5.79 8 8s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 10c-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/person_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M12 6c1.1 0 2 0.9 2 2s-0.9 2-2 2-2-0.9-2-2 0.9-2 2-2m0 10c2.7 0 5.8 1.29 6 2H6c0.23-0.72 3.31-2 6-2m0-12C9.79 4 8 5.79 8 8s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 10c-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/podcast_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M14 12c0 0.74-0.4 1.38-1 1.72V22h-2v-8.28c-0.6-0.35-1-0.98-1-1.72 0-1.1 0.9-2 2-2s2 0.9 2 2zm-2-6c-3.31 0-6 2.69-6 6 0 1.74 0.75 3.31 1.94 4.4l1.42-1.42C8.53 14.25 8 13.19 8 12c0-2.21 1.79-4 4-4s4 1.79 4 4c0 1.19-0.53 2.25-1.36 2.98l1.42 1.42C17.25 15.31 18 13.74 18 12c0-3.31-2.69-6-6-6zm0-4C6.48 2 2 6.48 2 12c0 2.85 1.2 5.41 3.11 7.24l1.42-1.42C4.98 16.36 4 14.29 4 12c0-4.41 3.59-8 8-8s8 3.59 8 8c0 2.29-0.98 4.36-2.53 5.82l1.42 1.42C20.8 17.41 22 14.85 22 12c0-5.52-4.48-10-10-10z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/qq_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <!-- http://isux-10000718.cos.myqcloud.com/QQ_brandbook.pdf -->\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"m 12,4 c 1.652363,9.26e-5 2.47457,0.5649965 3.167969,1.5566406 0.693399,0.9916442 1.103515,2.5386584 1.103515,4.1445313 0,0.1633986 -0.01072,0.6057291 -0.01172,0.6464841 l -0.0098,0.41211 0.191406,0.476562 v 5.126953 c 0,0.536512 -0.387306,1.687972 -1.291015,2.451172 l -1.316407,1.111328 c -0.583489,-0.02879 -1.366444,-0.04295 -1.609375,-0.07031 L 12,19.830078 l -0.224609,0.02539 c -0.242988,0.02737 -1.026071,0.04152 -1.609375,0.07031 L 8.8496094,18.814453 C 7.9458962,18.051256 7.5585938,16.899804 7.5585938,16.363281 V 11.232422 L 7.75,10.755859 7.7402344,10.34375 c 3.791e-4,0.01418 -0.011719,-0.4788287 -0.011719,-0.6425781 0,-1.6058729 0.4101162,-3.1528874 1.1035156,-4.1445313 C 9.5254307,4.5649967 10.347631,4.0000926 12,4 Z m 3.2e-5,-2.0000017 c -4.4589498,2.5e-4 -6.2715398,3.86028 -6.2715398,7.70124 0,0.22806 0.011,0.6691297 0.0117,0.6953097 l -0.9004,2.24611 c -0.2465,0.64276 -0.48963,1.31278 -0.66797,1.88674 -0.85041,2.73674 -0.57501,3.86916 -0.36524,3.89456 0.45019,0.0544 1.75197,-2.06056 1.75197,-2.06056 0,1.2256 0.6334,2.82442 2.00001,3.97854 -0.51206,0.15726 -1.1385,0.39471 -1.54298,0.69337 -0.36114,0.26665 -0.31512,0.53865 -0.25,0.64844 0.28578,0.48216 4.9015098,0.30835 6.2344298,0.15821 1.33291,0.15014 5.9506,0.32395 6.23637,-0.15821 0.0651,-0.10979 0.10919,-0.38179 -0.25195,-0.64844 -0.40452,-0.29869 -1.0309,-0.53611 -1.54298,-0.69337 1.3666,-1.15412 2.00001,-2.75294 2.00001,-3.97854 0,0 1.30173,2.11499 1.75197,2.06056 0.20977,-0.0254 0.48513,-1.15782 -0.36524,-3.89456 -0.17834,-0.57396 -0.42147,-1.24398 -0.66797,-1.88674 l -0.9004,-2.24611 c 6.4e-4,-0.0262 0.0117,-0.4672497 0.0117,-0.6953097 0,-3.84096 -1.81259,-7.70099 -6.27153,-7.70124 z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/reset_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M 6.35,6.35 C 7.8,4.9 9.79,4 12,4 c 4.42,0 7.99,3.58 7.99,8 0,4.42 -3.57,8 -7.99,8 -3.73,0 -6.84,-2.55 -7.73,-6 h 2.08 c 0.82,2.33 3.04,4 5.65,4 3.31,0 6,-2.69 6,-6 C 18,8.69 15.31,6 12,6 10.34,6 8.86,6.69 7.78,7.78 L 11,11 H 4 V 4 Z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/ringtone_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M20.1 7.7l-1 1c1.8 1.8 1.8 4.6 0 6.5l1 1c2.5-2.3 2.5-6.1 0-8.5zM18 9.8l-1 1c0.5 0.7 0.5 1.6 0 2.3l1 1c1.2-1.2 1.2-3 0-4.3zM14 1H4c-1.1 0-2 0.9-2 2v18c0 1.1 0.9 2 2 2h10c1.1 0 2-0.9 2-2V3c0-1.1-0.9-2-2-2zm0 19H4V4h10v16z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/save_icon_control_normal_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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=\"?colorControlNormal\">\n\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M17 3H5c-1.11 0-2 0.9-2 2v14c0 1.1 0.89 2 2 2h14c1.1 0 2-0.9 2-2V7l-4-4zm2 16H5V5h11.17L19 7.83V19zm-7-7c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3zM6 6h9v4H6z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/sd_card_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M18 4v16H6V8.83L10.83 4H18m0-2h-8L4 8v12c0 1.1 0.9 2 2 2h12c1.1 0 2-0.9 2-2V4c0-1.1-0.9-2-2-2zM9 7h2v4H9zm3 0h2v4h-2zm3 0h2v4h-2z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/search_icon_control_normal_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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=\"?colorControlNormal\">\n\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-5zm-6 0C7.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/selectable_item_background_oval.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<ripple\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:color=\"?colorControlHighlight\">\n\n    <item android:id=\"@android:id/mask\">\n        <shape android:shape=\"oval\">\n            <solid android:color=\"@android:color/white\" />\n        </shape>\n    </item>\n</ripple>\n"
  },
  {
    "path": "app/src/main/res/drawable/settings_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\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.65zm-1.98-1.71c0.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-4zm0 6c-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/shared_directory_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M20 6h-8l-2-2H4c-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-2zm0 12H4V6h5.17l2 2H20v10zm-5-5c1.1 0 2-0.9 2-2s-0.9-2-2-2-2 0.9-2 2 0.9 2 2 2zm-4 4h8v-1c0-1.33-2.67-2-4-2s-4 0.67-4 2v1z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/sort_icon_control_normal_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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:autoMirrored=\"true\"\n    android:tint=\"?colorControlNormal\">\n\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M3 18h6v-2H3v2zM3 6v2h18V6H3zm0 7h12v-2H3v2z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/stop_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M16 8v8H8V8h8m2-2H6v12h12V6z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/symbolic_link_badge_icon_18dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"18dp\"\n    android:height=\"18dp\"\n    android:viewportWidth=\"18\"\n    android:viewportHeight=\"18\">\n\n    <path\n        android:fillColor=\"@color/file_icon_grey\"\n        android:pathData=\"M 9 0 C 4.04 0 0 4.04 0 9 C 0 13.96 4.04 18 9 18 C 13.96 18 18 13.96 18 9 C 18 4.04 13.96 0 9 0 Z M 9 2 C 12.88 2 16 5.12 16 9 C 16 12.88 12.88 16 9 16 C 5.12 16 2 12.88 2 9 C 2 5.12 5.12 2 9 2 z M 5.18 5.18 L 5.18 7.18 L 9.41 7.18 L 4.47 12.11 L 5.89 13.53 L 10.82 8.59 L 10.82 12.82 L 12.82 12.82 L 12.82 5.18 L 5.18 5.18 Z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/text_file_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M8 16h8v2H8zM8 12h8v2H8zM14 2H6c-1.1 0-2 0.9-2 2v16c0 1.1 0.89 2 1.99 2H18c1.1 0 2-0.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/tim_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <!-- com.tencent.tim -->\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"m 10.697317,4.5287356 h 2.605364 v 4.0613027 l 3.831418,-1.302682 0.842912,2.4521073 -3.831417,1.3026824 2.375479,3.21839 -2.068966,1.532568 L 11.999999,12.651341 9.547893,15.793104 7.478927,14.260536 9.854406,11.042146 6.022988,9.7394636 6.8659,7.2873563 10.697317,8.5900383 Z M 7.4785156,4 h 9.0429684 c 0.964296,0 1.677735,0.7134388 1.677735,1.6777344 v 8.7363276 c 0,0.964296 -0.713439,1.677735 -1.677735,1.677735 h -2.613281 v 1.943359 L 11.279297,16.091797 H 7.4785156 c -0.9642952,0 -1.6777343,-0.713439 -1.6777344,-1.677735 V 5.6777344 C 5.8007812,4.7134388 6.5142203,4 7.4785156,4 Z M 7.478927,2 C 5.441226,2 3.800766,3.6404598 3.800766,5.678161 v 8.735632 c 0,2.037701 1.64046,3.678161 3.678161,3.678161 H 10.62069 L 15.908046,22 v -3.908046 h 0.613027 c 2.037701,0 3.678161,-1.64046 3.678161,-3.678161 V 5.678161 C 20.199234,3.6404598 18.558774,2 16.521073,2 Z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/transparent_divider_vertical_16dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <size android:height=\"16dp\" />\n</shape>\n"
  },
  {
    "path": "app/src/main/res/drawable/twitter_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M22.46,6C21.69,6.35 20.86,6.58 20,6.69C20.88,6.16 21.56,5.32 21.88,4.31C21.05,4.81 20.13,5.16 19.16,5.36C18.37,4.5 17.26,4 16,4C13.65,4 11.73,5.92 11.73,8.29C11.73,8.63 11.77,8.96 11.84,9.27C8.28,9.09 5.11,7.38 3,4.79C2.63,5.42 2.42,6.16 2.42,6.94C2.42,8.43 3.17,9.75 4.33,10.5C3.62,10.5 2.96,10.3 2.38,10C2.38,10 2.38,10 2.38,10.03C2.38,12.11 3.86,13.85 5.82,14.24C5.46,14.34 5.08,14.39 4.69,14.39C4.42,14.39 4.15,14.36 3.89,14.31C4.43,16 6,17.26 7.89,17.29C6.43,18.45 4.58,19.13 2.56,19.13C2.22,19.13 1.88,19.11 1.54,19.07C3.44,20.29 5.7,21 8.12,21C16,21 20.33,14.46 20.33,8.79C20.33,8.6 20.33,8.42 20.32,8.23C21.16,7.63 21.88,6.87 22.46,6Z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/video_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M4 6.47L5.76 10H20v8H4V6.47M22 4h-4l2 4h-3l-2-4h-2l2 4h-3l-2-4H8l2 4H7L5 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-2V4z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable/wechat_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"m 15.792969,11.216797 c 2.504366,3.7e-5 4.205705,1.637476 4.207031,3.294922 -0.0018,0.84968 -0.41593,1.704127 -1.3125,2.375 l -0.447266,0.335937 -0.568359,0.226563 c -0.576119,0.230973 -1.222865,0.357361 -1.878906,0.359375 -2.505314,-3.7e-5 -4.205038,-1.636833 -4.205078,-3.294922 4e-5,-1.658089 1.699764,-3.296838 4.205078,-3.296875 z m 7.48e-4,-1.9998439 a 6.206392,5.2959007 0 0 0 -6.2062841,5.2957909 6.206392,5.2959007 0 0 0 6.2062841,5.295792 6.206392,5.2959007 0 0 0 2.622406,-0.502668 l 2.113812,1.243628 -0.644339,-2.061055 A 6.206392,5.2959007 0 0 0 22,14.512744 6.206392,5.2959007 0 0 0 15.793717,9.2169531 Z M 9.1152344,5.4511719 h 0.00195 c 1.6235086,0.0013 2.9801716,0.6843349 3.9062506,1.65625 -3.1771873,0.9612211 -5.5486621,3.4739241 -5.88086,6.7011721 -0.019715,-0.0072 -0.042892,-0.0044 -0.0625,-0.01172 L 6.3769531,13.533203 5.8398438,13.150391 C 4.6257813,12.285712 4.0002922,11.063873 4,9.8085938 4.0000713,7.5075377 6.1514706,5.4521874 9.1152344,5.4511719 Z m 0.00154,-2.000668 a 7.1169517,6.3582081 0 0 0 -7.1167758,6.358032 7.1169517,6.3582081 0 0 0 2.6804978,4.9703611 l -0.7231771,2.313575 2.4190871,-1.422644 a 7.1169517,6.3582081 0 0 0 2.740368,0.49674 7.1169517,6.3582081 0 0 0 0.139893,-0.0065 6.8117666,5.9012746 0 0 1 -0.274452,-1.647303 6.8117666,5.9012746 0 0 1 6.8115006,-5.9010071 6.8117666,5.9012746 0 0 1 0.311796,0.013041 7.1169517,6.3582081 0 0 0 -6.9887376,-5.1742737 z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable-desk/device_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<!-- computer_icon_white_24dp.xml -->\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M20 18c1.1 0 1.99-0.9 1.99-2L22 6c0-1.1-0.9-2-2-2H4c-1.1 0-2 0.9-2 2v10c0 1.1 0.9 2 2 2M4 6h16v10H4V6zm0.67 13H1v2h22v-2h-3.67\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable-sw600dp/device_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<!-- tablet_icon_white_24dp.xml -->\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M21 4H3c-1.1 0-2 0.9-2 2v12c0 1.1 0.9 2 2 2h18c1.1 0 1.99-0.9 1.99-2L23 6c0-1.1-0.9-2-2-2zm-2 14H5V6h14v12z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable-television/device_icon_white_24dp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<!-- tv_icon_white_24dp.xml -->\n<vector\n    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\n    <path\n        android:fillColor=\"@android:color/white\"\n        android:pathData=\"M21 3H3c-1.1 0-2 0.9-2 2v12c0 1.1 0.9 2 2 2h5v2h8v-2h5c1.1 0 1.99-0.9 1.99-2L23 5c0-1.1-0.9-2-2-2zm0 14H3V5h18v12z\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable-v23/breadcrumb_item_foreground.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<ripple\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:color=\"?colorControlHighlight\">\n\n    <item\n        android:id=\"@android:id/mask\"\n        android:gravity=\"center_vertical\">\n        <shape>\n            <corners android:radius=\"20dp\" />\n            <size android:height=\"40dp\" />\n            <solid android:color=\"@android:color/white\" />\n        </shape>\n    </item>\n</ripple>\n"
  },
  {
    "path": "app/src/main/res/drawable-v23/m3_popupmenu_background_overlay.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!--\n  ~ Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item>\n        <shape>\n            <solid android:color=\"?colorSurface\" />\n            <corners\n                android:bottomLeftRadius=\"4dp\"\n                android:bottomRightRadius=\"4dp\"\n                android:topLeftRadius=\"4dp\"\n                android:topRightRadius=\"4dp\" />\n        </shape>\n    </item>\n\n    <item>\n        <shape>\n            <solid android:color=\"@color/m3_popupmenu_overlay_color\" />\n            <corners\n                android:bottomLeftRadius=\"4dp\"\n                android:bottomRightRadius=\"4dp\"\n                android:topLeftRadius=\"4dp\"\n                android:topRightRadius=\"4dp\" />\n        </shape>\n    </item>\n</layer-list>\n"
  },
  {
    "path": "app/src/main/res/drawable-v23/mtrl_popupmenu_background_overlay.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item>\n        <shape>\n            <solid android:color=\"?colorSurface\" />\n            <corners\n                android:bottomLeftRadius=\"4dp\"\n                android:bottomRightRadius=\"4dp\"\n                android:topLeftRadius=\"4dp\"\n                android:topRightRadius=\"4dp\" />\n        </shape>\n    </item>\n\n    <item>\n        <shape>\n            <solid android:color=\"@color/mtrl_popupmenu_overlay_color\" />\n            <corners\n                android:bottomLeftRadius=\"4dp\"\n                android:bottomRightRadius=\"4dp\"\n                android:topLeftRadius=\"4dp\"\n                android:topRightRadius=\"4dp\" />\n        </shape>\n    </item>\n</layer-list>\n"
  },
  {
    "path": "app/src/main/res/layout/about_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<androidx.coordinatorlayout.widget.CoordinatorLayout\n    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:fitsSystemWindows=\"true\">\n\n    <me.zhanghai.android.files.ui.CoordinatorAppBarLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?colorAppBarSurface\"\n        android:theme=\"?actionBarTheme\"\n        app:liftOnScroll=\"true\"\n        app:liftOnScrollTargetViewId=\"@id/scrollView\">\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?actionBarSize\"\n            app:popupTheme=\"?actionBarPopupTheme\" />\n    </me.zhanghai.android.files.ui.CoordinatorAppBarLayout>\n\n    <me.zhanghai.android.files.ui.CoordinatorScrollingFrameLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <androidx.core.widget.NestedScrollView\n            android:id=\"@+id/scrollView\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:clipToPadding=\"false\"\n            android:scrollbars=\"vertical\">\n\n            <LinearLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:padding=\"@dimen/screen_edge_margin\"\n                android:clipToPadding=\"false\"\n                android:divider=\"@drawable/transparent_divider_vertical_16dp\"\n                android:orientation=\"vertical\"\n                android:showDividers=\"middle\">\n\n                <com.google.android.material.card.MaterialCardView\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    app:cardPreventCornerOverlap=\"false\">\n\n                    <LinearLayout\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:orientation=\"vertical\"\n                        android:paddingTop=\"@dimen/list_vertical_padding\"\n                        android:paddingBottom=\"@dimen/list_vertical_padding\">\n\n                        <LinearLayout\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"?listPreferredItemHeight\"\n                            android:paddingStart=\"?android:listPreferredItemPaddingStart\"\n                            android:paddingEnd=\"?android:listPreferredItemPaddingEnd\"\n                            android:gravity=\"center_vertical\"\n                            android:orientation=\"horizontal\">\n\n                            <ImageView\n                                android:layout_width=\"@dimen/large_icon_size\"\n                                android:layout_height=\"@dimen/large_icon_size\"\n                                android:scaleType=\"centerCrop\"\n                                android:src=\"@mipmap/launcher_icon\" />\n\n                            <TextView\n                                android:layout_width=\"wrap_content\"\n                                android:layout_height=\"wrap_content\"\n                                android:layout_marginStart=\"@dimen/content_start_from_screen_edge_margin_minus_40dp\"\n                                android:ellipsize=\"end\"\n                                android:maxLines=\"1\"\n                                android:text=\"@string/app_name\"\n                                android:textAppearance=\"@style/TextAppearance.AppCompat.Headline\" />\n                        </LinearLayout>\n\n                        <me.zhanghai.android.foregroundcompat.ForegroundLinearLayout\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:paddingStart=\"?android:listPreferredItemPaddingStart\"\n                            android:paddingEnd=\"?android:listPreferredItemPaddingEnd\"\n                            android:paddingTop=\"8dp\"\n                            android:paddingBottom=\"8dp\"\n                            android:clickable=\"true\"\n                            android:focusable=\"true\"\n                            android:foreground=\"?selectableItemBackground\"\n                            android:gravity=\"center_vertical\"\n                            android:orientation=\"horizontal\">\n\n                            <ImageView\n                                android:layout_width=\"@dimen/icon_size\"\n                                android:layout_height=\"@dimen/icon_size\"\n                                android:scaleType=\"centerCrop\"\n                                android:src=\"@drawable/information_icon_white_24dp\"\n                                app:tint=\"?colorControlNormal\" />\n\n                            <LinearLayout\n                                android:layout_width=\"0dp\"\n                                android:layout_weight=\"1\"\n                                android:layout_height=\"wrap_content\"\n                                android:gravity=\"center_vertical\"\n                                android:orientation=\"vertical\">\n\n                                <TextView\n                                    android:layout_width=\"wrap_content\"\n                                    android:layout_height=\"wrap_content\"\n                                    android:layout_marginStart=\"@dimen/content_start_from_screen_edge_margin_minus_24dp\"\n                                    android:ellipsize=\"end\"\n                                    android:maxLines=\"1\"\n                                    android:text=\"@string/about_version_title\"\n                                    android:textAppearance=\"?textAppearanceListItem\" />\n\n                                <TextView\n                                    android:layout_width=\"wrap_content\"\n                                    android:layout_height=\"wrap_content\"\n                                    android:layout_marginStart=\"@dimen/content_start_from_screen_edge_margin_minus_24dp\"\n                                    android:ellipsize=\"end\"\n                                    android:maxLines=\"1\"\n                                    android:text=\"@string/app_version\"\n                                    android:textAppearance=\"@style/TextAppearance.AppCompat.Caption\" />\n                            </LinearLayout>\n                        </me.zhanghai.android.foregroundcompat.ForegroundLinearLayout>\n\n                        <me.zhanghai.android.foregroundcompat.ForegroundLinearLayout\n                            android:id=\"@+id/gitHubLayout\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"?listPreferredItemHeightSmall\"\n                            android:paddingStart=\"?android:listPreferredItemPaddingStart\"\n                            android:paddingEnd=\"?android:listPreferredItemPaddingEnd\"\n                            android:foreground=\"?selectableItemBackground\"\n                            android:gravity=\"center_vertical\"\n                            android:orientation=\"horizontal\">\n\n                            <ImageView\n                                android:layout_width=\"@dimen/icon_size\"\n                                android:layout_height=\"@dimen/icon_size\"\n                                android:scaleType=\"centerCrop\"\n                                android:src=\"@drawable/github_icon_white_24dp\"\n                                app:tint=\"?colorControlNormal\" />\n\n                            <TextView\n                                android:layout_width=\"wrap_content\"\n                                android:layout_height=\"wrap_content\"\n                                android:layout_marginStart=\"@dimen/content_start_from_screen_edge_margin_minus_24dp\"\n                                android:ellipsize=\"end\"\n                                android:maxLines=\"1\"\n                                android:text=\"@string/about_github_title\"\n                                android:textAppearance=\"?textAppearanceListItem\" />\n                        </me.zhanghai.android.foregroundcompat.ForegroundLinearLayout>\n\n                        <me.zhanghai.android.foregroundcompat.ForegroundLinearLayout\n                            android:id=\"@+id/licensesLayout\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"?listPreferredItemHeightSmall\"\n                            android:paddingStart=\"?android:listPreferredItemPaddingStart\"\n                            android:paddingEnd=\"?android:listPreferredItemPaddingEnd\"\n                            android:foreground=\"?selectableItemBackground\"\n                            android:gravity=\"center_vertical\"\n                            android:orientation=\"horizontal\">\n\n                            <ImageView\n                                android:layout_width=\"@dimen/icon_size\"\n                                android:layout_height=\"@dimen/icon_size\"\n                                android:scaleType=\"centerCrop\"\n                                android:src=\"@drawable/text_file_icon_white_24dp\"\n                                app:tint=\"?colorControlNormal\" />\n\n                            <TextView\n                                android:layout_width=\"wrap_content\"\n                                android:layout_height=\"wrap_content\"\n                                android:layout_marginStart=\"@dimen/content_start_from_screen_edge_margin_minus_24dp\"\n                                android:ellipsize=\"end\"\n                                android:maxLines=\"1\"\n                                android:text=\"@string/about_licenses_title\"\n                                android:textAppearance=\"?textAppearanceListItem\" />\n                        </me.zhanghai.android.foregroundcompat.ForegroundLinearLayout>\n\n                        <me.zhanghai.android.foregroundcompat.ForegroundLinearLayout\n                            android:id=\"@+id/privacyPolicyLayout\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"?listPreferredItemHeightSmall\"\n                            android:paddingStart=\"?android:listPreferredItemPaddingStart\"\n                            android:paddingEnd=\"?android:listPreferredItemPaddingEnd\"\n                            android:foreground=\"?selectableItemBackground\"\n                            android:gravity=\"center_vertical\"\n                            android:orientation=\"horizontal\"\n                            android:visibility=\"gone\">\n\n                            <ImageView\n                                android:layout_width=\"@dimen/icon_size\"\n                                android:layout_height=\"@dimen/icon_size\"\n                                android:scaleType=\"centerCrop\"\n                                android:src=\"@drawable/lock_icon_white_24dp\"\n                                app:tint=\"?colorControlNormal\" />\n\n                            <TextView\n                                android:layout_width=\"wrap_content\"\n                                android:layout_height=\"wrap_content\"\n                                android:layout_marginStart=\"@dimen/content_start_from_screen_edge_margin_minus_24dp\"\n                                android:ellipsize=\"end\"\n                                android:maxLines=\"1\"\n                                android:text=\"@string/about_privacy_policy_title\"\n                                android:textAppearance=\"?textAppearanceListItem\" />\n                        </me.zhanghai.android.foregroundcompat.ForegroundLinearLayout>\n                    </LinearLayout>\n                </com.google.android.material.card.MaterialCardView>\n\n                <com.google.android.material.card.MaterialCardView\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    app:cardPreventCornerOverlap=\"false\">\n\n                    <LinearLayout\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:orientation=\"vertical\"\n                        android:paddingTop=\"@dimen/list_vertical_padding\"\n                        android:paddingBottom=\"@dimen/list_vertical_padding\">\n\n                        <LinearLayout\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"?listPreferredItemHeightSmall\"\n                            android:paddingStart=\"?android:listPreferredItemPaddingStart\"\n                            android:paddingEnd=\"?android:listPreferredItemPaddingEnd\"\n                            android:gravity=\"center_vertical\"\n                            android:orientation=\"horizontal\">\n\n                            <TextView\n                                android:layout_width=\"wrap_content\"\n                                android:layout_height=\"wrap_content\"\n                                android:ellipsize=\"end\"\n                                android:maxLines=\"1\"\n                                android:text=\"@string/about_author_title\"\n                                android:textAppearance=\"@style/TextAppearance.AppCompat.Body2\"\n                                android:textColor=\"?android:textColorSecondary\" />\n                        </LinearLayout>\n\n                        <me.zhanghai.android.foregroundcompat.ForegroundLinearLayout\n                            android:id=\"@+id/authorNameLayout\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"?listPreferredItemHeightSmall\"\n                            android:paddingStart=\"?android:listPreferredItemPaddingStart\"\n                            android:paddingEnd=\"?android:listPreferredItemPaddingEnd\"\n                            android:clickable=\"true\"\n                            android:focusable=\"true\"\n                            android:foreground=\"?selectableItemBackground\"\n                            android:gravity=\"center_vertical\"\n                            android:orientation=\"horizontal\">\n\n                            <ImageView\n                                android:layout_width=\"@dimen/icon_size\"\n                                android:layout_height=\"@dimen/icon_size\"\n                                android:scaleType=\"centerCrop\"\n                                android:src=\"@drawable/person_icon_white_24dp\"\n                                app:tint=\"?colorControlNormal\" />\n\n                            <TextView\n                                android:layout_width=\"wrap_content\"\n                                android:layout_height=\"wrap_content\"\n                                android:layout_marginStart=\"@dimen/content_start_from_screen_edge_margin_minus_24dp\"\n                                android:ellipsize=\"end\"\n                                android:maxLines=\"1\"\n                                android:text=\"@string/about_author_name_title\"\n                                android:textAppearance=\"?textAppearanceListItem\" />\n                        </me.zhanghai.android.foregroundcompat.ForegroundLinearLayout>\n\n                        <me.zhanghai.android.foregroundcompat.ForegroundLinearLayout\n                            android:id=\"@+id/authorGitHubLayout\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"?listPreferredItemHeightSmall\"\n                            android:paddingStart=\"?android:listPreferredItemPaddingStart\"\n                            android:paddingEnd=\"?android:listPreferredItemPaddingEnd\"\n                            android:foreground=\"?selectableItemBackground\"\n                            android:gravity=\"center_vertical\"\n                            android:orientation=\"horizontal\">\n\n                            <ImageView\n                                android:layout_width=\"@dimen/icon_size\"\n                                android:layout_height=\"@dimen/icon_size\"\n                                android:scaleType=\"centerCrop\"\n                                android:src=\"@drawable/github_icon_white_24dp\"\n                                app:tint=\"?colorControlNormal\" />\n\n                            <TextView\n                                android:layout_width=\"wrap_content\"\n                                android:layout_height=\"wrap_content\"\n                                android:layout_marginStart=\"@dimen/content_start_from_screen_edge_margin_minus_24dp\"\n                                android:ellipsize=\"end\"\n                                android:maxLines=\"1\"\n                                android:text=\"@string/about_author_github_title\"\n                                android:textAppearance=\"?textAppearanceListItem\" />\n                        </me.zhanghai.android.foregroundcompat.ForegroundLinearLayout>\n\n                        <me.zhanghai.android.foregroundcompat.ForegroundLinearLayout\n                            android:id=\"@+id/authorTwitterLayout\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"?listPreferredItemHeightSmall\"\n                            android:paddingStart=\"?android:listPreferredItemPaddingStart\"\n                            android:paddingEnd=\"?android:listPreferredItemPaddingEnd\"\n                            android:foreground=\"?selectableItemBackground\"\n                            android:gravity=\"center_vertical\"\n                            android:orientation=\"horizontal\">\n\n                            <ImageView\n                                android:layout_width=\"@dimen/icon_size\"\n                                android:layout_height=\"@dimen/icon_size\"\n                                android:scaleType=\"centerCrop\"\n                                android:src=\"@drawable/twitter_icon_white_24dp\"\n                                app:tint=\"?colorControlNormal\" />\n\n                            <TextView\n                                android:layout_width=\"wrap_content\"\n                                android:layout_height=\"wrap_content\"\n                                android:layout_marginStart=\"@dimen/content_start_from_screen_edge_margin_minus_24dp\"\n                                android:ellipsize=\"end\"\n                                android:maxLines=\"1\"\n                                android:text=\"@string/about_author_twitter_title\"\n                                android:textAppearance=\"?textAppearanceListItem\" />\n                        </me.zhanghai.android.foregroundcompat.ForegroundLinearLayout>\n                    </LinearLayout>\n                </com.google.android.material.card.MaterialCardView>\n            </LinearLayout>\n        </androidx.core.widget.NestedScrollView>\n    </me.zhanghai.android.files.ui.CoordinatorScrollingFrameLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/add_lan_smb_server_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<androidx.coordinatorlayout.widget.CoordinatorLayout\n    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:fitsSystemWindows=\"true\">\n\n    <me.zhanghai.android.files.ui.CoordinatorAppBarLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?colorAppBarSurface\"\n        android:theme=\"?actionBarTheme\"\n        app:liftOnScroll=\"true\"\n        app:liftOnScrollTargetViewId=\"@id/recyclerView\">\n\n        <FrameLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\">\n\n            <androidx.appcompat.widget.Toolbar\n                android:id=\"@+id/toolbar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"?actionBarSize\"\n                app:popupTheme=\"?actionBarPopupTheme\" />\n\n            <com.google.android.material.progressindicator.LinearProgressIndicator\n                android:id=\"@+id/progress\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"bottom\"\n                android:indeterminate=\"true\" />\n        </FrameLayout>\n    </me.zhanghai.android.files.ui.CoordinatorAppBarLayout>\n\n    <me.zhanghai.android.files.ui.CoordinatorScrollingLinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <androidx.swiperefreshlayout.widget.ThemedSwipeRefreshLayout\n            android:id=\"@+id/swipeRefreshLayout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\">\n\n            <androidx.recyclerview.widget.RecyclerView\n                android:id=\"@+id/recyclerView\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:clipToPadding=\"false\"\n                android:scrollbars=\"vertical\" />\n        </androidx.swiperefreshlayout.widget.ThemedSwipeRefreshLayout>\n    </me.zhanghai.android.files.ui.CoordinatorScrollingLinearLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/archive_password_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<FrameLayout\n    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:paddingTop=\"@dimen/abc_dialog_title_divider_material\">\n\n    <com.google.android.material.textfield.TextInputLayout\n        android:id=\"@+id/passwordLayout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"?dialogPreferredPadding\"\n        android:layout_marginEnd=\"?dialogPreferredPadding\"\n        android:layout_marginTop=\"4dp\"\n        app:passwordToggleEnabled=\"true\">\n\n        <com.google.android.material.textfield.TextInputEditText\n            android:id=\"@+id/passwordEdit\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:inputType=\"textPassword\" />\n    </com.google.android.material.textfield.TextInputLayout>\n</FrameLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/bookmark_directory_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<me.zhanghai.android.foregroundcompat.ForegroundLinearLayout\n    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=\"@dimen/two_line_list_item_height\"\n    android:background=\"?android:colorBackground\"\n    android:foreground=\"?selectableItemBackground\"\n    android:gravity=\"center_vertical\"\n    android:orientation=\"horizontal\">\n\n    <ImageView\n        android:layout_width=\"@dimen/icon_size\"\n        android:layout_height=\"@dimen/icon_size\"\n        android:layout_marginStart=\"@dimen/screen_edge_margin\"\n        android:layout_marginEnd=\"@dimen/content_start_from_screen_edge_margin_minus_24dp\"\n        android:scaleType=\"centerCrop\"\n        android:src=\"@drawable/directory_icon_white_24dp\"\n        app:tint=\"?colorControlNormal\"/>\n\n    <LinearLayout\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_weight=\"1\"\n        android:orientation=\"vertical\">\n\n        <!-- Lint says android:ellipsize=\"middle\" and android:maxLines=\"1\" crashes. -->\n        <TextView\n            android:id=\"@+id/nameText\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"end\"\n            android:maxLines=\"1\"\n            android:textAppearance=\"?textAppearanceListItem\" />\n\n        <TextView\n            android:id=\"@+id/pathText\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"end\"\n            android:maxLines=\"1\"\n            android:textAppearance=\"?textAppearanceListItemSecondary\"\n            android:textColor=\"?android:textColorSecondary\" />\n    </LinearLayout>\n\n    <ImageView\n        android:id=\"@+id/dragHandleView\"\n        android:layout_width=\"@dimen/touch_target_size\"\n        android:layout_height=\"@dimen/touch_target_size\"\n        android:layout_marginStart=\"@dimen/content_start_from_screen_edge_margin_minus_36dp_considering_at_least_8dp\"\n        android:layout_marginEnd=\"@dimen/screen_edge_margin_minus_12dp_at_least_8dp\"\n        android:padding=\"@dimen/touch_target_icon_padding\"\n        android:src=\"@drawable/drag_handle_icon_white_24dp\"\n        app:tint=\"?colorControlNormal\" />\n</me.zhanghai.android.foregroundcompat.ForegroundLinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/bookmark_directory_list_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<androidx.coordinatorlayout.widget.CoordinatorLayout\n    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:fitsSystemWindows=\"true\">\n\n    <me.zhanghai.android.files.ui.CoordinatorAppBarLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?colorAppBarSurface\"\n        android:theme=\"?actionBarTheme\"\n        app:liftOnScroll=\"true\"\n        app:liftOnScrollTargetViewId=\"@id/recyclerView\">\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?actionBarSize\"\n            app:popupTheme=\"?actionBarPopupTheme\" />\n    </me.zhanghai.android.files.ui.CoordinatorAppBarLayout>\n\n    <me.zhanghai.android.files.ui.CoordinatorScrollingFrameLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <TextView\n            android:id=\"@+id/emptyView\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:text=\"@string/settings_bookmark_directory_list_empty\"\n            android:textAppearance=\"?textAppearanceListItem\"\n            android:visibility=\"gone\" />\n\n        <androidx.recyclerview.widget.RecyclerView\n            android:id=\"@+id/recyclerView\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:paddingBottom=\"@dimen/list_bottom_padding_with_fab\"\n            android:clipToPadding=\"false\"\n            android:fitsSystemWindows=\"true\"\n            android:scrollbarStyle=\"outsideOverlay\"\n            android:scrollbars=\"vertical\" />\n\n        <!-- Additional layout to receive window insets as margin. -->\n        <FrameLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"end|bottom\">\n\n            <com.google.android.material.floatingactionbutton.FloatingActionButton\n                android:id=\"@+id/fab\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_margin=\"@dimen/screen_edge_margin\"\n                android:src=\"@drawable/add_icon_white_24dp\"\n                android:theme=\"?actionBarTheme\" />\n        </FrameLayout>\n    </me.zhanghai.android.files.ui.CoordinatorScrollingFrameLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/breadcrumb_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<me.zhanghai.android.foregroundcompat.ForegroundLinearLayout\n    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=\"match_parent\"\n    android:paddingStart=\"12dp\"\n    android:paddingEnd=\"4dp\"\n    android:foreground=\"@drawable/breadcrumb_item_foreground\"\n    android:gravity=\"center_vertical\"\n    android:orientation=\"horizontal\">\n\n    <TextView\n        android:id=\"@+id/text\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginEnd=\"8dp\"\n        android:duplicateParentState=\"true\"\n        android:ellipsize=\"end\"\n        android:maxLines=\"1\"\n        android:textAppearance=\"@style/TextAppearance.Design.Tab\"\n        app:textAllCaps=\"false\" />\n\n    <ImageView\n        android:id=\"@+id/arrowImage\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"-4dp\"\n        android:duplicateParentState=\"true\"\n        android:src=\"@drawable/arrow_end_icon_white_24dp\" />\n</me.zhanghai.android.foregroundcompat.ForegroundLinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/color_picker_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:paddingTop=\"@dimen/abc_dialog_title_divider_material\">\n\n    <GridView\n        android:id=\"@+id/palette\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:paddingStart=\"?dialogPreferredPadding\"\n        android:paddingEnd=\"?dialogPreferredPadding\"\n        android:paddingTop=\"8dp\"\n        android:paddingBottom=\"8dp\"\n        android:choiceMode=\"singleChoice\"\n        android:clipToPadding=\"false\"\n        android:columnWidth=\"48dp\"\n        android:horizontalSpacing=\"16dp\"\n        android:listSelector=\"@android:color/transparent\"\n        android:numColumns=\"auto_fit\"\n        android:scrollIndicators=\"top|bottom\"\n        android:scrollbarStyle=\"outsideOverlay\"\n        android:stretchMode=\"spacingWidth\"\n        android:verticalSpacing=\"16dp\" />\n</FrameLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/color_preference_widget.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<View\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/swatch\"\n    android:layout_width=\"36dp\"\n    android:layout_height=\"36dp\"\n    android:background=\"@drawable/color_preference_widget_background\" />\n"
  },
  {
    "path": "app/src/main/res/layout/create_archive_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<FrameLayout\n    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:paddingTop=\"@dimen/abc_dialog_title_divider_material\">\n\n    <androidx.core.widget.NestedScrollView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:scrollbars=\"vertical\"\n        android:scrollIndicators=\"top|bottom\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"vertical\">\n\n            <include layout=\"@layout/name_dialog_name_include\" />\n\n            <me.zhanghai.android.files.filelist.CreateArchiveTypeRadioGroup\n                android:id=\"@+id/typeGroup\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"8dp\"\n                android:paddingStart=\"@dimen/dialog_padding_minus_6dp\"\n                android:paddingEnd=\"@dimen/dialog_padding\"\n                android:orientation=\"horizontal\">\n\n                <RadioButton\n                    android:id=\"@+id/zipRadio\"\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"wrap_content\"\n                    android:minHeight=\"@dimen/dense_single_line_list_item_height\"\n                    android:layout_weight=\"1\"\n                    android:text=\"@string/file_create_archive_type_zip\"\n                    android:textAppearance=\"?textAppearanceListItem\" />\n\n                <RadioButton\n                    android:id=\"@+id/tarXzRadio\"\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"wrap_content\"\n                    android:minHeight=\"@dimen/dense_single_line_list_item_height\"\n                    android:layout_weight=\"1\"\n                    android:text=\"@string/file_create_archive_type_tar_xz\"\n                    android:textAppearance=\"?textAppearanceListItem\" />\n\n                <RadioButton\n                    android:id=\"@+id/sevenZRadio\"\n                    android:layout_width=\"0dp\"\n                    android:layout_height=\"wrap_content\"\n                    android:minHeight=\"@dimen/dense_single_line_list_item_height\"\n                    android:layout_weight=\"1\"\n                    android:text=\"@string/file_create_archive_type_7z\"\n                    android:textAppearance=\"?textAppearanceListItem\"\n                    android:visibility=\"@integer/create_archive_type_seven_z_visibility\" />\n            </me.zhanghai.android.files.filelist.CreateArchiveTypeRadioGroup>\n\n            <com.google.android.material.textfield.TextInputLayout\n                android:id=\"@+id/passwordLayout\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginStart=\"?dialogPreferredPadding\"\n                android:layout_marginEnd=\"?dialogPreferredPadding\"\n                android:layout_marginTop=\"4dp\"\n                android:hint=\"@string/file_create_archive_password\"\n                app:passwordToggleEnabled=\"true\">\n\n                <com.google.android.material.textfield.TextInputEditText\n                    android:id=\"@+id/passwordEdit\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:inputType=\"textPassword\" />\n            </com.google.android.material.textfield.TextInputLayout>\n        </LinearLayout>\n    </androidx.core.widget.NestedScrollView>\n</FrameLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/dropdown_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<TextView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:minHeight=\"?listPreferredItemHeightSmall\"\n    android:paddingStart=\"?listPreferredItemPaddingStart\"\n    android:paddingEnd=\"?listPreferredItemPaddingEnd\"\n    android:ellipsize=\"end\"\n    android:gravity=\"center_vertical\"\n    android:maxLines=\"1\"\n    android:textAppearance=\"?textAppearanceListItem\" />\n"
  },
  {
    "path": "app/src/main/res/layout/edit_bookmark_directory_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:paddingTop=\"@dimen/abc_dialog_title_divider_material\">\n\n    <androidx.core.widget.NestedScrollView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:scrollbars=\"vertical\"\n        android:scrollIndicators=\"top|bottom\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:paddingStart=\"?dialogPreferredPadding\"\n            android:paddingEnd=\"?dialogPreferredPadding\"\n            android:paddingTop=\"8dp\"\n            android:paddingBottom=\"8dp\"\n            android:orientation=\"vertical\">\n\n            <com.google.android.material.textfield.TextInputLayout\n                android:id=\"@+id/nameLayout\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:hint=\"@string/navigation_edit_bookmark_directory_name\">\n\n                <com.google.android.material.textfield.TextInputEditText\n                    android:id=\"@+id/nameEdit\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:importantForAutofill=\"no\"\n                    android:inputType=\"text\">\n                    <requestFocus />\n                </com.google.android.material.textfield.TextInputEditText>\n            </com.google.android.material.textfield.TextInputLayout>\n\n            <me.zhanghai.android.files.ui.ReadOnlyTextInputLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"16dp\"\n                android:hint=\"@string/navigation_edit_bookmark_directory_path\">\n\n                <me.zhanghai.android.files.ui.ReadOnlyTextInputEditText\n                    android:id=\"@+id/pathText\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\" />\n            </me.zhanghai.android.files.ui.ReadOnlyTextInputLayout>\n        </LinearLayout>\n    </androidx.core.widget.NestedScrollView>\n</FrameLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/edit_device_storage_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:paddingTop=\"@dimen/abc_dialog_title_divider_material\">\n\n    <androidx.core.widget.NestedScrollView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:scrollbars=\"vertical\"\n        android:scrollIndicators=\"top|bottom\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:paddingStart=\"?dialogPreferredPadding\"\n            android:paddingEnd=\"?dialogPreferredPadding\"\n            android:paddingTop=\"8dp\"\n            android:paddingBottom=\"8dp\"\n            android:orientation=\"vertical\">\n\n            <com.google.android.material.textfield.TextInputLayout\n                android:id=\"@+id/nameLayout\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:hint=\"@string/storage_edit_device_storage_name\">\n\n                <com.google.android.material.textfield.TextInputEditText\n                    android:id=\"@+id/nameEdit\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:importantForAutofill=\"no\"\n                    android:inputType=\"text\">\n                    <requestFocus />\n                </com.google.android.material.textfield.TextInputEditText>\n            </com.google.android.material.textfield.TextInputLayout>\n\n            <me.zhanghai.android.files.ui.ReadOnlyTextInputLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"16dp\"\n                android:hint=\"@string/storage_edit_device_storage_path\">\n\n                <me.zhanghai.android.files.ui.ReadOnlyTextInputEditText\n                    android:id=\"@+id/pathText\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:textIsSelectable=\"true\" />\n            </me.zhanghai.android.files.ui.ReadOnlyTextInputLayout>\n        </LinearLayout>\n    </androidx.core.widget.NestedScrollView>\n</FrameLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/edit_document_tree_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:paddingTop=\"@dimen/abc_dialog_title_divider_material\">\n\n    <androidx.core.widget.NestedScrollView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:scrollbars=\"vertical\"\n        android:scrollIndicators=\"top|bottom\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:paddingStart=\"?dialogPreferredPadding\"\n            android:paddingEnd=\"?dialogPreferredPadding\"\n            android:paddingTop=\"8dp\"\n            android:paddingBottom=\"8dp\"\n            android:orientation=\"vertical\">\n\n            <com.google.android.material.textfield.TextInputLayout\n                android:id=\"@+id/nameLayout\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:hint=\"@string/storage_edit_document_tree_name\">\n\n                <com.google.android.material.textfield.TextInputEditText\n                    android:id=\"@+id/nameEdit\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:importantForAutofill=\"no\"\n                    android:inputType=\"text\">\n                    <requestFocus />\n                </com.google.android.material.textfield.TextInputEditText>\n            </com.google.android.material.textfield.TextInputLayout>\n\n            <me.zhanghai.android.files.ui.ReadOnlyTextInputLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"16dp\"\n                android:hint=\"@string/storage_edit_document_tree_uri\">\n\n                <me.zhanghai.android.files.ui.ReadOnlyTextInputEditText\n                    android:id=\"@+id/uriText\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:textIsSelectable=\"true\" />\n            </me.zhanghai.android.files.ui.ReadOnlyTextInputLayout>\n\n            <me.zhanghai.android.files.ui.ReadOnlyTextInputLayout\n                android:id=\"@+id/pathLayout\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"16dp\"\n                android:hint=\"@string/storage_edit_document_tree_path\">\n\n                <me.zhanghai.android.files.ui.ReadOnlyTextInputEditText\n                    android:id=\"@+id/pathText\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:textIsSelectable=\"true\" />\n            </me.zhanghai.android.files.ui.ReadOnlyTextInputLayout>\n        </LinearLayout>\n    </androidx.core.widget.NestedScrollView>\n</FrameLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/edit_external_storage_shortcut_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<FrameLayout\n    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:paddingTop=\"@dimen/abc_dialog_title_divider_material\">\n\n    <androidx.core.widget.NestedScrollView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:scrollbars=\"vertical\"\n        android:scrollIndicators=\"top|bottom\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:paddingStart=\"?dialogPreferredPadding\"\n            android:paddingEnd=\"?dialogPreferredPadding\"\n            android:paddingTop=\"8dp\"\n            android:orientation=\"vertical\">\n\n            <com.google.android.material.textfield.TextInputLayout\n                android:id=\"@+id/nameLayout\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:hint=\"@string/storage_edit_document_tree_name\"\n                app:errorEnabled=\"true\"\n                app:expandedHintEnabled=\"false\">\n\n                <com.google.android.material.textfield.TextInputEditText\n                    android:id=\"@+id/nameEdit\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:importantForAutofill=\"no\"\n                    android:inputType=\"text\">\n                    <requestFocus />\n                </com.google.android.material.textfield.TextInputEditText>\n            </com.google.android.material.textfield.TextInputLayout>\n\n            <com.google.android.material.textfield.TextInputLayout\n                android:id=\"@+id/rootIdLayout\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:hint=\"@string/storage_edit_external_storage_shortcut_root_id\"\n                app:errorEnabled=\"true\">\n\n                <com.google.android.material.textfield.TextInputEditText\n                    android:id=\"@+id/rootIdEdit\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:importantForAutofill=\"no\"\n                    android:inputType=\"text\" />\n            </com.google.android.material.textfield.TextInputLayout>\n\n            <com.google.android.material.textfield.TextInputLayout\n                android:id=\"@+id/pathLayout\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:hint=\"@string/storage_edit_external_storage_shortcut_path\">\n\n                <com.google.android.material.textfield.TextInputEditText\n                    android:id=\"@+id/pathEdit\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:importantForAutofill=\"no\"\n                    android:inputType=\"textUri\" />\n            </com.google.android.material.textfield.TextInputLayout>\n        </LinearLayout>\n    </androidx.core.widget.NestedScrollView>\n</FrameLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/edit_ftp_server_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<androidx.coordinatorlayout.widget.CoordinatorLayout\n    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:fitsSystemWindows=\"true\">\n\n    <me.zhanghai.android.files.ui.CoordinatorAppBarLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?colorAppBarSurface\"\n        android:theme=\"?actionBarTheme\"\n        app:liftOnScroll=\"true\"\n        app:liftOnScrollTargetViewId=\"@id/scrollView\">\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?actionBarSize\"\n            app:popupTheme=\"?actionBarPopupTheme\" />\n    </me.zhanghai.android.files.ui.CoordinatorAppBarLayout>\n\n    <me.zhanghai.android.files.ui.CoordinatorScrollingLinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <FrameLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"0dp\"\n            android:layout_weight=\"1\">\n\n            <ProgressBar\n                android:id=\"@+id/progress\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center\"\n                android:visibility=\"gone\" />\n\n            <androidx.core.widget.NestedScrollView\n                android:id=\"@+id/scrollView\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:scrollbars=\"vertical\"\n                android:scrollIndicators=\"bottom\">\n\n                <LinearLayout\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:paddingStart=\"@dimen/screen_edge_margin\"\n                    android:paddingEnd=\"@dimen/screen_edge_margin\"\n                    android:paddingTop=\"@dimen/screen_edge_margin\"\n                    android:paddingBottom=\"@dimen/screen_edge_margin_minus_16dp\"\n                    android:animateLayoutChanges=\"true\"\n                    android:orientation=\"vertical\">\n\n                    <com.google.android.material.textfield.TextInputLayout\n                        android:id=\"@+id/hostLayout\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:hint=\"@string/storage_edit_ftp_server_host\"\n                        app:errorEnabled=\"true\">\n\n                        <com.google.android.material.textfield.TextInputEditText\n                            android:id=\"@+id/hostEdit\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:inputType=\"textUri\">\n                            <requestFocus />\n                        </com.google.android.material.textfield.TextInputEditText>\n                    </com.google.android.material.textfield.TextInputLayout>\n\n                    <com.google.android.material.textfield.TextInputLayout\n                        android:id=\"@+id/portLayout\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:hint=\"@string/storage_edit_ftp_server_port\"\n                        app:errorEnabled=\"true\"\n                        app:expandedHintEnabled=\"false\"\n                        app:placeholderText=\"@string/storage_edit_ftp_server_port_placeholder\">\n\n                        <com.google.android.material.textfield.TextInputEditText\n                            android:id=\"@+id/portEdit\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:inputType=\"number\" />\n                    </com.google.android.material.textfield.TextInputLayout>\n\n                    <com.google.android.material.textfield.TextInputLayout\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:hint=\"@string/storage_edit_ftp_server_path\"\n                        app:errorEnabled=\"true\"\n                        app:expandedHintEnabled=\"false\"\n                        app:placeholderText=\"@string/storage_edit_ftp_server_path_placeholder\">\n\n                        <com.google.android.material.textfield.TextInputEditText\n                            android:id=\"@+id/pathEdit\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:inputType=\"textUri\" />\n                    </com.google.android.material.textfield.TextInputLayout>\n\n                    <com.google.android.material.textfield.TextInputLayout\n                        android:id=\"@+id/nameLayout\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:hint=\"@string/storage_edit_ftp_server_name\"\n                        app:errorEnabled=\"true\"\n                        app:expandedHintEnabled=\"false\"\n                        app:placeholderText=\"@string/storage_edit_ftp_server_name_placeholder\">\n\n                        <com.google.android.material.textfield.TextInputEditText\n                            android:id=\"@+id/nameEdit\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:importantForAutofill=\"no\"\n                            android:inputType=\"text\" />\n                    </com.google.android.material.textfield.TextInputLayout>\n\n                    <com.google.android.material.textfield.TextInputLayout\n                        android:id=\"@+id/protocolLayout\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:hint=\"@string/storage_edit_ftp_server_protocol\"\n                        app:errorEnabled=\"true\"\n                        app:expandedHintEnabled=\"false\"\n                        style=\"?textInputOutlinedExposedDropdownMenuStyle\">\n\n                        <AutoCompleteTextView\n                            android:id=\"@+id/protocolEdit\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:inputType=\"none\" />\n                    </com.google.android.material.textfield.TextInputLayout>\n\n                    <com.google.android.material.textfield.TextInputLayout\n                        android:id=\"@+id/authenticationTypeLayout\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:hint=\"@string/storage_edit_ftp_server_authentication_type\"\n                        app:errorEnabled=\"true\"\n                        app:expandedHintEnabled=\"false\"\n                        style=\"?textInputOutlinedExposedDropdownMenuStyle\">\n\n                        <AutoCompleteTextView\n                            android:id=\"@+id/authenticationTypeEdit\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:inputType=\"none\" />\n                    </com.google.android.material.textfield.TextInputLayout>\n\n                    <LinearLayout\n                        android:id=\"@+id/passwordAuthenticationLayout\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:orientation=\"vertical\">\n\n                        <com.google.android.material.textfield.TextInputLayout\n                            android:id=\"@+id/usernameLayout\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:hint=\"@string/storage_edit_ftp_server_username\"\n                            app:errorEnabled=\"true\">\n\n                            <com.google.android.material.textfield.TextInputEditText\n                                android:id=\"@+id/usernameEdit\"\n                                android:layout_width=\"match_parent\"\n                                android:layout_height=\"wrap_content\"\n                                android:inputType=\"text\" />\n                        </com.google.android.material.textfield.TextInputLayout>\n\n                        <com.google.android.material.textfield.TextInputLayout\n                            android:id=\"@+id/passwordLayout\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:hint=\"@string/storage_edit_ftp_server_password\"\n                            app:endIconMode=\"password_toggle\"\n                            app:errorEnabled=\"true\">\n\n                            <com.google.android.material.textfield.TextInputEditText\n                                android:id=\"@+id/passwordEdit\"\n                                android:layout_width=\"match_parent\"\n                                android:layout_height=\"wrap_content\"\n                                android:inputType=\"textPassword\" />\n                        </com.google.android.material.textfield.TextInputLayout>\n                    </LinearLayout>\n\n                    <com.google.android.material.textfield.TextInputLayout\n                        android:id=\"@+id/modeLayout\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:hint=\"@string/storage_edit_ftp_server_mode\"\n                        app:errorEnabled=\"true\"\n                        app:expandedHintEnabled=\"false\"\n                        style=\"?textInputOutlinedExposedDropdownMenuStyle\">\n\n                        <AutoCompleteTextView\n                            android:id=\"@+id/modeEdit\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:inputType=\"none\" />\n                    </com.google.android.material.textfield.TextInputLayout>\n\n                    <com.google.android.material.textfield.TextInputLayout\n                        android:id=\"@+id/encodingLayout\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:hint=\"@string/storage_edit_ftp_server_encoding\"\n                        app:errorEnabled=\"true\"\n                        app:expandedHintEnabled=\"false\"\n                        style=\"?textInputOutlinedExposedDropdownMenuStyle\">\n\n                        <AutoCompleteTextView\n                            android:id=\"@+id/encodingEdit\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:inputType=\"none\" />\n                    </com.google.android.material.textfield.TextInputLayout>\n                </LinearLayout>\n            </androidx.core.widget.NestedScrollView>\n        </FrameLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"bottom\"\n            android:paddingStart=\"@dimen/screen_edge_margin_minus_8dp\"\n            android:paddingEnd=\"@dimen/screen_edge_margin_minus_8dp\"\n            android:paddingTop=\"4dp\"\n            android:paddingBottom=\"4dp\"\n            android:orientation=\"horizontal\">\n\n            <Button\n                android:id=\"@+id/removeOrAddButton\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginStart=\"0dp\"\n                android:text=\"@string/save\"\n                style=\"?buttonBarButtonStyle\" />\n\n            <Space\n                android:layout_width=\"0dp\"\n                android:layout_height=\"0dp\"\n                android:layout_weight=\"1\" />\n\n            <Button\n                android:id=\"@+id/cancelButton\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@android:string/cancel\"\n                style=\"?buttonBarButtonStyle\" />\n\n            <Button\n                android:id=\"@+id/saveOrConnectAndAddButton\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/save\"\n                style=\"?buttonBarButtonStyle\" />\n        </LinearLayout>\n    </me.zhanghai.android.files.ui.CoordinatorScrollingLinearLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/edit_sftp_server_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<androidx.coordinatorlayout.widget.CoordinatorLayout\n    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:fitsSystemWindows=\"true\">\n\n    <me.zhanghai.android.files.ui.CoordinatorAppBarLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?colorAppBarSurface\"\n        android:theme=\"?actionBarTheme\"\n        app:liftOnScroll=\"true\"\n        app:liftOnScrollTargetViewId=\"@id/scrollView\">\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?actionBarSize\"\n            app:popupTheme=\"?actionBarPopupTheme\" />\n    </me.zhanghai.android.files.ui.CoordinatorAppBarLayout>\n\n    <me.zhanghai.android.files.ui.CoordinatorScrollingLinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <FrameLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"0dp\"\n            android:layout_weight=\"1\">\n\n            <ProgressBar\n                android:id=\"@+id/progress\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center\"\n                android:visibility=\"gone\" />\n\n            <androidx.core.widget.NestedScrollView\n                android:id=\"@+id/scrollView\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:scrollbars=\"vertical\"\n                android:scrollIndicators=\"bottom\">\n\n                <LinearLayout\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:paddingStart=\"@dimen/screen_edge_margin\"\n                    android:paddingEnd=\"@dimen/screen_edge_margin\"\n                    android:paddingTop=\"@dimen/screen_edge_margin\"\n                    android:paddingBottom=\"@dimen/screen_edge_margin_minus_16dp\"\n                    android:animateLayoutChanges=\"true\"\n                    android:orientation=\"vertical\">\n\n                    <com.google.android.material.textfield.TextInputLayout\n                        android:id=\"@+id/hostLayout\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:hint=\"@string/storage_edit_sftp_server_host\"\n                        app:errorEnabled=\"true\">\n\n                        <com.google.android.material.textfield.TextInputEditText\n                            android:id=\"@+id/hostEdit\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:inputType=\"textUri\">\n                            <requestFocus />\n                        </com.google.android.material.textfield.TextInputEditText>\n                    </com.google.android.material.textfield.TextInputLayout>\n\n                    <com.google.android.material.textfield.TextInputLayout\n                        android:id=\"@+id/portLayout\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:hint=\"@string/storage_edit_sftp_server_port\"\n                        app:errorEnabled=\"true\"\n                        app:expandedHintEnabled=\"false\"\n                        app:placeholderText=\"@string/storage_edit_sftp_server_port_placeholder\">\n\n                        <com.google.android.material.textfield.TextInputEditText\n                            android:id=\"@+id/portEdit\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:inputType=\"number\" />\n                    </com.google.android.material.textfield.TextInputLayout>\n\n                    <com.google.android.material.textfield.TextInputLayout\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:hint=\"@string/storage_edit_sftp_server_path\"\n                        app:errorEnabled=\"true\"\n                        app:expandedHintEnabled=\"false\"\n                        app:placeholderText=\"@string/storage_edit_sftp_server_path_placeholder\">\n\n                        <com.google.android.material.textfield.TextInputEditText\n                            android:id=\"@+id/pathEdit\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:inputType=\"textUri\" />\n                    </com.google.android.material.textfield.TextInputLayout>\n\n                    <com.google.android.material.textfield.TextInputLayout\n                        android:id=\"@+id/nameLayout\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:hint=\"@string/storage_edit_sftp_server_name\"\n                        app:errorEnabled=\"true\"\n                        app:expandedHintEnabled=\"false\"\n                        app:placeholderText=\"@string/storage_edit_sftp_server_name_placeholder\">\n\n                        <com.google.android.material.textfield.TextInputEditText\n                            android:id=\"@+id/nameEdit\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:importantForAutofill=\"no\"\n                            android:inputType=\"text\" />\n                    </com.google.android.material.textfield.TextInputLayout>\n\n                    <com.google.android.material.textfield.TextInputLayout\n                        android:id=\"@+id/authenticationTypeLayout\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:hint=\"@string/storage_edit_sftp_server_authentication_type\"\n                        app:errorEnabled=\"true\"\n                        app:expandedHintEnabled=\"false\"\n                        style=\"?textInputOutlinedExposedDropdownMenuStyle\">\n\n                        <AutoCompleteTextView\n                            android:id=\"@+id/authenticationTypeEdit\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:inputType=\"none\" />\n                    </com.google.android.material.textfield.TextInputLayout>\n\n                    <com.google.android.material.textfield.TextInputLayout\n                        android:id=\"@+id/usernameLayout\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:hint=\"@string/storage_edit_sftp_server_username\"\n                        app:errorEnabled=\"true\">\n\n                        <com.google.android.material.textfield.TextInputEditText\n                            android:id=\"@+id/usernameEdit\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:inputType=\"text\" />\n                    </com.google.android.material.textfield.TextInputLayout>\n\n                    <com.google.android.material.textfield.TextInputLayout\n                        android:id=\"@+id/passwordLayout\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:hint=\"@string/storage_edit_sftp_server_password\"\n                        app:endIconMode=\"password_toggle\"\n                        app:errorEnabled=\"true\">\n\n                        <com.google.android.material.textfield.TextInputEditText\n                            android:id=\"@+id/passwordEdit\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:inputType=\"textPassword\" />\n                    </com.google.android.material.textfield.TextInputLayout>\n\n                    <LinearLayout\n                        android:id=\"@+id/publicKeyAuthenticationLayout\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:orientation=\"vertical\">\n\n                        <com.google.android.material.textfield.TextInputLayout\n                            android:id=\"@+id/privateKeyLayout\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:hint=\"@string/storage_edit_sftp_server_private_key\"\n                            app:endIconContentDescription=\"@string/storage_edit_sftp_server_private_key_open_file\"\n                            app:endIconDrawable=\"@drawable/file_icon_white_24dp\"\n                            app:endIconMode=\"custom\"\n                            app:errorEnabled=\"true\">\n\n                            <com.google.android.material.textfield.TextInputEditText\n                                android:id=\"@+id/privateKeyEdit\"\n                                android:layout_width=\"match_parent\"\n                                android:layout_height=\"wrap_content\"\n                                android:inputType=\"textMultiLine\" />\n                        </com.google.android.material.textfield.TextInputLayout>\n\n                        <com.google.android.material.textfield.TextInputLayout\n                            android:id=\"@+id/privateKeyPasswordLayout\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:hint=\"@string/storage_edit_sftp_server_private_key_password\"\n                            app:endIconMode=\"password_toggle\"\n                            app:errorEnabled=\"true\"\n                            app:expandedHintEnabled=\"false\"\n                            app:placeholderText=\"@string/storage_edit_sftp_server_private_key_password_placeholder\">\n\n                            <com.google.android.material.textfield.TextInputEditText\n                                android:id=\"@+id/privateKeyPasswordEdit\"\n                                android:layout_width=\"match_parent\"\n                                android:layout_height=\"wrap_content\"\n                                android:inputType=\"textPassword\" />\n                        </com.google.android.material.textfield.TextInputLayout>\n                    </LinearLayout>\n                </LinearLayout>\n            </androidx.core.widget.NestedScrollView>\n        </FrameLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"bottom\"\n            android:paddingStart=\"@dimen/screen_edge_margin_minus_8dp\"\n            android:paddingEnd=\"@dimen/screen_edge_margin_minus_8dp\"\n            android:paddingTop=\"4dp\"\n            android:paddingBottom=\"4dp\"\n            android:orientation=\"horizontal\">\n\n            <Button\n                android:id=\"@+id/removeOrAddButton\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginStart=\"0dp\"\n                android:text=\"@string/save\"\n                style=\"?buttonBarButtonStyle\" />\n\n            <Space\n                android:layout_width=\"0dp\"\n                android:layout_height=\"0dp\"\n                android:layout_weight=\"1\" />\n\n            <Button\n                android:id=\"@+id/cancelButton\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@android:string/cancel\"\n                style=\"?buttonBarButtonStyle\" />\n\n            <Button\n                android:id=\"@+id/saveOrConnectAndAddButton\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/save\"\n                style=\"?buttonBarButtonStyle\" />\n        </LinearLayout>\n    </me.zhanghai.android.files.ui.CoordinatorScrollingLinearLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/edit_smb_server_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<androidx.coordinatorlayout.widget.CoordinatorLayout\n    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:fitsSystemWindows=\"true\">\n\n    <me.zhanghai.android.files.ui.CoordinatorAppBarLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?colorAppBarSurface\"\n        android:theme=\"?actionBarTheme\"\n        app:liftOnScroll=\"true\"\n        app:liftOnScrollTargetViewId=\"@id/scrollView\">\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?actionBarSize\"\n            app:popupTheme=\"?actionBarPopupTheme\" />\n    </me.zhanghai.android.files.ui.CoordinatorAppBarLayout>\n\n    <me.zhanghai.android.files.ui.CoordinatorScrollingLinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <FrameLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"0dp\"\n            android:layout_weight=\"1\">\n\n            <ProgressBar\n                android:id=\"@+id/progress\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center\"\n                android:visibility=\"gone\" />\n\n            <androidx.core.widget.NestedScrollView\n                android:id=\"@+id/scrollView\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:scrollbars=\"vertical\"\n                android:scrollIndicators=\"bottom\">\n\n                <LinearLayout\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:paddingStart=\"@dimen/screen_edge_margin\"\n                    android:paddingEnd=\"@dimen/screen_edge_margin\"\n                    android:paddingTop=\"@dimen/screen_edge_margin\"\n                    android:paddingBottom=\"@dimen/screen_edge_margin_minus_16dp\"\n                    android:animateLayoutChanges=\"true\"\n                    android:orientation=\"vertical\">\n\n                    <com.google.android.material.textfield.TextInputLayout\n                        android:id=\"@+id/hostLayout\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:hint=\"@string/storage_edit_smb_server_host\"\n                        app:errorEnabled=\"true\">\n\n                        <com.google.android.material.textfield.TextInputEditText\n                            android:id=\"@+id/hostEdit\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:inputType=\"textUri\">\n                            <requestFocus />\n                        </com.google.android.material.textfield.TextInputEditText>\n                    </com.google.android.material.textfield.TextInputLayout>\n\n                    <com.google.android.material.textfield.TextInputLayout\n                        android:id=\"@+id/portLayout\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:hint=\"@string/storage_edit_smb_server_port\"\n                        app:errorEnabled=\"true\"\n                        app:expandedHintEnabled=\"false\"\n                        app:placeholderText=\"@string/storage_edit_smb_server_port_placeholder\">\n\n                        <com.google.android.material.textfield.TextInputEditText\n                            android:id=\"@+id/portEdit\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:inputType=\"number\" />\n                    </com.google.android.material.textfield.TextInputLayout>\n\n                    <com.google.android.material.textfield.TextInputLayout\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:hint=\"@string/storage_edit_smb_server_path\"\n                        app:errorEnabled=\"true\"\n                        app:expandedHintEnabled=\"false\"\n                        app:placeholderText=\"@string/storage_edit_smb_server_path_placeholder\">\n\n                        <com.google.android.material.textfield.TextInputEditText\n                            android:id=\"@+id/pathEdit\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:inputType=\"textUri\" />\n                    </com.google.android.material.textfield.TextInputLayout>\n\n                    <com.google.android.material.textfield.TextInputLayout\n                        android:id=\"@+id/nameLayout\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:hint=\"@string/storage_edit_smb_server_name\"\n                        app:errorEnabled=\"true\"\n                        app:expandedHintEnabled=\"false\"\n                        app:placeholderText=\"@string/storage_edit_smb_server_name_placeholder\">\n\n                        <com.google.android.material.textfield.TextInputEditText\n                            android:id=\"@+id/nameEdit\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:importantForAutofill=\"no\"\n                            android:inputType=\"text\" />\n                    </com.google.android.material.textfield.TextInputLayout>\n\n                    <com.google.android.material.textfield.TextInputLayout\n                        android:id=\"@+id/authenticationTypeLayout\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:hint=\"@string/storage_edit_smb_server_authentication_type\"\n                        app:errorEnabled=\"true\"\n                        app:expandedHintEnabled=\"false\"\n                        style=\"?textInputOutlinedExposedDropdownMenuStyle\">\n\n                        <AutoCompleteTextView\n                            android:id=\"@+id/authenticationTypeEdit\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:inputType=\"none\" />\n                    </com.google.android.material.textfield.TextInputLayout>\n\n                    <LinearLayout\n                        android:id=\"@+id/passwordAuthenticationLayout\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:orientation=\"vertical\">\n\n                        <com.google.android.material.textfield.TextInputLayout\n                            android:id=\"@+id/usernameLayout\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:hint=\"@string/storage_edit_smb_server_username\"\n                            app:errorEnabled=\"true\">\n\n                            <com.google.android.material.textfield.TextInputEditText\n                                android:id=\"@+id/usernameEdit\"\n                                android:layout_width=\"match_parent\"\n                                android:layout_height=\"wrap_content\"\n                                android:inputType=\"text\" />\n                        </com.google.android.material.textfield.TextInputLayout>\n\n                        <com.google.android.material.textfield.TextInputLayout\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:hint=\"@string/storage_edit_smb_server_domain\"\n                            app:errorEnabled=\"true\">\n\n                            <com.google.android.material.textfield.TextInputEditText\n                                android:id=\"@+id/domainEdit\"\n                                android:layout_width=\"match_parent\"\n                                android:layout_height=\"wrap_content\"\n                                android:inputType=\"text\" />\n                        </com.google.android.material.textfield.TextInputLayout>\n\n                        <com.google.android.material.textfield.TextInputLayout\n                            android:id=\"@+id/passwordLayout\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:hint=\"@string/storage_edit_smb_server_password\"\n                            app:endIconMode=\"password_toggle\"\n                            app:errorEnabled=\"true\">\n\n                            <com.google.android.material.textfield.TextInputEditText\n                                android:id=\"@+id/passwordEdit\"\n                                android:layout_width=\"match_parent\"\n                                android:layout_height=\"wrap_content\"\n                                android:inputType=\"textPassword\" />\n                        </com.google.android.material.textfield.TextInputLayout>\n                    </LinearLayout>\n                </LinearLayout>\n            </androidx.core.widget.NestedScrollView>\n        </FrameLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"bottom\"\n            android:paddingStart=\"@dimen/screen_edge_margin_minus_8dp\"\n            android:paddingEnd=\"@dimen/screen_edge_margin_minus_8dp\"\n            android:paddingTop=\"4dp\"\n            android:paddingBottom=\"4dp\"\n            android:orientation=\"horizontal\">\n\n            <Button\n                android:id=\"@+id/removeOrAddButton\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginStart=\"0dp\"\n                android:text=\"@string/save\"\n                style=\"?buttonBarButtonStyle\" />\n\n            <Space\n                android:layout_width=\"0dp\"\n                android:layout_height=\"0dp\"\n                android:layout_weight=\"1\" />\n\n            <Button\n                android:id=\"@+id/cancelButton\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@android:string/cancel\"\n                style=\"?buttonBarButtonStyle\" />\n\n            <Button\n                android:id=\"@+id/saveOrConnectAndAddButton\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/save\"\n                style=\"?buttonBarButtonStyle\" />\n        </LinearLayout>\n    </me.zhanghai.android.files.ui.CoordinatorScrollingLinearLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/edit_webdav_server_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<androidx.coordinatorlayout.widget.CoordinatorLayout\n    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:fitsSystemWindows=\"true\">\n\n    <me.zhanghai.android.files.ui.CoordinatorAppBarLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?colorAppBarSurface\"\n        android:theme=\"?actionBarTheme\"\n        app:liftOnScroll=\"true\"\n        app:liftOnScrollTargetViewId=\"@id/scrollView\">\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?actionBarSize\"\n            app:popupTheme=\"?actionBarPopupTheme\" />\n    </me.zhanghai.android.files.ui.CoordinatorAppBarLayout>\n\n    <me.zhanghai.android.files.ui.CoordinatorScrollingLinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <FrameLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"0dp\"\n            android:layout_weight=\"1\">\n\n            <ProgressBar\n                android:id=\"@+id/progress\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"center\"\n                android:visibility=\"gone\" />\n\n            <androidx.core.widget.NestedScrollView\n                android:id=\"@+id/scrollView\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:scrollbars=\"vertical\"\n                android:scrollIndicators=\"bottom\">\n\n                <LinearLayout\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:paddingStart=\"@dimen/screen_edge_margin\"\n                    android:paddingEnd=\"@dimen/screen_edge_margin\"\n                    android:paddingTop=\"@dimen/screen_edge_margin\"\n                    android:paddingBottom=\"@dimen/screen_edge_margin_minus_16dp\"\n                    android:animateLayoutChanges=\"true\"\n                    android:orientation=\"vertical\">\n\n                    <com.google.android.material.textfield.TextInputLayout\n                        android:id=\"@+id/hostLayout\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:hint=\"@string/storage_edit_webdav_server_host\"\n                        app:errorEnabled=\"true\">\n\n                        <com.google.android.material.textfield.TextInputEditText\n                            android:id=\"@+id/hostEdit\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:inputType=\"textUri\">\n                            <requestFocus />\n                        </com.google.android.material.textfield.TextInputEditText>\n                    </com.google.android.material.textfield.TextInputLayout>\n\n                    <com.google.android.material.textfield.TextInputLayout\n                        android:id=\"@+id/portLayout\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:hint=\"@string/storage_edit_webdav_server_port\"\n                        app:errorEnabled=\"true\"\n                        app:expandedHintEnabled=\"false\"\n                        app:placeholderText=\"@string/storage_edit_webdav_server_port_placeholder\">\n\n                        <com.google.android.material.textfield.TextInputEditText\n                            android:id=\"@+id/portEdit\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:inputType=\"number\" />\n                    </com.google.android.material.textfield.TextInputLayout>\n\n                    <com.google.android.material.textfield.TextInputLayout\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:hint=\"@string/storage_edit_webdav_server_path\"\n                        app:errorEnabled=\"true\"\n                        app:expandedHintEnabled=\"false\"\n                        app:placeholderText=\"@string/storage_edit_webdav_server_path_placeholder\">\n\n                        <com.google.android.material.textfield.TextInputEditText\n                            android:id=\"@+id/pathEdit\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:inputType=\"textUri\" />\n                    </com.google.android.material.textfield.TextInputLayout>\n\n                    <com.google.android.material.textfield.TextInputLayout\n                        android:id=\"@+id/nameLayout\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:hint=\"@string/storage_edit_webdav_server_name\"\n                        app:errorEnabled=\"true\"\n                        app:expandedHintEnabled=\"false\"\n                        app:placeholderText=\"@string/storage_edit_webdav_server_name_placeholder\">\n\n                        <com.google.android.material.textfield.TextInputEditText\n                            android:id=\"@+id/nameEdit\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:importantForAutofill=\"no\"\n                            android:inputType=\"text\" />\n                    </com.google.android.material.textfield.TextInputLayout>\n\n                    <com.google.android.material.textfield.TextInputLayout\n                        android:id=\"@+id/protocolLayout\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:hint=\"@string/storage_edit_webdav_server_protocol\"\n                        app:errorEnabled=\"true\"\n                        app:expandedHintEnabled=\"false\"\n                        style=\"?textInputOutlinedExposedDropdownMenuStyle\">\n\n                        <AutoCompleteTextView\n                            android:id=\"@+id/protocolEdit\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:inputType=\"none\" />\n                    </com.google.android.material.textfield.TextInputLayout>\n\n                    <com.google.android.material.textfield.TextInputLayout\n                        android:id=\"@+id/authenticationTypeLayout\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:hint=\"@string/storage_edit_webdav_server_authentication_type\"\n                        app:errorEnabled=\"true\"\n                        app:expandedHintEnabled=\"false\"\n                        style=\"?textInputOutlinedExposedDropdownMenuStyle\">\n\n                        <AutoCompleteTextView\n                            android:id=\"@+id/authenticationTypeEdit\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:inputType=\"none\" />\n                    </com.google.android.material.textfield.TextInputLayout>\n\n                    <LinearLayout\n                        android:id=\"@+id/passwordAuthenticationLayout\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:orientation=\"vertical\">\n\n                        <com.google.android.material.textfield.TextInputLayout\n                            android:id=\"@+id/usernameLayout\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:hint=\"@string/storage_edit_webdav_server_username\"\n                            app:errorEnabled=\"true\">\n\n                            <com.google.android.material.textfield.TextInputEditText\n                                android:id=\"@+id/usernameEdit\"\n                                android:layout_width=\"match_parent\"\n                                android:layout_height=\"wrap_content\"\n                                android:inputType=\"text\" />\n                        </com.google.android.material.textfield.TextInputLayout>\n\n                        <com.google.android.material.textfield.TextInputLayout\n                            android:id=\"@+id/passwordLayout\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:hint=\"@string/storage_edit_webdav_server_password\"\n                            app:endIconMode=\"password_toggle\"\n                            app:errorEnabled=\"true\">\n\n                            <com.google.android.material.textfield.TextInputEditText\n                                android:id=\"@+id/passwordEdit\"\n                                android:layout_width=\"match_parent\"\n                                android:layout_height=\"wrap_content\"\n                                android:inputType=\"textPassword\" />\n                        </com.google.android.material.textfield.TextInputLayout>\n                    </LinearLayout>\n\n                    <com.google.android.material.textfield.TextInputLayout\n                        android:id=\"@+id/accessTokenLayout\"\n                        android:layout_width=\"match_parent\"\n                        android:layout_height=\"wrap_content\"\n                        android:hint=\"@string/storage_edit_webdav_server_access_token\"\n                        app:errorEnabled=\"true\">\n\n                        <com.google.android.material.textfield.TextInputEditText\n                            android:id=\"@+id/accessTokenEdit\"\n                            android:layout_width=\"match_parent\"\n                            android:layout_height=\"wrap_content\"\n                            android:inputType=\"text\" />\n                    </com.google.android.material.textfield.TextInputLayout>\n                </LinearLayout>\n            </androidx.core.widget.NestedScrollView>\n        </FrameLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"bottom\"\n            android:paddingStart=\"@dimen/screen_edge_margin_minus_8dp\"\n            android:paddingEnd=\"@dimen/screen_edge_margin_minus_8dp\"\n            android:paddingTop=\"4dp\"\n            android:paddingBottom=\"4dp\"\n            android:orientation=\"horizontal\">\n\n            <Button\n                android:id=\"@+id/removeOrAddButton\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginStart=\"0dp\"\n                android:text=\"@string/save\"\n                style=\"?buttonBarButtonStyle\" />\n\n            <Space\n                android:layout_width=\"0dp\"\n                android:layout_height=\"0dp\"\n                android:layout_weight=\"1\" />\n\n            <Button\n                android:id=\"@+id/cancelButton\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@android:string/cancel\"\n                style=\"?buttonBarButtonStyle\" />\n\n            <Button\n                android:id=\"@+id/saveOrConnectAndAddButton\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:text=\"@string/save\"\n                style=\"?buttonBarButtonStyle\" />\n        </LinearLayout>\n    </me.zhanghai.android.files.ui.CoordinatorScrollingLinearLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/file_item_grid.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<me.zhanghai.android.files.ui.CheckableForegroundLinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/itemLayout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:foreground=\"?selectableItemBackground\"\n    android:gravity=\"center_vertical\"\n    android:orientation=\"vertical\">\n\n    <me.zhanghai.android.files.ui.AspectRatioFrameLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"@dimen/screen_edge_margin\"\n        android:layout_marginEnd=\"@dimen/screen_edge_margin\"\n        android:layout_marginTop=\"@dimen/screen_edge_margin\"\n        android:duplicateParentState=\"true\"\n        app:aspectRatio=\"1.78\">\n\n        <me.zhanghai.android.files.ui.DisabledAlphaImageView\n            android:id=\"@+id/directoryThumbnailImage\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:duplicateParentState=\"true\"\n            android:scaleType=\"fitCenter\"\n            android:src=\"@drawable/file_directory_thumbnail\" />\n\n        <me.zhanghai.android.files.ui.DisabledAlphaImageView\n            android:id=\"@+id/thumbnailIconImage\"\n            android:layout_width=\"@dimen/large_icon_size\"\n            android:layout_height=\"@dimen/large_icon_size\"\n            android:layout_gravity=\"center\"\n            android:duplicateParentState=\"true\"\n            android:scaleType=\"centerCrop\" />\n\n        <!--\n          ~ Can't use ShapeableImageView stroke support due to\n          ~ https://github.com/material-components/material-components-android/issues/1329\n          -->\n        <me.zhanghai.android.files.ui.DisabledAlphaImageView\n            android:id=\"@+id/thumbnailImage\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:duplicateParentState=\"true\"\n            android:scaleType=\"centerCrop\"\n            app:shapeAppearance=\"?shapeAppearanceSmallComponent\" />\n\n        <View\n            android:id=\"@+id/thumbnailOutlineView\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:background=\"@drawable/file_item_grid_thumbnail_outline\"\n            android:duplicateParentState=\"true\" />\n    </me.zhanghai.android.files.ui.AspectRatioFrameLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/single_line_list_item_height\"\n        android:layout_marginBottom=\"@dimen/screen_edge_margin_minus_8dp\"\n        android:duplicateParentState=\"true\"\n        android:gravity=\"center_vertical\"\n        android:orientation=\"horizontal\">\n\n        <FrameLayout\n            android:id=\"@+id/iconLayout\"\n            android:layout_width=\"@dimen/touch_target_size\"\n            android:layout_height=\"@dimen/touch_target_size\"\n            android:layout_marginStart=\"@dimen/screen_edge_margin_minus_12dp\"\n            android:duplicateParentState=\"true\"\n            android:padding=\"@dimen/touch_target_large_icon_padding\">\n\n            <me.zhanghai.android.files.ui.DisabledAlphaImageView\n                android:id=\"@+id/iconImage\"\n                android:layout_width=\"@dimen/icon_size\"\n                android:layout_height=\"@dimen/icon_size\"\n                android:layout_gravity=\"center\"\n                android:duplicateParentState=\"true\"\n                android:scaleType=\"centerCrop\" />\n\n            <me.zhanghai.android.files.ui.DisabledAlphaImageView\n                android:id=\"@+id/appIconBadgeImage\"\n                android:layout_width=\"@dimen/badge_size_plus_1dp\"\n                android:layout_height=\"@dimen/badge_size_plus_1dp\"\n                android:layout_gravity=\"end|bottom\"\n                android:layout_marginStart=\"-0.5dp\"\n                android:layout_marginEnd=\"-0.5dp\"\n                android:layout_marginTop=\"-0.5dp\"\n                android:layout_marginBottom=\"-0.5dp\"\n                android:duplicateParentState=\"true\" />\n\n            <me.zhanghai.android.files.ui.DisabledAlphaImageView\n                android:id=\"@+id/badgeImage\"\n                android:layout_width=\"@dimen/badge_size\"\n                android:layout_height=\"@dimen/badge_size\"\n                android:layout_gravity=\"end|bottom\"\n                android:background=\"@drawable/badge_background_18dp\"\n                android:duplicateParentState=\"true\" />\n\n            <me.zhanghai.android.files.ui.DisabledAlphaImageView\n                android:layout_width=\"@dimen/badge_size\"\n                android:layout_height=\"@dimen/badge_size\"\n                android:layout_gravity=\"end|bottom\"\n                android:duplicateParentState=\"true\"\n                android:src=\"@drawable/checkable_badge_18dp\" />\n        </FrameLayout>\n\n        <TextView\n            android:id=\"@+id/nameText\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:duplicateParentState=\"true\"\n            android:ellipsize=\"end\"\n            android:gravity=\"center\"\n            android:maxLines=\"2\"\n            android:textAppearance=\"?textAppearanceListItemSecondary\" />\n\n        <!--\n          ~ Cannot use android:duplicateParentState=\"true\" because a button needs to have its own\n          ~ pressed state, etc. Necessary states will be duplicated manually in the adapter\n          ~ implementation.\n          -->\n        <me.zhanghai.android.foregroundcompat.ForegroundImageButton\n            android:id=\"@+id/menuButton\"\n            android:layout_width=\"@dimen/touch_target_size\"\n            android:layout_height=\"@dimen/touch_target_size\"\n            android:layout_marginEnd=\"@dimen/screen_edge_margin_minus_12dp\"\n            android:padding=\"@dimen/touch_target_icon_padding\"\n            android:background=\"@null\"\n            android:foreground=\"@drawable/selectable_item_background_oval\"\n            android:src=\"@drawable/more_vertical_icon_white_24dp\"\n            app:tint=\"?colorControlNormal\" />\n    </LinearLayout>\n</me.zhanghai.android.files.ui.CheckableForegroundLinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/file_item_list.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<me.zhanghai.android.files.ui.CheckableForegroundLinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/itemLayout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"@dimen/two_line_list_item_height\"\n    android:foreground=\"?selectableItemBackground\"\n    android:gravity=\"center_vertical\"\n    android:orientation=\"horizontal\">\n\n    <FrameLayout\n        android:id=\"@+id/iconLayout\"\n        android:layout_width=\"@dimen/touch_target_size\"\n        android:layout_height=\"@dimen/touch_target_size\"\n        android:layout_marginStart=\"@dimen/screen_edge_margin_minus_4dp\"\n        android:layout_marginEnd=\"@dimen/content_start_from_screen_edge_margin_minus_44dp\"\n        android:clipToPadding=\"false\"\n        android:duplicateParentState=\"true\"\n        android:padding=\"@dimen/touch_target_large_icon_padding\">\n\n        <me.zhanghai.android.files.ui.DisabledAlphaImageView\n            android:id=\"@+id/iconImage\"\n            android:layout_width=\"@dimen/icon_size\"\n            android:layout_height=\"@dimen/icon_size\"\n            android:layout_gravity=\"center\"\n            android:duplicateParentState=\"true\"\n            android:scaleType=\"centerCrop\" />\n\n        <me.zhanghai.android.files.ui.DisabledAlphaImageView\n            android:id=\"@+id/thumbnailImage\"\n            android:layout_width=\"@dimen/large_icon_size\"\n            android:layout_height=\"@dimen/large_icon_size\"\n            android:duplicateParentState=\"true\"\n            android:scaleType=\"centerCrop\" />\n\n        <me.zhanghai.android.files.ui.DisabledAlphaImageView\n            android:id=\"@+id/appIconBadgeImage\"\n            android:layout_width=\"@dimen/badge_size_plus_1dp\"\n            android:layout_height=\"@dimen/badge_size_plus_1dp\"\n            android:layout_gravity=\"end|bottom\"\n            android:layout_marginStart=\"-0.5dp\"\n            android:layout_marginEnd=\"-0.5dp\"\n            android:layout_marginTop=\"-0.5dp\"\n            android:layout_marginBottom=\"-0.5dp\"\n            android:duplicateParentState=\"true\" />\n\n        <me.zhanghai.android.files.ui.DisabledAlphaImageView\n            android:id=\"@+id/badgeImage\"\n            android:layout_width=\"@dimen/badge_size\"\n            android:layout_height=\"@dimen/badge_size\"\n            android:layout_gravity=\"end|bottom\"\n            android:background=\"@drawable/badge_background_18dp\"\n            android:duplicateParentState=\"true\" />\n\n        <me.zhanghai.android.files.ui.DisabledAlphaImageView\n            android:layout_width=\"@dimen/badge_size\"\n            android:layout_height=\"@dimen/badge_size\"\n            android:layout_gravity=\"end|bottom\"\n            android:duplicateParentState=\"true\"\n            android:src=\"@drawable/checkable_badge_18dp\" />\n    </FrameLayout>\n\n    <LinearLayout\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_weight=\"1\"\n        android:duplicateParentState=\"true\"\n        android:orientation=\"vertical\">\n\n        <!-- Lint says android:ellipsize=\"middle\" and android:maxLines=\"1\" crashes. -->\n        <TextView\n            android:id=\"@+id/nameText\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:duplicateParentState=\"true\"\n            android:marqueeRepeatLimit=\"marquee_forever\"\n            android:singleLine=\"true\"\n            android:textAppearance=\"?textAppearanceListItem\" />\n\n        <me.zhanghai.android.files.ui.AutoGoneTextView\n            android:id=\"@+id/descriptionText\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:duplicateParentState=\"true\"\n            android:ellipsize=\"end\"\n            android:maxLines=\"1\"\n            android:textAppearance=\"?textAppearanceListItemSecondary\"\n            android:textColor=\"?android:textColorSecondary\" />\n    </LinearLayout>\n\n    <!--\n      ~ Cannot use android:duplicateParentState=\"true\" because a button needs to have its own\n      ~ pressed state, etc. Necessary states will be duplicated manually in the adapter\n      ~ implementation.\n      -->\n    <me.zhanghai.android.foregroundcompat.ForegroundImageButton\n        android:id=\"@+id/menuButton\"\n        android:layout_width=\"@dimen/touch_target_size\"\n        android:layout_height=\"@dimen/touch_target_size\"\n        android:layout_marginStart=\"@dimen/content_start_from_screen_edge_margin_minus_36dp_considering_at_least_8dp\"\n        android:layout_marginEnd=\"@dimen/screen_edge_margin_minus_12dp_at_least_8dp\"\n        android:padding=\"@dimen/touch_target_icon_padding\"\n        android:background=\"@null\"\n        android:foreground=\"@drawable/selectable_item_background_oval\"\n        android:src=\"@drawable/more_vertical_icon_white_24dp\"\n        app:tint=\"?colorControlNormal\" />\n</me.zhanghai.android.files.ui.CheckableForegroundLinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/file_job_conflict_dialog_view.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<LinearLayout\n    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    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/two_line_list_item_height\"\n        android:paddingStart=\"?dialogPreferredPadding\"\n        android:paddingEnd=\"?dialogPreferredPadding\"\n        android:clipToPadding=\"false\"\n        android:gravity=\"center_vertical\"\n        android:orientation=\"horizontal\">\n\n        <FrameLayout\n            android:layout_width=\"@dimen/touch_target_size\"\n            android:layout_height=\"@dimen/touch_target_size\"\n            android:layout_marginStart=\"-4dp\"\n            android:layout_marginEnd=\"12dp\"\n            android:clipToPadding=\"false\"\n            android:padding=\"@dimen/touch_target_large_icon_padding\">\n\n            <ImageView\n                android:id=\"@+id/targetIconImage\"\n                android:layout_width=\"@dimen/icon_size\"\n                android:layout_height=\"@dimen/icon_size\"\n                android:layout_gravity=\"center\"\n                android:scaleType=\"centerCrop\" />\n\n            <ImageView\n                android:id=\"@+id/targetThumbnailImage\"\n                android:layout_width=\"@dimen/large_icon_size\"\n                android:layout_height=\"@dimen/large_icon_size\"\n                android:scaleType=\"centerCrop\" />\n\n            <me.zhanghai.android.files.ui.DisabledAlphaImageView\n                android:id=\"@+id/targetAppIconBadgeImage\"\n                android:layout_width=\"@dimen/badge_size_plus_1dp\"\n                android:layout_height=\"@dimen/badge_size_plus_1dp\"\n                android:layout_gravity=\"end|bottom\"\n                android:layout_marginStart=\"-0.5dp\"\n                android:layout_marginEnd=\"-0.5dp\"\n                android:layout_marginTop=\"-0.5dp\"\n                android:layout_marginBottom=\"-0.5dp\"\n                android:duplicateParentState=\"true\" />\n\n            <ImageView\n                android:id=\"@+id/targetBadgeImage\"\n                android:layout_width=\"@dimen/badge_size\"\n                android:layout_height=\"@dimen/badge_size\"\n                android:layout_gravity=\"end|bottom\"\n                android:background=\"@drawable/badge_background_18dp\" />\n        </FrameLayout>\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:orientation=\"vertical\">\n\n            <!-- Lint says android:ellipsize=\"middle\" and android:maxLines=\"1\" crashes. -->\n            <TextView\n                android:id=\"@+id/targetNameText\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:ellipsize=\"middle\"\n                android:singleLine=\"true\"\n                android:textAppearance=\"?textAppearanceListItem\" />\n\n            <TextView\n                android:id=\"@+id/targetDescriptionText\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:ellipsize=\"end\"\n                android:maxLines=\"1\"\n                android:textAppearance=\"?textAppearanceListItemSecondary\"\n                android:textColor=\"?android:textColorSecondary\" />\n        </LinearLayout>\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"@dimen/two_line_list_item_height\"\n        android:layout_marginTop=\"-8dp\"\n        android:paddingStart=\"?dialogPreferredPadding\"\n        android:paddingEnd=\"?dialogPreferredPadding\"\n        android:clipToPadding=\"false\"\n        android:gravity=\"center_vertical\"\n        android:orientation=\"horizontal\">\n\n        <FrameLayout\n            android:layout_width=\"@dimen/touch_target_size\"\n            android:layout_height=\"@dimen/touch_target_size\"\n            android:layout_marginStart=\"-4dp\"\n            android:layout_marginEnd=\"12dp\"\n            android:padding=\"@dimen/touch_target_large_icon_padding\"\n            android:clipToPadding=\"false\">\n\n            <ImageView\n                android:id=\"@+id/sourceIconImage\"\n                android:layout_width=\"@dimen/icon_size\"\n                android:layout_height=\"@dimen/icon_size\"\n                android:layout_gravity=\"center\"\n                android:scaleType=\"centerCrop\" />\n\n            <ImageView\n                android:id=\"@+id/sourceThumbnailImage\"\n                android:layout_width=\"@dimen/large_icon_size\"\n                android:layout_height=\"@dimen/large_icon_size\"\n                android:scaleType=\"centerCrop\" />\n\n            <me.zhanghai.android.files.ui.DisabledAlphaImageView\n                android:id=\"@+id/sourceAppIconBadgeImage\"\n                android:layout_width=\"@dimen/badge_size_plus_1dp\"\n                android:layout_height=\"@dimen/badge_size_plus_1dp\"\n                android:layout_gravity=\"end|bottom\"\n                android:layout_marginStart=\"-0.5dp\"\n                android:layout_marginEnd=\"-0.5dp\"\n                android:layout_marginTop=\"-0.5dp\"\n                android:layout_marginBottom=\"-0.5dp\"\n                android:duplicateParentState=\"true\" />\n\n            <ImageView\n                android:id=\"@+id/sourceBadgeImage\"\n                android:layout_width=\"@dimen/badge_size\"\n                android:layout_height=\"@dimen/badge_size\"\n                android:layout_gravity=\"end|bottom\"\n                android:background=\"@drawable/badge_background_18dp\" />\n        </FrameLayout>\n\n        <LinearLayout\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_weight=\"1\"\n            android:orientation=\"vertical\">\n\n            <!-- Lint says android:ellipsize=\"middle\" and android:maxLines=\"1\" crashes. -->\n            <TextView\n                android:id=\"@+id/sourceNameText\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:ellipsize=\"middle\"\n                android:singleLine=\"true\"\n                android:textAppearance=\"?textAppearanceListItem\" />\n\n            <TextView\n                android:id=\"@+id/sourceDescriptionText\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:ellipsize=\"end\"\n                android:maxLines=\"1\"\n                android:textAppearance=\"?textAppearanceListItemSecondary\"\n                android:textColor=\"?android:textColorSecondary\" />\n        </LinearLayout>\n    </LinearLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"-8dp\"\n        android:orientation=\"vertical\">\n\n        <me.zhanghai.android.foregroundcompat.ForegroundLinearLayout\n            android:id=\"@+id/showNameLayout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:paddingStart=\"?dialogPreferredPadding\"\n            android:paddingEnd=\"?dialogPreferredPadding\"\n            android:paddingTop=\"8dp\"\n            android:paddingBottom=\"8dp\"\n            android:foreground=\"?selectableItemBackground\"\n            android:gravity=\"center_vertical\"\n            android:orientation=\"horizontal\">\n\n            <!-- NOTE: Somehow android:layout_margin=\"-3dp\" doesn't work. -->\n            <ImageView\n                android:id=\"@+id/showNameArrowImage\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginStart=\"-3dp\"\n                android:layout_marginEnd=\"-3dp\"\n                android:layout_marginTop=\"-3dp\"\n                android:layout_marginBottom=\"-3dp\"\n                android:src=\"@drawable/arrow_end_icon_white_24dp\"\n                app:tint=\"?colorControlNormal\" />\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginStart=\"?dialogPreferredPadding\"\n                android:text=\"@string/file_job_conflict_show_name\"\n                android:textAppearance=\"?textAppearanceListItem\" />\n        </me.zhanghai.android.foregroundcompat.ForegroundLinearLayout>\n\n        <com.google.android.material.textfield.TextInputLayout\n            android:id=\"@+id/nameLayout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"?dialogPreferredPadding\"\n            android:layout_marginEnd=\"?dialogPreferredPadding\"\n            android:layout_marginTop=\"3dp\"\n            android:layout_marginBottom=\"8dp\"\n            android:visibility=\"gone\"\n            app:endIconMode=\"custom\"\n            app:endIconDrawable=\"@drawable/reset_icon_white_24dp\"\n            app:endIconContentDescription=\"@string/reset\"\n            app:expandedHintEnabled=\"false\"\n            app:placeholderText=\"@string/file_job_conflict_name_hint\">\n\n            <com.google.android.material.textfield.TextInputEditText\n                android:id=\"@+id/nameEdit\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:importantForAutofill=\"no\"\n                android:inputType=\"text\"\n                android:maxLines=\"1\" />\n        </com.google.android.material.textfield.TextInputLayout>\n    </LinearLayout>\n\n    <CheckBox\n        android:id=\"@+id/allCheck\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"@dimen/dialog_padding_minus_7dp\"\n        android:layout_marginEnd=\"?dialogPreferredPadding\"\n        android:paddingStart=\"@dimen/dialog_padding_minus_7dp\"\n        android:paddingTop=\"8dp\"\n        android:paddingBottom=\"8dp\"\n        android:text=\"@string/file_job_action_all\"\n        android:textAppearance=\"?textAppearanceListItem\" />\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/file_job_error_dialog_view.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:paddingStart=\"?dialogPreferredPadding\"\n    android:paddingEnd=\"?dialogPreferredPadding\"\n    android:clipToPadding=\"false\"\n    android:orientation=\"vertical\">\n\n    <Button\n        android:id=\"@+id/remountButton\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"-8dp\"\n        android:layout_marginEnd=\"-8dp\"\n        style=\"?borderlessButtonStyle\" />\n\n    <Space\n        android:id=\"@+id/allSpace\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"8dp\" />\n\n    <CheckBox\n        android:id=\"@+id/allCheck\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"-7dp\"\n        android:paddingStart=\"@dimen/dialog_padding_minus_7dp\"\n        android:paddingTop=\"8dp\"\n        android:paddingBottom=\"8dp\"\n        android:text=\"@string/file_job_action_all\"\n        android:textAppearance=\"?textAppearanceListItem\" />\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/file_list_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <!--\n      ~ View binding complains about different root view IDs, so we have to wrap them inside a\n      ~ <merge> tag.\n      -->\n    <include layout=\"@layout/file_list_fragment_include\" />\n</FrameLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/file_list_fragment_app_bar_include.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<merge\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <!-- app:statusBarBackground is broken when scrolling. -->\n    <me.zhanghai.android.files.ui.CoordinatorAppBarLayout\n        android:id=\"@+id/appBarLayout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?colorAppBarSurface\"\n        android:theme=\"?actionBarTheme\"\n        app:liftOnScroll=\"true\"\n        app:liftOnScrollTargetViewId=\"@id/recyclerView\">\n\n        <FrameLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            app:layout_scrollFlags=\"@integer/file_list_toolbar_scroll_flags\">\n\n            <me.zhanghai.android.files.ui.CrossfadeSubtitleToolbar\n                android:id=\"@+id/toolbar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"?actionBarSize\"\n                android:paddingStart=\"@dimen/file_list_toolbar_padding_start\"\n                android:paddingEnd=\"@dimen/file_list_toolbar_padding_end_with_overflow\"\n                app:navigationIcon=\"@drawable/menu_icon_control_normal_24dp\"\n                app:navigationContentDescription=\"@string/open_navigation_drawer\"\n                app:popupTheme=\"?actionBarPopupTheme\"\n                app:subtitleTextAppearance=\"@style/TextAppearance.AppCompat.Widget.ActionBar.Subtitle.Small\" />\n\n            <me.zhanghai.android.files.ui.OverlayToolbar\n                android:id=\"@+id/overlayToolbar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"?actionBarSize\"\n                android:paddingStart=\"@dimen/file_list_toolbar_padding_start\"\n                android:paddingEnd=\"@dimen/file_list_toolbar_padding_end_with_overflow\"\n                android:background=\"?colorAppBarSurface\"\n                android:visibility=\"gone\"\n                app:navigationIcon=\"@drawable/close_icon_control_normal_24dp\"\n                app:popupTheme=\"?actionBarPopupTheme\" />\n        </FrameLayout>\n\n        <me.zhanghai.android.files.filelist.BreadcrumbLayout\n            android:id=\"@+id/breadcrumbLayout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:paddingStart=\"@dimen/content_start_margin_minus_12dp\"\n            android:paddingEnd=\"@dimen/screen_edge_margin_minus_12dp\" />\n    </me.zhanghai.android.files.ui.CoordinatorAppBarLayout>\n</merge>\n"
  },
  {
    "path": "app/src/main/res/layout/file_list_fragment_bottom_bar_include.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<merge\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <me.zhanghai.android.files.ui.BottomBarLayout\n        android:id=\"@+id/bottomBarLayout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"bottom\"\n        android:background=\"?colorSurface\"\n        android:elevation=\"@dimen/design_appbar_elevation\"\n        android:fitsSystemWindows=\"true\"\n        android:theme=\"?actionBarTheme\">\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/bottomToolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?actionBarSize\"\n            android:paddingStart=\"@dimen/file_list_toolbar_padding_start\"\n            android:paddingEnd=\"@dimen/file_list_toolbar_padding_end_no_overflow\"\n            app:navigationIcon=\"@drawable/close_icon_control_normal_24dp\"\n            app:navigationContentDescription=\"@string/close\"\n            app:popupTheme=\"?actionBarPopupTheme\">\n\n            <EditText\n                android:id=\"@+id/bottomCreateFileNameEdit\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:background=\"@null\"\n                android:hint=\"@string/file_list_create_file_name_hint\"\n                android:importantForAutofill=\"no\"\n                android:inputType=\"text\"\n                android:maxLines=\"1\" />\n        </androidx.appcompat.widget.Toolbar>\n    </me.zhanghai.android.files.ui.BottomBarLayout>\n</merge>\n"
  },
  {
    "path": "app/src/main/res/layout/file_list_fragment_content_include.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<merge\n    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\n    <me.zhanghai.android.files.ui.CoordinatorScrollingFrameLayout\n        android:id=\"@+id/contentLayout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <ProgressBar\n            android:id=\"@+id/progress\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:visibility=\"gone\" />\n\n        <TextView\n            android:id=\"@+id/errorText\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:paddingTop=\"12dp\"\n            android:paddingBottom=\"32dp\"\n            android:drawablePadding=\"8dp\"\n            android:gravity=\"center_horizontal\"\n            android:textAppearance=\"?textAppearanceListItem\"\n            android:visibility=\"gone\"\n            app:drawableTopCompat=\"@drawable/error_icon_240dp\" />\n\n        <TextView\n            android:id=\"@+id/emptyView\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:paddingTop=\"12dp\"\n            android:paddingBottom=\"32dp\"\n            android:drawablePadding=\"8dp\"\n            android:gravity=\"center_horizontal\"\n            android:text=\"@string/file_list_empty\"\n            android:textAppearance=\"?textAppearanceListItem\"\n            android:visibility=\"gone\"\n            app:drawableTopCompat=\"@drawable/file_icon_240dp\" />\n\n        <androidx.swiperefreshlayout.widget.ThemedSwipeRefreshLayout\n            android:id=\"@+id/swipeRefreshLayout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\">\n\n            <androidx.recyclerview.widget.RecyclerView\n                android:id=\"@+id/recyclerView\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:paddingBottom=\"@dimen/list_bottom_padding_with_fab\"\n                android:clipToPadding=\"false\"\n                android:fitsSystemWindows=\"true\"\n                tools:listitem=\"@layout/file_item_list\" />\n        </androidx.swiperefreshlayout.widget.ThemedSwipeRefreshLayout>\n    </me.zhanghai.android.files.ui.CoordinatorScrollingFrameLayout>\n</merge>\n"
  },
  {
    "path": "app/src/main/res/layout/file_list_fragment_include.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<merge xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <androidx.drawerlayout.widget.DrawerLayout\n        android:id=\"@+id/drawerLayout\"\n        android:layout_height=\"match_parent\"\n        android:layout_width=\"match_parent\"\n        android:fitsSystemWindows=\"true\">\n\n        <me.zhanghai.android.files.ui.IgnoreFitsSystemWindowsFullyDraggableDrawerContentLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:fitsSystemWindows=\"true\">\n\n            <me.zhanghai.android.files.ui.PersistentBarLayout\n                android:id=\"@+id/persistentBarLayout\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:fitsSystemWindows=\"true\">\n\n                <androidx.coordinatorlayout.widget.CoordinatorLayout\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"match_parent\"\n                    android:fitsSystemWindows=\"true\">\n\n                    <include layout=\"@layout/file_list_fragment_app_bar_include\" />\n\n                    <include layout=\"@layout/file_list_fragment_content_include\" />\n\n                </androidx.coordinatorlayout.widget.CoordinatorLayout>\n\n                <include layout=\"@layout/file_list_fragment_bottom_bar_include\" />\n\n                <include layout=\"@layout/file_list_fragment_speed_dial_include\" />\n            </me.zhanghai.android.files.ui.PersistentBarLayout>\n        </me.zhanghai.android.files.ui.IgnoreFitsSystemWindowsFullyDraggableDrawerContentLayout>\n\n        <!--\n          ~ We have to set android:fitsSystemWindows=\"true\" here to prevent DrawerLayout from\n          ~ applying the window insets as margins for this view.\n          -->\n        <me.zhanghai.android.files.ui.NavigationFrameLayout\n            android:id=\"@+id/navigationFragment\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"match_parent\"\n            android:layout_gravity=\"start\"\n            android:background=\"?colorSurface\"\n            android:fitsSystemWindows=\"true\" />\n    </androidx.drawerlayout.widget.DrawerLayout>\n</merge>\n"
  },
  {
    "path": "app/src/main/res/layout/file_list_fragment_speed_dial_include.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<merge\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <com.leinardi.android.speeddial.SpeedDialOverlayLayout\n        android:id=\"@+id/speedDialOverlayLayout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_gravity=\"fill\"\n        android:fitsSystemWindows=\"true\" />\n\n    <FrameLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:clipChildren=\"false\"\n        android:clipToPadding=\"false\"\n        android:elevation=\"@dimen/sd_open_elevation\"\n        android:fitsSystemWindows=\"true\">\n\n        <me.zhanghai.android.files.ui.ThemedSpeedDialView\n            android:id=\"@+id/speedDialView\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"end|bottom\"\n            android:layout_margin=\"@dimen/screen_edge_margin_minus_16dp\"\n            app:sdMainFabAnimationRotateAngle=\"135\"\n            app:sdMainFabClosedSrc=\"@drawable/add_icon_white_24dp\"\n            app:sdOverlayLayout=\"@id/speedDialOverlayLayout\" />\n    </FrameLayout>\n</merge>\n"
  },
  {
    "path": "app/src/main/res/layout/file_properties_checksum_compare_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<com.google.android.material.textfield.TextInputLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/compareLayout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:hint=\"@string/file_properties_checksum_compare\"\n    app:errorEnabled=\"true\"\n    app:errorIconDrawable=\"@null\"\n    app:expandedHintEnabled=\"false\"\n    app:placeholderText=\"@string/file_properties_checksum_compare_placeholder\">\n\n    <com.google.android.material.textfield.TextInputEditText\n        android:id=\"@+id/compareEdit\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:importantForAutofill=\"no\"\n        android:inputType=\"textMultiLine\" />\n</com.google.android.material.textfield.TextInputLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/file_properties_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<LinearLayout\n    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:paddingTop=\"@dimen/abc_dialog_title_divider_material\"\n    android:orientation=\"vertical\">\n\n    <FrameLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\">\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"1dp\"\n            android:layout_gravity=\"bottom\"\n            android:background=\"?colorControlHighlight\" />\n\n        <com.google.android.material.tabs.TabLayout\n            android:id=\"@+id/tabLayout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            app:tabMode=\"scrollable\"\n            app:tabPaddingStart=\"?dialogPreferredPadding\"\n            app:tabPaddingEnd=\"?dialogPreferredPadding\" />\n    </FrameLayout>\n\n    <me.zhanghai.android.files.ui.WrapFirstPageContentViewPager\n        android:id=\"@+id/viewPager\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\" />\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/file_properties_tab_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <ProgressBar\n        android:id=\"@+id/progress\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        android:visibility=\"gone\" />\n\n    <TextView\n        android:id=\"@+id/errorText\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        android:gravity=\"center_horizontal\"\n        android:textAppearance=\"?textAppearanceListItem\"\n        android:visibility=\"gone\" />\n\n    <androidx.swiperefreshlayout.widget.ThemedSwipeRefreshLayout\n        android:id=\"@+id/swipeRefreshLayout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <androidx.core.widget.NestedScrollView\n            android:id=\"@+id/scrollView\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:fillViewport=\"true\"\n            android:scrollIndicators=\"top|bottom\"\n            android:scrollbars=\"vertical\">\n\n            <LinearLayout\n                android:id=\"@+id/linearLayout\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:paddingStart=\"?dialogPreferredPadding\"\n                android:paddingEnd=\"?dialogPreferredPadding\"\n                android:paddingTop=\"16dp\"\n                android:paddingBottom=\"8dp\"\n                android:divider=\"@drawable/transparent_divider_vertical_16dp\"\n                android:orientation=\"vertical\"\n                android:showDividers=\"middle\" />\n        </androidx.core.widget.NestedScrollView>\n    </androidx.swiperefreshlayout.widget.ThemedSwipeRefreshLayout>\n</FrameLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/file_properties_tab_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<me.zhanghai.android.files.ui.ReadOnlyTextInputLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/textInputLayout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <me.zhanghai.android.files.ui.ReadOnlyTextInputEditText\n        android:id=\"@+id/text\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\" />\n</me.zhanghai.android.files.ui.ReadOnlyTextInputLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/ftp_server_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<androidx.coordinatorlayout.widget.CoordinatorLayout\n    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:fitsSystemWindows=\"true\">\n\n    <me.zhanghai.android.files.ui.CoordinatorAppBarLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?colorAppBarSurface\"\n        android:theme=\"?actionBarTheme\"\n        app:liftOnScroll=\"true\"\n        app:liftOnScrollTargetViewId=\"@id/recycler_view\">\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?actionBarSize\"\n            app:popupTheme=\"?actionBarPopupTheme\" />\n    </me.zhanghai.android.files.ui.CoordinatorAppBarLayout>\n\n    <me.zhanghai.android.files.ui.CoordinatorScrollingFrameLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <fragment\n            android:id=\"@+id/preferenceFragment\"\n            android:name=\"me.zhanghai.android.files.ftpserver.FtpServerPreferenceFragment\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\" />\n    </me.zhanghai.android.files.ui.CoordinatorScrollingFrameLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/image_viewer_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<FrameLayout\n    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.viewpager2.widget.ViewPager2\n        android:id=\"@+id/viewPager\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n\n    <FrameLayout\n        android:id=\"@+id/appBarLayout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?colorAppBarSurface\">\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?actionBarSize\"\n            android:theme=\"?actionBarTheme\"\n            app:popupTheme=\"?actionBarPopupTheme\" />\n    </FrameLayout>\n</FrameLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/image_viewer_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <ProgressBar\n        android:id=\"@+id/progress\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        android:indeterminate=\"true\"\n        android:visibility=\"gone\"\n        style=\"@style/Widget.AppCompat.ProgressBar\" />\n\n    <TextView\n        android:id=\"@+id/errorText\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"center\"\n        android:gravity=\"center_horizontal\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Title\"\n        android:visibility=\"gone\" />\n\n    <com.github.chrisbanes.photoview.PhotoView\n        android:id=\"@+id/image\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:visibility=\"gone\" />\n\n    <me.zhanghai.android.files.ui.SaveStateSubsamplingScaleImageView\n        android:id=\"@+id/largeImage\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:visibility=\"gone\" />\n</FrameLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/lan_smb_server_add_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<me.zhanghai.android.foregroundcompat.ForegroundTextView\n    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=\"?listPreferredItemHeightSmall\"\n    android:paddingStart=\"@dimen/screen_edge_margin\"\n    android:paddingEnd=\"@dimen/screen_edge_margin\"\n    android:drawablePadding=\"@dimen/content_start_from_screen_edge_margin_minus_24dp\"\n    android:ellipsize=\"end\"\n    android:foreground=\"?selectableItemBackground\"\n    android:gravity=\"center_vertical\"\n    android:maxLines=\"1\"\n    android:text=\"@string/storage_add_lan_smb_server_add\"\n    android:textAppearance=\"?textAppearanceListItemSecondary\"\n    app:drawableStartCompat=\"@drawable/add_icon_white_24dp\"\n    app:drawableTint=\"?colorControlNormal\" />\n"
  },
  {
    "path": "app/src/main/res/layout/lan_smb_server_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<me.zhanghai.android.foregroundcompat.ForegroundLinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/itemLayout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"?listPreferredItemHeight\"\n    android:paddingStart=\"@dimen/screen_edge_margin\"\n    android:paddingEnd=\"@dimen/screen_edge_margin\"\n    android:foreground=\"?selectableItemBackground\"\n    android:gravity=\"center_vertical\"\n    android:orientation=\"horizontal\">\n\n    <ImageView\n        android:layout_width=\"@dimen/icon_size\"\n        android:layout_height=\"@dimen/icon_size\"\n        android:scaleType=\"centerCrop\"\n        android:src=\"@drawable/computer_icon_white_24dp\"\n        app:tint=\"?colorControlNormal\" />\n\n    <LinearLayout\n        android:layout_width=\"0dp\"\n        android:layout_weight=\"1\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"@dimen/content_start_from_screen_edge_margin_minus_24dp\"\n        android:orientation=\"vertical\">\n\n        <TextView\n            android:id=\"@+id/hostText\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"end\"\n            android:maxLines=\"1\"\n            android:textAppearance=\"?textAppearanceListItemSecondary\" />\n\n        <TextView\n            android:id=\"@+id/addressText\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"end\"\n            android:maxLines=\"1\"\n            android:textAppearance=\"?textAppearanceListItemSecondary\"\n            android:textColor=\"?android:textColorSecondary\" />\n    </LinearLayout>\n</me.zhanghai.android.foregroundcompat.ForegroundLinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/lan_smb_server_loading_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<me.zhanghai.android.foregroundcompat.ForegroundTextView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"?listPreferredItemHeightSmall\"\n    android:paddingStart=\"@dimen/content_start_margin\"\n    android:paddingEnd=\"@dimen/screen_edge_margin\"\n    android:ellipsize=\"end\"\n    android:foreground=\"?selectableItemBackground\"\n    android:gravity=\"center_vertical\"\n    android:maxLines=\"1\"\n    android:text=\"@string/storage_add_lan_smb_server_loading\"\n    android:textAppearance=\"?textAppearanceListItemSecondary\"\n    android:textColor=\"?android:textColorSecondary\" />\n"
  },
  {
    "path": "app/src/main/res/layout/mode_bit_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:paddingStart=\"5dp\"\n    android:paddingEnd=\"12dp\">\n\n    <CheckBox\n        android:id=\"@+id/modeBitCheck\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:minHeight=\"?listPreferredItemHeightSmall\"\n        android:paddingStart=\"5dp\"\n        android:clickable=\"false\"\n        android:focusable=\"false\"\n        android:textAppearance=\"?textAppearanceListItem\" />\n</FrameLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/name_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:paddingTop=\"@dimen/abc_dialog_title_divider_material\">\n\n    <androidx.core.widget.NestedScrollView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:scrollbars=\"vertical\"\n        android:scrollIndicators=\"top|bottom\">\n\n        <FrameLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:paddingBottom=\"8dp\">\n\n            <include layout=\"@layout/name_dialog_name_include\" />\n        </FrameLayout>\n    </androidx.core.widget.NestedScrollView>\n</FrameLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/name_dialog_name_include.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<merge xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <com.google.android.material.textfield.TextInputLayout\n        android:id=\"@+id/nameLayout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"?dialogPreferredPadding\"\n        android:layout_marginEnd=\"?dialogPreferredPadding\"\n        android:layout_marginTop=\"4dp\">\n\n        <com.google.android.material.textfield.TextInputEditText\n            android:id=\"@+id/nameEdit\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:importantForAutofill=\"no\"\n            android:inputType=\"text\">\n            <requestFocus />\n        </com.google.android.material.textfield.TextInputEditText>\n    </com.google.android.material.textfield.TextInputLayout>\n</merge>\n"
  },
  {
    "path": "app/src/main/res/layout/navigation_divider_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <com.google.android.material.divider.MaterialDivider\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\" />\n</FrameLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/navigation_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<me.zhanghai.android.files.navigation.NavigationRecyclerView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/recyclerView\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"match_parent\"\n    android:clipToPadding=\"false\"\n    android:scrollbarStyle=\"outsideOverlay\"\n    android:scrollbars=\"vertical\" />\n"
  },
  {
    "path": "app/src/main/res/layout/navigation_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<me.zhanghai.android.files.ui.CheckableForegroundLinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/itemLayout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"?listPreferredItemHeightSmall\"\n    android:gravity=\"center_vertical\"\n    android:orientation=\"horizontal\">\n\n    <ImageView\n        android:id=\"@+id/iconImage\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:duplicateParentState=\"true\"\n        android:scaleType=\"centerCrop\" />\n\n    <LinearLayout\n        android:id=\"@+id/textLayout\"\n        android:layout_width=\"0dp\"\n        android:layout_weight=\"1\"\n        android:layout_height=\"wrap_content\"\n        android:duplicateParentState=\"true\"\n        android:orientation=\"vertical\">\n\n        <TextView\n            android:id=\"@+id/titleText\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:duplicateParentState=\"true\"\n            android:ellipsize=\"end\"\n            android:maxLines=\"1\" />\n\n        <me.zhanghai.android.files.ui.AutoGoneTextView\n            android:id=\"@+id/subtitleText\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:duplicateParentState=\"true\"\n            android:ellipsize=\"end\"\n            android:maxLines=\"1\" />\n    </LinearLayout>\n</me.zhanghai.android.files.ui.CheckableForegroundLinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/permission_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<me.zhanghai.android.foregroundcompat.ForegroundLinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/itemLayout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:minHeight=\"?listPreferredItemHeightSmall\"\n    android:paddingStart=\"?dialogPreferredPadding\"\n    android:paddingEnd=\"?dialogPreferredPadding\"\n    android:paddingTop=\"8dp\"\n    android:paddingBottom=\"8dp\"\n    android:foreground=\"?selectableItemBackground\"\n    android:gravity=\"center_vertical\"\n    android:orientation=\"vertical\">\n\n    <TextView\n        android:id=\"@+id/labelText\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:textAppearance=\"?textAppearanceListItemSecondary\" />\n\n    <TextView\n        android:id=\"@+id/nameText\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:textAppearance=\"?textAppearanceListItemSecondary\"\n        android:textColor=\"?android:textColorSecondary\" />\n\n    <me.zhanghai.android.files.ui.AutoGoneTextView\n        android:id=\"@+id/descriptionText\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:textAppearance=\"?textAppearanceListItemSecondary\"\n        android:textColor=\"?android:textColorSecondary\" />\n</me.zhanghai.android.foregroundcompat.ForegroundLinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/permission_list_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:paddingTop=\"@dimen/abc_dialog_title_divider_material\">\n\n    <FrameLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:minHeight=\"?listPreferredItemHeight\">\n\n        <ProgressBar\n            android:id=\"@+id/progress\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:visibility=\"gone\" />\n\n        <TextView\n            android:id=\"@+id/errorText\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:gravity=\"center_horizontal\"\n            android:textAppearance=\"?textAppearanceListItem\"\n            android:visibility=\"gone\" />\n\n        <TextView\n            android:id=\"@+id/emptyView\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:gravity=\"center_horizontal\"\n            android:text=\"@string/empty\"\n            android:textAppearance=\"?textAppearanceListItem\"\n            android:visibility=\"gone\" />\n\n        <androidx.recyclerview.widget.RecyclerView\n            android:id=\"@+id/recyclerView\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:clipToPadding=\"false\"\n            android:scrollIndicators=\"top|bottom\"\n            android:scrollbars=\"vertical\" />\n    </FrameLayout>\n</FrameLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/principal_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<me.zhanghai.android.files.ui.CheckableForegroundLinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/itemLayout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"?listPreferredItemHeight\"\n    android:paddingStart=\"?dialogPreferredPadding\"\n    android:paddingEnd=\"?dialogPreferredPadding\"\n    android:foreground=\"?selectableItemBackground\"\n    android:gravity=\"center_vertical\"\n    android:orientation=\"horizontal\">\n\n    <ImageView\n        android:id=\"@+id/iconImage\"\n        android:layout_width=\"@dimen/icon_size\"\n        android:layout_height=\"@dimen/icon_size\"\n        android:scaleType=\"centerCrop\" />\n\n    <LinearLayout\n        android:layout_width=\"0dp\"\n        android:layout_weight=\"1\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"?dialogPreferredPadding\"\n        android:layout_marginEnd=\"?dialogPreferredPadding\"\n        android:orientation=\"vertical\">\n\n        <TextView\n            android:id=\"@+id/principalText\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"end\"\n            android:maxLines=\"1\"\n            android:textAppearance=\"?textAppearanceListItemSecondary\" />\n\n        <me.zhanghai.android.files.ui.AutoGoneTextView\n            android:id=\"@+id/labelText\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"end\"\n            android:maxLines=\"1\"\n            android:textAppearance=\"?textAppearanceListItemSecondary\"\n            android:textColor=\"?android:textColorSecondary\" />\n    </LinearLayout>\n\n    <RadioButton\n        android:id=\"@+id/radio\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"-4dp\"\n        android:clickable=\"false\"\n        android:duplicateParentState=\"true\"\n        android:focusable=\"false\" />\n</me.zhanghai.android.files.ui.CheckableForegroundLinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/set_mode_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:paddingTop=\"@dimen/abc_dialog_title_divider_material\">\n\n    <androidx.core.widget.NestedScrollView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:scrollbars=\"vertical\"\n        android:scrollIndicators=\"top|bottom\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:paddingStart=\"?dialogPreferredPadding\"\n            android:paddingEnd=\"?dialogPreferredPadding\"\n            android:paddingTop=\"8dp\"\n            android:clipToPadding=\"false\"\n            android:orientation=\"vertical\">\n\n            <me.zhanghai.android.files.ui.ReadOnlyTextInputLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:hint=\"@string/file_properties_permission_owner\">\n\n                <me.zhanghai.android.files.ui.ReadOnlyTextInputEditText\n                    android:id=\"@+id/ownerText\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\" />\n\n                <me.zhanghai.android.files.ui.DropDownView\n                    android:id=\"@+id/ownerDropDown\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"0dp\" />\n            </me.zhanghai.android.files.ui.ReadOnlyTextInputLayout>\n\n            <me.zhanghai.android.files.ui.ReadOnlyTextInputLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"16dp\"\n                android:hint=\"@string/file_properties_permission_group\">\n\n                <me.zhanghai.android.files.ui.ReadOnlyTextInputEditText\n                    android:id=\"@+id/groupText\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\" />\n\n                <me.zhanghai.android.files.ui.DropDownView\n                    android:id=\"@+id/groupDropDown\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"0dp\" />\n            </me.zhanghai.android.files.ui.ReadOnlyTextInputLayout>\n\n            <me.zhanghai.android.files.ui.ReadOnlyTextInputLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"16dp\"\n                android:hint=\"@string/file_properties_permission_set_mode_others\">\n\n                <me.zhanghai.android.files.ui.ReadOnlyTextInputEditText\n                    android:id=\"@+id/othersText\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\" />\n\n                <me.zhanghai.android.files.ui.DropDownView\n                    android:id=\"@+id/othersDropDown\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"0dp\" />\n            </me.zhanghai.android.files.ui.ReadOnlyTextInputLayout>\n\n            <me.zhanghai.android.files.ui.ReadOnlyTextInputLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"16dp\"\n                android:hint=\"@string/file_properties_permission_set_mode_special\">\n\n                <me.zhanghai.android.files.ui.ReadOnlyTextInputEditText\n                    android:id=\"@+id/specialText\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\" />\n\n                <me.zhanghai.android.files.ui.DropDownView\n                    android:id=\"@+id/specialDropDown\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"0dp\" />\n            </me.zhanghai.android.files.ui.ReadOnlyTextInputLayout>\n\n            <CheckBox\n                android:id=\"@+id/recursiveCheck\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginStart=\"-7dp\"\n                android:layout_marginTop=\"8dp\"\n                android:paddingStart=\"@dimen/dialog_padding_minus_7dp\"\n                android:paddingTop=\"8dp\"\n                android:paddingBottom=\"8dp\"\n                android:text=\"@string/file_properties_permission_recursive\"\n                android:textAppearance=\"?textAppearanceListItem\" />\n\n            <CheckBox\n                android:id=\"@+id/uppercaseXCheck\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginStart=\"-7dp\"\n                android:paddingStart=\"@dimen/dialog_padding_minus_7dp\"\n                android:paddingTop=\"8dp\"\n                android:paddingBottom=\"8dp\"\n                android:text=\"@string/file_properties_permission_set_mode_uppercase_x\"\n                android:textAppearance=\"?textAppearanceListItem\" />\n        </LinearLayout>\n    </androidx.core.widget.NestedScrollView>\n</FrameLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/set_principal_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<LinearLayout\n    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:paddingTop=\"@dimen/abc_dialog_title_divider_material\"\n    android:orientation=\"vertical\">\n\n    <EditText\n        android:id=\"@+id/filterEdit\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"?listPreferredItemHeightSmall\"\n        android:paddingStart=\"?dialogPreferredPadding\"\n        android:paddingEnd=\"?dialogPreferredPadding\"\n        android:background=\"@null\"\n        android:drawablePadding=\"24dp\"\n        app:drawableStartCompat=\"@drawable/filter_icon_control_normal_24dp\"\n        android:hint=\"@string/file_properties_permission_set_principal_filter_hint\"\n        android:importantForAutofill=\"no\"\n        android:inputType=\"text\"\n        android:textAppearance=\"?textAppearanceListItem\" />\n\n    <FrameLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:minHeight=\"?listPreferredItemHeight\"\n        android:layout_weight=\"1\">\n\n        <ProgressBar\n            android:id=\"@+id/progress\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:visibility=\"gone\" />\n\n        <TextView\n            android:id=\"@+id/errorText\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:gravity=\"center_horizontal\"\n            android:textAppearance=\"?textAppearanceListItem\"\n            android:visibility=\"gone\" />\n\n        <TextView\n            android:id=\"@+id/emptyView\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:gravity=\"center_horizontal\"\n            android:text=\"@string/empty\"\n            android:textAppearance=\"?textAppearanceListItem\"\n            android:visibility=\"gone\" />\n\n        <androidx.recyclerview.widget.RecyclerView\n            android:id=\"@+id/recyclerView\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:clipToPadding=\"false\"\n            android:scrollIndicators=\"top|bottom\"\n            android:scrollbars=\"vertical\" />\n    </FrameLayout>\n\n    <CheckBox\n        android:id=\"@+id/recursiveCheck\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:minHeight=\"?listPreferredItemHeightSmall\"\n        android:layout_marginStart=\"@dimen/dialog_padding_minus_4dp\"\n        android:paddingStart=\"@dimen/dialog_padding_minus_4dp\"\n        android:paddingEnd=\"?dialogPreferredPadding\"\n        android:paddingTop=\"8dp\"\n        android:paddingBottom=\"8dp\"\n        android:text=\"@string/file_properties_permission_recursive\"\n        android:textAppearance=\"?textAppearanceListItem\" />\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/set_selinux_context_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:paddingTop=\"@dimen/abc_dialog_title_divider_material\">\n\n    <androidx.core.widget.NestedScrollView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:scrollbars=\"vertical\"\n        android:scrollIndicators=\"top|bottom\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:paddingStart=\"?dialogPreferredPadding\"\n            android:paddingEnd=\"?dialogPreferredPadding\"\n            android:paddingTop=\"4dp\"\n            android:clipToPadding=\"false\"\n            android:orientation=\"vertical\">\n\n            <com.google.android.material.textfield.TextInputLayout\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\">\n\n                <com.google.android.material.textfield.TextInputEditText\n                    android:id=\"@+id/seLinuxContextEdit\"\n                    android:layout_width=\"match_parent\"\n                    android:layout_height=\"wrap_content\"\n                    android:importantForAutofill=\"no\"\n                    android:inputType=\"text\">\n                    <requestFocus />\n                </com.google.android.material.textfield.TextInputEditText>\n            </com.google.android.material.textfield.TextInputLayout>\n\n            <CheckBox\n                android:id=\"@+id/recursiveCheck\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginStart=\"-7dp\"\n                android:layout_marginTop=\"8dp\"\n                android:paddingStart=\"@dimen/dialog_padding_minus_7dp\"\n                android:paddingTop=\"8dp\"\n                android:paddingBottom=\"8dp\"\n                android:text=\"@string/file_properties_permission_recursive\"\n                android:textAppearance=\"?textAppearanceListItem\" />\n        </LinearLayout>\n    </androidx.core.widget.NestedScrollView>\n</FrameLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/settings_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<androidx.coordinatorlayout.widget.CoordinatorLayout\n    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:fitsSystemWindows=\"true\">\n\n    <me.zhanghai.android.files.ui.CoordinatorAppBarLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?colorAppBarSurface\"\n        android:theme=\"?actionBarTheme\"\n        app:liftOnScroll=\"true\"\n        app:liftOnScrollTargetViewId=\"@id/recycler_view\">\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?actionBarSize\"\n            app:popupTheme=\"?actionBarPopupTheme\" />\n    </me.zhanghai.android.files.ui.CoordinatorAppBarLayout>\n\n    <me.zhanghai.android.files.ui.CoordinatorScrollingFrameLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <fragment\n            android:id=\"@+id/preferenceFragment\"\n            android:name=\"me.zhanghai.android.files.settings.SettingsPreferenceFragment\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\" />\n    </me.zhanghai.android.files.ui.CoordinatorScrollingFrameLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/standard_directory_list_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<androidx.coordinatorlayout.widget.CoordinatorLayout\n    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:fitsSystemWindows=\"true\">\n\n    <me.zhanghai.android.files.ui.CoordinatorAppBarLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?colorAppBarSurface\"\n        android:theme=\"?actionBarTheme\"\n        app:liftOnScroll=\"true\"\n        app:liftOnScrollTargetViewId=\"@id/recycler_view\">\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?actionBarSize\"\n            app:popupTheme=\"?actionBarPopupTheme\" />\n    </me.zhanghai.android.files.ui.CoordinatorAppBarLayout>\n\n    <me.zhanghai.android.files.ui.CoordinatorScrollingFrameLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <fragment\n            android:id=\"@+id/preferenceFragment\"\n            android:name=\"me.zhanghai.android.files.settings.StandardDirectoryListPreferenceFragment\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\" />\n    </me.zhanghai.android.files.ui.CoordinatorScrollingFrameLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/storage_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<me.zhanghai.android.foregroundcompat.ForegroundLinearLayout\n    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=\"@dimen/two_line_list_item_height\"\n    android:background=\"?android:colorBackground\"\n    android:foreground=\"?selectableItemBackground\"\n    android:gravity=\"center_vertical\"\n    android:orientation=\"horizontal\">\n\n    <ImageView\n        android:id=\"@+id/iconImage\"\n        android:layout_width=\"@dimen/icon_size\"\n        android:layout_height=\"@dimen/icon_size\"\n        android:layout_marginStart=\"@dimen/screen_edge_margin\"\n        android:layout_marginEnd=\"@dimen/content_start_from_screen_edge_margin_minus_24dp\"\n        android:scaleType=\"centerCrop\"\n        app:tint=\"?colorControlNormal\"/>\n\n    <LinearLayout\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:layout_weight=\"1\"\n        android:orientation=\"vertical\">\n\n        <!-- Lint says android:ellipsize=\"middle\" and android:maxLines=\"1\" crashes. -->\n        <TextView\n            android:id=\"@+id/nameText\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"end\"\n            android:maxLines=\"1\"\n            android:textAppearance=\"?textAppearanceListItem\"\n            android:textColor=\"@color/storage_name\" />\n\n        <TextView\n            android:id=\"@+id/descriptionText\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:ellipsize=\"end\"\n            android:maxLines=\"1\"\n            android:textAppearance=\"?textAppearanceListItemSecondary\"\n            android:textColor=\"?android:textColorSecondary\" />\n    </LinearLayout>\n\n    <ImageView\n        android:id=\"@+id/dragHandleView\"\n        android:layout_width=\"@dimen/touch_target_size\"\n        android:layout_height=\"@dimen/touch_target_size\"\n        android:layout_marginStart=\"@dimen/content_start_from_screen_edge_margin_minus_36dp_considering_at_least_8dp\"\n        android:layout_marginEnd=\"@dimen/screen_edge_margin_minus_12dp_at_least_8dp\"\n        android:padding=\"@dimen/touch_target_icon_padding\"\n        android:src=\"@drawable/drag_handle_icon_white_24dp\"\n        app:tint=\"?colorControlNormal\" />\n</me.zhanghai.android.foregroundcompat.ForegroundLinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/storage_list_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2020 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<androidx.coordinatorlayout.widget.CoordinatorLayout\n    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:fitsSystemWindows=\"true\">\n\n    <me.zhanghai.android.files.ui.CoordinatorAppBarLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?colorAppBarSurface\"\n        android:theme=\"?actionBarTheme\"\n        app:liftOnScroll=\"true\"\n        app:liftOnScrollTargetViewId=\"@id/recyclerView\">\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?actionBarSize\"\n            app:popupTheme=\"?actionBarPopupTheme\" />\n    </me.zhanghai.android.files.ui.CoordinatorAppBarLayout>\n\n    <me.zhanghai.android.files.ui.CoordinatorScrollingFrameLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <TextView\n            android:id=\"@+id/emptyView\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:text=\"@string/storage_list_empty\"\n            android:textAppearance=\"?textAppearanceListItem\"\n            android:visibility=\"gone\" />\n\n        <androidx.recyclerview.widget.RecyclerView\n            android:id=\"@+id/recyclerView\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:paddingBottom=\"@dimen/list_bottom_padding_with_fab\"\n            android:clipToPadding=\"false\"\n            android:fitsSystemWindows=\"true\"\n            android:scrollbarStyle=\"outsideOverlay\"\n            android:scrollbars=\"vertical\" />\n\n        <!-- Additional layout to receive window insets as margin. -->\n        <FrameLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"end|bottom\">\n\n            <com.google.android.material.floatingactionbutton.FloatingActionButton\n                android:id=\"@+id/fab\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_margin=\"@dimen/screen_edge_margin\"\n                android:src=\"@drawable/add_icon_white_24dp\"\n                android:theme=\"?actionBarTheme\" />\n        </FrameLayout>\n    </me.zhanghai.android.files.ui.CoordinatorScrollingFrameLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/switch_preference_compat_widget.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<!-- @see @androidx.preference:preference:layout/preference_widget_switch_compat -->\n<com.google.android.material.switchmaterial.SwitchMaterial\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/switchWidget\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@null\"\n    android:clickable=\"false\"\n    android:focusable=\"false\" />\n"
  },
  {
    "path": "app/src/main/res/layout/switch_preference_compat_widget_material3.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<!-- @see @androidx.preference:preference:layout/preference_widget_switch_compat -->\n<com.google.android.material.materialswitch.MaterialSwitch\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/switchWidget\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@null\"\n    android:clickable=\"false\"\n    android:focusable=\"false\" />\n"
  },
  {
    "path": "app/src/main/res/layout/text_editor_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<androidx.coordinatorlayout.widget.CoordinatorLayout\n    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:fitsSystemWindows=\"true\">\n\n    <me.zhanghai.android.files.ui.CoordinatorAppBarLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"?colorAppBarSurface\"\n        android:theme=\"?actionBarTheme\"\n        app:liftOnScroll=\"true\"\n        app:liftOnScrollTargetViewId=\"@id/scrollView\">\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?actionBarSize\"\n            app:popupTheme=\"?actionBarPopupTheme\" />\n    </me.zhanghai.android.files.ui.CoordinatorAppBarLayout>\n\n    <me.zhanghai.android.files.ui.CoordinatorScrollingFrameLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n\n        <ProgressBar\n            android:id=\"@+id/progress\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:visibility=\"gone\" />\n\n        <TextView\n            android:id=\"@+id/errorText\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:paddingTop=\"12dp\"\n            android:paddingBottom=\"32dp\"\n            android:drawablePadding=\"8dp\"\n            android:gravity=\"center_horizontal\"\n            android:textAppearance=\"?textAppearanceListItem\"\n            android:visibility=\"gone\"\n            app:drawableTopCompat=\"@drawable/error_icon_240dp\" />\n\n        <!-- TODO: Use a WebView for large amount of text. -->\n        <me.zhanghai.android.fastscroll.FastScrollNestedScrollView\n            android:id=\"@+id/scrollView\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            android:clipToPadding=\"false\"\n            android:fillViewport=\"true\">\n\n            <me.zhanghai.android.files.ui.ScrollingChildEditText\n                android:id=\"@+id/textEdit\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:padding=\"@dimen/screen_edge_margin\"\n                android:background=\"@null\"\n                android:gravity=\"top\"\n                android:importantForAutofill=\"no\"\n                android:inputType=\"textMultiLine\"\n                android:textAppearance=\"?textAppearanceListItemSecondary\">\n                <requestFocus />\n            </me.zhanghai.android.files.ui.ScrollingChildEditText>\n        </me.zhanghai.android.fastscroll.FastScrollNestedScrollView>\n    </me.zhanghai.android.files.ui.CoordinatorScrollingFrameLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "app/src/main/res/layout-sw600dp-land/file_list_fragment_include.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<merge xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <me.zhanghai.android.files.ui.PersistentBarLayout\n        android:id=\"@+id/persistentBarLayout\"\n        android:layout_height=\"match_parent\"\n        android:layout_width=\"match_parent\"\n        android:fitsSystemWindows=\"true\">\n\n        <LinearLayout\n            android:layout_height=\"match_parent\"\n            android:layout_width=\"match_parent\"\n            android:fitsSystemWindows=\"true\"\n            android:orientation=\"vertical\">\n\n            <include layout=\"@layout/file_list_fragment_app_bar_include\" />\n\n            <me.zhanghai.android.files.ui.PersistentDrawerLayout\n                android:id=\"@+id/persistentDrawerLayout\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"0dp\"\n                android:layout_weight=\"1\">\n\n                <me.zhanghai.android.files.ui.NavigationFrameLayout\n                    android:id=\"@+id/navigationFragment\"\n                    android:layout_width=\"wrap_content\"\n                    android:layout_height=\"match_parent\"\n                    android:layout_gravity=\"start\"\n                    android:background=\"?colorSurface\" />\n\n                <include layout=\"@layout/file_list_fragment_content_include\" />\n            </me.zhanghai.android.files.ui.PersistentDrawerLayout>\n        </LinearLayout>\n\n        <include layout=\"@layout/file_list_fragment_bottom_bar_include\" />\n\n        <include layout=\"@layout/file_list_fragment_speed_dial_include\" />\n    </me.zhanghai.android.files.ui.PersistentBarLayout>\n</merge>\n"
  },
  {
    "path": "app/src/main/res/menu/file_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item\n        android:id=\"@+id/action_open_with\"\n        android:title=\"@string/file_item_action_open_with\" />\n\n    <item\n        android:id=\"@+id/action_cut\"\n        android:title=\"@string/cut\" />\n\n    <item\n        android:id=\"@+id/action_copy\"\n        android:title=\"@string/copy\" />\n\n    <item\n        android:id=\"@+id/action_delete\"\n        android:title=\"@string/delete\" />\n\n    <item\n        android:id=\"@+id/action_rename\"\n        android:title=\"@string/rename\" />\n\n    <item\n        android:id=\"@+id/action_extract\"\n        android:title=\"@string/file_item_action_extract\" />\n\n    <item\n        android:id=\"@+id/action_archive\"\n        android:title=\"@string/file_item_action_archive\" />\n\n    <item\n        android:id=\"@+id/action_share\"\n        android:title=\"@string/share\" />\n\n    <item\n        android:id=\"@+id/action_copy_path\"\n        android:title=\"@string/file_item_action_copy_path\" />\n\n    <item\n        android:id=\"@+id/action_add_bookmark\"\n        android:title=\"@string/file_item_action_add_bookmark\" />\n\n    <item\n        android:id=\"@+id/action_create_shortcut\"\n        android:title=\"@string/file_item_action_create_shortcut\" />\n\n    <item\n        android:id=\"@+id/action_properties\"\n        android:title=\"@string/file_item_action_properties\" />\n</menu>\n"
  },
  {
    "path": "app/src/main/res/menu/file_list.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<menu\n    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_search\"\n        android:icon=\"@drawable/search_icon_control_normal_24dp\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/search\"\n        app:actionViewClass=\"me.zhanghai.android.files.ui.FixQueryChangeSearchView\"\n        app:showAsAction=\"always|collapseActionView\" />\n\n    <item\n        android:id=\"@+id/action_view_sort\"\n        android:icon=\"@drawable/sort_icon_control_normal_24dp\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/file_list_action_view_sort\"\n        app:showAsAction=\"always\">\n        <menu>\n            <group\n                android:id=\"@+id/group_view\"\n                android:checkableBehavior=\"single\">\n                <item\n                    android:id=\"@+id/action_view_list\"\n                    android:title=\"@string/file_list_action_view_list\" />\n                <item\n                    android:id=\"@+id/action_view_grid\"\n                    android:title=\"@string/file_list_action_view_grid\" />\n            </group>\n            <group\n                android:id=\"@+id/group_sort\"\n                android:checkableBehavior=\"single\">\n                <item\n                    android:id=\"@+id/action_sort_by_name\"\n                    android:title=\"@string/file_list_action_sort_by_name\" />\n                <item\n                    android:id=\"@+id/action_sort_by_type\"\n                    android:title=\"@string/file_list_action_sort_by_type\" />\n                <item\n                    android:id=\"@+id/action_sort_by_size\"\n                    android:title=\"@string/file_list_action_sort_by_size\" />\n                <item\n                    android:id=\"@+id/action_sort_by_last_modified\"\n                    android:title=\"@string/file_list_action_sort_by_last_modified\" />\n                <item\n                    android:id=\"@+id/action_sort_order_ascending\"\n                    android:checkable=\"true\"\n                    android:title=\"@string/file_list_action_sort_order_ascending\" />\n                <item\n                    android:id=\"@+id/action_sort_directories_first\"\n                    android:checkable=\"true\"\n                    android:title=\"@string/file_list_action_sort_directories_first\" />\n            </group>\n            <item\n                android:id=\"@+id/action_view_sort_path_specific\"\n                android:checkable=\"true\"\n                android:title=\"@string/file_list_action_view_sort_path_specific\" />\n        </menu>\n    </item>\n\n    <item\n        android:id=\"@+id/action_new_task\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/file_list_action_new_task\"\n        app:showAsAction=\"never\" />\n\n    <item\n        android:id=\"@+id/action_navigate_up\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/file_list_action_navigate_up\"\n        app:showAsAction=\"never\" />\n\n    <item\n        android:id=\"@+id/action_navigate_to\"\n        android:alphabeticShortcut=\"l\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/file_list_action_navigate_to\"\n        app:showAsAction=\"never\" />\n\n    <item\n        android:id=\"@+id/action_refresh\"\n        android:alphabeticShortcut=\"r\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/refresh\"\n        app:showAsAction=\"never\" />\n\n    <item\n        android:id=\"@+id/action_select_all\"\n        android:alphabeticShortcut=\"a\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/select_all\"\n        app:showAsAction=\"never\" />\n\n    <item\n        android:id=\"@+id/action_show_hidden_files\"\n        android:alphabeticShortcut=\"h\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/file_list_action_show_hidden_files\"\n        android:checkable=\"true\"\n        app:showAsAction=\"never\" />\n\n    <item\n        android:id=\"@+id/action_share\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/share\"\n        app:showAsAction=\"never\" />\n\n    <item\n        android:id=\"@+id/action_copy_path\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/file_list_action_copy_path\"\n        app:showAsAction=\"never\" />\n\n    <item\n        android:id=\"@+id/action_open_in_terminal\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/file_list_action_open_in_terminal\"\n        app:showAsAction=\"never\" />\n\n    <item\n        android:id=\"@+id/action_add_bookmark\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/file_list_action_add_bookmark\"\n        app:showAsAction=\"never\" />\n\n    <item\n        android:id=\"@+id/action_create_shortcut\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/file_list_action_create_shortcut\"\n        app:showAsAction=\"never\" />\n</menu>\n"
  },
  {
    "path": "app/src/main/res/menu/file_list_breadcrumb.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item\n        android:id=\"@+id/action_copy_path\"\n        android:title=\"@string/file_list_breadcrumb_action_copy_path\" />\n\n    <item\n        android:id=\"@+id/action_open_in_new_task\"\n        android:title=\"@string/file_list_breadcrumb_action_open_in_new_task\" />\n</menu>\n"
  },
  {
    "path": "app/src/main/res/menu/file_list_paste.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<menu\n    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_paste\"\n        android:alphabeticShortcut=\"v\"\n        android:icon=\"@drawable/paste_icon_control_normal_24dp\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/paste\"\n        app:showAsAction=\"always\" />\n</menu>\n"
  },
  {
    "path": "app/src/main/res/menu/file_list_pick.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<menu\n    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_open\"\n        android:icon=\"@drawable/check_icon_control_normal_24dp\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/select\"\n        app:showAsAction=\"always\" />\n\n    <item\n        android:id=\"@+id/action_create\"\n        android:icon=\"@drawable/save_icon_control_normal_24dp\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/save\"\n        app:showAsAction=\"always\" />\n\n    <item\n        android:id=\"@+id/action_select_all\"\n        android:alphabeticShortcut=\"a\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/select_all\"\n        app:showAsAction=\"never\" />\n</menu>\n"
  },
  {
    "path": "app/src/main/res/menu/file_list_pick_bottom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<menu\n    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_open\"\n        android:icon=\"@drawable/check_icon_control_normal_24dp\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/select\"\n        app:showAsAction=\"always\" />\n\n    <item\n        android:id=\"@+id/action_create\"\n        android:icon=\"@drawable/save_icon_control_normal_24dp\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/save\"\n        app:showAsAction=\"always\" />\n</menu>\n"
  },
  {
    "path": "app/src/main/res/menu/file_list_select.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<menu\n    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_cut\"\n        android:alphabeticShortcut=\"x\"\n        android:icon=\"@drawable/cut_icon_control_normal_24dp\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/cut\"\n        app:showAsAction=\"always\" />\n\n    <item\n        android:id=\"@+id/action_copy\"\n        android:alphabeticShortcut=\"c\"\n        android:icon=\"@drawable/copy_icon_control_normal_24dp\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/copy\"\n        app:showAsAction=\"always\" />\n\n    <item\n        android:id=\"@+id/action_delete\"\n        android:icon=\"@drawable/delete_icon_control_normal_24dp\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/delete\"\n        app:showAsAction=\"always\" />\n\n    <item\n        android:id=\"@+id/action_extract\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/file_list_select_action_extract\"\n        app:showAsAction=\"never\" />\n\n    <item\n        android:id=\"@+id/action_archive\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/file_list_select_action_archive\"\n        app:showAsAction=\"never\" />\n\n    <item\n        android:id=\"@+id/action_share\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/share\"\n        app:showAsAction=\"never\" />\n\n    <item\n        android:id=\"@+id/action_select_all\"\n        android:alphabeticShortcut=\"a\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/select_all\"\n        app:showAsAction=\"never\" />\n</menu>\n"
  },
  {
    "path": "app/src/main/res/menu/file_list_speed_dial.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item\n        android:id=\"@+id/action_create_directory\"\n        android:icon=\"@drawable/directory_icon_white_24dp\"\n        android:title=\"@string/file_list_action_create_directory\" />\n\n    <item\n        android:id=\"@+id/action_create_file\"\n        android:icon=\"@drawable/file_icon_white_24dp\"\n        android:title=\"@string/file_list_action_create_file\" />\n</menu>\n"
  },
  {
    "path": "app/src/main/res/menu/image_viewer.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<menu\n    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_delete\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/delete\"\n        app:showAsAction=\"never\" />\n\n    <item\n        android:id=\"@+id/action_share\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/share\"\n        app:showAsAction=\"never\" />\n</menu>\n"
  },
  {
    "path": "app/src/main/res/menu/text_editor.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<menu\n    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\n    <item\n        android:id=\"@+id/action_save\"\n        android:icon=\"@drawable/save_icon_control_normal_24dp\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/save\"\n        app:showAsAction=\"always\"\n        tools:ignore=\"AlwaysShowAction\" />\n\n    <item\n        android:id=\"@+id/action_reload\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/reload\"\n        app:showAsAction=\"never\" />\n\n    <item\n        android:id=\"@+id/action_encoding\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/text_editor_encoding\"\n        app:showAsAction=\"never\">\n        <menu />\n    </item>\n</menu>\n"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/directory_shortcut_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/shortcut_icon_background\" />\n    <foreground android:drawable=\"@drawable/directory_shortcut_icon_foreground\" />\n</adaptive-icon>\n"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/downloads_shortcut_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/shortcut_icon_background\" />\n    <foreground android:drawable=\"@drawable/downloads_shortcut_icon_foreground\" />\n</adaptive-icon>\n"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/file_shortcut_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/shortcut_icon_background\" />\n    <foreground android:drawable=\"@drawable/file_shortcut_icon_foreground\" />\n</adaptive-icon>\n"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ftp_server_shortcut_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/shortcut_icon_background\" />\n    <foreground android:drawable=\"@drawable/ftp_server_shortcut_icon_foreground\" />\n</adaptive-icon>\n"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/launcher_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@color/color_primary_light\" />\n    <foreground android:drawable=\"@mipmap/launcher_icon_foreground\" />\n    <monochrome android:drawable=\"@drawable/launcher_icon_monochrome\" />\n</adaptive-icon>\n"
  },
  {
    "path": "app/src/main/res/raw/keep.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:keep=\"@style/Theme.MaterialFiles*\" />\n"
  },
  {
    "path": "app/src/main/res/raw/licenses.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<notices>\n\n    <notice>\n        <name>Material Files</name>\n        <url>https://github.com/zhanghai/MaterialFiles</url>\n        <copyright>Copyright 2018 Hai Zhang</copyright>\n        <license>GNU General Public License 3.0</license>\n    </notice>\n\n    <notice>\n        <name>AndroidSVG</name>\n        <url>https://github.com/BigBadaboom/androidsvg</url>\n        <copyright>Copyright 2013 Paul LeBeau, Cave Rock Software Ltd.</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>SubsamplingScaleImageView</name>\n        <url>https://github.com/davemorrissey/subsampling-scale-image-view</url>\n        <copyright>Copyright 2016 David Morrissey</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>FullDraggableDrawer</name>\n        <url>https://github.com/PureWriter/FullDraggableDrawer</url>\n        <copyright>Copyright 2021 Drakeet Xu</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>Stetho</name>\n        <url>https://github.com/facebook/stetho</url>\n        <copyright>Copyright 2015 Facebook Inc.</copyright>\n        <license>BSD 3-Clause License</license>\n    </notice>\n\n    <notice>\n        <name>dav4jvm</name>\n        <url>https://github.com/bitfireAT/dav4jvm</url>\n        <copyright>Copyright 2015 dav4jvm contributors</copyright>\n        <license>Mozilla Public License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>PhotoView</name>\n        <url>https://github.com/chrisbanes/PhotoView</url>\n        <copyright>Copyright 2011, 2012 Chris Banes</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>libsu</name>\n        <url>https://github.com/topjohnwu/libsu</url>\n        <copyright>Copyright 2021 John \"topjohnwu\" Wu</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>Material Shadow 9-Patch</name>\n        <url>https://github.com/h6ah4i/android-materialshadowninepatch</url>\n        <copyright>Copyright 2015 Haruki Hasegawa</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>Advanced RecyclerView</name>\n        <url>https://github.com/h6ah4i/android-advancedrecyclerview</url>\n        <copyright>Copyright 2015 Haruki Hasegawa</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>SMBJ</name>\n        <url>https://github.com/hierynomus/smbj</url>\n        <copyright>Copyright 2016 SMBJ Contributors</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>ThreeTenABP</name>\n        <url>https://github.com/JakeWharton/ThreeTenABP</url>\n        <copyright>Copyright 2015 Jake Wharton</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>Floating Action Button Speed Dial</name>\n        <url>https://github.com/leinardi/FloatingActionButtonSpeedDial</url>\n        <copyright>Copyright 2018 Roberto Leinardi</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>SMBJ-RPC</name>\n        <url>https://github.com/rapid7/smbj-rpc</url>\n        <copyright>Copyright 2017 Rapid7 Inc.</copyright>\n        <license>BSD 3-Clause License</license>\n    </notice>\n\n    <notice>\n        <name>Apache Commons Net</name>\n        <url>https://commons.apache.org/proper/commons-net/</url>\n        <copyright>Copyright 2003 The Apache Software Foundation</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>Insetter</name>\n        <url>https://github.com/chrisbanes/insetter</url>\n        <copyright>Copyright 2019 Google LLC</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>RikkaX</name>\n        <url>https://github.com/RikkaApps/RikkaX</url>\n        <copyright>Copyright 2017 Rikka</copyright>\n        <license>MIT License</license>\n    </notice>\n\n    <notice>\n        <name>Shizuku-API</name>\n        <url>https://github.com/RikkaApps/Shizuku-API</url>\n        <copyright>Copyright 2021 RikkaW</copyright>\n        <license>MIT License</license>\n    </notice>\n\n    <notice>\n        <name>jcifs-ng</name>\n        <url>https://github.com/AgNO3/jcifs-ng</url>\n        <copyright>Copyright 2016 AgNO3 Gmbh &amp; Co. KG</copyright>\n        <license>GNU Lesser General Public License 2.1</license>\n    </notice>\n\n    <notice>\n        <name>Coil</name>\n        <url>https://github.com/coil-kt/coil</url>\n        <copyright>Copyright 2020 Coil Contributors</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>AppIconLoader</name>\n        <url>https://github.com/zhanghai/AppIconLoader</url>\n        <copyright>Copyright 2020 Google LLC</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>AndroidFastScroll</name>\n        <url>https://github.com/zhanghai/AndroidFastScroll</url>\n        <copyright>Copyright 2019 Google LLC</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>AndroidForegroundCompat</name>\n        <url>https://github.com/google/AndroidForegroundCompat</url>\n        <copyright>Copyright 2019 Google LLC</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>libarchive-android</name>\n        <url>https://github.com/zhanghai/libarchive-android</url>\n        <copyright>Copyright 2023 Google LLC</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>libarchive</name>\n        <url>https://github.com/libarchive/libarchive</url>\n        <copyright>Copyright 2003 Tim Kientzle</copyright>\n        <license>BSD 2-Clause License</license>\n    </notice>\n\n    <notice>\n        <name>bzip2</name>\n        <url>https://gitlab.com/bzip2/bzip2</url>\n        <copyright>Copyright 1996 Julian Seward</copyright>\n        <license>BSD 3-Clause License</license>\n    </notice>\n\n    <notice>\n        <name>lz4</name>\n        <url>https://github.com/lz4/lz4</url>\n        <copyright>Copyright 2011 Yann Collet</copyright>\n        <license>BSD 2-Clause License</license>\n    </notice>\n\n    <notice>\n        <name>zstd</name>\n        <url>https://github.com/facebook/zstd</url>\n        <copyright>Copyright Meta Platforms, Inc. and affiliates</copyright>\n        <license>BSD 3-Clause License</license>\n    </notice>\n\n    <notice>\n        <name>mbedtls</name>\n        <url>https://github.com/Mbed-TLS/mbedtls</url>\n        <copyright>Copyright The Mbed TLS Contributors</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>libselinux-android</name>\n        <url>https://github.com/zhanghai/libselinux-android</url>\n        <copyright>Copyright 2019 Hai Zhang</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>AndroidRetroFile</name>\n        <url>https://github.com/zhanghai/AndroidRetroFile</url>\n        <copyright>Copyright 2018 Hai Zhang</copyright>\n        <license>GNU General Public License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>SystemUiHelper</name>\n        <url>https://github.com/zhanghai/SystemUiHelper</url>\n        <copyright>Copyright 2015 Hai Zhang</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>android-retrostreams</name>\n        <url>https://github.com/retrostreams/android-retrostreams</url>\n        <copyright>Copyright 1994 Oracle and/or its affiliates</copyright>\n        <license>GNU General Public License 2.0</license>\n    </notice>\n\n    <notice>\n        <name>Apache FtpServer</name>\n        <url>https://mina.apache.org/ftpserver-project/index.html</url>\n        <copyright>Copyright 2003 The Apache Software Foundation</copyright>\n        <license>Apache Software License 2.0</license>\n    </notice>\n</notices>\n"
  },
  {
    "path": "app/src/main/res/resources.properties",
    "content": "#\n# Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n# All Rights Reserved.\n#\n\nunqualifiedResLocale=en-US\n"
  },
  {
    "path": "app/src/main/res/values/attrs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <attr name=\"colorAppBarSurface\" format=\"color\" />\n\n    <declare-styleable name=\"AspectRatioFrameLayout\">\n        <attr name=\"aspectRatio\" format=\"float\" />\n    </declare-styleable>\n\n    <declare-styleable name=\"NavigationViewExtra\">\n        <attr name=\"itemSubtitleTextAppearance\" format=\"reference\" />\n        <attr name=\"itemSubtitleTextColor\" format=\"color\" />\n        <attr name=\"itemSubtitleTextSize\" format=\"dimension\" />\n        <attr name=\"dividerVerticalPadding\" format=\"dimension\" />\n    </declare-styleable>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <color name=\"dark_30_percent\">#4D000000</color>\n    <color name=\"dark_50_percent\">#80000000</color>\n    <color name=\"dark_70_percent\">#B3000000</color>\n\n    <color name=\"system_window_scrim_light\">#99FFFFFF</color>\n    <color name=\"system_window_scrim_dark\">#66202124</color>\n    <color name=\"system_window_scrim\">@color/system_window_scrim_light</color>\n\n    <color name=\"system_window_scrim_light_compat\">#44000000</color>\n    <color name=\"system_window_scrim_compat\">@color/system_window_scrim_light_compat</color>\n\n    <color name=\"system_window_scrim_dark_black\">#66000000</color>\n    <!-- Make lint happy, although actually we never use this in a non-night configuration. -->\n    <color name=\"color_primary_dark_black\">@android:color/transparent</color>\n\n    <color name=\"color_primary_light\">@color/google_blue_600</color>\n    <color name=\"color_primary_dark\">@color/google_blue_300</color>\n    <color name=\"color_primary\">@color/color_primary_light</color>\n\n    <color name=\"color_surface_light\">@android:color/white</color>\n    <color name=\"color_surface_dark\">@color/google_grey_900</color>\n    <color name=\"color_surface\">@color/color_surface_light</color>\n\n    <color name=\"activity_icon_tint\">@color/secondary_text_default_material_light</color>\n\n    <color name=\"shortcut_icon_background\">#F5F5F5</color>\n\n    <color name=\"file_icon_light_blue\">#4B86F0</color>\n    <color name=\"file_icon_blue\">#4285F4</color>\n    <color name=\"file_icon_cyan\">#24C1E0</color>\n    <color name=\"file_icon_light_green\">#34A853</color>\n    <color name=\"file_icon_green\">#22A667</color>\n    <color name=\"file_icon_deep_green\">#0F9D58</color>\n    <color name=\"file_icon_grey\">#5F6368</color>\n    <color name=\"file_icon_orange\">#FD7541</color>\n    <color name=\"file_icon_purple\">#A142F4</color>\n    <color name=\"file_icon_red\">#EA4335</color>\n    <color name=\"file_icon_deep_red\">#DB4437</color>\n    <color name=\"file_icon_yellow\">#F4B400</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/colors_custom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<resources>\n    <color name=\"material_red\">@color/material_red_500</color>\n    <color name=\"material_pink\">@color/material_pink_500</color>\n    <color name=\"material_purple\">@color/material_purple_500</color>\n    <color name=\"material_deep_purple\">@color/material_deep_purple_500</color>\n    <color name=\"material_indigo\">@color/material_indigo_500</color>\n    <color name=\"material_blue\">@color/material_blue_500</color>\n    <color name=\"material_light_blue\">@color/material_light_blue_500</color>\n    <color name=\"material_cyan\">@color/material_cyan_500</color>\n    <color name=\"material_teal\">@color/material_teal_500</color>\n    <color name=\"material_green\">@color/material_green_500</color>\n    <color name=\"material_light_green\">@color/material_light_green_500</color>\n    <color name=\"material_lime\">@color/material_lime_500</color>\n    <color name=\"material_yellow\">@color/material_yellow_500</color>\n    <color name=\"material_amber\">@color/material_amber_500</color>\n    <color name=\"material_orange\">@color/material_orange_500</color>\n    <color name=\"material_deep_orange\">@color/material_deep_orange_500</color>\n    <color name=\"material_brown\">@color/material_brown_500</color>\n    <color name=\"material_grey\">@color/material_grey_500</color>\n    <color name=\"material_blue_grey\">@color/material_blue_grey_500</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/colors_google.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<!-- @see https://chromium.googlesource.com/chromium/src/+/master/ui/webui/resources/cr_elements/shared_vars_css.html -->\n<resources>\n    <color name=\"google_blue_600\">#1A73E8</color>\n    <color name=\"google_blue_300\">#8AB4F8</color>\n    <color name=\"google_grey_900\">#202124</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/colors_google_material3.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<!-- @see https://chromium.googlesource.com/chromium/src/+/HEAD/ui/android/java/res/values/color_palette.xml -->\n<resources\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:ignore=\"PrivateResource\">\n\n    <color name=\"m3_ref_palette_neutral0\">#000000</color>\n    <color name=\"m3_ref_palette_neutral4\">#0E0E0F</color>\n    <color name=\"m3_ref_palette_neutral6\">#131314</color>\n    <color name=\"m3_ref_palette_neutral10\">#1F1F1F</color>\n    <color name=\"m3_ref_palette_neutral12\">#1F2020</color>\n    <color name=\"m3_ref_palette_neutral17\">#2A2A2A</color>\n    <color name=\"m3_ref_palette_neutral20\">#303030</color>\n    <color name=\"m3_ref_palette_neutral22\">#343535</color>\n    <color name=\"m3_ref_palette_neutral24\">#393939</color>\n    <color name=\"m3_ref_palette_neutral30\">#474747</color>\n    <color name=\"m3_ref_palette_neutral40\">#5E5E5E</color>\n    <color name=\"m3_ref_palette_neutral50\">#757575</color>\n    <color name=\"m3_ref_palette_neutral60\">#8F8F8F</color>\n    <color name=\"m3_ref_palette_neutral70\">#ABABAB</color>\n    <color name=\"m3_ref_palette_neutral80\">#C7C7C7</color>\n    <color name=\"m3_ref_palette_neutral87\">#DADADA</color>\n    <color name=\"m3_ref_palette_neutral90\">#E3E3E3</color>\n    <color name=\"m3_ref_palette_neutral92\">#E9E8E8</color>\n    <color name=\"m3_ref_palette_neutral94\">#EFEDED</color>\n    <color name=\"m3_ref_palette_neutral95\">#F2F2F2</color>\n    <color name=\"m3_ref_palette_neutral96\">#F4F3F2</color>\n    <color name=\"m3_ref_palette_neutral98\">#FAF9F8</color>\n    <color name=\"m3_ref_palette_neutral99\">#FDFCFB</color>\n    <color name=\"m3_ref_palette_neutral100\">#FFFFFF</color>\n    <color name=\"m3_ref_palette_neutral_variant0\">#000000</color>\n    <color name=\"m3_ref_palette_neutral_variant10\">#191D1C</color>\n    <color name=\"m3_ref_palette_neutral_variant20\">#2D312F</color>\n    <color name=\"m3_ref_palette_neutral_variant30\">#444746</color>\n    <color name=\"m3_ref_palette_neutral_variant40\">#5C5F5E</color>\n    <color name=\"m3_ref_palette_neutral_variant50\">#747775</color>\n    <color name=\"m3_ref_palette_neutral_variant60\">#8E918F</color>\n    <color name=\"m3_ref_palette_neutral_variant70\">#A9ACAA</color>\n    <color name=\"m3_ref_palette_neutral_variant80\">#C4C7C5</color>\n    <color name=\"m3_ref_palette_neutral_variant90\">#E1E3E1</color>\n    <color name=\"m3_ref_palette_neutral_variant95\">#EFF2EF</color>\n    <color name=\"m3_ref_palette_neutral_variant99\">#FAFDFB</color>\n    <color name=\"m3_ref_palette_neutral_variant100\">#FFFFFF</color>\n    <color name=\"m3_ref_palette_primary0\">#000000</color>\n    <color name=\"m3_ref_palette_primary10\">#041E49</color>\n    <color name=\"m3_ref_palette_primary20\">#062E6F</color>\n    <color name=\"m3_ref_palette_primary30\">#0842A0</color>\n    <color name=\"m3_ref_palette_primary40\">#0B57D0</color>\n    <color name=\"m3_ref_palette_primary50\">#1B6EF3</color>\n    <color name=\"m3_ref_palette_primary60\">#4C8DF6</color>\n    <color name=\"m3_ref_palette_primary70\">#7CACF8</color>\n    <color name=\"m3_ref_palette_primary80\">#A8C7FA</color>\n    <color name=\"m3_ref_palette_primary90\">#D3E3FD</color>\n    <color name=\"m3_ref_palette_primary95\">#ECF3FE</color>\n    <color name=\"m3_ref_palette_primary99\">#FAFBFF</color>\n    <color name=\"m3_ref_palette_primary100\">#FFFFFF</color>\n    <color name=\"m3_ref_palette_secondary0\">#000000</color>\n    <color name=\"m3_ref_palette_secondary10\">#001D35</color>\n    <color name=\"m3_ref_palette_secondary20\">#003355</color>\n    <color name=\"m3_ref_palette_secondary30\">#004A77</color>\n    <color name=\"m3_ref_palette_secondary40\">#00639B</color>\n    <color name=\"m3_ref_palette_secondary50\">#047DB7</color>\n    <color name=\"m3_ref_palette_secondary60\">#3998D3</color>\n    <color name=\"m3_ref_palette_secondary70\">#5AB3F0</color>\n    <color name=\"m3_ref_palette_secondary80\">#7FCFFF</color>\n    <color name=\"m3_ref_palette_secondary90\">#C2E7FF</color>\n    <color name=\"m3_ref_palette_secondary95\">#DFF3FF</color>\n    <color name=\"m3_ref_palette_secondary99\">#F7FCFF</color>\n    <color name=\"m3_ref_palette_secondary100\">#FFFFFF</color>\n    <color name=\"m3_ref_palette_tertiary0\">#000000</color>\n    <color name=\"m3_ref_palette_tertiary10\">#072711</color>\n    <color name=\"m3_ref_palette_tertiary20\">#0A3818</color>\n    <color name=\"m3_ref_palette_tertiary30\">#0F5223</color>\n    <color name=\"m3_ref_palette_tertiary40\">#146C2E</color>\n    <color name=\"m3_ref_palette_tertiary50\">#198639</color>\n    <color name=\"m3_ref_palette_tertiary60\">#1EA446</color>\n    <color name=\"m3_ref_palette_tertiary70\">#37BE5F</color>\n    <color name=\"m3_ref_palette_tertiary80\">#6DD58C</color>\n    <color name=\"m3_ref_palette_tertiary90\">#C4EED0</color>\n    <color name=\"m3_ref_palette_tertiary95\">#E7F8ED</color>\n    <color name=\"m3_ref_palette_tertiary99\">#F2FFEE</color>\n    <color name=\"m3_ref_palette_tertiary100\">#FFFFFF</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/colors_material.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <color name=\"material_red_50\">#FFEBEE</color>\n    <color name=\"material_red_100\">#FFCDD2</color>\n    <color name=\"material_red_200\">#EF9A9A</color>\n    <color name=\"material_red_300\">#E57373</color>\n    <color name=\"material_red_400\">#EF5350</color>\n    <color name=\"material_red_500\">#F44336</color>\n    <color name=\"material_red_600\">#E53935</color>\n    <color name=\"material_red_700\">#D32F2F</color>\n    <color name=\"material_red_800\">#C62828</color>\n    <color name=\"material_red_900\">#B71C1C</color>\n    <color name=\"material_red_a100\">#FF8A80</color>\n    <color name=\"material_red_a200\">#FF5252</color>\n    <color name=\"material_red_a400\">#FF1744</color>\n    <color name=\"material_red_a700\">#D50000</color>\n\n    <color name=\"material_pink_50\">#FCE4EC</color>\n    <color name=\"material_pink_100\">#F8BBD0</color>\n    <color name=\"material_pink_200\">#F48FB1</color>\n    <color name=\"material_pink_300\">#F06292</color>\n    <color name=\"material_pink_400\">#EC407A</color>\n    <color name=\"material_pink_500\">#E91E63</color>\n    <color name=\"material_pink_600\">#D81B60</color>\n    <color name=\"material_pink_700\">#C2185B</color>\n    <color name=\"material_pink_800\">#AD1457</color>\n    <color name=\"material_pink_900\">#880E4F</color>\n    <color name=\"material_pink_a100\">#FF80AB</color>\n    <color name=\"material_pink_a200\">#FF4081</color>\n    <color name=\"material_pink_a400\">#F50057</color>\n    <color name=\"material_pink_a700\">#C51162</color>\n\n    <color name=\"material_purple_50\">#F3E5F5</color>\n    <color name=\"material_purple_100\">#E1BEE7</color>\n    <color name=\"material_purple_200\">#CE93D8</color>\n    <color name=\"material_purple_300\">#BA68C8</color>\n    <color name=\"material_purple_400\">#AB47BC</color>\n    <color name=\"material_purple_500\">#9C27B0</color>\n    <color name=\"material_purple_600\">#8E24AA</color>\n    <color name=\"material_purple_700\">#7B1FA2</color>\n    <color name=\"material_purple_800\">#6A1B9A</color>\n    <color name=\"material_purple_900\">#4A148C</color>\n    <color name=\"material_purple_a100\">#EA80FC</color>\n    <color name=\"material_purple_a200\">#E040FB</color>\n    <color name=\"material_purple_a400\">#D500F9</color>\n    <color name=\"material_purple_a700\">#AA00FF</color>\n\n    <color name=\"material_deep_purple_50\">#EDE7F6</color>\n    <color name=\"material_deep_purple_100\">#D1C4E9</color>\n    <color name=\"material_deep_purple_200\">#B39DDB</color>\n    <color name=\"material_deep_purple_300\">#9575CD</color>\n    <color name=\"material_deep_purple_400\">#7E57C2</color>\n    <color name=\"material_deep_purple_500\">#673AB7</color>\n    <color name=\"material_deep_purple_600\">#5E35B1</color>\n    <color name=\"material_deep_purple_700\">#512DA8</color>\n    <color name=\"material_deep_purple_800\">#4527A0</color>\n    <color name=\"material_deep_purple_900\">#311B92</color>\n    <color name=\"material_deep_purple_a100\">#B388FF</color>\n    <color name=\"material_deep_purple_a200\">#7C4DFF</color>\n    <color name=\"material_deep_purple_a400\">#651FFF</color>\n    <color name=\"material_deep_purple_a700\">#6200EA</color>\n\n    <color name=\"material_indigo_50\">#E8EAF6</color>\n    <color name=\"material_indigo_100\">#C5CAE9</color>\n    <color name=\"material_indigo_200\">#9FA8DA</color>\n    <color name=\"material_indigo_300\">#7986CB</color>\n    <color name=\"material_indigo_400\">#5C6BC0</color>\n    <color name=\"material_indigo_500\">#3F51B5</color>\n    <color name=\"material_indigo_600\">#3949AB</color>\n    <color name=\"material_indigo_700\">#303F9F</color>\n    <color name=\"material_indigo_800\">#283593</color>\n    <color name=\"material_indigo_900\">#1A237E</color>\n    <color name=\"material_indigo_a100\">#8C9EFF</color>\n    <color name=\"material_indigo_a200\">#536DFE</color>\n    <color name=\"material_indigo_a400\">#3D5AFE</color>\n    <color name=\"material_indigo_a700\">#304FFE</color>\n\n    <color name=\"material_blue_50\">#E3F2FD</color>\n    <color name=\"material_blue_100\">#BBDEFB</color>\n    <color name=\"material_blue_200\">#90CAF9</color>\n    <color name=\"material_blue_300\">#64B5F6</color>\n    <color name=\"material_blue_400\">#42A5F5</color>\n    <color name=\"material_blue_500\">#2196F3</color>\n    <color name=\"material_blue_600\">#1E88E5</color>\n    <color name=\"material_blue_700\">#1976D2</color>\n    <color name=\"material_blue_800\">#1565C0</color>\n    <color name=\"material_blue_900\">#0D47A1</color>\n    <color name=\"material_blue_a100\">#82B1FF</color>\n    <color name=\"material_blue_a200\">#448AFF</color>\n    <color name=\"material_blue_a400\">#2979FF</color>\n    <color name=\"material_blue_a700\">#2962FF</color>\n\n    <color name=\"material_light_blue_50\">#E1F5FE</color>\n    <color name=\"material_light_blue_100\">#B3E5FC</color>\n    <color name=\"material_light_blue_200\">#81D4FA</color>\n    <color name=\"material_light_blue_300\">#4FC3F7</color>\n    <color name=\"material_light_blue_400\">#29B6F6</color>\n    <color name=\"material_light_blue_500\">#03A9F4</color>\n    <color name=\"material_light_blue_600\">#039BE5</color>\n    <color name=\"material_light_blue_700\">#0288D1</color>\n    <color name=\"material_light_blue_800\">#0277BD</color>\n    <color name=\"material_light_blue_900\">#01579B</color>\n    <color name=\"material_light_blue_a100\">#80D8FF</color>\n    <color name=\"material_light_blue_a200\">#40C4FF</color>\n    <color name=\"material_light_blue_a400\">#00B0FF</color>\n    <color name=\"material_light_blue_a700\">#0091EA</color>\n\n    <color name=\"material_cyan_50\">#E0F7FA</color>\n    <color name=\"material_cyan_100\">#B2EBF2</color>\n    <color name=\"material_cyan_200\">#80DEEA</color>\n    <color name=\"material_cyan_300\">#4DD0E1</color>\n    <color name=\"material_cyan_400\">#26C6DA</color>\n    <color name=\"material_cyan_500\">#00BCD4</color>\n    <color name=\"material_cyan_600\">#00ACC1</color>\n    <color name=\"material_cyan_700\">#0097A7</color>\n    <color name=\"material_cyan_800\">#00838F</color>\n    <color name=\"material_cyan_900\">#006064</color>\n    <color name=\"material_cyan_a100\">#84FFFF</color>\n    <color name=\"material_cyan_a200\">#18FFFF</color>\n    <color name=\"material_cyan_a400\">#00E5FF</color>\n    <color name=\"material_cyan_a700\">#00B8D4</color>\n\n    <color name=\"material_teal_50\">#E0F2F1</color>\n    <color name=\"material_teal_100\">#B2DFDB</color>\n    <color name=\"material_teal_200\">#80CBC4</color>\n    <color name=\"material_teal_300\">#4DB6AC</color>\n    <color name=\"material_teal_400\">#26A69A</color>\n    <color name=\"material_teal_500\">#009688</color>\n    <color name=\"material_teal_600\">#00897B</color>\n    <color name=\"material_teal_700\">#00796B</color>\n    <color name=\"material_teal_800\">#00695C</color>\n    <color name=\"material_teal_900\">#004D40</color>\n    <color name=\"material_teal_a100\">#A7FFEB</color>\n    <color name=\"material_teal_a200\">#64FFDA</color>\n    <color name=\"material_teal_a400\">#1DE9B6</color>\n    <color name=\"material_teal_a700\">#00BFA5</color>\n\n    <color name=\"material_green_50\">#E8F5E9</color>\n    <color name=\"material_green_100\">#C8E6C9</color>\n    <color name=\"material_green_200\">#A5D6A7</color>\n    <color name=\"material_green_300\">#81C784</color>\n    <color name=\"material_green_400\">#66BB6A</color>\n    <color name=\"material_green_500\">#4CAF50</color>\n    <color name=\"material_green_600\">#43A047</color>\n    <color name=\"material_green_700\">#388E3C</color>\n    <color name=\"material_green_800\">#2E7D32</color>\n    <color name=\"material_green_900\">#1B5E20</color>\n    <color name=\"material_green_a100\">#B9F6CA</color>\n    <color name=\"material_green_a200\">#69F0AE</color>\n    <color name=\"material_green_a400\">#00E676</color>\n    <color name=\"material_green_a700\">#00C853</color>\n\n    <color name=\"material_light_green_50\">#F1F8E9</color>\n    <color name=\"material_light_green_100\">#DCEDC8</color>\n    <color name=\"material_light_green_200\">#C5E1A5</color>\n    <color name=\"material_light_green_300\">#AED581</color>\n    <color name=\"material_light_green_400\">#9CCC65</color>\n    <color name=\"material_light_green_500\">#8BC34A</color>\n    <color name=\"material_light_green_600\">#7CB342</color>\n    <color name=\"material_light_green_700\">#689F38</color>\n    <color name=\"material_light_green_800\">#558B2F</color>\n    <color name=\"material_light_green_900\">#33691E</color>\n    <color name=\"material_light_green_a100\">#CCFF90</color>\n    <color name=\"material_light_green_a200\">#B2FF59</color>\n    <color name=\"material_light_green_a400\">#76FF03</color>\n    <color name=\"material_light_green_a700\">#64DD17</color>\n\n    <color name=\"material_lime_50\">#F9FBE7</color>\n    <color name=\"material_lime_100\">#F0F4C3</color>\n    <color name=\"material_lime_200\">#E6EE9C</color>\n    <color name=\"material_lime_300\">#DCE775</color>\n    <color name=\"material_lime_400\">#D4E157</color>\n    <color name=\"material_lime_500\">#CDDC39</color>\n    <color name=\"material_lime_600\">#C0CA33</color>\n    <color name=\"material_lime_700\">#AFB42B</color>\n    <color name=\"material_lime_800\">#9E9D24</color>\n    <color name=\"material_lime_900\">#827717</color>\n    <color name=\"material_lime_a100\">#F4FF81</color>\n    <color name=\"material_lime_a200\">#EEFF41</color>\n    <color name=\"material_lime_a400\">#C6FF00</color>\n    <color name=\"material_lime_a700\">#AEEA00</color>\n\n    <color name=\"material_yellow_50\">#FFFDE7</color>\n    <color name=\"material_yellow_100\">#FFF9C4</color>\n    <color name=\"material_yellow_200\">#FFF59D</color>\n    <color name=\"material_yellow_300\">#FFF176</color>\n    <color name=\"material_yellow_400\">#FFEE58</color>\n    <color name=\"material_yellow_500\">#FFEB3B</color>\n    <color name=\"material_yellow_600\">#FDD835</color>\n    <color name=\"material_yellow_700\">#FBC02D</color>\n    <color name=\"material_yellow_800\">#F9A825</color>\n    <color name=\"material_yellow_900\">#F57F17</color>\n    <color name=\"material_yellow_a100\">#FFFF8D</color>\n    <color name=\"material_yellow_a200\">#FFFF00</color>\n    <color name=\"material_yellow_a400\">#FFEA00</color>\n    <color name=\"material_yellow_a700\">#FFD600</color>\n\n    <color name=\"material_amber_50\">#FFF8E1</color>\n    <color name=\"material_amber_100\">#FFECB3</color>\n    <color name=\"material_amber_200\">#FFE082</color>\n    <color name=\"material_amber_300\">#FFD54F</color>\n    <color name=\"material_amber_400\">#FFCA28</color>\n    <color name=\"material_amber_500\">#FFC107</color>\n    <color name=\"material_amber_600\">#FFB300</color>\n    <color name=\"material_amber_700\">#FFA000</color>\n    <color name=\"material_amber_800\">#FF8F00</color>\n    <color name=\"material_amber_900\">#FF6F00</color>\n    <color name=\"material_amber_a100\">#FFE57F</color>\n    <color name=\"material_amber_a200\">#FFD740</color>\n    <color name=\"material_amber_a400\">#FFC400</color>\n    <color name=\"material_amber_a700\">#FFAB00</color>\n\n    <color name=\"material_orange_50\">#FFF3E0</color>\n    <color name=\"material_orange_100\">#FFE0B2</color>\n    <color name=\"material_orange_200\">#FFCC80</color>\n    <color name=\"material_orange_300\">#FFB74D</color>\n    <color name=\"material_orange_400\">#FFA726</color>\n    <color name=\"material_orange_500\">#FF9800</color>\n    <color name=\"material_orange_600\">#FB8C00</color>\n    <color name=\"material_orange_700\">#F57C00</color>\n    <color name=\"material_orange_800\">#EF6C00</color>\n    <color name=\"material_orange_900\">#E65100</color>\n    <color name=\"material_orange_a100\">#FFD180</color>\n    <color name=\"material_orange_a200\">#FFAB40</color>\n    <color name=\"material_orange_a400\">#FF9100</color>\n    <color name=\"material_orange_a700\">#FF6D00</color>\n\n    <color name=\"material_deep_orange_50\">#FBE9E7</color>\n    <color name=\"material_deep_orange_100\">#FFCCBC</color>\n    <color name=\"material_deep_orange_200\">#FFAB91</color>\n    <color name=\"material_deep_orange_300\">#FF8A65</color>\n    <color name=\"material_deep_orange_400\">#FF7043</color>\n    <color name=\"material_deep_orange_500\">#FF5722</color>\n    <color name=\"material_deep_orange_600\">#F4511E</color>\n    <color name=\"material_deep_orange_700\">#E64A19</color>\n    <color name=\"material_deep_orange_800\">#D84315</color>\n    <color name=\"material_deep_orange_900\">#BF360C</color>\n    <color name=\"material_deep_orange_a100\">#FF9E80</color>\n    <color name=\"material_deep_orange_a200\">#FF6E40</color>\n    <color name=\"material_deep_orange_a400\">#FF3D00</color>\n    <color name=\"material_deep_orange_a700\">#DD2C00</color>\n\n    <color name=\"material_brown_50\">#EFEBE9</color>\n    <color name=\"material_brown_100\">#D7CCC8</color>\n    <color name=\"material_brown_200\">#BCAAA4</color>\n    <color name=\"material_brown_300\">#A1887F</color>\n    <color name=\"material_brown_400\">#8D6E63</color>\n    <color name=\"material_brown_500\">#795548</color>\n    <color name=\"material_brown_600\">#6D4C41</color>\n    <color name=\"material_brown_700\">#5D4037</color>\n    <color name=\"material_brown_800\">#4E342E</color>\n    <color name=\"material_brown_900\">#3E2723</color>\n\n    <color name=\"material_grey_50\">#FAFAFA</color>\n    <color name=\"material_grey_100\">#F5F5F5</color>\n    <color name=\"material_grey_200\">#EEEEEE</color>\n    <color name=\"material_grey_300\">#E0E0E0</color>\n    <color name=\"material_grey_400\">#BDBDBD</color>\n    <color name=\"material_grey_500\">#9E9E9E</color>\n    <color name=\"material_grey_600\">#757575</color>\n    <color name=\"material_grey_700\">#616161</color>\n    <color name=\"material_grey_800\">#424242</color>\n    <color name=\"material_grey_900\">#212121</color>\n\n    <color name=\"material_blue_grey_50\">#ECEFF1</color>\n    <color name=\"material_blue_grey_100\">#CFD8DC</color>\n    <color name=\"material_blue_grey_200\">#B0BEC5</color>\n    <color name=\"material_blue_grey_300\">#90A4AE</color>\n    <color name=\"material_blue_grey_400\">#78909C</color>\n    <color name=\"material_blue_grey_500\">#607D8B</color>\n    <color name=\"material_blue_grey_600\">#546E7A</color>\n    <color name=\"material_blue_grey_700\">#455A64</color>\n    <color name=\"material_blue_grey_800\">#37474F</color>\n    <color name=\"material_blue_grey_900\">#263238</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/colors_material3.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <!-- This is actually unused on v29+, so it's okay to hard code. -->\n    <color name=\"system_window_scrim_light_material3\">#99FFFFFF</color>\n    <color name=\"system_window_scrim_dark_material3\">#661F1F1F</color>\n    <color name=\"system_window_scrim_material3\">@color/system_window_scrim_light_material3</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <dimen name=\"screen_edge_margin\">16dp</dimen>\n    <dimen name=\"screen_edge_margin_minus_4dp\">12dp</dimen>\n    <dimen name=\"screen_edge_margin_minus_8dp\">8dp</dimen>\n    <dimen name=\"screen_edge_margin_minus_12dp\">4dp</dimen>\n    <dimen name=\"screen_edge_margin_minus_12dp_at_least_8dp\">8dp</dimen>\n    <dimen name=\"screen_edge_margin_minus_16dp\">0dp</dimen>\n    <dimen name=\"content_start_margin\">72dp</dimen>\n    <dimen name=\"content_start_margin_minus_12dp\">60dp</dimen>\n    <dimen name=\"content_start_from_screen_edge_margin_minus_24dp\">32dp</dimen>\n    <dimen name=\"content_start_from_screen_edge_margin_minus_36dp_considering_at_least_8dp\">16dp</dimen>\n    <dimen name=\"content_start_from_screen_edge_margin_minus_40dp\">16dp</dimen>\n    <dimen name=\"content_start_from_screen_edge_margin_minus_44dp\">12dp</dimen>\n    <dimen name=\"dialog_padding\">24dp</dimen>\n    <dimen name=\"dialog_padding_minus_4dp\">20dp</dimen>\n    <dimen name=\"dialog_padding_minus_6dp\">18dp</dimen>\n    <dimen name=\"dialog_padding_minus_7dp\">17dp</dimen>\n    <dimen name=\"dialog_padding_minus_16dp\">8dp</dimen>\n    <dimen name=\"navigation_max_width\">320dp</dimen>\n    <dimen name=\"navigation_header_height\">172dp</dimen>\n    <dimen name=\"navigation_item_horizontal_padding\">24dp</dimen>\n    <dimen name=\"navigation_item_icon_padding\">24dp</dimen>\n    <dimen name=\"tab_layout_height\">48dp</dimen>\n    <dimen name=\"list_vertical_padding\">8dp</dimen>\n    <dimen name=\"list_bottom_padding_with_fab\">88dp</dimen>\n    <dimen name=\"dense_single_line_list_item_height\">40dp</dimen>\n    <dimen name=\"single_line_list_item_height\">56dp</dimen>\n    <dimen name=\"two_line_list_item_height\">72dp</dimen>\n    <dimen name=\"list_item_vertical_padding\">16dp</dimen>\n    <dimen name=\"horizontal_divider_height\">1dp</dimen>\n    <dimen name=\"small_icon_size\">18dp</dimen>\n    <dimen name=\"icon_size\">24dp</dimen>\n    <dimen name=\"large_icon_size\">40dp</dimen>\n    <dimen name=\"touch_target_size\">48dp</dimen>\n    <dimen name=\"touch_target_icon_padding\">12dp</dimen>\n    <dimen name=\"touch_target_large_icon_padding\">4dp</dimen>\n    <dimen name=\"badge_size\">18dp</dimen>\n    <dimen name=\"badge_size_plus_1dp\">19dp</dimen>\n\n    <dimen name=\"file_list_toolbar_padding_start\">8dp</dimen>\n    <dimen name=\"file_list_toolbar_padding_end_no_overflow\">14dp</dimen>\n    <dimen name=\"file_list_toolbar_padding_end_with_overflow\">10dp</dimen>\n\n    <!-- Reduce fast scroll thumb touch target size because we have the popup menu beside it. -->\n    <dimen name=\"afs_min_touch_target_size\">24dp</dimen>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/dimens_material3.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <dimen name=\"navigation_item_horizontal_padding_material3\">@dimen/m3_navigation_item_horizontal_padding</dimen>\n    <dimen name=\"navigation_item_icon_padding_material3\">@dimen/m3_navigation_item_icon_padding</dimen>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/donottranslate_prefs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <string name=\"pref_default_value_empty\" />\n\n    <string name=\"pref_key_storages\">key_storages</string>\n    <string name=\"pref_key_file_list_default_directory\">key_file_list_default_directory</string>\n    <string name=\"pref_key_file_list_persistent_drawer_open\">key_file_list_persistent_drawer_open</string>\n    <bool name=\"pref_default_value_file_list_persistent_drawer_open\">true</bool>\n    <string name=\"pref_key_file_list_show_hidden_files\">key_file_list_show_hidden_files</string>\n    <bool name=\"pref_default_value_file_list_show_hidden_files\">false</bool>\n    <string name=\"pref_key_file_list_view_type\">key_file_list_view_type</string>\n    <string name=\"pref_default_value_file_list_view_type\">0</string>\n    <string name=\"pref_key_file_list_sort_options\">key_file_list_sort_options</string>\n    <string name=\"pref_key_create_archive_type\">key_create_archive_type</string>\n\n    <string name=\"pref_key_ftp_server_state\">key_ftp_server_state</string>\n    <bool name=\"pref_visible_ftp_server_add_tile\">false</bool>\n    <string name=\"pref_key_ftp_server_anonymous_login\">key_ftp_server_anonymous_login</string>\n    <bool name=\"pref_default_value_ftp_server_anonymous_login\">true</bool>\n    <string name=\"pref_key_ftp_server_username\">key_ftp_server_username</string>\n    <string name=\"pref_default_value_ftp_server_username\">admin</string>\n    <string name=\"pref_key_ftp_server_password\">key_ftp_server_password</string>\n    <string name=\"pref_key_ftp_server_port\">key_ftp_server_port</string>\n    <integer name=\"pref_default_value_ftp_server_port\">2121</integer>\n    <string name=\"pref_key_ftp_server_home_directory\">key_ftp_server_home_directory</string>\n    <string name=\"pref_key_ftp_server_writable\">key_ftp_server_writable</string>\n    <bool name=\"pref_default_value_ftp_server_writable\">true</bool>\n\n    <string name=\"pref_key_locale\">key_locale</string>\n    <string name=\"pref_key_theme_color\">key_theme_color</string>\n    <string name=\"pref_default_value_theme_color\">0</string>\n    <string name=\"pref_key_material_design_3\">key_material_design_3</string>\n    <bool name=\"pref_default_value_material_design_3\">false</bool>\n    <string name=\"pref_key_night_mode\">key_night_mode</string>\n    <string name=\"pref_default_value_night_mode\">0</string>\n    <string-array name=\"pref_entry_values_night_mode\">\n        <item>0</item>\n        <item>1</item>\n        <item>2</item>\n        <item>3</item>\n        <item>4</item>\n    </string-array>\n    <string name=\"pref_key_black_night_mode\">key_black_night_mode</string>\n    <bool name=\"pref_default_value_black_night_mode\">false</bool>\n    <string name=\"pref_key_file_list_animation\">key_file_list_animation</string>\n    <bool name=\"pref_default_value_file_list_animation\">true</bool>\n    <string name=\"pref_key_file_name_ellipsize\">key_file_name_ellipsize</string>\n    <string name=\"pref_default_value_file_name_ellipsize\">1</string>\n    <string-array name=\"pref_entry_values_file_name_ellipsize\">\n        <item>0</item>\n        <item>1</item>\n        <item>2</item>\n        <item>3</item>\n    </string-array>\n    <string name=\"pref_key_standard_directory_settings\">key_standard_directories</string>\n    <string name=\"pref_key_bookmark_directories\">key_bookmark_directories</string>\n    <string name=\"pref_key_root_strategy\">key_root_strategy</string>\n    <string name=\"pref_default_value_root_strategy\">1</string>\n    <string-array name=\"pref_entry_values_root_strategy\">\n        <item>0</item>\n        <item>1</item>\n        <item>2</item>\n    </string-array>\n    <string name=\"pref_key_archive_file_name_encoding\">key_archive_file_name_encoding</string>\n    <string name=\"pref_default_value_archive_file_name_encoding\">UTF-8</string>\n    <string name=\"pref_key_open_apk_default_action\">key_open_apk_default_action</string>\n    <string name=\"pref_default_value_open_apk_default_action\">2</string>\n    <string-array name=\"pref_entry_values_open_apk_default_action\">\n        <item>0</item>\n        <item>1</item>\n        <item>2</item>\n    </string-array>\n    <string name=\"pref_key_show_pdf_thumbnail_pre_28\">key_show_pdf_thumbnail_pre_28</string>\n    <bool name=\"pref_default_value_show_pdf_thumbnail_pre_28\">false</bool>\n    <bool name=\"pref_visible_show_pdf_thumbnail_pre_28\">true</bool>\n    <string name=\"pref_key_read_remote_files_for_thumbnail\">key_read_remote_files_for_thumbnail</string>\n    <bool name=\"pref_default_value_read_remote_files_for_thumbnail\">true</bool>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/integers.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <integer name=\"visibility_visible\">0</integer>\n    <integer name=\"visibility_invisible\">1</integer>\n    <integer name=\"visibility_gone\">2</integer>\n\n    <integer name=\"scrollFlags_noScroll\">0</integer>\n    <integer name=\"scrollFlags_scroll_enterAlways\">5</integer>\n\n    <integer name=\"file_list_toolbar_scroll_flags\">@integer/scrollFlags_scroll_enterAlways</integer>\n\n    <!-- SevenZOutputFile requires SeekableByteChannel which requires Android N. -->\n    <integer name=\"create_archive_type_seven_z_visibility\">@integer/visibility_gone</integer>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">File</string>\n    <string name=\"file_type_name_apk\">Android package</string>\n    <string name=\"file_type_name_archive\">%1$s archive</string>\n    <string name=\"file_type_name_audio\">%1$s audio</string>\n    <string name=\"file_type_name_calendar\">%1$s calendar</string>\n    <string name=\"file_type_name_certificate\">%1$s certificate</string>\n    <string name=\"file_type_name_code\">%1$s document</string>\n    <string name=\"file_type_name_contact\">Electronic business card</string>\n    <string name=\"file_type_name_directory\">Folder</string>\n    <string name=\"file_type_name_document\">%1$s document</string>\n    <string name=\"file_type_name_ebook\">%1$s e-book</string>\n    <string name=\"file_type_name_email\">%1$s email message</string>\n    <string name=\"file_type_name_font\">%1$s font</string>\n    <string name=\"file_type_name_generic\">%1$s file</string>\n    <string name=\"file_type_name_image\">%1$s image</string>\n    <string name=\"file_type_name_pdf\">PDF document</string>\n    <string name=\"file_type_name_presentation\">%1$s presentation</string>\n    <string name=\"file_type_name_spreadsheet\">%1$s spreadsheet</string>\n    <string name=\"file_type_name_text\">%1$s document</string>\n    <string name=\"file_type_name_text_plain\">Plain text document</string>\n    <string name=\"file_type_name_video\">%1$s video</string>\n    <string name=\"file_type_name_word\">Word document</string>\n    <string name=\"file_type_name_powerpoint\">PowerPoint presentation</string>\n    <string name=\"file_type_name_excel\">Excel spreadsheet</string>\n    <string name=\"file_type_name_posix_character_device\">Character device</string>\n    <string name=\"file_type_name_posix_block_device\">Block device</string>\n    <string name=\"file_type_name_posix_fifo\">Pipe</string>\n    <string name=\"file_type_name_posix_symbolic_link\">Link</string>\n    <string name=\"file_type_name_posix_socket\">Socket</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">Link (broken)</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <string name=\"app_name\">Material Files</string>\n\n    <string name=\"close\">Close</string>\n    <string name=\"copy\">Copy</string>\n    <string name=\"cut\">Cut</string>\n    <string name=\"default_\">Default</string>\n    <string name=\"delete\">Delete</string>\n    <string name=\"discard\">Discard</string>\n    <string name=\"edit\">Edit</string>\n    <string name=\"empty\">Empty</string>\n    <string name=\"empty_placeholder\">(Empty)</string>\n    <string name=\"error\">Error</string>\n    <string name=\"hide\">Hide</string>\n    <string name=\"install\">Install</string>\n    <string name=\"keep_editing\">Keep editing</string>\n    <string name=\"loading\">Loading…</string>\n    <string name=\"maybe_later\">Maybe later</string>\n    <string name=\"merge\">Merge</string>\n    <string name=\"none\">None</string>\n    <string name=\"open_navigation_drawer\">Open navigation drawer</string>\n    <string name=\"paste\">Paste</string>\n    <string name=\"refresh\">Refresh</string>\n    <string name=\"reload\">Reload</string>\n    <string name=\"remove\">Remove</string>\n    <string name=\"rename\">Rename</string>\n    <string name=\"replace\">Replace</string>\n    <string name=\"reset\">Reset</string>\n    <string name=\"retry\">Retry</string>\n    <string name=\"save\">Save</string>\n    <string name=\"search\">Search</string>\n    <string name=\"select\">Select</string>\n    <string name=\"select_all\">Select all</string>\n    <string name=\"share\">Share</string>\n    <string name=\"show\">Show</string>\n    <string name=\"skip\">Skip</string>\n    <string name=\"stop\">Stop</string>\n    <string name=\"system_default\">System default</string>\n    <string name=\"unknown\">Unknown</string>\n    <string name=\"view\">View</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">No application found to handle this action</string>\n    <string name=\"copied_to_clipboard_format\" translatable=\"false\">@string/preference_copied</string>\n    <string name=\"open_settings\">Open settings</string>\n    <string name=\"shortcut_created\">Shortcut created</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"one\">%1$,d byte</item>\n        <item quantity=\"other\">%1$,d bytes</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">App needs access to manage all files. Please allow the access in the upcoming system setting.</string>\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">App needs permission to access files. Please click “ALLOW” in the upcoming system dialog.</string>\n    <string name=\"storage_permission_permanently_denied_message\">App needs permission to access files. Please grant the “Storage” permission in system settings.</string>\n    <!-- The allow button is in sentence case now. -->\n    <string name=\"notification_permission_rationale_message\">App needs permission to post notifications about file operations. Please click “Allow” in the upcoming system dialog.</string>\n    <string name=\"notification_permission_permanently_denied_message\">App needs permission to post notifications about file operations. Please grant the “Notification” permission in system settings.</string>\n\n    <string name=\"notification_channel_background_activity_start_name\">Actions while background</string>\n    <string name=\"notification_channel_background_activity_start_description\">Take actions while app is in the background</string>\n    <string name=\"notification_channel_file_job_name\">File operations</string>\n    <string name=\"notification_channel_file_job_description\">Display and control file operations</string>\n    <string name=\"notification_channel_ftp_server_name\">FTP server</string>\n    <string name=\"notification_channel_ftp_server_description\">Display and control the FTP server</string>\n\n    <string name=\"file_action_archive_password_title\">Password required</string>\n    <string name=\"file_action_archive_password_message_format\">“%1$s” is password-protected.</string>\n    <string name=\"file_action_archive_password_error_empty\">Password cannot be empty</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"one\">Preparing to compress %1$,d file (%2$s)</item>\n        <item quantity=\"other\">Preparing to compress %1$,d files (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">Compressing “%1$s” into “%2$s”</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"one\">Compressing %1$,d file into “%2$s”</item>\n        <item quantity=\"other\">Compressing %1$,d files into “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"one\">Preparing to copy %1$,d file (%2$s)</item>\n        <item quantity=\"other\">Preparing to copy %1$,d files (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">Copying “%1$s” to “%2$s”</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"one\">Copying %1$,d file to “%2$s”</item>\n        <item quantity=\"other\">Copying %1$,d files to “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"one\">Preparing to extract %1$,d file (%2$s)</item>\n        <item quantity=\"other\">Preparing to extract %1$,d files (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">Extracting “%1$s” to “%2$s”</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"one\">Extracting %1$,d file to “%2$s”</item>\n        <item quantity=\"other\">Extracting %1$,d files to “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"one\">Preparing to move %1$,d file (%2$s)</item>\n        <item quantity=\"other\">Preparing to move %1$,d files (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">Moving “%1$s” to “%2$s”</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"one\">Moving %1$,d file to “%2$s”</item>\n        <item quantity=\"other\">Moving %1$,d files to “%2$s”</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"one\">Preparing to delete %1$,d file</item>\n        <item quantity=\"other\">Preparing to delete %1$,d files</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">Deleting “%1$s”</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"one\">Deleting %1$,d file</item>\n        <item quantity=\"other\">Deleting %1$,d files</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"one\">Preparing to change owner for %1$,d file</item>\n        <item quantity=\"other\">Preparing to change owner for %1$,d files</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">Changing owner for “%1$s”</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"one\">Changing owner for %1$,d file</item>\n        <item quantity=\"other\">Changing owner for %1$,d files</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"one\">Preparing to change group for %1$,d file</item>\n        <item quantity=\"other\">Preparing to change group for %1$,d files</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">Changing group for “%1$s”</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"one\">Changing group for %1$,d file</item>\n        <item quantity=\"other\">Changing group for %1$,d files</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"one\">Preparing to change mode for %1$,d file</item>\n        <item quantity=\"other\">Preparing to change mode for %1$,d files</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">Changing mode for “%1$s”</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"one\">Changing mode for %1$,d file</item>\n        <item quantity=\"other\">Changing mode for %1$,d files</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Preparing to change SELinux context for %1$,d file</item>\n        <item quantity=\"other\">Preparing to change SELinux context for %1$,d files</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">Changing SELinux context for “%1$s”</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Changing SELinux context for %1$,d file</item>\n        <item quantity=\"other\">Changing SELinux context for %1$,d files</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Preparing to restore SELinux context for %1$,d file</item>\n        <item quantity=\"other\">Preparing to restore SELinux context for %1$,d files</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">Restoring SELinux context for “%1$s”</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Restoring SELinux context for %1$,d file</item>\n        <item quantity=\"other\">Restoring SELinux context for %1$,d files</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">Writing “%1$s”</string>\n    <string name=\"file_job_create_error_title\">Error while creating</string>\n    <string name=\"file_job_create_error_message_format\">There was an error creating “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">Cannot copy a folder into itself</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">Cannot extract a folder into itself</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">Cannot move a folder into itself</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">The destination folder is inside the source folder.</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">Cannot copy a file over itself</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">Cannot extract a file over itself</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">Cannot move a file over itself</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">The source file would be overwritten by the destination.</string>\n    <string name=\"file_job_replace_title_format\">Replace file “%1$s”?</string>\n    <string name=\"file_job_replace_message_format\">Another file with the same name already exists in “%1$s”.\\nReplacing it will overwrite its content.</string>\n    <string name=\"file_job_replace_target_name\">Original file</string>\n    <string name=\"file_job_replace_source_name\">Replace with</string>\n    <string name=\"file_job_merge_title_format\">Merge folder “%1$s”?</string>\n    <string name=\"file_job_merge_copy_message_format\">Merging will ask for confirmation before replacing any files in the folder that conflict with the files being copied.</string>\n    <string name=\"file_job_merge_extract_message_format\">Merging will ask for confirmation before replacing any files in the folder that conflict with the files being extracted.</string>\n    <string name=\"file_job_merge_move_message_format\">Merging will ask for confirmation before replacing any files in the folder that conflict with the files being moved.</string>\n    <string name=\"file_job_merge_target_name\">Original folder</string>\n    <string name=\"file_job_merge_source_name\">Merge with</string>\n    <string name=\"file_job_conflict_show_name\">Select a new name for the destination</string>\n    <string name=\"file_job_conflict_name_hint\">New name</string>\n    <string name=\"file_job_archive_error_title_format\">Error while compressing “%1$s”</string>\n    <string name=\"file_job_archive_error_message_format\">There was an error compressing the file into “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">Error while copying “%1$s”</string>\n    <string name=\"file_job_copy_error_message_format\">There was an error copying the file into “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">Error while extracting “%1$s”</string>\n    <string name=\"file_job_extract_error_message_format\">There was an error extracting the file into “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">Error while moving “%1$s”</string>\n    <string name=\"file_job_move_error_message_format\">There was an error moving the file into “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">Error while deleting</string>\n    <string name=\"file_job_delete_error_message_format\">There was an error deleting “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">Error while renaming “%1$s”</string>\n    <string name=\"file_job_rename_error_message_format\">There was an error renaming the file to “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">Error while changing owner for “%1$s”</string>\n    <string name=\"file_job_set_owner_error_message_format\">There was an error changing the owner to “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">Error while changing group for “%1$s”</string>\n    <string name=\"file_job_set_group_error_message_format\">There was an error changing the group to “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">Error while changing mode for “%1$s”</string>\n    <string name=\"file_job_set_mode_error_message_format\">There was an error changing the mode to “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">Error while changing SELinux context for “%1$s”</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">There was an error changing the SELinux context to “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">Error while restoring SELinux context</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">There was an error restoring the SELinux context for “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">Error while writing</string>\n    <string name=\"file_job_write_error_message_format\">There was an error writing “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">There was an error getting information about “%1$s”.</string>\n    <string name=\"file_job_remount_format\">Remount “%1$s” as read-write</string>\n    <string name=\"file_job_remount_loading_format\">Remounting “%1$s” as read-write…</string>\n    <string name=\"file_job_remount_success_format\">“%1$s” has been remounted as read-write</string>\n    <string name=\"file_job_action_all\">Apply this action to all files</string>\n\n    <string name=\"file_open_apk_message\">This file is an Android package. Do you want to install it, or view its contents?</string>\n    <string name=\"file_edit_title\" translatable=\"false\">@string/edit</string>\n    <string name=\"file_open_as_title\">Open as…</string>\n    <string name=\"file_open_as_title_format\">Open “%1$s” as</string>\n    <string name=\"file_open_as_type_text\">Text</string>\n    <string name=\"file_open_as_type_image\">Image</string>\n    <string name=\"file_open_as_type_audio\">Audio</string>\n    <string name=\"file_open_as_type_video\">Video</string>\n    <string name=\"file_open_as_type_directory\">Folder</string>\n    <string name=\"file_open_as_type_any\">Other</string>\n    <string name=\"file_install_apk_from_background_title_format\">“%1$s” is ready to be installed</string>\n    <string name=\"file_install_apk_from_background_text\">Tap to install</string>\n    <string name=\"file_open_from_background_title_format\">“%1$s” is ready to be opened</string>\n    <string name=\"file_open_from_background_text\">Tap to open</string>\n    <string name=\"file_name_error_empty\">File name cannot be empty</string>\n    <string name=\"file_name_error_invalid\">Invalid file name</string>\n    <string name=\"file_name_error_already_exists\">A file with this name already exists</string>\n    <string name=\"file_replace_message_format\">Replace “%1$s”?</string>\n    <string name=\"file_delete_message_file_format\">Delete “%1$s”?</string>\n    <string name=\"file_delete_message_directory_format\">Delete folder “%1$s” and its contents?</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"one\">Delete %1$,d file?</item>\n        <item quantity=\"other\">Delete %1$,d files?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"one\">Delete %1$,d folder and its content?</item>\n        <item quantity=\"other\">Delete %1$,d folders and their contents?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"one\">Delete %1$,d item?</item>\n        <item quantity=\"other\">Delete %1$,d items?</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">Create archive</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_create_archive_password\">Password (optional)</string>\n    <string name=\"file_add_bookmark_success\">Bookmark added</string>\n    <string name=\"file_create_file_title\">New file</string>\n    <string name=\"file_create_directory_title\">New folder</string>\n\n    <string name=\"file_list_title\">Files</string>\n    <plurals name=\"file_list_title_open_file\">\n        <item quantity=\"one\">Select a file</item>\n        <item quantity=\"other\">Select files</item>\n    </plurals>\n    <string name=\"file_list_title_create_file\">Save file</string>\n    <plurals name=\"file_list_title_open_directory\">\n        <item quantity=\"one\">Select a folder</item>\n        <item quantity=\"other\">Select folders</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"one\">%1$,d folder</item>\n        <item quantity=\"other\">%1$,d folders</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"one\">%1$,d file</item>\n        <item quantity=\"other\">%1$,d files</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">,\\u0020</string>\n    <string name=\"file_list_path_error_empty\">Path cannot be empty</string>\n    <string name=\"file_list_path_error_invalid\">Invalid path</string>\n    <string name=\"file_list_action_view_sort\">View and sort</string>\n    <string name=\"file_list_action_view_list\">List</string>\n    <string name=\"file_list_action_view_grid\">Grid</string>\n    <string name=\"file_list_action_sort_by_name\">Name</string>\n    <string name=\"file_list_action_sort_by_type\">Type</string>\n    <string name=\"file_list_action_sort_by_size\">Size</string>\n    <string name=\"file_list_action_sort_by_last_modified\">Last modified</string>\n    <string name=\"file_list_action_sort_order_ascending\">Ascending</string>\n    <string name=\"file_list_action_sort_directories_first\">Folders first</string>\n    <string name=\"file_list_action_view_sort_path_specific\">Only for this folder</string>\n    <string name=\"file_list_action_new_task\">New window</string>\n    <string name=\"file_list_action_navigate_up\">Go up</string>\n    <string name=\"file_list_action_navigate_to\">Go to</string>\n    <string name=\"file_list_navigate_to_title\" translatable=\"false\">@string/file_list_action_navigate_to</string>\n    <string name=\"file_list_action_show_hidden_files\">Show hidden files</string>\n    <string name=\"file_list_action_copy_path\">Copy path</string>\n    <string name=\"file_list_action_open_in_terminal\">Open in terminal</string>\n    <string name=\"file_list_action_add_bookmark\">Add bookmark</string>\n    <string name=\"file_list_action_create_shortcut\">Create shortcut</string>\n    <string name=\"file_list_breadcrumb_action_copy_path\" translatable=\"false\">@string/file_list_action_copy_path</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">Open in new window</string>\n    <string name=\"file_list_select_title_format\">%1$,d</string>\n    <string name=\"file_list_select_action_extract\" translatable=\"false\">@string/file_item_action_extract</string>\n    <string name=\"file_list_select_action_archive\" translatable=\"false\">@string/file_item_action_archive</string>\n    <string name=\"file_list_create_file_name_hint\">File name</string>\n    <string name=\"file_list_create_file_name_error_empty\" translatable=\"false\">@string/file_name_error_empty</string>\n    <string name=\"file_list_create_file_name_error_invalid\" translatable=\"false\">@string/file_name_error_invalid</string>\n    <string name=\"file_list_open_current_directory_format\">Select “%1$s”</string>\n    <string name=\"file_list_paste_move_title_format\">Moving %1$,d</string>\n    <string name=\"file_list_paste_copy_title_format\">Copying %1$,d</string>\n    <string name=\"file_list_paste_extract_title_format\">Extracting %1$,d</string>\n    <string name=\"file_list_paste_action_extract_here\">Extract here</string>\n    <string name=\"file_list_empty\">No files</string>\n    <string name=\"file_list_action_create_file\">File</string>\n    <string name=\"file_list_action_create_directory\">Folder</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">Open with</string>\n    <string name=\"file_item_action_extract\">Extract</string>\n    <string name=\"file_item_action_archive\">Compress</string>\n    <string name=\"file_item_action_copy_path\" translatable=\"false\">@string/file_list_action_copy_path</string>\n    <string name=\"file_item_action_add_bookmark\" translatable=\"false\">@string/file_list_action_add_bookmark</string>\n    <string name=\"file_item_action_create_shortcut\" translatable=\"false\">@string/file_list_action_create_shortcut</string>\n    <string name=\"file_item_action_properties\">Properties</string>\n\n    <string name=\"file_properties_title_format\">“%1$s” properties</string>\n    <string name=\"file_properties_basic\">Basic</string>\n    <string name=\"file_properties_basic_name\">Name</string>\n    <string name=\"file_properties_basic_type\">Type</string>\n    <string name=\"file_properties_basic_type_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">Link to %1$s (%2$s)</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">Link Target</string>\n    <string name=\"file_properties_basic_size\">Size</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_contents\">Contents</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"one\">%1$,d item, with size %2$s</item>\n        <item quantity=\"other\">%1$,d items, totaling %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">Last Modified</string>\n    <string name=\"file_properties_basic_parent_directory\">Parent Folder</string>\n    <string name=\"file_properties_basic_archive_file\">Archive File</string>\n    <string name=\"file_properties_basic_archive_entry\">Archive Entry</string>\n    <string name=\"file_properties_basic_free_space\">Free Space</string>\n    <string name=\"file_properties_permission\">Permissions</string>\n    <string name=\"file_properties_permission_owner\">Owner</string>\n    <string name=\"file_properties_permission_group\">Group</string>\n    <string name=\"file_properties_permission_principal_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_permission_set_owner_title\">Change owner</string>\n    <string name=\"file_properties_permission_set_group_title\">Change group</string>\n    <string name=\"file_properties_permission_set_principal_filter_hint\">Enter a name or ID</string>\n    <string name=\"file_properties_permission_set_principal_system\">System</string>\n    <string name=\"file_properties_permission_mode\">Mode</string>\n    <string name=\"file_properties_permission_mode_format\">%1$s (%2$04o)</string>\n    <string name=\"file_properties_permission_set_mode_title\">Change mode</string>\n    <string name=\"file_properties_permission_set_mode_others\">Others</string>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_file\">\n        <item>Read</item>\n        <item>Write</item>\n        <item>Execute</item>\n    </string-array>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_directory\">\n        <item>Read</item>\n        <item>Write</item>\n        <item>Search</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_special\">Special</string>\n    <string-array name=\"file_properties_permission_set_mode_special_mode_bits\">\n        <item>Set UID</item>\n        <item>Set GID</item>\n        <item>Sticky bit</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_uppercase_x\">Don’t add “Execute” for enclosed files</string>\n    <string name=\"file_properties_permission_selinux_context\">SELinux Context</string>\n    <string name=\"file_properties_permission_set_selinux_context_title\">Change SELinux context</string>\n    <string name=\"file_properties_permission_set_selinux_context_restore\">Restore</string>\n    <string name=\"file_properties_permission_recursive\">Apply to enclosed files</string>\n    <string name=\"file_properties_image\">Image</string>\n    <string name=\"file_properties_media_dimensions\">Dimensions</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">Taken on</string>\n    <string name=\"file_properties_media_coordinates\">Coordinates</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f, %2$.3f</string>\n    <string name=\"file_properties_media_address\">Location</string>\n    <string name=\"file_properties_image_gps_altitude\">Altitude</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f m</string>\n    <string name=\"file_properties_image_equipment\">Camera</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">Aperture</string>\n    <string name=\"file_properties_image_f_number_format\" translatable=\"false\">f/%1$.1f</string>\n    <string name=\"file_properties_image_shutter_speed\">Shutter speed</string>\n    <string name=\"file_properties_image_shutter_speed_with_denominator_format\" translatable=\"false\">1/%1$d</string>\n    <string name=\"file_properties_image_focal_length\">Focal length</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f mm</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">ISO equivalent</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">Software</string>\n    <string name=\"file_properties_image_description\">Description</string>\n    <string name=\"file_properties_image_artist\">Creator</string>\n    <string name=\"file_properties_image_copyright\">Copyright</string>\n    <string name=\"file_properties_audio\">Audio</string>\n    <string name=\"file_properties_media_title\">Title</string>\n    <string name=\"file_properties_audio_artist\">Artist</string>\n    <string name=\"file_properties_audio_album\">Album</string>\n    <string name=\"file_properties_audio_album_artist\">Album artist</string>\n    <string name=\"file_properties_audio_composer\">Composer</string>\n    <string name=\"file_properties_audio_disc_number\">Disc</string>\n    <string name=\"file_properties_audio_track_number\">Track</string>\n    <string name=\"file_properties_audio_year\">Year</string>\n    <string name=\"file_properties_audio_genre\">Genre</string>\n    <string name=\"file_properties_media_duration\">Duration</string>\n    <string name=\"file_properties_media_bit_rate\">Bit rate</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d kbps</string>\n    <string name=\"file_properties_audio_sample_rate\">Sample rate</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Hz</string>\n    <string name=\"file_properties_video\">Video</string>\n    <string name=\"file_properties_apk\">APK</string>\n    <string name=\"file_properties_apk_label\">Name</string>\n    <string name=\"file_properties_apk_package_name\">Package name</string>\n    <string name=\"file_properties_apk_version\">Version</string>\n    <string name=\"file_properties_apk_version_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_apk_min_sdk_version\">Minimum system version</string>\n    <string name=\"file_properties_apk_target_sdk_version\">Target system version</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s (%2$s, %3$d)</string>\n    <string-array name=\"file_properites_apk_sdk_version_names\" translatable=\"false\">\n        <item>Android ?</item>\n        <item>Android 1.0</item>\n        <item>Android 1.1</item>\n        <item>Android 1.5</item>\n        <item>Android 1.6</item>\n        <item>Android 2.0</item>\n        <item>Android 2.0.1</item>\n        <item>Android 2.1</item>\n        <item>Android 2.2</item>\n        <item>Android 2.3</item>\n        <item>Android 2.3.3</item>\n        <item>Android 3.0</item>\n        <item>Android 3.1</item>\n        <item>Android 3.2</item>\n        <item>Android 4.0</item>\n        <item>Android 4.0.3</item>\n        <item>Android 4.1</item>\n        <item>Android 4.2</item>\n        <item>Android 4.3</item>\n        <item>Android 4.4</item>\n        <item>Android 4.4W</item>\n        <item>Android 5.0</item>\n        <item>Android 5.1</item>\n        <item>Android 6.0</item>\n        <item>Android 7.0</item>\n        <item>Android 7.1</item>\n        <item>Android 8.0</item>\n        <item>Android 8.1</item>\n        <item>Android 9</item>\n        <item>Android 10</item>\n        <item>Android 11</item>\n        <item>Android 12</item>\n        <item>Android 12L</item>\n        <item>Android 13</item>\n        <item>Android 14</item>\n        <item>Android ?</item>\n    </string-array>\n    <string-array name=\"file_properites_apk_sdk_version_codenames\" translatable=\"false\">\n        <item>\\?</item>\n        <item>B</item>\n        <item>B_1_1</item>\n        <item>C</item>\n        <item>D</item>\n        <item>E</item>\n        <item>E_0_1</item>\n        <item>E_MR1</item>\n        <item>F</item>\n        <item>G</item>\n        <item>G_MR1</item>\n        <item>H</item>\n        <item>H_MR1</item>\n        <item>H_MR2</item>\n        <item>ICS</item>\n        <item>ICS_MR1</item>\n        <item>JB</item>\n        <item>JB_MR1</item>\n        <item>JB_MR2</item>\n        <item>KK</item>\n        <item>KK_W</item>\n        <item>L</item>\n        <item>L_MR1</item>\n        <item>M</item>\n        <item>N</item>\n        <item>N_MR1</item>\n        <item>O</item>\n        <item>O_MR1</item>\n        <item>P</item>\n        <item>Q</item>\n        <item>R</item>\n        <item>S</item>\n        <item>S_V2</item>\n        <item>T</item>\n        <item>U</item>\n        <item>\\?</item>\n    </string-array>\n    <string name=\"file_properties_apk_requested_permissions\">Permissions</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">No permissions requested</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"one\">%1$,d permission requested</item>\n        <item quantity=\"other\">%1$,d permissions requested</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">Signatures</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">No valid signatures</string>\n    <string name=\"file_properties_apk_past_signature_digests\">Old signatures</string>\n    <string name=\"file_properties_checksum\">Checksums</string>\n    <string name=\"file_properties_checksum_compare\">Compare</string>\n    <string name=\"file_properties_checksum_compare_placeholder\">Any checksum</string>\n    <string name=\"file_properties_checksum_compare_match_format\">Matches %1$s checksum</string>\n    <string name=\"file_properties_checksum_compare_prefix_match_format\">Matches prefix of %1$s checksum</string>\n    <string name=\"file_properties_checksum_compare_no_match\">No matching checksum found</string>\n    <string name=\"file_properties_checksum_crc32\" translatable=\"false\">CRC32</string>\n    <string name=\"file_properties_checksum_md5\" translatable=\"false\">MD5</string>\n    <string name=\"file_properties_checksum_sha_1\" translatable=\"false\">SHA-1</string>\n    <string name=\"file_properties_checksum_sha_256\" translatable=\"false\">SHA-256</string>\n    <string name=\"file_properties_checksum_sha_512\" translatable=\"false\">SHA-512</string>\n\n    <string name=\"storage_file_system_root_title\">Root</string>\n    <string name=\"storage_list_title\">Storage</string>\n    <string name=\"storage_list_empty\">No storage</string>\n    <string name=\"storage_add_storage_title\">Add storage</string>\n    <string name=\"storage_add_storage_android_data\" translatable=\"false\">Android/data</string>\n    <string name=\"storage_add_storage_android_obb\" translatable=\"false\">Android/obb</string>\n    <string name=\"storage_add_storage_document_tree\">External storage</string>\n    <string name=\"storage_add_storage_ftp_server\">FTP server</string>\n    <string name=\"storage_add_storage_sftp_server\">SFTP server</string>\n    <string name=\"storage_add_storage_smb_server\">SMB server</string>\n    <string name=\"storage_add_storage_webdav_server\">WebDAV server</string>\n    <string name=\"storage_edit_device_storage_title\">Edit device storage</string>\n    <string name=\"storage_edit_device_storage_name\">Name</string>\n    <string name=\"storage_edit_device_storage_path\">Path</string>\n    <string name=\"storage_add_external_storage_shortcut_title\">Add external storage shortcut</string>\n    <string name=\"storage_edit_external_storage_shortcut_title\">Edit external storage shortcut</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id\">Storage volume</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id_error_empty\">Enter a storage volume</string>\n    <string name=\"storage_edit_external_storage_shortcut_path\">Path</string>\n    <string name=\"storage_add_document_tree_title\">Add external storage</string>\n    <string name=\"storage_edit_document_tree_title\">Edit external storage</string>\n    <string name=\"storage_edit_document_tree_name\">Name</string>\n    <string name=\"storage_edit_document_tree_uri\">URI</string>\n    <string name=\"storage_edit_document_tree_path\">Path</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">Edit FTP server</string>\n    <string name=\"storage_edit_ftp_server_title_add\">Add FTP server</string>\n    <string name=\"storage_edit_ftp_server_host\">Hostname</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">Enter a hostname</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">Invalid hostname</string>\n    <string name=\"storage_edit_ftp_server_port\">Port</string>\n    <string name=\"storage_edit_ftp_server_port_placeholder\" translatable=\"false\">21</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">Invalid port</string>\n    <string name=\"storage_edit_ftp_server_path\">Path</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">Can be left empty</string>\n    <string name=\"storage_edit_ftp_server_name\">Name</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">Use hostname</string>\n    <string name=\"storage_edit_ftp_server_protocol\">Protocol</string>\n    <string-array name=\"storage_edit_ftp_server_protocol_entries\" translatable=\"false\">\n        <item>FTP</item>\n        <item>FTPS</item>\n        <item>FTPES</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_authentication_type\">Authentication</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>Password</item>\n        <item>Anonymous</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">Username</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">Enter a username</string>\n    <string name=\"storage_edit_ftp_server_password\">Password</string>\n    <string name=\"storage_edit_ftp_server_mode\">Mode</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>Active</item>\n        <item>Passive</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">Encoding</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">Connect and add</string>\n    <string name=\"storage_edit_ftp_server_add\">Add</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">Edit SFTP server</string>\n    <string name=\"storage_edit_sftp_server_title_add\">Add SFTP server</string>\n    <string name=\"storage_edit_sftp_server_host\">Hostname</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">Enter a hostname</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">Invalid hostname</string>\n    <string name=\"storage_edit_sftp_server_port\">Port</string>\n    <string name=\"storage_edit_sftp_server_port_placeholder\" translatable=\"false\">22</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">Invalid port</string>\n    <string name=\"storage_edit_sftp_server_path\">Path</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">Can be left empty</string>\n    <string name=\"storage_edit_sftp_server_name\">Name</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">Use hostname</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">Authentication</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>Password</item>\n        <item>Public key</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">Username</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">Enter a username</string>\n    <string name=\"storage_edit_sftp_server_password\">Password</string>\n    <string name=\"storage_edit_sftp_server_private_key\">Private key</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">Open file</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">Enter a private key</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">Invalid private key</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">Private key password</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">Can be left empty</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">Invalid private key password</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">Connect and add</string>\n    <string name=\"storage_edit_sftp_server_add\">Add</string>\n    <string name=\"storage_add_lan_smb_server_title\" translatable=\"false\">@string/storage_edit_smb_server_title_add</string>\n    <string name=\"storage_add_lan_smb_server_loading\">Searching for SMB servers…</string>\n    <string name=\"storage_add_lan_smb_server_add\">Add manually</string>\n    <string name=\"storage_edit_smb_server_title_edit\">Edit SMB server</string>\n    <string name=\"storage_edit_smb_server_title_add\">Add SMB server</string>\n    <string name=\"storage_edit_smb_server_host\">Hostname</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">Enter a hostname</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">Invalid hostname</string>\n    <string name=\"storage_edit_smb_server_port\">Port</string>\n    <string name=\"storage_edit_smb_server_port_placeholder\" translatable=\"false\">445</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">Invalid port</string>\n    <string name=\"storage_edit_smb_server_path\">Path</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">Can be left empty</string>\n    <string name=\"storage_edit_smb_server_name\">Name</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">Use hostname</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">Authentication</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>Password</item>\n        <item>Guest</item>\n        <item>Anonymous</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">Username</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">Enter a username</string>\n    <string name=\"storage_edit_smb_server_password\">Password</string>\n    <string name=\"storage_edit_smb_server_domain\">Domain</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">Connect and add</string>\n    <string name=\"storage_edit_smb_server_add\">Add</string>\n    <string name=\"storage_edit_webdav_server_title_edit\">Edit WebDAV server</string>\n    <string name=\"storage_edit_webdav_server_title_add\">Add WebDAV server</string>\n    <string name=\"storage_edit_webdav_server_host\">Hostname</string>\n    <string name=\"storage_edit_webdav_server_host_error_empty\">Enter a hostname</string>\n    <string name=\"storage_edit_webdav_server_host_error_invalid\">Invalid hostname</string>\n    <string name=\"storage_edit_webdav_server_port\">Port</string>\n    <string name=\"storage_edit_webdav_server_port_placeholder\" translatable=\"false\">443</string>\n    <string name=\"storage_edit_webdav_server_port_error_invalid\">Invalid port</string>\n    <string name=\"storage_edit_webdav_server_path\">Path</string>\n    <string name=\"storage_edit_webdav_server_path_placeholder\">Can be left empty</string>\n    <string name=\"storage_edit_webdav_server_name\">Name</string>\n    <string name=\"storage_edit_webdav_server_name_placeholder\">Use hostname</string>\n    <string name=\"storage_edit_webdav_server_protocol\">Protocol</string>\n    <string-array name=\"storage_edit_webdav_server_protocol_entries\" translatable=\"false\">\n        <item>HTTP</item>\n        <item>HTTPS</item>\n    </string-array>\n    <string name=\"storage_edit_webdav_server_authentication_type\">Authentication</string>\n    <string-array name=\"storage_edit_webdav_server_authentication_type_entries\">\n        <item>Password</item>\n        <item>Access token</item>\n        <item>None</item>\n    </string-array>\n    <string name=\"storage_edit_webdav_server_username\">Username</string>\n    <string name=\"storage_edit_webdav_server_username_error_empty\">Enter a username</string>\n    <string name=\"storage_edit_webdav_server_password\">Password</string>\n    <string name=\"storage_edit_webdav_server_access_token\">Access token</string>\n    <string name=\"storage_edit_webdav_server_access_token_error_empty\">Enter an access token</string>\n    <string name=\"storage_edit_webdav_server_connect_and_add\">Connect and add</string>\n    <string name=\"storage_edit_webdav_server_add\">Add</string>\n\n    <string name=\"navigation_storage_subtitle_format\">%1$s free of %2$s</string>\n    <string name=\"navigation_add_storage\">Add storage…</string>\n    <string name=\"navigation_standard_directory_alarms\">Alarms</string>\n    <string name=\"navigation_standard_directory_dcim\">DCIM</string>\n    <string name=\"navigation_standard_directory_documents\">Documents</string>\n    <string name=\"navigation_standard_directory_downloads\">Downloads</string>\n    <string name=\"navigation_standard_directory_movies\">Movies</string>\n    <string name=\"navigation_standard_directory_music\">Music</string>\n    <string name=\"navigation_standard_directory_notifications\">Notifications</string>\n    <string name=\"navigation_standard_directory_pictures\">Pictures</string>\n    <string name=\"navigation_standard_directory_podcasts\">Podcasts</string>\n    <string name=\"navigation_standard_directory_ringtones\">Ringtones</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">Bookmark folder</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">Name</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">Path</string>\n    <string name=\"navigation_ftp_server\" translatable=\"false\">@string/ftp_server_title</string>\n    <string name=\"navigation_settings\" translatable=\"false\">@string/settings_title</string>\n    <string name=\"navigation_about\" translatable=\"false\">@string/about_title</string>\n\n    <string name=\"archive_viewer_title\">Archive viewer</string>\n    <string name=\"image_viewer_title\">Image viewer</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"image_viewer_delete_message_format\" translatable=\"false\">@string/file_delete_message_file_format</string>\n    <string name=\"save_as_title\">Save as</string>\n    <string name=\"save_as_error\">Error while saving file</string>\n    <string name=\"save_as_success_format\">“%1$s” has been saved to “%2$s”</string>\n    <string name=\"text_editor_title\">Text editor</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">Saved</string>\n    <string name=\"text_editor_reload_message\">Are you sure you want to reload? Unsaved changes to this document will be lost.</string>\n    <string name=\"text_editor_encoding\">Encoding</string>\n    <string name=\"text_editor_close_message\">Are you sure you want to discard unsaved changes to this document?</string>\n\n    <string name=\"ftp_server_title\">FTP server</string>\n    <string name=\"ftp_server_state_title\">Status</string>\n    <string name=\"ftp_server_state_summary_starting\">Starting…</string>\n    <string name=\"ftp_server_state_summary_running\">Running</string>\n    <string name=\"ftp_server_state_summary_stopping\">Stopping…</string>\n    <string name=\"ftp_server_state_summary_stopped\">Not started</string>\n    <string name=\"ftp_server_url_title\">URL</string>\n    <string name=\"ftp_server_url_summary_no_local_inet_address\">Local IP address is unknown</string>\n    <string name=\"ftp_server_url_menu_copy_url\">Copy URL</string>\n    <string name=\"ftp_server_url_menu_copy_password\">Copy password</string>\n    <string name=\"ftp_server_add_tile_title\">Add to Quick Settings</string>\n    <string name=\"ftp_server_add_tile_result_added\">“FTP server” has been added to Quick Settings</string>\n    <string name=\"ftp_server_add_tile_result_already_added\">“FTP server” is already added to Quick Settings</string>\n    <string name=\"ftp_server_add_tile_result_error\">Error while adding “FTP server” to Quick Settings</string>\n    <string name=\"ftp_server_configuration_title\">Configuration</string>\n    <string name=\"ftp_server_anonymous_login_title\">Anonymous login</string>\n    <string name=\"ftp_server_username_title\">Username</string>\n    <string name=\"ftp_server_password_title\">Password</string>\n    <string name=\"ftp_server_port_title\">Port</string>\n    <string name=\"ftp_server_home_directory_title\">Root folder</string>\n    <string name=\"ftp_server_writable_title\">Allow writing</string>\n    <string name=\"ftp_server_notification_title\" translatable=\"false\">@string/ftp_server_title</string>\n    <string name=\"ftp_server_notification_text_no_local_inet_address\" translatable=\"false\">@string/ftp_server_url_summary_no_local_inet_address</string>\n\n    <string name=\"settings_title\">Settings</string>\n    <string name=\"settings_interface_title\">Interface</string>\n    <string name=\"settings_locale_title\">Language</string>\n    <string name=\"settings_theme_color_title\">Theme color</string>\n    <string name=\"settings_theme_color_summary\">Color that appears most frequently in the app</string>\n    <string name=\"settings_material_design_3_title\">Material Design 3</string>\n    <string name=\"settings_night_mode_title\">Night mode</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>Follow system</item>\n        <item>Off</item>\n        <item>On</item>\n        <item>Based on time</item>\n        <item>Based on battery saver</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">Black night mode</string>\n    <string name=\"settings_file_list_animation_title\">File list animation</string>\n    <string name=\"settings_file_name_ellipsize_title\">Display long file name</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>Ellipsize the beginning</item>\n        <item>Ellipsize the middle</item>\n        <item>Ellipsize the end</item>\n        <item>Marquee</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">Behavior</string>\n    <string name=\"settings_default_directory_title\">Default folder</string>\n    <string name=\"settings_storages_title\" translatable=\"false\">@string/storage_list_title</string>\n    <string name=\"settings_storages_summary_empty\" translatable=\"false\">@string/storage_list_empty</string>\n    <string name=\"settings_standard_directories_title\">Standard folders</string>\n    <string name=\"settings_standard_directories_summary_empty\">No standard folders enabled</string>\n    <string name=\"settings_standard_directory_list_title\" translatable=\"false\">@string/settings_standard_directories_title</string>\n    <string name=\"settings_bookmark_directory_screenshots\">Screenshots</string>\n    <string name=\"settings_bookmark_directories_title\">Bookmark folders</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">No bookmark folders</string>\n    <string name=\"settings_bookmark_directory_list_title\" translatable=\"false\">@string/settings_bookmark_directories_title</string>\n    <string name=\"settings_bookmark_directory_list_empty\" translatable=\"false\">@string/settings_bookmark_directories_summary_empty</string>\n    <string name=\"settings_root_strategy_title\">Root access mode</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"one\">You have %1$,d file operation running and changing root access mode now may result in unexpected error. Are you sure you want to change it now?</item>\n        <item quantity=\"other\">You have %1$,d file operations running and changing root access mode now may result in unexpected error. Are you sure you want to change it now?</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>Normal access only</item>\n        <item>Automatic</item>\n        <item>Root access only</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">Archive file name encoding</string>\n    <string name=\"settings_open_apk_default_action_title\">Open Android package</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>Install</item>\n        <item>View contents</item>\n        <item>Ask what to do</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_title\">Show thumbnail for PDF documents</string>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">Might make the app unstable on older versions of Android</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">Read remote files for thumbnail</string>\n\n    <string name=\"about_title\">About</string>\n    <string name=\"about_version_title\">Version</string>\n    <string name=\"about_github_title\">View on GitHub</string>\n    <string name=\"about_licenses_title\">Licenses</string>\n    <string name=\"about_privacy_policy_title\">Privacy policy</string>\n    <string name=\"about_author_title\">Author</string>\n    <string name=\"about_author_name_title\">Hai Zhang</string>\n    <string name=\"about_author_github_title\">Follow on GitHub</string>\n    <string name=\"about_author_twitter_title\">Follow on Twitter</string>\n\n    <string name=\"shortcut_downloads_title\" translatable=\"false\">@string/navigation_standard_directory_downloads</string>\n    <string name=\"shortcut_ftp_server_title\" translatable=\"false\">@string/ftp_server_title</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources xmlns:tools=\"http://schemas.android.com/tools\">\n\n    <style name=\"Preference.MaterialFiles.SwitchPreferenceCompat\" parent=\"Preference.SwitchPreferenceCompat.Material\">\n        <item name=\"android:widgetLayout\">@layout/switch_preference_compat_widget</item>\n    </style>\n\n    <style name=\"ShapeAppearance.MaterialFiles.MediumComponent\" parent=\"ShapeAppearance.MaterialComponents.MediumComponent\">\n        <item name=\"cornerSize\">8dp</item>\n    </style>\n\n    <style name=\"TextAppearance.AppCompat.Widget.ActionBar.Subtitle.Small\">\n        <item name=\"android:textSize\" tools:ignore=\"SpUsage\">12dp</item>\n    </style>\n\n    <!--\n      ~ See also drawable-v21/mtrl_popupmenu_background.xml and\n      ~ drawable-v23/mtrl_popupmenu_background_dark.xml which are overridden.\n      -->\n    <style name=\"Widget.MaterialFiles.ListView.DropDown\" parent=\"Widget.AppCompat.ListView.DropDown\">\n        <item name=\"android:paddingTop\">8dp</item>\n        <item name=\"android:paddingBottom\">8dp</item>\n        <item name=\"android:clipToPadding\">false</item>\n    </style>\n\n    <style name=\"Widget.MaterialFiles.NavigationView\" parent=\"Widget.MaterialComponents.NavigationView\">\n        <item name=\"itemHorizontalPadding\">@dimen/navigation_item_horizontal_padding</item>\n        <item name=\"itemVerticalPadding\" tools:ignore=\"PrivateResource\">@dimen/design_navigation_item_vertical_padding</item>\n        <item name=\"itemShapeAppearanceOverlay\">@style/ShapeAppearanceOverlay.MaterialFiles.NavigationView.Item</item>\n        <item name=\"itemShapeInsetStart\">0dp</item>\n        <item name=\"itemShapeInsetEnd\">8dp</item>\n        <item name=\"itemShapeInsetTop\">0dp</item>\n        <item name=\"itemShapeInsetBottom\">0dp</item>\n        <item name=\"itemIconTint\">@color/navigation_item_subtitle_text_color</item>\n        <item name=\"itemIconPadding\">@dimen/navigation_item_icon_padding</item>\n        <item name=\"itemSubtitleTextAppearance\">?textAppearanceBody2</item>\n        <item name=\"itemSubtitleTextColor\">@color/navigation_item_subtitle_text_color</item>\n        <item name=\"itemSubtitleTextSize\">10sp</item>\n        <item name=\"dividerVerticalPadding\">@dimen/design_navigation_separator_vertical_padding</item>\n    </style>\n    <style name=\"ShapeAppearanceOverlay.MaterialFiles.NavigationView.Item\" parent=\"\">\n        <item name=\"cornerSize\">50%</item>\n        <item name=\"cornerSizeTopLeft\">0dp</item>\n        <item name=\"cornerSizeBottomLeft\">0dp</item>\n    </style>\n\n    <style name=\"Widget.MaterialFiles.CardView\" parent=\"Widget.MaterialComponents.CardView\">\n        <item name=\"android:stateListAnimator\">@null</item>\n        <item name=\"cardElevation\">0dp</item>\n        <item name=\"strokeColor\">@color/mtrl_btn_stroke_color_selector</item>\n        <item name=\"strokeWidth\">@dimen/mtrl_btn_stroke_size</item>\n    </style>\n\n    <!-- See also ThemeOverlay.AppCompat.ActionBar . -->\n    <style name=\"ThemeOverlay.MaterialFiles.ActionBar\" parent=\"\">\n        <item name=\"colorControlNormal\">?android:textColorPrimary</item>\n        <item name=\"searchViewStyle\">@style/Widget.AppCompat.SearchView.ActionBar</item>\n    </style>\n\n    <style name=\"ThemeOverlay.MaterialFiles.ActionBarPopup\" parent=\"\">\n        <item name=\"colorControlNormal\">?android:textColorSecondary</item>\n        <item name=\"searchViewStyle\">@style/Widget.AppCompat.SearchView</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/styles_material3.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:ignore=\"PrivateResource\">\n\n    <style name=\"Preference.MaterialFiles.Material3.SimpleMenuPreference\" parent=\"Preference.SimpleMenuPreference\">\n        <item name=\"android:popupMenuStyle\">@style/Widget.MaterialFiles.Material3.Preference.SimpleMenuPreference.PopupMenu</item>\n    </style>\n    <style name=\"Widget.MaterialFiles.Material3.Preference.SimpleMenuPreference.PopupMenu\" parent=\"Widget.Preference.SimpleMenuPreference.PopupMenu\">\n        <item name=\"android:popupBackground\">@drawable/m3_popupmenu_background_overlay</item>\n    </style>\n\n    <style name=\"Preference.MaterialFiles.Material3.SwitchPreferenceCompat\" parent=\"Preference.SwitchPreferenceCompat.Material\">\n        <item name=\"android:widgetLayout\">@layout/switch_preference_compat_widget_material3</item>\n    </style>\n\n    <style name=\"ShapeAppearance.MaterialFiles.Material3.SmallComponent\" parent=\"ShapeAppearance.Material3.SmallComponent\">\n        <item name=\"cornerSize\">8dp</item>\n    </style>\n\n    <style name=\"ShapeAppearance.MaterialFiles.Material3.MediumComponent\" parent=\"ShapeAppearance.Material3.MediumComponent\">\n        <item name=\"cornerSize\">12dp</item>\n    </style>\n\n    <style name=\"ShapeAppearance.MaterialFiles.Material3.LargeComponent\" parent=\"ShapeAppearance.Material3.LargeComponent\">\n        <item name=\"cornerSize\">16dp</item>\n    </style>\n\n    <style name=\"TextAppearance.MaterialFiles.Material3.ListItem\" parent=\"TextAppearance.Material3.TitleMedium\">\n        <item name=\"fontFamily\">@string/m3_ref_typeface_plain_regular</item>\n        <item name=\"android:fontFamily\">@string/m3_ref_typeface_plain_regular</item>\n    </style>\n\n    <style name=\"TextAppearance.MaterialFiles.Material3.ListItemSmall\" parent=\"TextAppearance.Material3.TitleSmall\">\n        <item name=\"fontFamily\">@string/m3_ref_typeface_plain_regular</item>\n        <item name=\"android:fontFamily\">@string/m3_ref_typeface_plain_regular</item>\n    </style>\n\n    <style name=\"Widget.MaterialFiles.Material3.NavigationView\" parent=\"Widget.Material3.NavigationView\">\n        <item name=\"itemHorizontalPadding\">@dimen/navigation_item_horizontal_padding_material3</item>\n        <item name=\"itemSubtitleTextAppearance\">?textAppearanceBodyMedium</item>\n        <item name=\"itemSubtitleTextColor\">@color/navigation_item_subtitle_text_color_material3</item>\n        <item name=\"itemSubtitleTextSize\">10sp</item>\n        <item name=\"itemIconPadding\">@dimen/navigation_item_icon_padding_material3</item>\n        <item name=\"dividerVerticalPadding\">@dimen/design_navigation_separator_vertical_padding</item>\n    </style>\n\n    <style name=\"Widget.MaterialFiles.Material3.TabLayout\" parent=\"Widget.Material3.TabLayout\">\n        <item name=\"android:background\">@android:color/transparent</item>\n    </style>\n\n    <style name=\"Widget.MaterialFiles.Material3.Toolbar\" parent=\"Widget.Material3.Toolbar\">\n        <item name=\"contentInsetStartWithNavigation\">@dimen/abc_action_bar_content_inset_with_nav</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <style name=\"Platform.V21.Theme.MaterialFiles\" parent=\"Theme.MaterialComponents.DayNight.NoActionBar\">\n        <item name=\"colorPrimaryDark\">@color/system_window_scrim_compat</item>\n    </style>\n    <style name=\"Platform.Theme.MaterialFiles\" parent=\"Platform.V21.Theme.MaterialFiles\" />\n    <style name=\"Base.Theme.MaterialFiles\" parent=\"Platform.Theme.MaterialFiles\">\n\n        <item name=\"actionBarTheme\">@style/ThemeOverlay.MaterialFiles.ActionBar</item>\n        <item name=\"actionBarPopupTheme\">@style/ThemeOverlay.MaterialFiles.ActionBarPopup</item>\n        <item name=\"android:colorBackground\">?colorSurface</item>\n        <item name=\"dropDownListViewStyle\">@style/Widget.MaterialFiles.ListView.DropDown</item>\n        <!-- https://developer.android.com/training/tv/start/start#transition-color -->\n        <item name=\"android:windowAllowEnterTransitionOverlap\">true</item>\n        <item name=\"android:windowAllowReturnTransitionOverlap\">true</item>\n\n        <item name=\"colorPrimary\">@color/color_primary</item>\n        <item name=\"colorPrimaryVariant\">?colorPrimary</item>\n        <item name=\"colorSecondary\">?colorPrimary</item>\n        <item name=\"colorOnSecondary\">?colorOnPrimary</item>\n        <item name=\"colorSecondaryVariant\">?colorSecondary</item>\n        <item name=\"colorSurface\">@color/color_surface</item>\n        <item name=\"shapeAppearanceMediumComponent\">@style/ShapeAppearance.MaterialFiles.MediumComponent</item>\n        <item name=\"buttonBarButtonStyle\">@style/Widget.MaterialComponents.Button.TextButton.Dialog</item>\n        <item name=\"materialCardViewStyle\">@style/Widget.MaterialFiles.CardView</item>\n        <item name=\"navigationViewStyle\">@style/Widget.MaterialFiles.NavigationView</item>\n        <item name=\"switchPreferenceCompatStyle\">@style/Preference.MaterialFiles.SwitchPreferenceCompat</item>\n        <item name=\"textInputStyle\">?textInputOutlinedStyle</item>\n        <item name=\"minTouchTargetSize\">0dp</item>\n\n        <item name=\"colorAppBarSurface\">?colorSurface</item>\n    </style>\n    <style name=\"Theme.MaterialFiles\" parent=\"Base.Theme.MaterialFiles\" />\n\n    <style name=\"Platform.V21.Theme.MaterialFiles.Translucent\" parent=\"Theme.MaterialFiles\">\n        <item name=\"android:navigationBarColor\">@android:color/transparent</item>\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n    </style>\n    <style name=\"Platform.Theme.MaterialFiles.Translucent\" parent=\"Platform.V21.Theme.MaterialFiles.Translucent\" />\n    <style name=\"Base.Theme.MaterialFiles.Translucent\" parent=\"Platform.Theme.MaterialFiles.Translucent\">\n        <item name=\"android:colorBackgroundCacheHint\">@null</item>\n        <item name=\"android:windowAnimationStyle\">@null</item>\n        <item name=\"android:windowBackground\">@android:color/transparent</item>\n        <item name=\"android:windowContentOverlay\">@null</item>\n        <item name=\"android:windowDisablePreview\">true</item>\n        <item name=\"android:windowIsTranslucent\">true</item>\n        <item name=\"windowNoTitle\">true</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent\" parent=\"Base.Theme.MaterialFiles.Translucent\" />\n\n    <style name=\"Platform.V21.Theme.MaterialFiles.Immersive\" parent=\"Theme.MaterialComponents.NoActionBar\">\n        <item name=\"android:navigationBarColor\">?colorPrimaryDark</item>\n\n        <item name=\"colorPrimaryDark\">@color/dark_50_percent</item>\n    </style>\n    <style name=\"Platform.Theme.MaterialFiles.Immersive\" parent=\"Platform.V21.Theme.MaterialFiles.Immersive\" />\n    <style name=\"Base.Theme.MaterialFiles.Immersive\" parent=\"Platform.Theme.MaterialFiles.Immersive\">\n\n        <item name=\"actionBarTheme\">@style/ThemeOverlay.MaterialFiles.ActionBar</item>\n        <item name=\"actionBarPopupTheme\">@style/ThemeOverlay.MaterialFiles.ActionBarPopup</item>\n        <item name=\"android:colorBackground\">?colorSurface</item>\n        <item name=\"dropDownListViewStyle\">@style/Widget.MaterialFiles.ListView.DropDown</item>\n        <!-- https://developer.android.com/training/tv/start/start#transition-color -->\n        <item name=\"android:windowAllowEnterTransitionOverlap\">true</item>\n        <item name=\"android:windowAllowReturnTransitionOverlap\">true</item>\n        <item name=\"android:windowBackground\">@android:color/black</item>\n\n        <item name=\"colorPrimary\">@color/color_primary_dark</item>\n        <item name=\"colorPrimaryVariant\">?colorPrimary</item>\n        <item name=\"colorSecondary\">?colorPrimary</item>\n        <item name=\"colorOnSecondary\">?colorOnPrimary</item>\n        <item name=\"colorSecondaryVariant\">?colorSecondary</item>\n        <item name=\"colorSurface\">@color/color_surface_dark</item>\n        <item name=\"shapeAppearanceMediumComponent\">@style/ShapeAppearance.MaterialFiles.MediumComponent</item>\n        <item name=\"buttonBarButtonStyle\">@style/Widget.MaterialComponents.Button.TextButton.Dialog</item>\n        <item name=\"materialCardViewStyle\">@style/Widget.MaterialFiles.CardView</item>\n        <item name=\"navigationViewStyle\">@style/Widget.MaterialFiles.NavigationView</item>\n        <item name=\"switchPreferenceCompatStyle\">@style/Preference.MaterialFiles.SwitchPreferenceCompat</item>\n        <item name=\"textInputStyle\">?textInputOutlinedStyle</item>\n        <item name=\"minTouchTargetSize\">0dp</item>\n\n        <item name=\"colorAppBarSurface\">@color/dark_50_percent</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive\" parent=\"Base.Theme.MaterialFiles.Immersive\" />\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/themes_custom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<resources>\n\n    <style name=\"Theme.MaterialFiles.color_primary\">\n        <item name=\"colorPrimary\">@color/color_primary</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.color_primary\">\n        <item name=\"colorPrimary\">@color/color_primary</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.color_primary\">\n        <item name=\"colorPrimary\">@color/color_primary_dark</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_red\">\n        <item name=\"colorPrimary\">@color/material_red</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_red\">\n        <item name=\"colorPrimary\">@color/material_red</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_red\">\n        <item name=\"colorPrimary\">@color/material_red_300</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_pink\">\n        <item name=\"colorPrimary\">@color/material_pink</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_pink\">\n        <item name=\"colorPrimary\">@color/material_pink</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_pink\">\n        <item name=\"colorPrimary\">@color/material_pink_300</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_purple\">\n        <item name=\"colorPrimary\">@color/material_purple</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_purple\">\n        <item name=\"colorPrimary\">@color/material_purple</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_purple\">\n        <item name=\"colorPrimary\">@color/material_purple_300</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_deep_purple\">\n        <item name=\"colorPrimary\">@color/material_deep_purple</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_deep_purple\">\n        <item name=\"colorPrimary\">@color/material_deep_purple</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_deep_purple\">\n        <item name=\"colorPrimary\">@color/material_deep_purple_300</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_indigo\">\n        <item name=\"colorPrimary\">@color/material_indigo</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_indigo\">\n        <item name=\"colorPrimary\">@color/material_indigo</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_indigo\">\n        <item name=\"colorPrimary\">@color/material_indigo_300</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_blue\">\n        <item name=\"colorPrimary\">@color/material_blue</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_blue\">\n        <item name=\"colorPrimary\">@color/material_blue</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_blue\">\n        <item name=\"colorPrimary\">@color/material_blue_300</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_light_blue\">\n        <item name=\"colorPrimary\">@color/material_light_blue</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_light_blue\">\n        <item name=\"colorPrimary\">@color/material_light_blue</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_light_blue\">\n        <item name=\"colorPrimary\">@color/material_light_blue_300</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_cyan\">\n        <item name=\"colorPrimary\">@color/material_cyan</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_cyan\">\n        <item name=\"colorPrimary\">@color/material_cyan</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_cyan\">\n        <item name=\"colorPrimary\">@color/material_cyan_300</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_teal\">\n        <item name=\"colorPrimary\">@color/material_teal</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_teal\">\n        <item name=\"colorPrimary\">@color/material_teal</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_teal\">\n        <item name=\"colorPrimary\">@color/material_teal_300</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_green\">\n        <item name=\"colorPrimary\">@color/material_green</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_green\">\n        <item name=\"colorPrimary\">@color/material_green</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_green\">\n        <item name=\"colorPrimary\">@color/material_green_300</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_light_green\">\n        <item name=\"colorPrimary\">@color/material_light_green</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_light_green\">\n        <item name=\"colorPrimary\">@color/material_light_green</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_light_green\">\n        <item name=\"colorPrimary\">@color/material_light_green_300</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_lime\">\n        <item name=\"colorPrimary\">@color/material_lime</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_lime\">\n        <item name=\"colorPrimary\">@color/material_lime</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_lime\">\n        <item name=\"colorPrimary\">@color/material_lime_300</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_yellow\">\n        <item name=\"colorPrimary\">@color/material_yellow</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_yellow\">\n        <item name=\"colorPrimary\">@color/material_yellow</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_yellow\">\n        <item name=\"colorPrimary\">@color/material_yellow_300</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_amber\">\n        <item name=\"colorPrimary\">@color/material_amber</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_amber\">\n        <item name=\"colorPrimary\">@color/material_amber</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_amber\">\n        <item name=\"colorPrimary\">@color/material_amber_300</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_orange\">\n        <item name=\"colorPrimary\">@color/material_orange</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_orange\">\n        <item name=\"colorPrimary\">@color/material_orange</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_orange\">\n        <item name=\"colorPrimary\">@color/material_orange_300</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_deep_orange\">\n        <item name=\"colorPrimary\">@color/material_deep_orange</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_deep_orange\">\n        <item name=\"colorPrimary\">@color/material_deep_orange</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_deep_orange\">\n        <item name=\"colorPrimary\">@color/material_deep_orange_300</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_brown\">\n        <item name=\"colorPrimary\">@color/material_brown</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_brown\">\n        <item name=\"colorPrimary\">@color/material_brown</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_brown\">\n        <item name=\"colorPrimary\">@color/material_brown_300</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_grey\">\n        <item name=\"colorPrimary\">@color/material_grey</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_grey\">\n        <item name=\"colorPrimary\">@color/material_grey</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_grey\">\n        <item name=\"colorPrimary\">@color/material_grey_300</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_blue_grey\">\n        <item name=\"colorPrimary\">@color/material_blue_grey</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_blue_grey\">\n        <item name=\"colorPrimary\">@color/material_blue_grey</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_blue_grey\">\n        <item name=\"colorPrimary\">@color/material_blue_grey_300</item>\n    </style>\n\n    <style name=\"Theme.MaterialFiles.color_primary.Black\" />\n    <style name=\"Theme.MaterialFiles.Translucent.color_primary.Black\" />\n    <style name=\"Theme.MaterialFiles.Immersive.color_primary.Black\" />\n    <style name=\"Theme.MaterialFiles.material_red.Black\" />\n    <style name=\"Theme.MaterialFiles.Translucent.material_red.Black\" />\n    <style name=\"Theme.MaterialFiles.Immersive.material_red.Black\" />\n    <style name=\"Theme.MaterialFiles.material_pink.Black\" />\n    <style name=\"Theme.MaterialFiles.Translucent.material_pink.Black\" />\n    <style name=\"Theme.MaterialFiles.Immersive.material_pink.Black\" />\n    <style name=\"Theme.MaterialFiles.material_purple.Black\" />\n    <style name=\"Theme.MaterialFiles.Translucent.material_purple.Black\" />\n    <style name=\"Theme.MaterialFiles.Immersive.material_purple.Black\" />\n    <style name=\"Theme.MaterialFiles.material_deep_purple.Black\" />\n    <style name=\"Theme.MaterialFiles.Translucent.material_deep_purple.Black\" />\n    <style name=\"Theme.MaterialFiles.Immersive.material_deep_purple.Black\" />\n    <style name=\"Theme.MaterialFiles.material_indigo.Black\" />\n    <style name=\"Theme.MaterialFiles.Translucent.material_indigo.Black\" />\n    <style name=\"Theme.MaterialFiles.Immersive.material_indigo.Black\" />\n    <style name=\"Theme.MaterialFiles.material_blue.Black\" />\n    <style name=\"Theme.MaterialFiles.Translucent.material_blue.Black\" />\n    <style name=\"Theme.MaterialFiles.Immersive.material_blue.Black\" />\n    <style name=\"Theme.MaterialFiles.material_light_blue.Black\" />\n    <style name=\"Theme.MaterialFiles.Translucent.material_light_blue.Black\" />\n    <style name=\"Theme.MaterialFiles.Immersive.material_light_blue.Black\" />\n    <style name=\"Theme.MaterialFiles.material_cyan.Black\" />\n    <style name=\"Theme.MaterialFiles.Translucent.material_cyan.Black\" />\n    <style name=\"Theme.MaterialFiles.Immersive.material_cyan.Black\" />\n    <style name=\"Theme.MaterialFiles.material_teal.Black\" />\n    <style name=\"Theme.MaterialFiles.Translucent.material_teal.Black\" />\n    <style name=\"Theme.MaterialFiles.Immersive.material_teal.Black\" />\n    <style name=\"Theme.MaterialFiles.material_green.Black\" />\n    <style name=\"Theme.MaterialFiles.Translucent.material_green.Black\" />\n    <style name=\"Theme.MaterialFiles.Immersive.material_green.Black\" />\n    <style name=\"Theme.MaterialFiles.material_light_green.Black\" />\n    <style name=\"Theme.MaterialFiles.Translucent.material_light_green.Black\" />\n    <style name=\"Theme.MaterialFiles.Immersive.material_light_green.Black\" />\n    <style name=\"Theme.MaterialFiles.material_lime.Black\" />\n    <style name=\"Theme.MaterialFiles.Translucent.material_lime.Black\" />\n    <style name=\"Theme.MaterialFiles.Immersive.material_lime.Black\" />\n    <style name=\"Theme.MaterialFiles.material_yellow.Black\" />\n    <style name=\"Theme.MaterialFiles.Translucent.material_yellow.Black\" />\n    <style name=\"Theme.MaterialFiles.Immersive.material_yellow.Black\" />\n    <style name=\"Theme.MaterialFiles.material_amber.Black\" />\n    <style name=\"Theme.MaterialFiles.Translucent.material_amber.Black\" />\n    <style name=\"Theme.MaterialFiles.Immersive.material_amber.Black\" />\n    <style name=\"Theme.MaterialFiles.material_orange.Black\" />\n    <style name=\"Theme.MaterialFiles.Translucent.material_orange.Black\" />\n    <style name=\"Theme.MaterialFiles.Immersive.material_orange.Black\" />\n    <style name=\"Theme.MaterialFiles.material_deep_orange.Black\" />\n    <style name=\"Theme.MaterialFiles.Translucent.material_deep_orange.Black\" />\n    <style name=\"Theme.MaterialFiles.Immersive.material_deep_orange.Black\" />\n    <style name=\"Theme.MaterialFiles.material_brown.Black\" />\n    <style name=\"Theme.MaterialFiles.Translucent.material_brown.Black\" />\n    <style name=\"Theme.MaterialFiles.Immersive.material_brown.Black\" />\n    <style name=\"Theme.MaterialFiles.material_grey.Black\" />\n    <style name=\"Theme.MaterialFiles.Translucent.material_grey.Black\" />\n    <style name=\"Theme.MaterialFiles.Immersive.material_grey.Black\" />\n    <style name=\"Theme.MaterialFiles.material_blue_grey.Black\" />\n    <style name=\"Theme.MaterialFiles.Translucent.material_blue_grey.Black\" />\n    <style name=\"Theme.MaterialFiles.Immersive.material_blue_grey.Black\" />\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/themes_material3.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <style name=\"Platform.V21.Theme.MaterialFiles.Material3\" parent=\"Theme.Material3.DynamicColors.DayNight.NoActionBar\">\n        <item name=\"colorPrimaryDark\">@color/system_window_scrim_compat</item>\n    </style>\n    <style name=\"Platform.Theme.MaterialFiles.Material3\" parent=\"Platform.V21.Theme.MaterialFiles.Material3\" />\n    <style name=\"Base.Theme.MaterialFiles.Material3\" parent=\"Platform.Theme.MaterialFiles.Material3\">\n\n        <item name=\"actionBarTheme\">@style/ThemeOverlay.MaterialFiles.ActionBar</item>\n        <item name=\"actionBarPopupTheme\">@style/ThemeOverlay.MaterialFiles.ActionBarPopup</item>\n        <item name=\"android:colorBackground\">?colorSurface</item>\n        <item name=\"dropDownListViewStyle\">@style/Widget.MaterialFiles.ListView.DropDown</item>\n        <!-- https://developer.android.com/training/tv/start/start#transition-color -->\n        <item name=\"android:windowAllowEnterTransitionOverlap\">true</item>\n        <item name=\"android:windowAllowReturnTransitionOverlap\">true</item>\n\n        <item name=\"colorAccent\">?colorPrimary</item>\n        <item name=\"shapeAppearanceSmallComponent\">@style/ShapeAppearance.MaterialFiles.Material3.SmallComponent</item>\n        <item name=\"shapeAppearanceMediumComponent\">@style/ShapeAppearance.MaterialFiles.Material3.MediumComponent</item>\n        <item name=\"shapeAppearanceLargeComponent\">@style/ShapeAppearance.MaterialFiles.Material3.LargeComponent</item>\n        <item name=\"textAppearanceListItem\">@style/TextAppearance.MaterialFiles.Material3.ListItem</item>\n        <item name=\"android:textAppearanceListItem\">?textAppearanceListItem</item>\n        <item name=\"textAppearanceListItemSmall\">@style/TextAppearance.MaterialFiles.Material3.ListItemSmall</item>\n        <item name=\"android:textAppearanceListItemSmall\">?textAppearanceListItemSmall</item>\n        <item name=\"floatingActionButtonStyle\">?floatingActionButtonSecondaryStyle</item>\n        <item name=\"materialCardViewStyle\">@style/Widget.MaterialFiles.CardView</item>\n        <item name=\"navigationViewStyle\">@style/Widget.MaterialFiles.Material3.NavigationView</item>\n        <item name=\"simpleMenuPreferenceStyle\">@style/Preference.MaterialFiles.Material3.SimpleMenuPreference</item>\n        <item name=\"switchPreferenceCompatStyle\">@style/Preference.MaterialFiles.Material3.SwitchPreferenceCompat</item>\n        <item name=\"tabStyle\">@style/Widget.MaterialFiles.Material3.TabLayout</item>\n        <item name=\"textInputStyle\">?textInputOutlinedStyle</item>\n        <item name=\"toolbarStyle\">@style/Widget.MaterialFiles.Material3.Toolbar</item>\n        <item name=\"minTouchTargetSize\">0dp</item>\n\n        <item name=\"colorAppBarSurface\">?colorSurface</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Material3\" parent=\"Base.Theme.MaterialFiles.Material3\" />\n\n    <style name=\"Platform.V21.Theme.MaterialFiles.Material3.Translucent\" parent=\"Theme.MaterialFiles.Material3\">\n        <item name=\"android:navigationBarColor\">@android:color/transparent</item>\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n    </style>\n    <style name=\"Platform.Theme.MaterialFiles.Material3.Translucent\" parent=\"Platform.V21.Theme.MaterialFiles.Material3.Translucent\" />\n    <style name=\"Base.Theme.MaterialFiles.Material3.Translucent\" parent=\"Platform.Theme.MaterialFiles.Material3.Translucent\">\n        <item name=\"android:colorBackgroundCacheHint\">@null</item>\n        <item name=\"android:windowAnimationStyle\">@null</item>\n        <item name=\"android:windowBackground\">@android:color/transparent</item>\n        <item name=\"android:windowContentOverlay\">@null</item>\n        <item name=\"android:windowDisablePreview\">true</item>\n        <item name=\"android:windowIsTranslucent\">true</item>\n        <item name=\"windowNoTitle\">true</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Material3.Translucent\" parent=\"Base.Theme.MaterialFiles.Material3.Translucent\" />\n\n    <style name=\"Platform.V21.Theme.MaterialFiles.Material3.Immersive\" parent=\"Theme.Material3.DynamicColors.Dark.NoActionBar\">\n        <item name=\"android:navigationBarColor\">?colorPrimaryDark</item>\n\n        <item name=\"colorPrimaryDark\">@color/dark_50_percent</item>\n    </style>\n    <style name=\"Platform.Theme.MaterialFiles.Material3.Immersive\" parent=\"Platform.V21.Theme.MaterialFiles.Material3.Immersive\" />\n    <style name=\"Base.Theme.MaterialFiles.Material3.Immersive\" parent=\"Platform.Theme.MaterialFiles.Material3.Immersive\">\n\n        <item name=\"actionBarTheme\">@style/ThemeOverlay.MaterialFiles.ActionBar</item>\n        <item name=\"actionBarPopupTheme\">@style/ThemeOverlay.MaterialFiles.ActionBarPopup</item>\n        <item name=\"android:colorBackground\">?colorSurface</item>\n        <item name=\"dropDownListViewStyle\">@style/Widget.MaterialFiles.ListView.DropDown</item>\n        <!-- https://developer.android.com/training/tv/start/start#transition-color -->\n        <item name=\"android:windowAllowEnterTransitionOverlap\">true</item>\n        <item name=\"android:windowAllowReturnTransitionOverlap\">true</item>\n        <item name=\"android:windowBackground\">@android:color/black</item>\n\n        <item name=\"colorAccent\">?colorPrimary</item>\n        <item name=\"shapeAppearanceSmallComponent\">@style/ShapeAppearance.MaterialFiles.Material3.SmallComponent</item>\n        <item name=\"shapeAppearanceMediumComponent\">@style/ShapeAppearance.MaterialFiles.Material3.MediumComponent</item>\n        <item name=\"shapeAppearanceLargeComponent\">@style/ShapeAppearance.MaterialFiles.Material3.LargeComponent</item>\n        <item name=\"textAppearanceListItem\">@style/TextAppearance.MaterialFiles.Material3.ListItem</item>\n        <item name=\"android:textAppearanceListItem\">?textAppearanceListItem</item>\n        <item name=\"textAppearanceListItemSmall\">@style/TextAppearance.MaterialFiles.Material3.ListItemSmall</item>\n        <item name=\"android:textAppearanceListItemSmall\">?textAppearanceListItemSmall</item>\n        <item name=\"floatingActionButtonStyle\">?floatingActionButtonSecondaryStyle</item>\n        <item name=\"materialCardViewStyle\">@style/Widget.MaterialFiles.CardView</item>\n        <item name=\"navigationViewStyle\">@style/Widget.MaterialFiles.Material3.NavigationView</item>\n        <item name=\"simpleMenuPreferenceStyle\">@style/Preference.MaterialFiles.Material3.SimpleMenuPreference</item>\n        <item name=\"switchPreferenceCompatStyle\">@style/Preference.MaterialFiles.Material3.SwitchPreferenceCompat</item>\n        <item name=\"tabStyle\">@style/Widget.MaterialFiles.Material3.TabLayout</item>\n        <item name=\"textInputStyle\">?textInputOutlinedStyle</item>\n        <item name=\"toolbarStyle\">@style/Widget.MaterialFiles.Material3.Toolbar</item>\n        <item name=\"minTouchTargetSize\">0dp</item>\n\n        <item name=\"colorAppBarSurface\">@color/dark_50_percent</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Material3.Immersive\" parent=\"Base.Theme.MaterialFiles.Material3.Immersive\" />\n\n    <style name=\"Theme.MaterialFiles.Material3.Black\" />\n    <style name=\"Theme.MaterialFiles.Material3.Translucent.Black\" />\n    <style name=\"Theme.MaterialFiles.Material3.Immersive.Black\" />\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ar/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">ملف</string>\n    <string name=\"file_type_name_apk\">حزمة Android</string>\n    <string name=\"file_type_name_archive\">%1$s أرشيف</string>\n    <string name=\"file_type_name_audio\">%1$s صوت</string>\n    <string name=\"file_type_name_calendar\">%1$s تقويم</string>\n    <string name=\"file_type_name_certificate\">%1$s شهادة</string>\n    <string name=\"file_type_name_code\">%1$s مستند</string>\n    <string name=\"file_type_name_contact\">بطاقة عمل إلكترونية</string>\n    <string name=\"file_type_name_directory\">مجلد</string>\n    <string name=\"file_type_name_document\">%1$s مستند</string>\n    <string name=\"file_type_name_ebook\">%1$s كتاب إلكتروني e-book</string>\n    <string name=\"file_type_name_email\">%1$s رسالة الكترونية</string>\n    <string name=\"file_type_name_font\">%1$s خط</string>\n    <string name=\"file_type_name_generic\">%1$s ملف</string>\n    <string name=\"file_type_name_image\">%1$s صورة</string>\n    <string name=\"file_type_name_pdf\">مستند PDF</string>\n    <string name=\"file_type_name_presentation\">%1$s عرض تقديمي</string>\n    <string name=\"file_type_name_spreadsheet\">%1$s جداول</string>\n    <string name=\"file_type_name_text\">%1$s نصي</string>\n    <string name=\"file_type_name_text_plain\">مستند نص عادي</string>\n    <string name=\"file_type_name_video\">%1$s فيديو</string>\n    <string name=\"file_type_name_word\">مستند وورد</string>\n    <string name=\"file_type_name_powerpoint\">عرض باور بوينت</string>\n    <string name=\"file_type_name_excel\">جداول إكسل</string>\n    <string name=\"file_type_name_posix_character_device\">جهاز الصفة</string>\n    <string name=\"file_type_name_posix_block_device\">حظر الجهاز</string>\n    <string name=\"file_type_name_posix_fifo\">أنبوب</string>\n    <string name=\"file_type_name_posix_symbolic_link\">الارتباط</string>\n    <string name=\"file_type_name_posix_socket\">قابس</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">الارتباط (معطل)</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ar/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <string name=\"app_name\">ملفات متيريال</string>\n\n    <string name=\"close\">غلق</string>\n    <string name=\"copy\">نسخ</string>\n    <string name=\"cut\">قص</string>\n    <string name=\"default_\">الافتراضي</string>\n    <string name=\"delete\">حذف</string>\n    <string name=\"discard\">تجاهل</string>\n    <string name=\"edit\">تعديل</string>\n    <string name=\"empty\">فارغ</string>\n    <string name=\"empty_placeholder\">(فارغ)</string>\n    <string name=\"error\">خطأ</string>\n    <string name=\"hide\">إخفاء</string>\n    <string name=\"install\">تثبيت</string>\n    <string name=\"keep_editing\">استمر في التحرير</string>\n    <string name=\"loading\">جار التحميل…</string>\n    <string name=\"maybe_later\">ربما في وقت لاحق</string>\n    <string name=\"merge\">دمج</string>\n    <string name=\"none\">لا شيء</string>\n    <string name=\"open_navigation_drawer\">افتح درج التنقل</string>\n    <string name=\"paste\">لصق</string>\n    <string name=\"refresh\">تحديث</string>\n    <string name=\"reload\">إعادة تحميل</string>\n    <string name=\"remove\">إزالة</string>\n    <string name=\"rename\">إعادة تسمية</string>\n    <string name=\"replace\">استبدل</string>\n    <string name=\"reset\">إعادة تعيين</string>\n    <string name=\"retry\">أعادة المحاولة</string>\n    <string name=\"save\">حفظ</string>\n    <string name=\"search\">بحث</string>\n    <string name=\"select\">تحديد</string>\n    <string name=\"select_all\">تحديد الكل</string>\n    <string name=\"share\">مشاركة</string>\n    <string name=\"show\">إظهار</string>\n    <string name=\"skip\">تخطى</string>\n    <string name=\"stop\">إيقاف</string>\n    <string name=\"system_default\">النظام الافتراضي</string>\n    <string name=\"unknown\">غير معروف</string>\n    <string name=\"view\">عرض</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">لم يتم العثور على تطبيق للتعامل مع هذا الإجراء</string>\n    <string name=\"open_settings\">أفتح الإعدادات</string>\n    <string name=\"shortcut_created\">تم إنشاء الاختصار</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"zero\">%1$,d بايت</item>\n        <item quantity=\"one\">%1$,d بايت</item>\n        <item quantity=\"two\">%1$,d بايت</item>\n        <item quantity=\"few\">%1$,d بايتات</item>\n        <item quantity=\"many\">%1$,d بايت</item>\n        <item quantity=\"other\">%1$,d بايت</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">يحتاج التطبيق الوصول لإدارة جميع الملفات. لرجاء النقر فوق \\\"السماح\\\" في مربع حوار النظام القادم.</string>\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">يحتاج التطبيق إلى إذن للوصول إلى الملفات. الرجاء النقر فوق \\\"السماح\\\" في مربع حوار النظام القادم.</string>\n    <string name=\"storage_permission_permanently_denied_message\">يحتاج التطبيق إلى إذن للوصول إلى الملفات. الرجاء منح إذن \\\"التخزين\\\" في إعدادات النظام.</string>\n    <!-- The allow button is in sentence case now. -->\n    <string name=\"notification_permission_rationale_message\">يحتاج التطبيق إلى إذن لنشر إشعارات حول عمليات الملف. الرجاء النقر فوق \\\"السماح\\\" في مربع حوار النظام القادم.</string>\n    <string name=\"notification_permission_permanently_denied_message\">يحتاج التطبيق إلى إذن لنشر إشعارات حول عمليات الملف. يرجى منح إذن \\\"الإخطار\\\" في إعدادات النظام.</string>\n\n    <string name=\"notification_channel_background_activity_start_name\">الإجراءات أثناء العمل في الخلفية</string>\n    <string name=\"notification_channel_background_activity_start_description\">اتخذ إجراءات أثناء عمل التطبيق في الخلفية</string>\n    <string name=\"notification_channel_file_job_name\">عمليات الملف</string>\n    <string name=\"notification_channel_file_job_description\">عرض والتحكم في عمليات الملف</string>\n    <string name=\"notification_channel_ftp_server_name\">خادم FTP </string>\n    <string name=\"notification_channel_ftp_server_description\">عرض والتحكم في خادم FTP</string>\n\n    <string name=\"file_action_archive_password_title\">كلمة المرور مطلوبة</string>\n    <string name=\"file_action_archive_password_message_format\">”%1$s“ محمي بكلمة مرور</string>\n    <string name=\"file_action_archive_password_error_empty\">كلمة المرور لا يمكن أن تكون فارغة</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"zero\">التحضير لضغط %1$,d ملف (%2$s)</item>\n        <item quantity=\"one\">التحضير لضغط %1$,d ملف (%2$s)</item>\n        <item quantity=\"two\">التحضير لضغط %1$,d ملفان (%2$s)</item>\n        <item quantity=\"few\">التحضير لضغط %1$,d ملفات (%2$s)</item>\n        <item quantity=\"many\">التحضير لضغط %1$,d ملف (%2$s)</item>\n        <item quantity=\"other\">التحضير لضغط %1$,d ملف (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">ضغط ”%1$s“ في ”%2$s“</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"zero\">ضغط %1$,d ملف في ”%2$s“</item>\n        <item quantity=\"one\">ضغط %1$,d ملف في ”%2$s“</item>\n        <item quantity=\"two\">ضغط %1$,d ملفان في ”%2$s“</item>\n        <item quantity=\"few\">ضغط %1$,d ملفات في ”%2$s“</item>\n        <item quantity=\"many\">ضغط %1$,d ملف في ”%2$s“</item>\n        <item quantity=\"other\">ضغط %1$,d ملف في ”%2$s“</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"zero\">التحضير لنسخ %1$,d ملف (%2$s)</item>\n        <item quantity=\"one\">التحضير لنسخ %1$,d ملف (%2$s)</item>\n        <item quantity=\"two\">التحضير لنسخ %1$,d ملفان (%2$s)</item>\n        <item quantity=\"few\">التحضير لنسخ %1$,d ملفات (%2$s)</item>\n        <item quantity=\"many\">التحضير لنسخ %1$,d ملف (%2$s)</item>\n        <item quantity=\"other\">التحضير لنسخ %1$,d ملف (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">نسخ ”%1$s“ إلى ”%2$s“</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"zero\">نسخ %1$,d ملف إلى ”%2$s“</item>\n        <item quantity=\"one\">نسخ %1$,d ملف إلى ”%2$s“</item>\n        <item quantity=\"two\">نسخ %1$,d ملفان إلى ”%2$s“</item>\n        <item quantity=\"few\">نسخ %1$,d ملفات إلى ”%2$s“</item>\n        <item quantity=\"many\">نسخ %1$,d ملف إلى ”%2$s“</item>\n        <item quantity=\"other\">نسخ %1$,d ملف إلى ”%2$s“</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"zero\">التحضير لاستخراج %1$,d ملف (%2$s)</item>\n        <item quantity=\"one\">التحضير لاستخراج %1$,d ملف (%2$s)</item>\n        <item quantity=\"two\">التحضير لاستخراج %1$,d ملفان (%2$s)</item>\n        <item quantity=\"few\">التحضير لاستخراج %1$,d ملفات (%2$s)</item>\n        <item quantity=\"many\">التحضير لاستخراج %1$,d ملف (%2$s)</item>\n        <item quantity=\"other\">التحضير لاستخراج %1$,d ملف (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">استخراج ”%1$s“ إلى ”%2$s“</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"zero\">استخراج %1$,d ملف إلى ”%2$s“</item>\n        <item quantity=\"one\">استخراج %1$,d ملف إلى ”%2$s“</item>\n        <item quantity=\"two\">استخراج %1$,d ملفان إلى ”%2$s“</item>\n        <item quantity=\"few\">استخراج %1$,d ملفات إلى ”%2$s“</item>\n        <item quantity=\"many\">استخراج %1$,d ملف إلى ”%2$s“</item>\n        <item quantity=\"other\">استخراج %1$,d ملف إلى ”%2$s“</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"zero\">التحضير لنقل %1$,d ملف (%2$s)</item>\n        <item quantity=\"one\">التحضير لنقل %1$,d ملف (%2$s)</item>\n        <item quantity=\"two\">التحضير لنقل %1$,d ملفان (%2$s)</item>\n        <item quantity=\"few\">التحضير لنقل %1$,d ملفات (%2$s)</item>\n        <item quantity=\"many\">التحضير لنقل %1$,d ملف (%2$s)</item>\n        <item quantity=\"other\">التحضير لنقل %1$,d ملف (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">نقل ”%1$s“ إلى ”%2$s“</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"zero\">نقل %1$,d ملف إلى ”%2$s“</item>\n        <item quantity=\"one\">نقل %1$,d ملف إلى ”%2$s“</item>\n        <item quantity=\"two\">نقل %1$,d ملفان إلى ”%2$s“</item>\n        <item quantity=\"few\">نقل %1$,d ملفات إلى ”%2$s“</item>\n        <item quantity=\"many\">نقل %1$,d ملف إلى ”%2$s“</item>\n        <item quantity=\"other\">نقل %1$,d ملف إلى ”%2$s“</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"zero\">التحضير لحذف %1$,d ملف</item>\n        <item quantity=\"one\">التحضير لحذف %1$,d ملف</item>\n        <item quantity=\"two\">التحضير لحذف %1$,d ملفان</item>\n        <item quantity=\"few\">التحضير لحذف %1$,d ملفات</item>\n        <item quantity=\"many\">التحضير لحذف %1$,d ملف</item>\n        <item quantity=\"other\">التحضير لحذف %1$,d ملف</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">حذف ”%1$s“</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"zero\">حذف %1$,d ملف</item>\n        <item quantity=\"one\">حذف %1$,d ملف</item>\n        <item quantity=\"two\">حذف %1$,d ملفان</item>\n        <item quantity=\"few\">حذف %1$,d ملفات</item>\n        <item quantity=\"many\">حذف %1$,d ملف</item>\n        <item quantity=\"other\">حذف %1$,d ملف</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"zero\">التحضير لتغيير المالك لـ %1$,d ملف</item>\n        <item quantity=\"one\">التحضير لتغيير المالك لـ %1$,d ملف</item>\n        <item quantity=\"two\">التحضير لتغيير المالك لـ %1$,d ملفان</item>\n        <item quantity=\"few\">التحضير لتغيير المالك لـ %1$,d ملفات</item>\n        <item quantity=\"many\">التحضير لتغيير المالك لـ %1$,d ملف</item>\n        <item quantity=\"other\">التحضير لتغيير المالك لـ %1$,d ملف</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">تغيير المالك لـ ”%1$s“</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"zero\">تغيير المالك لـ %1$,d ملف</item>\n        <item quantity=\"one\">تغيير المالك لـ %1$,d ملف</item>\n        <item quantity=\"two\">تغيير المالك لـ %1$,d ملفان</item>\n        <item quantity=\"few\">تغيير المالك لـ %1$,d ملفات</item>\n        <item quantity=\"many\">تغيير المالك لـ %1$,d ملف</item>\n        <item quantity=\"other\">تغيير المالك لـ %1$,d ملف</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"zero\">التحضير لتغيير المجموعة لـ %1$,d ملف</item>\n        <item quantity=\"one\">التحضير لتغيير المجموعة لـ %1$,d ملف</item>\n        <item quantity=\"two\">التحضير لتغيير المجموعة لـ %1$,d ملفان</item>\n        <item quantity=\"few\">التحضير لتغيير المجموعة لـ %1$,d ملفات</item>\n        <item quantity=\"many\">التحضير لتغيير المجموعة لـ %1$,d ملف</item>\n        <item quantity=\"other\">التحضير لتغيير المجموعة لـ %1$,d ملف</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">تغيير المجموعة لـ  ”%1$s“</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"zero\">تغيير المجموعة لـ %1$,d ملف</item>\n        <item quantity=\"one\">تغيير المجموعة لـ %1$,d ملف</item>\n        <item quantity=\"two\">تغيير المجموعة لـ %1$,d ملفان</item>\n        <item quantity=\"few\">تغيير المجموعة لـ %1$,d ملفات</item>\n        <item quantity=\"many\">تغيير المجموعة لـ %1$,d ملف</item>\n        <item quantity=\"other\">تغيير المجموعة لـ %1$,d ملف</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"zero\">التحضير لتغيير الوضع لـ %1$,d ملف</item>\n        <item quantity=\"one\">التحضير لتغيير الوضع لـ %1$,d ملف</item>\n        <item quantity=\"two\">التحضير لتغيير الوضع لـ %1$,d ملفان</item>\n        <item quantity=\"few\">التحضير لتغيير الوضع لـ %1$,d ملفات</item>\n        <item quantity=\"many\">التحضير لتغيير الوضع لـ %1$,d ملف</item>\n        <item quantity=\"other\">التحضير لتغيير الوضع لـ %1$,d ملف</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">تغيير الوضع لـ ”%1$s“</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"zero\">تغيير الوضع لـ %1$,d ملف</item>\n        <item quantity=\"one\">تغيير الوضع لـ %1$,d ملف</item>\n        <item quantity=\"two\">تغيير الوضع لـ %1$,d ملفان</item>\n        <item quantity=\"few\">تغيير الوضع لـ %1$,d ملفات</item>\n        <item quantity=\"many\">تغيير الوضع لـ %1$,d ملف</item>\n        <item quantity=\"other\">تغيير الوضع لـ %1$,d ملف</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"zero\">التحضير لتغيير سياق SELinux لـ %1$,d ملف</item>\n        <item quantity=\"one\">التحضير لتغيير سياق SELinux لـ %1$,d ملف</item>\n        <item quantity=\"two\">التحضير لتغيير سياق SELinux لـ %1$,d ملفان</item>\n        <item quantity=\"few\">التحضير لتغيير سياق SELinux لـ %1$,d ملفات</item>\n        <item quantity=\"many\">التحضير لتغيير سياق SELinux لـ %1$,d ملف</item>\n        <item quantity=\"other\">التحضير لتغيير سياق SELinux لـ %1$,d ملف</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">تغيير سياق SELinux لـ ”%1$s“</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"zero\">تغيير سياق SELinux لـ %1$,d ملف</item>\n        <item quantity=\"one\">تغيير سياق SELinux لـ %1$,d ملف</item>\n        <item quantity=\"two\">تغيير سياق SELinux لـ %1$,d ملفان</item>\n        <item quantity=\"few\">تغيير سياق SELinux لـ %1$,d ملفات</item>\n        <item quantity=\"many\">تغيير سياق SELinux لـ %1$,d ملف</item>\n        <item quantity=\"other\">تغيير سياق SELinux لـ %1$,d ملف</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"zero\">التحضير لاستعادة سياق SELinux لـ %1$,d ملف</item>\n        <item quantity=\"one\">التحضير لاستعادة سياق SELinux لـ %1$,d ملف</item>\n        <item quantity=\"two\">التحضير لاستعادة سياق SELinux لـ %1$,d ملفان</item>\n        <item quantity=\"few\">التحضير لاستعادة سياق SELinux لـ %1$,d ملفات</item>\n        <item quantity=\"many\">التحضير لاستعادة سياق SELinux لـ %1$,d ملف</item>\n        <item quantity=\"other\">التحضير لاستعادة سياق SELinux لـ %1$,d ملف</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">استعادة سياق SELinux لـ ”%1$s“</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"zero\">استعادة سياق SELinux لـ %1$,d ملف</item>\n        <item quantity=\"one\">استعادة سياق SELinux لـ %1$,d ملف</item>\n        <item quantity=\"two\">استعادة سياق SELinux لـ %1$,d ملفان</item>\n        <item quantity=\"few\">استعادة سياق SELinux لـ %1$,d ملفات</item>\n        <item quantity=\"many\">استعادة سياق SELinux لـ %1$,d ملف</item>\n        <item quantity=\"other\">استعادة سياق SELinux لـ %1$,d ملف</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">جاري الكتابة ”%1$s“</string>\n    <string name=\"file_job_create_error_title\">خطأ أثناء الإنشاء</string>\n    <string name=\"file_job_create_error_message_format\">حدث خطأ في إنشاء ”%1$s“.\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">لا يمكن نسخ مجلد في نفسه</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">لا يمكن استخراج مجلد في نفسه</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">لا يمكن نقل مجلد إلى نفسه</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">المجلد الوجهة موجود داخل المجلد المصدر.</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">لا يمكن نسخ ملف على نفسه</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">لا يمكن استخراج ملف على نفسه</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">لا يمكن نقل ملف فوق نفسه</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">ستتم الكتابة فوق الملف المصدر بواسطة الوجهة.</string>\n    <string name=\"file_job_replace_title_format\">استبدل الملف ”%1$s“؟</string>\n    <string name=\"file_job_replace_message_format\">يوجد ملف آخر بنفس الاسم بالفعل في ”%1$s“.\\n سيؤدي استبداله إلى الكتابة فوق محتوياته.</string>\n    <string name=\"file_job_replace_target_name\">الملف الأصلي</string>\n    <string name=\"file_job_replace_source_name\">استبدل بـ</string>\n    <string name=\"file_job_merge_title_format\">دمج مجلد ”%1$s“؟</string>\n    <string name=\"file_job_merge_copy_message_format\">سيطلب الدمج التأكيد قبل استبدال أي ملفات في المجلد تتعارض مع الملفات التي يتم نسخها.</string>\n    <string name=\"file_job_merge_extract_message_format\">سيطلب الدمج التأكيد قبل استبدال أي ملفات في المجلد تتعارض مع الملفات التي يتم استخراجها.</string>\n    <string name=\"file_job_merge_move_message_format\">سيطلب الدمج التأكيد قبل استبدال أي ملفات في المجلد تتعارض مع الملفات التي يتم نقلها.</string>\n    <string name=\"file_job_merge_target_name\">المجلد الأصلي</string>\n    <string name=\"file_job_merge_source_name\">دمج مع</string>\n    <string name=\"file_job_conflict_show_name\">حدد اسمًا جديدًا للوجهة</string>\n    <string name=\"file_job_conflict_name_hint\">اسم جديد</string>\n    <string name=\"file_job_archive_error_title_format\">خطأ أثناء ضغط ”%1$s“</string>\n    <string name=\"file_job_archive_error_message_format\">حدث خطأ أثناء ضغط الملف إلى ”%1$s“.\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">خطأ أثناء نسخ”%1$s“</string>\n    <string name=\"file_job_copy_error_message_format\">حدث خطأ في نسخ الملف إلى ”%1$s“.\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">خطأ أثناء استخراج ”%1$s“</string>\n    <string name=\"file_job_extract_error_message_format\">حدث خطأ أثناء استخراج الملف إلى ”%1$s“.\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">خطأ أثناء نقل ”%1$s“</string>\n    <string name=\"file_job_move_error_message_format\">حدث خطأ أثناء نقل الملف إلى ”%1$s“.\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">خطأ أثناء الحذف</string>\n    <string name=\"file_job_delete_error_message_format\">حدث خطأ أثناء حذف ”%1$s“.\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">خطأ أثناء إعادة تسمية ”%1$s“</string>\n    <string name=\"file_job_rename_error_message_format\">حدث خطأ أثناء إعادة تسمية الملف إلى ”%1$s“.\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">خطأ أثناء تغيير مالك لـ ”%1$s“</string>\n    <string name=\"file_job_set_owner_error_message_format\">حدث خطأ في تغيير المالك إلى ”%1$s“.\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">خطأ أثناء تغيير المجموعة لـ ”%1$s“</string>\n    <string name=\"file_job_set_group_error_message_format\">حدث خطأ في تغيير المجموعة إلى ”%1$s“.\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">خطأ أثناء الوضع لـ ”%1$s“</string>\n    <string name=\"file_job_set_mode_error_message_format\">حدث خطأ في تغيير الوضع إلى ”%1$s“.\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">خطأ أثناء تغيير سياق SELinux لـ ”%1$s“</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">حدث خطأ في تغيير سياق SELinux إلى ”%1$s“.\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">خطأ أثناء استعادة سياق SELinux</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">حدث خطأ أثناء استعادة سياق SELinux إلى ”%1$s“.\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">خطأ أثناء الكتابة</string>\n    <string name=\"file_job_write_error_message_format\">حدث خطأ في كتابة ”%1$s“.\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">حدث خطأ أثناء الحصول على معلومات حول ”%1$s“.</string>\n    <string name=\"file_job_remount_format\">أعد تحميل ”%1$s“ في وضع قراءة-كتابة</string>\n    <string name=\"file_job_remount_loading_format\">جاري إعادة تحميل ”%1$s“ في وضع قراءة-كتابة…</string>\n    <string name=\"file_job_remount_success_format\">تمت إعادة تحميل ”%1$s“ في وضع قراءة-كتابة</string>\n    <string name=\"file_job_action_all\">قم بتطبيق هذا الإجراء على جميع الملفات</string>\n\n    <string name=\"file_open_apk_message\">هذا الملف عبارة عن حزمة تطبيق لـ Android. هل تريد تثبيته أم عرض محتوياته؟</string>\n    <string name=\"file_open_as_title\">افتح كـ …</string>\n    <string name=\"file_open_as_title_format\">افتح ”%1$s“ كـ</string>\n    <string name=\"file_open_as_type_text\">نص</string>\n    <string name=\"file_open_as_type_image\">صورة</string>\n    <string name=\"file_open_as_type_audio\">صوت</string>\n    <string name=\"file_open_as_type_video\">فيديو</string>\n    <string name=\"file_open_as_type_directory\">مجلد</string>\n    <string name=\"file_open_as_type_any\">آخر</string>\n    <string name=\"file_install_apk_from_background_title_format\">”%1$s“ جاهز للتثبيت</string>\n    <string name=\"file_install_apk_from_background_text\">انقر للتثبيت</string>\n    <string name=\"file_open_from_background_title_format\">”%1$s“ جاهز للفتح</string>\n    <string name=\"file_open_from_background_text\">انقر للفتح</string>\n    <string name=\"file_name_error_empty\">لا يمكن أن يكون اسم الملف فارغًا</string>\n    <string name=\"file_name_error_invalid\">اسم الملف غير صالح</string>\n    <string name=\"file_name_error_already_exists\">يوجد ملف بهذا الاسم بالفعل</string>\n    <string name=\"file_replace_message_format\">استبدال ”%1$s“؟</string>\n    <string name=\"file_delete_message_file_format\">حذف ”%1$s“؟</string>\n    <string name=\"file_delete_message_directory_format\">حذف المجلد ”%1$s“  ومحتوياته؟</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"zero\">هل تريد حذف %1$,d من الملفات؟</item>\n        <item quantity=\"one\">هل تريد حذف %1$,d من الملفات؟</item>\n        <item quantity=\"two\">هل تريد حذف %1$,d من الملفات؟</item>\n        <item quantity=\"few\">هل تريد حذف %1$,d من الملفات؟</item>\n        <item quantity=\"many\">هل تريد حذف %1$,d من الملفات؟</item>\n        <item quantity=\"other\">هل تريد حذف %1$,d من الملفات؟</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"zero\">هل تريد حذف %1$,d مجلدات ومحتوياتها؟</item>\n        <item quantity=\"one\">هل تريد حذف %1$,d مجلدات ومحتوياتها؟</item>\n        <item quantity=\"two\">هل تريد حذف %1$,d مجلدات ومحتوياتها؟</item>\n        <item quantity=\"few\">هل تريد حذف %1$,d مجلدات ومحتوياتها؟</item>\n        <item quantity=\"many\">هل تريد حذف %1$,d مجلدات ومحتوياتها؟</item>\n        <item quantity=\"other\">هل تريد حذف %1$,d مجلدات ومحتوياتها؟</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"zero\">هل تريد حذف %1$,d عنصر؟</item>\n        <item quantity=\"one\">هل تريد حذف %1$,d عنصر؟</item>\n        <item quantity=\"two\">هل تريد حذف %1$,d عنصر؟</item>\n        <item quantity=\"few\">هل تريد حذف %1$,d عنصر؟</item>\n        <item quantity=\"many\">هل تريد حذف %1$,d عنصر؟</item>\n        <item quantity=\"other\">هل تريد حذف %1$,d عنصر؟</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">إنشاء أرشيف</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_create_archive_password\">كلمة مرور (اختياري)</string>\n    <string name=\"file_add_bookmark_success\">تمت إضافة إشارة مرجعية</string>\n    <string name=\"file_create_file_title\">ملف جديد</string>\n    <string name=\"file_create_directory_title\">مجلد جديد</string>\n\n    <string name=\"file_list_title\">ملفات</string>\n    <plurals name=\"file_list_title_open_file\">\n        <item quantity=\"zero\">اختيار ملف</item>\n        <item quantity=\"one\">اختيار ملف</item>\n        <item quantity=\"two\">اختيار ملفان</item>\n        <item quantity=\"few\">اختيار ملفات</item>\n        <item quantity=\"many\">اختيار ملفات</item>\n        <item quantity=\"other\">اختيار ملفات</item>\n    </plurals>\n    <string name=\"file_list_title_create_file\">حفظ الملف</string>\n    <plurals name=\"file_list_title_open_directory\">\n        <item quantity=\"zero\">اختيار مجلد</item>\n        <item quantity=\"one\">اختيار مجلد</item>\n        <item quantity=\"two\">اختيار مجلدان</item>\n        <item quantity=\"few\">اختيار مجلدات</item>\n        <item quantity=\"many\">اختيار مجلدات</item>\n        <item quantity=\"other\">اختيار مجلدات</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"zero\">%1$,d مجلد</item>\n        <item quantity=\"one\">%1$,d مجلد</item>\n        <item quantity=\"two\">%1$,d مجلدان</item>\n        <item quantity=\"few\">%1$,d مجلدات</item>\n        <item quantity=\"many\">%1$,d مجلد</item>\n        <item quantity=\"other\">%1$,d مجلد</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"zero\">%1$,d ملف</item>\n        <item quantity=\"one\">%1$,d ملف</item>\n        <item quantity=\"two\">%1$,d ملفان</item>\n        <item quantity=\"few\">%1$,d ملفات</item>\n        <item quantity=\"many\">%1$,d ملف</item>\n        <item quantity=\"other\">%1$,d ملف</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">,\\u0020</string>\n    <string name=\"file_list_path_error_empty\">لا يمكن أن يكون المسار فارغًا</string>\n    <string name=\"file_list_path_error_invalid\">مسار غير صالح</string>\n    <string name=\"file_list_action_view_sort\">عرض وفرز</string>\n    <string name=\"file_list_action_view_list\">قائمة</string>\n    <string name=\"file_list_action_view_grid\">شبكة</string>\n    <string name=\"file_list_action_sort_by_name\">اسم</string>\n    <string name=\"file_list_action_sort_by_type\">نوع</string>\n    <string name=\"file_list_action_sort_by_size\">حجم</string>\n    <string name=\"file_list_action_sort_by_last_modified\">آخر تعديل</string>\n    <string name=\"file_list_action_sort_order_ascending\">تصاعدي</string>\n    <string name=\"file_list_action_sort_directories_first\">المجلدات أولًا</string>\n    <string name=\"file_list_action_view_sort_path_specific\">فقط لهذا المجلد</string>\n    <string name=\"file_list_action_new_task\">نافذة جديدة</string>\n    <string name=\"file_list_action_navigate_up\">اذهب للأعلى</string>\n    <string name=\"file_list_action_navigate_to\">اذهب إلى</string>\n    <string name=\"file_list_action_show_hidden_files\">أظهر الملفات المخفية</string>\n    <string name=\"file_list_action_copy_path\">انسخ المسار</string>\n    <string name=\"file_list_action_open_in_terminal\">فتح في نافذة محرر الأوامر</string>\n    <string name=\"file_list_action_add_bookmark\">اضافة للإشاراة المرجعية</string>\n    <string name=\"file_list_action_create_shortcut\">إنشاء اختصار</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">افتح في نافذة جديدة</string>\n    <string name=\"file_list_select_title_format\">%1$,d</string>\n    <string name=\"file_list_create_file_name_hint\">اسم الملف</string>\n    <string name=\"file_list_open_current_directory_format\">اختيار ”%1$s“</string>\n    <string name=\"file_list_paste_move_title_format\">نقل %1$,d</string>\n    <string name=\"file_list_paste_copy_title_format\">نسخ %1$,d</string>\n    <string name=\"file_list_paste_extract_title_format\">استخراج %1$,d</string>\n    <string name=\"file_list_paste_action_extract_here\">استخرج هنا</string>\n    <string name=\"file_list_empty\">لا توجد ملفات</string>\n    <string name=\"file_list_action_create_file\">ملف</string>\n    <string name=\"file_list_action_create_directory\">مجلد</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">فتح بـ</string>\n    <string name=\"file_item_action_extract\">استخرج</string>\n    <string name=\"file_item_action_archive\">ضغط</string>\n    <string name=\"file_item_action_properties\">الخصائص</string>\n\n    <string name=\"file_properties_title_format\">”%1$s“ خصائص</string>\n    <string name=\"file_properties_basic\">أساسي</string>\n    <string name=\"file_properties_basic_name\">الاسم</string>\n    <string name=\"file_properties_basic_type\">النوع</string>\n    <string name=\"file_properties_basic_type_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">رابط لـ %1$s (%2$s)</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">رابط الهدف</string>\n    <string name=\"file_properties_basic_size\">الحجم</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_contents\">محتويات</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"zero\">%1$,d عنصر بإجمالي %2$s</item>\n        <item quantity=\"one\">%1$,d عنصر بإجمالي %2$s</item>\n        <item quantity=\"two\">%1$,d عنصران بإجمالي %2$s</item>\n        <item quantity=\"few\">%1$,d عناصر بإجمالي %2$s</item>\n        <item quantity=\"many\">%1$,d عنصر بإجمالي %2$s</item>\n        <item quantity=\"other\">%1$,d عنصر بإجمالي %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">آخر تعديل</string>\n    <string name=\"file_properties_basic_parent_directory\">المجلد الأصلي</string>\n    <string name=\"file_properties_basic_archive_file\">ملف الأرشيف</string>\n    <string name=\"file_properties_basic_archive_entry\">إدخال الأرشيف</string>\n    <string name=\"file_properties_basic_free_space\">مساحة فارغة</string>\n    <string name=\"file_properties_permission\">الصلاحيات</string>\n    <string name=\"file_properties_permission_owner\">المالك</string>\n    <string name=\"file_properties_permission_group\">المجموعة</string>\n    <string name=\"file_properties_permission_principal_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_permission_set_owner_title\">تغيير المالك</string>\n    <string name=\"file_properties_permission_set_group_title\">تغيير المجموعة</string>\n    <string name=\"file_properties_permission_set_principal_filter_hint\">أدخل اسماً أو عنوان ID</string>\n    <string name=\"file_properties_permission_set_principal_system\">النظام</string>\n    <string name=\"file_properties_permission_mode\">الوضع</string>\n    <string name=\"file_properties_permission_mode_format\">%1$s (%2$04o)</string>\n    <string name=\"file_properties_permission_set_mode_title\">تغيير الوضع</string>\n    <string name=\"file_properties_permission_set_mode_others\">الآخرون</string>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_file\">\n        <item>قراءة</item>\n        <item>كتابة</item>\n        <item>تنفيذ</item>\n    </string-array>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_directory\">\n        <item>قراءة</item>\n        <item>كتابة</item>\n        <item>بحث</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_special\">خاص</string>\n    <string-array name=\"file_properties_permission_set_mode_special_mode_bits\">\n        <item>تعيين UID</item>\n        <item>تعيين GID</item>\n        <item>بت لزجة</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_uppercase_x\">لا تقم بإضافة \\\"تنفيذ\\\" للملفات المرفقة</string>\n    <string name=\"file_properties_permission_selinux_context\">سياق SELinux</string>\n    <string name=\"file_properties_permission_set_selinux_context_title\">تغيير سياق SELinux</string>\n    <string name=\"file_properties_permission_set_selinux_context_restore\">استعادة</string>\n    <string name=\"file_properties_permission_recursive\">تطبيق على الملفات المرفقة</string>\n    <string name=\"file_properties_image\">صورة</string>\n    <string name=\"file_properties_media_dimensions\">الأبعاد</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d  \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">اتخذ</string>\n    <string name=\"file_properties_media_coordinates\">تنسيق</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f، %2$.3f</string>\n    <string name=\"file_properties_media_address\">موقع</string>\n    <string name=\"file_properties_image_gps_altitude\">الارتفاع</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f m</string>\n    <string name=\"file_properties_image_equipment\">كاميرا</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">فتحة العدسة</string>\n    <string name=\"file_properties_image_shutter_speed\">سرعة الغالق</string>\n    <string name=\"file_properties_image_focal_length\">البعد البؤري</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f mm</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">مكافئ ISO</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">البرمجيات</string>\n    <string name=\"file_properties_image_description\">وصف</string>\n    <string name=\"file_properties_image_artist\">المنشئ</string>\n    <string name=\"file_properties_image_copyright\">حقوق النشر</string>\n    <string name=\"file_properties_audio\">الصوت</string>\n    <string name=\"file_properties_media_title\">العنوان</string>\n    <string name=\"file_properties_audio_artist\">الفنان</string>\n    <string name=\"file_properties_audio_album\">الألبوم</string>\n    <string name=\"file_properties_audio_album_artist\">فنان الألبوم</string>\n    <string name=\"file_properties_audio_composer\">الملحن</string>\n    <string name=\"file_properties_audio_disc_number\">القرص</string>\n    <string name=\"file_properties_audio_track_number\">المسار</string>\n    <string name=\"file_properties_audio_year\">السنة</string>\n    <string name=\"file_properties_audio_genre\">النوع</string>\n    <string name=\"file_properties_media_duration\">المدة الزمنية</string>\n    <string name=\"file_properties_media_bit_rate\">معدل البت /البت ريت</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d kbps</string>\n    <string name=\"file_properties_audio_sample_rate\">معدل العينة</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Hz</string>\n    <string name=\"file_properties_video\">فيديو</string>\n    <string name=\"file_properties_apk\">APK</string>\n    <string name=\"file_properties_apk_label\">اسم</string>\n    <string name=\"file_properties_apk_package_name\">اسم الحزمة</string>\n    <string name=\"file_properties_apk_version\">الإصدار</string>\n    <string name=\"file_properties_apk_version_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_apk_min_sdk_version\">الحد الأدنى لإصدار النظام</string>\n    <string name=\"file_properties_apk_target_sdk_version\">إصدار النظام المستهدف</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s (%2$s، %3$d)</string>\n    <string name=\"file_properties_apk_requested_permissions\">الأذونات</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">لا توجد أذونات مطلوبة</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"zero\">تم طلب %1$,d أذن</item>\n        <item quantity=\"one\">تم طلب %1$,d أذن</item>\n        <item quantity=\"two\">تم طلب %1$,d أذنان</item>\n        <item quantity=\"few\">تم طلب %1$,d أذونات</item>\n        <item quantity=\"many\">تم طلب %1$,d أذن</item>\n        <item quantity=\"other\">تم طلب %1$,d أذن</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">التوقيعات</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">لاتوجد تواقيع موثقة</string>\n    <string name=\"file_properties_apk_past_signature_digests\">التوقيعات القديمة</string>\n    <string name=\"file_properties_checksum\">مجاميع الإختبارية</string>\n    <string name=\"file_properties_checksum_compare\">مقارنة</string>\n    <string name=\"file_properties_checksum_compare_placeholder\">أي مجموع الاختباري</string>\n    <string name=\"file_properties_checksum_compare_match_format\">تطابقات %1$s المجموع الاختباري</string>\n    <string name=\"file_properties_checksum_compare_prefix_match_format\">تطابقات بادئة %1$s المجموع الاختباري</string>\n    <string name=\"file_properties_checksum_compare_no_match\">لم يتم العثور على المجموع الاختباري المطابق</string>\n    <string name=\"storage_file_system_root_title\">الروت</string>\n    <string name=\"storage_list_title\">وحدة تخزين</string>\n    <string name=\"storage_list_empty\">لا توجد وحدة تخزين</string>\n    <string name=\"storage_add_storage_title\">إضافة وحدة تخزين</string>\n    <string name=\"storage_add_storage_document_tree\">وحدة التخزين الخارجية</string>\n    <string name=\"storage_add_storage_ftp_server\">خادم FTP </string>\n    <string name=\"storage_add_storage_sftp_server\">خادم SFTP </string>\n    <string name=\"storage_add_storage_smb_server\">خادم SMB</string>\n    <string name=\"storage_add_storage_webdav_server\">خادم WebDAV</string>\n    <string name=\"storage_edit_device_storage_title\">تحرير وحدة تخزين الجهاز</string>\n    <string name=\"storage_edit_device_storage_name\">الاسم</string>\n    <string name=\"storage_edit_device_storage_path\">المسار</string>\n    <string name=\"storage_add_external_storage_shortcut_title\">إضافة اختصار التخزين الخارجي</string>\n    <string name=\"storage_edit_external_storage_shortcut_title\">تعديل اختصار التخزين الخارجي</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id\">حجم التخزين</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id_error_empty\">أدخل حجم التخزين</string>\n    <string name=\"storage_edit_external_storage_shortcut_path\">المسار</string>\n    <string name=\"storage_add_document_tree_title\">أضف وحدة تخزين خارجية</string>\n    <string name=\"storage_edit_document_tree_title\">تحرير وحدة التخزين الخارجية</string>\n    <string name=\"storage_edit_document_tree_name\">الاسم</string>\n    <string name=\"storage_edit_document_tree_uri\">URI</string>\n    <string name=\"storage_edit_document_tree_path\">المسار</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">تحرير خادم FTP</string>\n    <string name=\"storage_edit_ftp_server_title_add\">إضافة خادم FTP</string>\n    <string name=\"storage_edit_ftp_server_host\">اسم المضيف</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">أدخل اسم مضيف</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">اسم مضيف غير صالح</string>\n    <string name=\"storage_edit_ftp_server_port\">المنفذ</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">منفذ غير صالح</string>\n    <string name=\"storage_edit_ftp_server_path\">المسار</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">يمكن تركه فارغًا</string>\n    <string name=\"storage_edit_ftp_server_name\">الاسم</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">استخدم اسم المضيف</string>\n    <string name=\"storage_edit_ftp_server_protocol\">بروتوكول</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">المصادقة</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>كلمة المرور</item>\n        <item>مجهول</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">اسم المستخدم</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">أدخل اسم مستخدم</string>\n    <string name=\"storage_edit_ftp_server_password\">كلمة المرور</string>\n    <string name=\"storage_edit_ftp_server_mode\">الوضع</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>نشط</item>\n        <item>غير نشط</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">ترميز</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">قم بالاتصال والإضافة</string>\n    <string name=\"storage_edit_ftp_server_add\">إضافة</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">تحرير خادم SFTP </string>\n    <string name=\"storage_edit_sftp_server_title_add\">إضافة خادم SFTP </string>\n    <string name=\"storage_edit_sftp_server_host\">اسم المضيف</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">أدخل اسم مضيف</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">اسم مضيف غير صالح</string>\n    <string name=\"storage_edit_sftp_server_port\">المنفذ</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">منفذ غير صالح</string>\n    <string name=\"storage_edit_sftp_server_path\">المسار</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">يمكن تركه فارغا</string>\n    <string name=\"storage_edit_sftp_server_name\">الاسم</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">استخدم اسم المضيف</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">المصادقة</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>كلمة المرور</item>\n        <item>مفتاح عام</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">اسم المستخدم</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">أدخل اسم مستخدم</string>\n    <string name=\"storage_edit_sftp_server_password\">كلمة المرور</string>\n    <string name=\"storage_edit_sftp_server_private_key\">مفتاح خاص</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">فتح ملف</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">ادخال مفتاح خاص</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">المفتاح الخاص غير صحيح</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">كلمة مرور المفتاح الخاص</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">يمكن تركه فارغًا</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">كلمة مرور المفتاح الخاص غير صالحة</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">قم بالاتصال والإضافة</string>\n    <string name=\"storage_edit_sftp_server_add\">إضافة</string>\n    <string name=\"storage_add_lan_smb_server_loading\">البحث عن خوادم SMB…</string>\n    <string name=\"storage_add_lan_smb_server_add\">إضافة يدويًا</string>\n    <string name=\"storage_edit_smb_server_title_edit\">تحرير خادم SMB</string>\n    <string name=\"storage_edit_smb_server_title_add\">إضافة خادم SMB</string>\n    <string name=\"storage_edit_smb_server_host\">اسم المضيف</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">أدخل اسم مضيف</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">اسم مضيف غير صالح</string>\n    <string name=\"storage_edit_smb_server_port\">منفذ</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">منفذ غير صالح</string>\n    <string name=\"storage_edit_smb_server_path\">مسار</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">يمكن تركه فارغا</string>\n    <string name=\"storage_edit_smb_server_name\">الاسم</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">استخدم اسم المضيف</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">المصادقة</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>كلمة المرور</item>\n        <item>ضيف</item>\n        <item>مجهول</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">اسم المستخدم</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">أدخل اسم مستخدم</string>\n    <string name=\"storage_edit_smb_server_password\">كلمة المرور</string>\n    <string name=\"storage_edit_smb_server_domain\">دومين</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">قم بالاتصال والإضافة</string>\n    <string name=\"storage_edit_smb_server_add\">إضافة</string>\n    <string name=\"storage_edit_webdav_server_title_edit\">تعديل خادم WebDAV</string>\n    <string name=\"storage_edit_webdav_server_title_add\">إضافة خادم WebDAV</string>\n    <string name=\"storage_edit_webdav_server_host\">اسم المضيف</string>\n    <string name=\"storage_edit_webdav_server_host_error_empty\">أدخل اسم المضيف</string>\n    <string name=\"storage_edit_webdav_server_host_error_invalid\">اسم مضيف غير صالح</string>\n    <string name=\"storage_edit_webdav_server_port\">منفذ</string>\n    <string name=\"storage_edit_webdav_server_port_error_invalid\">منفذ غير صالح</string>\n    <string name=\"storage_edit_webdav_server_path\">المسار</string>\n    <string name=\"storage_edit_webdav_server_path_placeholder\">يمكن تركه فارغاً</string>\n    <string name=\"storage_edit_webdav_server_name\">الاسم</string>\n    <string name=\"storage_edit_webdav_server_name_placeholder\">استخدم اسم المضيف</string>\n    <string name=\"storage_edit_webdav_server_protocol\">بروتوكول</string>\n    <string name=\"storage_edit_webdav_server_authentication_type\">مصداقة</string>\n    <string-array name=\"storage_edit_webdav_server_authentication_type_entries\">\n        <item>كلمة المرور</item>\n        <item>رمز الوصول</item>\n        <item>لا شيء</item>\n    </string-array>\n    <string name=\"storage_edit_webdav_server_username\">اسم المستخدم</string>\n    <string name=\"storage_edit_webdav_server_username_error_empty\">أدخل اسم المستخدم</string>\n    <string name=\"storage_edit_webdav_server_password\">كلمة المرور</string>\n    <string name=\"storage_edit_webdav_server_access_token\">رمز الوصول</string>\n    <string name=\"storage_edit_webdav_server_access_token_error_empty\">أدخل رمز الوصول</string>\n    <string name=\"storage_edit_webdav_server_connect_and_add\">ربط وإضافة</string>\n    <string name=\"storage_edit_webdav_server_add\">إضافة</string>\n\n    <string name=\"navigation_storage_subtitle_format\">%1$s خالي من %2$s</string>\n    <string name=\"navigation_add_storage\">إضافة وحدة تخزين…</string>\n    <string name=\"navigation_standard_directory_alarms\">المنبه</string>\n    <string name=\"navigation_standard_directory_dcim\">الكاميرا</string>\n    <string name=\"navigation_standard_directory_documents\">المستندات</string>\n    <string name=\"navigation_standard_directory_downloads\">التنزيلات</string>\n    <string name=\"navigation_standard_directory_movies\">الأفلام</string>\n    <string name=\"navigation_standard_directory_music\">الموسيقى</string>\n    <string name=\"navigation_standard_directory_notifications\">الإشعارات</string>\n    <string name=\"navigation_standard_directory_pictures\">الصور</string>\n    <string name=\"navigation_standard_directory_podcasts\">المدونة الصوتية /بودكاست</string>\n    <string name=\"navigation_standard_directory_ringtones\">النغمات</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">مجلد الإشارات المرجعية</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">الاسم</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">المسار</string>\n    <string name=\"archive_viewer_title\">عارض الأرشيف</string>\n    <string name=\"image_viewer_title\">عارض الصور</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"save_as_title\">حفظ كـ</string>\n    <string name=\"save_as_error\">خطأ أثناء حفظ الملف</string>\n    <string name=\"save_as_success_format\">تم حفظ ”%1$s“ إلى ”%2$s“</string>\n    <string name=\"text_editor_title\">محرر النص</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">تم الحفظ</string>\n    <string name=\"text_editor_reload_message\">هل أنت متأكد أنك تريد إعادة التحميل؟ ستفقد التغييرات غير المحفوظة على هذا المستند.</string>\n    <string name=\"text_editor_encoding\">ترميز</string>\n    <string name=\"text_editor_close_message\">هل أنت متأكد أنك تريد تجاهل التغييرات غير المحفوظة في هذا المستند؟</string>\n\n    <string name=\"ftp_server_title\">خادم FTP </string>\n    <string name=\"ftp_server_state_title\">الحالة</string>\n    <string name=\"ftp_server_state_summary_starting\">جاري البدء …</string>\n    <string name=\"ftp_server_state_summary_running\">قيد التشغيل</string>\n    <string name=\"ftp_server_state_summary_stopping\">جاري الإيقاف…</string>\n    <string name=\"ftp_server_state_summary_stopped\">لم يبدأ</string>\n    <string name=\"ftp_server_url_title\">الرابط</string>\n    <string name=\"ftp_server_url_summary_no_local_inet_address\">عنوان IP المحلي غير معروف</string>\n    <string name=\"ftp_server_url_menu_copy_url\">نسخ الرابط</string>\n    <string name=\"ftp_server_url_menu_copy_password\">نسخ كلمة المرور</string>\n    <string name=\"ftp_server_add_tile_title\">أضف إلى الإعدادات السريعة</string>\n    <string name=\"ftp_server_add_tile_result_added\">تم إضافة ”خادم FTP“ للإعدادات السريعة</string>\n    <string name=\"ftp_server_add_tile_result_already_added\">تمت إضافة \\\"خادم FTP\\\" بالفعل إلى الإعدادات السريعة</string>\n    <string name=\"ftp_server_add_tile_result_error\">حدث خطأ أثناء إضافة \\\"خادم FTP\\\" إلى الإعدادات السريعة</string>\n    <string name=\"ftp_server_configuration_title\">التهيئة</string>\n    <string name=\"ftp_server_anonymous_login_title\">تسجيل دخول مجهول</string>\n    <string name=\"ftp_server_username_title\">اسم المستخدم</string>\n    <string name=\"ftp_server_password_title\">كلمة المرور</string>\n    <string name=\"ftp_server_port_title\">منفذ</string>\n    <string name=\"ftp_server_home_directory_title\">المجلد الرئيسي</string>\n    <string name=\"ftp_server_writable_title\">اسمح بالكتابة</string>\n    <string name=\"settings_title\">الإعدادات</string>\n    <string name=\"settings_interface_title\">واجهة المستخدم</string>\n    <string name=\"settings_locale_title\">لغة</string>\n    <string name=\"settings_theme_color_title\">لون الثيم</string>\n    <string name=\"settings_theme_color_summary\">اللون الذي يظهر بشكل متكرر في التطبيق</string>\n    <string name=\"settings_material_design_3_title\">تصميم متيريال 3</string>\n    <string name=\"settings_night_mode_title\">الوضع الليلي</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>اتبع النظام</item>\n        <item>إيقاف</item>\n        <item>تشغيل</item>\n        <item>يعتمد على الوقت</item>\n        <item>يعتمد على وضع توفير الطاقة</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">الوضع الليلي الأسود</string>\n    <string name=\"settings_file_list_animation_title\">التأثيرات المتحركة لقائمة الملفات</string>\n    <string name=\"settings_file_name_ellipsize_title\">عرض اسم ملف طويل</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>قلل من البداية</item>\n        <item>قلل من المنتصف</item>\n        <item>قلل من النهاية</item>\n        <item>شاشة اسمية</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">السلوك</string>\n    <string name=\"settings_default_directory_title\">المجلد الإفتراضى</string>\n    <string name=\"settings_standard_directories_title\">المجلدات الأساسية</string>\n    <string name=\"settings_standard_directories_summary_empty\">لم يتم تمكين المجلدات الأساسية</string>\n    <string name=\"settings_bookmark_directory_screenshots\">لقطات الشاشة</string>\n    <string name=\"settings_bookmark_directories_title\">مجلدات الإشارات المرجعية</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">لا توجد مجلدات الإشارات المرجعية</string>\n    <string name=\"settings_root_strategy_title\">وضع الروت</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"zero\">لديك %1$,d عملية ملف قيد التشغيل وقد يؤدي تغيير وضع الوصول إلى الجذر الآن إلى حدوث خطأ غير متوقع. هل أنت متأكد أنك تريد تغييره الآن؟</item>\n        <item quantity=\"one\">لديك %1$,d عملية ملف قيد التشغيل وقد يؤدي تغيير وضع الوصول إلى الجذر الآن إلى حدوث خطأ غير متوقع. هل أنت متأكد أنك تريد تغييره الآن؟</item>\n        <item quantity=\"two\">لديك %1$,d عمليتان ملف قيد التشغيل وقد يؤدي تغيير وضع الوصول إلى الجذر الآن إلى حدوث خطأ غير متوقع. هل أنت متأكد أنك تريد تغييره الآن؟</item>\n        <item quantity=\"few\">لديك %1$,d عمليات ملف قيد التشغيل وقد يؤدي تغيير وضع الوصول إلى الجذر الآن إلى حدوث خطأ غير متوقع. هل أنت متأكد أنك تريد تغييره الآن؟</item>\n        <item quantity=\"many\">لديك %1$,d عملية ملف قيد التشغيل وقد يؤدي تغيير وضع الوصول إلى الجذر الآن إلى حدوث خطأ غير متوقع. هل أنت متأكد أنك تريد تغييره الآن؟</item>\n        <item quantity=\"other\">لديك %1$,d عملية ملف قيد التشغيل وقد يؤدي تغيير وضع الروت الآن إلى حدوث خطأ غير متوقع. هل أنت متأكد أنك تريد تغييره الآن؟</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>الوصول العادي فقط</item>\n        <item>تلقائي</item>\n        <item>الروت فقط</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">أرشيف اسم ملف الترميز</string>\n    <string name=\"settings_open_apk_default_action_title\">افتح حزمة Android</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>تثبيت</item>\n        <item>عرض المحتويات</item>\n        <item>اسأل ماذا تفعل</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_title\">عرض الصور المصغرة لمستندات PDF</string>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">قد يجعل التطبيق غير مستقر على الإصدارات الأقدم من Android</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">قراءة الملفات البعيدة للصور المصغرة</string>\n\n    <string name=\"about_title\">حول</string>\n    <string name=\"about_version_title\">الإصدار</string>\n    <string name=\"about_github_title\">عرض على GitHub</string>\n    <string name=\"about_licenses_title\">التراخيص</string>\n    <string name=\"about_privacy_policy_title\">سياسة الخصوصة</string>\n    <string name=\"about_author_title\">المؤلف</string>\n    <string name=\"about_author_name_title\">هاي تشانغ Hai Zhang</string>\n    <string name=\"about_author_github_title\">تابعني على GitHub</string>\n    <string name=\"about_author_twitter_title\">متابعة على Twitter</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-bg/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">Файл</string>\n    <string name=\"file_type_name_apk\">Пакет на Андроид</string>\n    <string name=\"file_type_name_archive\">%1$s архив</string>\n    <string name=\"file_type_name_audio\">%1$s аудио</string>\n    <string name=\"file_type_name_calendar\">%1$s календар</string>\n    <string name=\"file_type_name_certificate\">%1$s сертификат</string>\n    <string name=\"file_type_name_code\">%1$s документ</string>\n    <string name=\"file_type_name_contact\">Електронна визитка</string>\n    <string name=\"file_type_name_directory\">Папка</string>\n    <string name=\"file_type_name_document\">%1$s документ</string>\n    <string name=\"file_type_name_ebook\">%1$s електронна книга</string>\n    <string name=\"file_type_name_email\">%1$s електронно съобщение</string>\n    <string name=\"file_type_name_font\">%1$s шрифт</string>\n    <string name=\"file_type_name_generic\">%1$s файл</string>\n    <string name=\"file_type_name_image\">%1$s изображение</string>\n    <string name=\"file_type_name_pdf\">Документ на PDF</string>\n    <string name=\"file_type_name_presentation\">%1$s презентация</string>\n    <string name=\"file_type_name_spreadsheet\">%1$s таблица</string>\n    <string name=\"file_type_name_text\">%1$s документ</string>\n    <string name=\"file_type_name_text_plain\">Текстов документ</string>\n    <string name=\"file_type_name_video\">%1$s видео</string>\n    <string name=\"file_type_name_word\">Документ на Word</string>\n    <string name=\"file_type_name_powerpoint\">Презентация на PowerPoint</string>\n    <string name=\"file_type_name_excel\">Електронна таблица на Excel</string>\n    <string name=\"file_type_name_posix_character_device\">Символно устройство</string>\n    <string name=\"file_type_name_posix_block_device\">Блоково устройство</string>\n    <string name=\"file_type_name_posix_fifo\">Тръба (Pipe)</string>\n    <string name=\"file_type_name_posix_symbolic_link\">Препратка</string>\n    <string name=\"file_type_name_posix_socket\">Socket</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">Препратка (неработеща)</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-bg/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <string name=\"app_name\">Material Files</string>\n\n    <string name=\"close\">Затваряне</string>\n    <string name=\"copy\">Копиране</string>\n    <string name=\"cut\">Изрязване</string>\n    <string name=\"default_\">По подразбиране</string>\n    <string name=\"delete\">Изтриване</string>\n    <string name=\"discard\">Отказ</string>\n    <string name=\"edit\">Редактиране</string>\n    <string name=\"empty\">Празно</string>\n    <string name=\"empty_placeholder\">(Празно)</string>\n    <string name=\"error\">Грешка</string>\n    <string name=\"hide\">Скриване</string>\n    <string name=\"install\">Инсталиране</string>\n    <string name=\"keep_editing\">Продължаване редактирането</string>\n    <string name=\"loading\">Зареждане…</string>\n    <string name=\"maybe_later\">По-късно</string>\n    <string name=\"merge\">Обединаване</string>\n    <string name=\"none\">Няма</string>\n    <string name=\"open_navigation_drawer\">Отваря менюто</string>\n    <string name=\"paste\">Поставяне</string>\n    <string name=\"refresh\">Презареждане</string>\n    <string name=\"reload\">Презареждане</string>\n    <string name=\"remove\">Премахване</string>\n    <string name=\"rename\">Преименуване</string>\n    <string name=\"replace\">Замяна</string>\n    <string name=\"reset\">Нулиране</string>\n    <string name=\"retry\">Повтаряне</string>\n    <string name=\"save\">Запазване</string>\n    <string name=\"search\">Търсене</string>\n    <string name=\"select\">Избиране</string>\n    <string name=\"select_all\">Избиране на всички</string>\n    <string name=\"share\">Споделяне</string>\n    <string name=\"show\">Показване</string>\n    <string name=\"skip\">Пропускане</string>\n    <string name=\"stop\">Спиране</string>\n    <string name=\"system_default\">Според системата</string>\n    <string name=\"unknown\">Неизвестно</string>\n    <string name=\"view\">Изглед</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">Не е намерено приложение, което да извърши действието</string>\n    <string name=\"open_settings\">Настройки</string>\n    <string name=\"shortcut_created\">Прекият път е създаден</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"one\">%1$,d байт</item>\n        <item quantity=\"other\">%1$,d байта</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">Необходими са права за управление на всички файлове. Дайте достъп в последващите системни настройки.</string>\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">Необходими са права за достъп до файлове. Докоснете „Разрешаване“ в следващия системен прозорец</string>\n    <string name=\"storage_permission_permanently_denied_message\">Необходими са права за достъпа до файлове. Разрешете правото „ХРАНИЛИЩЕ“ в системните настройки.</string>\n    <!-- The allow button is in sentence case now. -->\n    <string name=\"notification_permission_rationale_message\">Необходими са права за изпращане на известия относно действия с файлове. Докоснете „Разрешаване“ в следващия системен прозорец</string>\n    <string name=\"notification_permission_permanently_denied_message\">Необходими са права за изпращане на известия относно действия с файлове. Дайте правото „Известия“ в следващия системен прозорец</string>\n\n    <string name=\"notification_channel_background_activity_start_name\">Действия на заден план</string>\n    <string name=\"notification_channel_background_activity_start_description\">Изпълняване на действия докато приложението е на заден план</string>\n    <string name=\"notification_channel_file_job_name\">Действия с файлове</string>\n    <string name=\"notification_channel_file_job_description\">Показване и управление на действия с файлове</string>\n    <string name=\"notification_channel_ftp_server_name\">Сървър на FTP</string>\n    <string name=\"notification_channel_ftp_server_description\">Показване и управление на сървър на FTP</string>\n\n    <string name=\"file_action_archive_password_title\">Необходима е парола</string>\n    <string name=\"file_action_archive_password_message_format\">Файлът „%1$s“ е защитен с парола</string>\n    <string name=\"file_action_archive_password_error_empty\">Паролата не може да е празна</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"one\">Подготвя се за компресиране %1$,d файл (%2$s)</item>\n        <item quantity=\"other\">Подготвят се за компресиране %1$,d файла (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">Компресиране на „%1$s“ в „%2$s“</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"one\">Компресиране на %1$,d файл в „%2$s“</item>\n        <item quantity=\"other\">Компресиране на %1$,d файла в „%2$s“</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"one\">Подготвя се за копиране %1$,d файл (%2$s)</item>\n        <item quantity=\"other\">Подготвят се за копиране %1$,d файла (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">Копиране на %1$s в %2$s</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"one\">Копиране на %1$,d файл в „%2$s“</item>\n        <item quantity=\"other\">Копиране на %1$,d файла в „%2$s“</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"one\">Приготвя се за извличане %1$,d файл (%2$s)</item>\n        <item quantity=\"other\">Подготвят се за извличане %1$,d файла (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">Извличане на „%1$s“ в „%2$s“</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"one\">Извличане на %1$,d файл в “%2$s”</item>\n        <item quantity=\"other\">Извличане на %1$,d файла в „%2$s“</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"one\">Подготвя се за преместване %1$,d файл (%2$s)</item>\n        <item quantity=\"other\">Подготвят се за преместване %1$,d файла (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">Преместване на „%1$s“ в „%2$s“</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"one\">Преместване на %1$,d файл към „%2$s“</item>\n        <item quantity=\"other\">Преместване на %1$,d файла към „%2$s“</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"one\">Подготвя се за изтриване %1$,d файл</item>\n        <item quantity=\"other\">Подготвят се за изтриване %1$,d файла</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">Изтриване на „%1$s“</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"one\">Изтриване на %1$,d файл</item>\n        <item quantity=\"other\">Изтриване на %1$,d файла</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"one\">Подготвя се за смяна на притежателя %1$,d файл</item>\n        <item quantity=\"other\">Подготвят се за смяна на притежателя %1$,d файла</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">Промяна на притежателя на „%1$s“</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"one\">Промяна на притежателя на %1$,d файл</item>\n        <item quantity=\"other\">Промяна на притежателя на %1$,d файла</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"one\">Подготвя се за промяна на групата %1$,d файл</item>\n        <item quantity=\"other\">Подготвят се за промяна на групата %1$,d файла</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">Промяна на групата на „%1$s“</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"one\">Промяна на групата на %1$,d файл</item>\n        <item quantity=\"other\">Промяна на групата на %1$,d файла</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"one\">Подготвят се за промяна на правата %1$,d файл</item>\n        <item quantity=\"other\">Подготвят се за промяна на правата %1$,d файла</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">Промяна на правата на „%1$s“</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"one\">Промяна на правата на %1$,d файл</item>\n        <item quantity=\"other\">Промяна на правата на %1$,d файла</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Подготвя се за смяна на контекста на SELinux %1$,d файл</item>\n        <item quantity=\"other\">Подготвят се за смяна на контекста на SELinux %1$,d файла</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">Промяна на контекста на SELinux на „%1$s“</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Промяна на SELinux контекста на %1$,d файл</item>\n        <item quantity=\"other\">Промяна на контекста на SELinux на %1$,d файла</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Подготвя се за възстановяване на контекста на SELinux %1$,d файл</item>\n        <item quantity=\"other\">Подготвят се за възстановяване на контекста на SELinux %1$,d файла</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">Възстановяване на контекста на SELinux „%1$s“</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Възстановяване на SELinux контекста за %1$,d файл</item>\n        <item quantity=\"other\">Възстановяване на контекста на SELinux %1$,d файла</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">Записване на „%1$s“</string>\n    <string name=\"file_job_create_error_title\">Грешка при създаване</string>\n    <string name=\"file_job_create_error_message_format\">Грешка при създаване на „%1$s“.\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">Папката не може да бъде копирана в себе си</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">Папката не може да бъде извлечена в себе си</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">Папката не може да бъде преместена в себе си</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">Целевата папка е в папката източник</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">Файлът не може да бъде копиран върху себе си</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">Файлът не може да бъде извлечен върху себе си</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">Файлът не може да бъде преместен върху себе си</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">Изходният файл би бил презаписан от целевия.</string>\n    <string name=\"file_job_replace_title_format\">Заменяне на файла „%1$s“?</string>\n    <string name=\"file_job_replace_message_format\">Друг файл със същото име вече съществува в “%1$s”.\\nПри презаписване ще бъде заменено и съдържанието му.</string>\n    <string name=\"file_job_replace_target_name\">Оригинален файл</string>\n    <string name=\"file_job_replace_source_name\">Замяна с</string>\n    <string name=\"file_job_merge_title_format\">Обединяване на папката “%1$s”?</string>\n    <string name=\"file_job_merge_copy_message_format\">Обединяването ще поиска потвърждение, преди да замени всички файлове в папката, които са в конфликт с копираните файлове.</string>\n    <string name=\"file_job_merge_extract_message_format\">Обединяването ще поиска потвърждение, преди да замени всички файлове в папката, които са в конфликт с извличаните файлове.</string>\n    <string name=\"file_job_merge_move_message_format\">Обединяването ще поиска потвърждение, преди да замени всички файлове в папката, които са в конфликт с преместваните файлове.</string>\n    <string name=\"file_job_merge_target_name\">Оригинална папка</string>\n    <string name=\"file_job_merge_source_name\">Обединяване с</string>\n    <string name=\"file_job_conflict_show_name\">Избиране на ново име</string>\n    <string name=\"file_job_conflict_name_hint\">Ново име</string>\n    <string name=\"file_job_archive_error_title_format\">Грешка при компресирането на “%1$s”</string>\n    <string name=\"file_job_archive_error_message_format\">Възникна грешка докато файлът се компресираше в “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">Грешка при копирането на “%1$s”</string>\n    <string name=\"file_job_copy_error_message_format\">Възникна грешка докато файлът се копираше в “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">Грешка при извличането на “%1$s”</string>\n    <string name=\"file_job_extract_error_message_format\">Възникна грешка при извличането на файлът в “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">Грешка при преместването на “%1$s”</string>\n    <string name=\"file_job_move_error_message_format\">Възникна грешка при преместването на файла в “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">Грешка при изтриване</string>\n    <string name=\"file_job_delete_error_message_format\">Възникна грешка при изтриването на “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">Възникна грешка при преименуването на “%1$s”</string>\n    <string name=\"file_job_rename_error_message_format\">Възникна грешка при преименуването на файла на “%1$s”\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">Грешка при смяната на притежателя на “%1$s”</string>\n    <string name=\"file_job_set_owner_error_message_format\">Възникна грешка при смяната на притежателя на “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">Грешка при промяна на групата на “%1$s”</string>\n    <string name=\"file_job_set_group_error_message_format\">Възникна грешка при промяната на групата на “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">Грешка при промяната на правата на “%1$s”</string>\n    <string name=\"file_job_set_mode_error_message_format\">Възникна грешка при промяна на правата на “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">Грешка при промяна на SELinux контекста на “%1$s”</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">Възникна грешка при промяната на SELinux контекста на “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">Грешка при възстановяване на SELinux контекста</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">Възникна грешка при възстановяване на SELinux контекста на “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">Грешка при писане</string>\n    <string name=\"file_job_write_error_message_format\">Възникна грешка при писането на “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">Възникна грешка при получаването на информация за “%1$s”.</string>\n    <string name=\"file_job_remount_format\">Закачане “%1$s” за писане и четене</string>\n    <string name=\"file_job_remount_loading_format\">Закачане “%1$s” за писане и четене…</string>\n    <string name=\"file_job_remount_success_format\">“%1$s” беше закачено за писане и четене</string>\n    <string name=\"file_job_action_all\">Прилагане за всички файлове</string>\n\n    <string name=\"file_open_apk_message\">Този файл е пакет на Андроид. Искате да го инсталирате или да видите съдържанието му?</string>\n    <string name=\"file_open_as_title\">Отваряне като…</string>\n    <string name=\"file_open_as_title_format\">Отваряне на “%1$s” като</string>\n    <string name=\"file_open_as_type_text\">Текст</string>\n    <string name=\"file_open_as_type_image\">Изображение</string>\n    <string name=\"file_open_as_type_audio\">Аудио</string>\n    <string name=\"file_open_as_type_video\">Видео</string>\n    <string name=\"file_open_as_type_directory\">Папка</string>\n    <string name=\"file_open_as_type_any\">Друго</string>\n    <string name=\"file_install_apk_from_background_title_format\">Приложението „%1$s” е готово да бъде инсталирано</string>\n    <string name=\"file_install_apk_from_background_text\">Натиснете за инсталиране</string>\n    <string name=\"file_open_from_background_title_format\">Файлът „%1$s“ е готов да бъде отворен</string>\n    <string name=\"file_open_from_background_text\">Натиснете за отворяне</string>\n    <string name=\"file_name_error_empty\">Файлът не може да е празен</string>\n    <string name=\"file_name_error_invalid\">Невалидно име на файл</string>\n    <string name=\"file_name_error_already_exists\">Файл с това име вече съществува</string>\n    <string name=\"file_replace_message_format\">Заменяне на „%1$s“?</string>\n    <string name=\"file_delete_message_file_format\">Изтриване на “%1$s”?</string>\n    <string name=\"file_delete_message_directory_format\">Изтриване на папката „%1$s“ и нейното съдържание?</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"one\">Премахване на %1$,d файл?</item>\n        <item quantity=\"other\">Премахване на %1$,d файла?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"one\">Премахване на %1$,d папка и съдържанието ѝ?</item>\n        <item quantity=\"other\">Премахване на %1$,d папки и съдържанието им?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"one\">Премахване на %1$,d елемент?</item>\n        <item quantity=\"other\">Премахване на %1$,d елемента?</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">Създаване на архив</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_create_archive_password\">Парола (по желание)</string>\n    <string name=\"file_add_bookmark_success\">Добавена е отметка</string>\n    <string name=\"file_create_file_title\">Нов файл</string>\n    <string name=\"file_create_directory_title\">Нова папка</string>\n\n    <string name=\"file_list_title\">Файлове</string>\n    <plurals name=\"file_list_title_open_file\">\n        <item quantity=\"one\">Избиране на файл</item>\n        <item quantity=\"other\">Избиране на файлове</item>\n    </plurals>\n    <string name=\"file_list_title_create_file\">Запазване на файл</string>\n    <plurals name=\"file_list_title_open_directory\">\n        <item quantity=\"one\">Избиране на папка</item>\n        <item quantity=\"other\">Избиране на папки</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"one\">%1$,d папка</item>\n        <item quantity=\"other\">%1$,d папки</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"one\">%1$,d файл</item>\n        <item quantity=\"other\">%1$,d файлове</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">,\\u0020</string>\n    <string name=\"file_list_path_error_empty\">Пътят не може да е празен</string>\n    <string name=\"file_list_path_error_invalid\">Неприемлив път</string>\n    <string name=\"file_list_action_view_sort\">Преглед и сортиране</string>\n    <string name=\"file_list_action_view_list\">Списък</string>\n    <string name=\"file_list_action_view_grid\">Решетка</string>\n    <string name=\"file_list_action_sort_by_name\">Име</string>\n    <string name=\"file_list_action_sort_by_type\">Вид</string>\n    <string name=\"file_list_action_sort_by_size\">Размер</string>\n    <string name=\"file_list_action_sort_by_last_modified\">Последна промяна</string>\n    <string name=\"file_list_action_sort_order_ascending\">Възходящ</string>\n    <string name=\"file_list_action_sort_directories_first\">Първо папките</string>\n    <string name=\"file_list_action_view_sort_path_specific\">Само за тази папка</string>\n    <string name=\"file_list_action_new_task\">Нов прозорец</string>\n    <string name=\"file_list_action_navigate_up\">Нагоре</string>\n    <string name=\"file_list_action_navigate_to\">Отваряне на</string>\n    <string name=\"file_list_action_show_hidden_files\">Показване на скрити файлове</string>\n    <string name=\"file_list_action_copy_path\">Копиране на път</string>\n    <string name=\"file_list_action_open_in_terminal\">Отваряне в терминал</string>\n    <string name=\"file_list_action_add_bookmark\">Добавяне на отметка</string>\n    <string name=\"file_list_action_create_shortcut\">Създаване на пряк път</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">Отваряне в нов прозорез</string>\n    <string name=\"file_list_select_title_format\">%1$,d</string>\n    <string name=\"file_list_create_file_name_hint\">Име на файл</string>\n    <string name=\"file_list_open_current_directory_format\">Избиране на „%1$s”</string>\n    <string name=\"file_list_paste_move_title_format\">Преместване на %1$,d</string>\n    <string name=\"file_list_paste_copy_title_format\">Копиране на %1$,d</string>\n    <string name=\"file_list_paste_extract_title_format\">Извличане на %1$,d</string>\n    <string name=\"file_list_paste_action_extract_here\">Извличане тук</string>\n    <string name=\"file_list_empty\">Няма файлове</string>\n    <string name=\"file_list_action_create_file\">Файл</string>\n    <string name=\"file_list_action_create_directory\">Папка</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">Отваряне с</string>\n    <string name=\"file_item_action_extract\">Извличане</string>\n    <string name=\"file_item_action_archive\">Компресиране</string>\n    <string name=\"file_item_action_properties\">Свойства</string>\n\n    <string name=\"file_properties_title_format\">Свойства на “%1$s”</string>\n    <string name=\"file_properties_basic\">Основни</string>\n    <string name=\"file_properties_basic_name\">Име</string>\n    <string name=\"file_properties_basic_type\">Вид</string>\n    <string name=\"file_properties_basic_type_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">Препратка към %1$s(%2$s)</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">Цел на препратката</string>\n    <string name=\"file_properties_basic_size\">Размер</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_contents\">Съдържание</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"one\">%1$,d елемент, общо %2$s</item>\n        <item quantity=\"other\">%1$,d елемента, общо %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">Последна промяна</string>\n    <string name=\"file_properties_basic_parent_directory\">Предишна папка</string>\n    <string name=\"file_properties_basic_archive_file\">Архивен файл</string>\n    <string name=\"file_properties_basic_archive_entry\">Запис в архива</string>\n    <string name=\"file_properties_basic_free_space\">Свободно пространство</string>\n    <string name=\"file_properties_permission\">Права</string>\n    <string name=\"file_properties_permission_owner\">Собственик</string>\n    <string name=\"file_properties_permission_group\">Група</string>\n    <string name=\"file_properties_permission_principal_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_permission_set_owner_title\">Промяна на собственик</string>\n    <string name=\"file_properties_permission_set_group_title\">Промяна на група</string>\n    <string name=\"file_properties_permission_set_principal_filter_hint\">Въведете име или ID</string>\n    <string name=\"file_properties_permission_set_principal_system\">Система</string>\n    <string name=\"file_properties_permission_mode\">Права</string>\n    <string name=\"file_properties_permission_mode_format\">%1$s (%2$04o)</string>\n    <string name=\"file_properties_permission_set_mode_title\">Промяна на права</string>\n    <string name=\"file_properties_permission_set_mode_others\">Други</string>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_file\">\n        <item>Четене</item>\n        <item>Писане</item>\n        <item>Изпълняване</item>\n    </string-array>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_directory\">\n        <item>Четене</item>\n        <item>Писане</item>\n        <item>Търсене</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_special\">Специални</string>\n    <string-array name=\"file_properties_permission_set_mode_special_mode_bits\">\n        <item>Задаване на UID</item>\n        <item>Задаване на GID</item>\n        <item>Лепкав бит</item>\n    </string-array>\n    <string name=\"file_properties_permission_selinux_context\">Контекст на SELinux</string>\n    <string name=\"file_properties_permission_set_selinux_context_title\">Смяна на контекста на SELinux</string>\n    <string name=\"file_properties_permission_set_selinux_context_restore\">Възстановяване</string>\n    <string name=\"file_properties_permission_recursive\">Рекурсивно прилагане</string>\n    <string name=\"file_properties_image\">Изображение</string>\n    <string name=\"file_properties_media_dimensions\">Размери</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">Направена на</string>\n    <string name=\"file_properties_media_coordinates\">Координати</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f, %2$.3f</string>\n    <string name=\"file_properties_media_address\">Местоположене</string>\n    <string name=\"file_properties_image_gps_altitude\">Височина</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f м</string>\n    <string name=\"file_properties_image_equipment\">Камера</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">Бленда</string>\n    <string name=\"file_properties_image_shutter_speed\">Скорост на затвора</string>\n    <string name=\"file_properties_image_focal_length\">Фокусно разстояние</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f мм</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">Еквивалент по ISO</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">Софтуер</string>\n    <string name=\"file_properties_image_description\">Описание</string>\n    <string name=\"file_properties_image_artist\">Създател</string>\n    <string name=\"file_properties_image_copyright\">Авторски права</string>\n    <string name=\"file_properties_audio\">Аудио</string>\n    <string name=\"file_properties_media_title\">Име</string>\n    <string name=\"file_properties_audio_artist\">Артист</string>\n    <string name=\"file_properties_audio_album\">Албум</string>\n    <string name=\"file_properties_audio_album_artist\">Артист на албума</string>\n    <string name=\"file_properties_audio_composer\">Композитор</string>\n    <string name=\"file_properties_audio_disc_number\">Диск</string>\n    <string name=\"file_properties_audio_track_number\">Песен</string>\n    <string name=\"file_properties_audio_year\">Година</string>\n    <string name=\"file_properties_audio_genre\">Жанр</string>\n    <string name=\"file_properties_media_duration\">Времетраене</string>\n    <string name=\"file_properties_media_bit_rate\">Скорост на данните</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d Кб/с</string>\n    <string name=\"file_properties_audio_sample_rate\">Честота на дискретизация</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Хц</string>\n    <string name=\"file_properties_video\">Видео</string>\n    <string name=\"file_properties_apk\">APK</string>\n    <string name=\"file_properties_apk_label\">Име</string>\n    <string name=\"file_properties_apk_package_name\">Име на пакета</string>\n    <string name=\"file_properties_apk_version\">Издание</string>\n    <string name=\"file_properties_apk_version_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_apk_min_sdk_version\">Минимална версия на системата</string>\n    <string name=\"file_properties_apk_target_sdk_version\">Целева версия на системата</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s (%2$s, %3$d)</string>\n    <string name=\"file_properties_apk_requested_permissions\">Права</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">Няма заявени права</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"one\">Заявен %1$,d право</item>\n        <item quantity=\"other\">Поискани са %1$,d права</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">Подписи</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">Няма валидни подписи</string>\n    <string name=\"file_properties_apk_past_signature_digests\">Стари подписи</string>\n    <string name=\"storage_file_system_root_title\">Коренова папка</string>\n    <string name=\"storage_list_title\">Хранилище</string>\n    <string name=\"storage_list_empty\">Няма хранилища</string>\n    <string name=\"storage_add_storage_title\">Добавяне на хранилище</string>\n    <string name=\"storage_add_storage_document_tree\">Външно хранилище</string>\n    <string name=\"storage_add_storage_ftp_server\">Сървър за FTP</string>\n    <string name=\"storage_add_storage_sftp_server\">Сървър за SFTP</string>\n    <string name=\"storage_add_storage_smb_server\">Сървър за SMB</string>\n    <string name=\"storage_add_storage_webdav_server\">Сървър за WebDAV</string>\n    <string name=\"storage_edit_device_storage_title\">Редактиране на хранилище</string>\n    <string name=\"storage_edit_device_storage_name\">Име</string>\n    <string name=\"storage_edit_device_storage_path\">Път</string>\n    <string name=\"storage_add_document_tree_title\">Добавяне на външно хранилище</string>\n    <string name=\"storage_edit_document_tree_title\">Редакция на външно хранилище</string>\n    <string name=\"storage_edit_document_tree_name\">Име</string>\n    <string name=\"storage_edit_document_tree_uri\">URI</string>\n    <string name=\"storage_edit_document_tree_path\">Път</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">Променяне на сървър за FTP</string>\n    <string name=\"storage_edit_ftp_server_title_add\">Нов сървър за FTP</string>\n    <string name=\"storage_edit_ftp_server_host\">Име на машина</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">Въведете име на машина</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">Недействително име на машина</string>\n    <string name=\"storage_edit_ftp_server_port\">Порт</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">Неприемлив порт</string>\n    <string name=\"storage_edit_ftp_server_path\">Път</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">Може да бъде празно</string>\n    <string name=\"storage_edit_ftp_server_name\">Наименование</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">Използване на името на машина</string>\n    <string name=\"storage_edit_ftp_server_protocol\">Протокол</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">Удостоверяване</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>Парола</item>\n        <item>Анонимно</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">Потребител</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">Въведете потребителско име</string>\n    <string name=\"storage_edit_ftp_server_password\">Парола</string>\n    <string name=\"storage_edit_ftp_server_mode\">Режим</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>Активен</item>\n        <item>Пасивен</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">Кодиране на знаците</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">Свързване и добавяне</string>\n    <string name=\"storage_edit_ftp_server_add\">Добавяне</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">Променяне на сървър за SFTP</string>\n    <string name=\"storage_edit_sftp_server_title_add\">Нов сървър за SFTP</string>\n    <string name=\"storage_edit_sftp_server_host\">Име на машина</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">Въведете име на машина</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">Недействително име на машина</string>\n    <string name=\"storage_edit_sftp_server_port\">Порт</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">Неприемлив порт</string>\n    <string name=\"storage_edit_sftp_server_path\">Път</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">Може да бъде празно</string>\n    <string name=\"storage_edit_sftp_server_name\">Име</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">Използване на името на машина</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">Удостоверяване</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>Парола</item>\n        <item>Явен ключ</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">Потребител</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">Въведете потребителско име</string>\n    <string name=\"storage_edit_sftp_server_password\">Парола</string>\n    <string name=\"storage_edit_sftp_server_private_key\">Скрит ключ</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">Отваряне на файл</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">Въведете скрит ключ</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">Неприемлив скрит ключ</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">Парола на скрития ключ</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">Може да бъде празно</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">Неприемлива парола за скрит ключ</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">Свързване и добавяне</string>\n    <string name=\"storage_edit_sftp_server_add\">Добавяне</string>\n    <string name=\"storage_add_lan_smb_server_loading\">Търсене на SMB сървъри…</string>\n    <string name=\"storage_add_lan_smb_server_add\">Ръчно добавяне</string>\n    <string name=\"storage_edit_smb_server_title_edit\">Редакция на SMB сървър</string>\n    <string name=\"storage_edit_smb_server_title_add\">Добавяне на SMB сървър</string>\n    <string name=\"storage_edit_smb_server_host\">Име на машина</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">Въведете име на машина</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">Недействително име на машина</string>\n    <string name=\"storage_edit_smb_server_port\">Порт</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">Неприемлив порт</string>\n    <string name=\"storage_edit_smb_server_path\">Път</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">Може да бъде празно</string>\n    <string name=\"storage_edit_smb_server_name\">Име</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">Използване на името на машина</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">Вид удостоверяване</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>Парола</item>\n        <item>Гост</item>\n        <item>Анонимен</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">Потребител</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">Въведете потребителско име</string>\n    <string name=\"storage_edit_smb_server_password\">Парола</string>\n    <string name=\"storage_edit_smb_server_domain\">Домейн</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">Свързване и добавяне</string>\n    <string name=\"storage_edit_smb_server_add\">Добавяне</string>\n    <string name=\"storage_edit_webdav_server_title_edit\">Променяне на сървър за WebDAV</string>\n    <string name=\"storage_edit_webdav_server_title_add\">Нов сървър за WebDAV</string>\n    <string name=\"storage_edit_webdav_server_host\">Име на машина</string>\n    <string name=\"storage_edit_webdav_server_host_error_empty\">Въведете име на машина</string>\n    <string name=\"storage_edit_webdav_server_host_error_invalid\">Недействително име на машина</string>\n    <string name=\"storage_edit_webdav_server_port\">Порт</string>\n    <string name=\"storage_edit_webdav_server_port_error_invalid\">Неприемлив порт</string>\n    <string name=\"storage_edit_webdav_server_path\">Път</string>\n    <string name=\"storage_edit_webdav_server_path_placeholder\">Може да бъде празно</string>\n    <string name=\"storage_edit_webdav_server_name\">Име</string>\n    <string name=\"storage_edit_webdav_server_name_placeholder\">Използване на името на машина</string>\n    <string name=\"storage_edit_webdav_server_protocol\">Протокол</string>\n    <string name=\"storage_edit_webdav_server_authentication_type\">Вид удостоверяване</string>\n    <string-array name=\"storage_edit_webdav_server_authentication_type_entries\">\n        <item>Парола</item>\n        <item>Код за достъп</item>\n        <item>Няма</item>\n    </string-array>\n    <string name=\"storage_edit_webdav_server_username\">Потребител</string>\n    <string name=\"storage_edit_webdav_server_username_error_empty\">Въведете потребителско име</string>\n    <string name=\"storage_edit_webdav_server_password\">Парола</string>\n    <string name=\"storage_edit_webdav_server_access_token\">Код за достъп</string>\n    <string name=\"storage_edit_webdav_server_access_token_error_empty\">Въведете код за достъп</string>\n    <string name=\"storage_edit_webdav_server_connect_and_add\">Свързване и добавяне</string>\n    <string name=\"storage_edit_webdav_server_add\">Добавяне</string>\n\n    <string name=\"navigation_storage_subtitle_format\">Свободни са %1$s от %2$s</string>\n    <string name=\"navigation_add_storage\">Добавяне на хранилище…</string>\n    <string name=\"navigation_standard_directory_alarms\">Будилници</string>\n    <string name=\"navigation_standard_directory_dcim\">DCIM</string>\n    <string name=\"navigation_standard_directory_documents\">Документи</string>\n    <string name=\"navigation_standard_directory_downloads\">Изтегляния</string>\n    <string name=\"navigation_standard_directory_movies\">Филми</string>\n    <string name=\"navigation_standard_directory_music\">Музика</string>\n    <string name=\"navigation_standard_directory_notifications\">Известия</string>\n    <string name=\"navigation_standard_directory_pictures\">Картини</string>\n    <string name=\"navigation_standard_directory_podcasts\">Podcasts</string>\n    <string name=\"navigation_standard_directory_ringtones\">Тонове за звънене</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">Отмятане на папка</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">Име</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">Път</string>\n    <string name=\"archive_viewer_title\">Преглед на архиви</string>\n    <string name=\"image_viewer_title\">Преглед на изображение</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"text_editor_title\">Текстов редактор</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">Запазен</string>\n    <string name=\"text_editor_reload_message\">Сигурни ли сте, че желаете да презаредите? Незапазените промени в документа ще бъдат загубени.</string>\n    <string name=\"text_editor_encoding\">Кодиране на знаците</string>\n    <string name=\"text_editor_close_message\">Сигурни ли сте, че желаете да отхвърлите незапазените промени в документа?</string>\n\n    <string name=\"ftp_server_title\">Сървър за FTP</string>\n    <string name=\"ftp_server_state_title\">Състояние</string>\n    <string name=\"ftp_server_state_summary_starting\">Стартиране…</string>\n    <string name=\"ftp_server_state_summary_running\">Работещ</string>\n    <string name=\"ftp_server_state_summary_stopping\">Спиране…</string>\n    <string name=\"ftp_server_state_summary_stopped\">Не е стартиран</string>\n    <string name=\"ftp_server_url_title\">URL</string>\n    <string name=\"ftp_server_url_summary_no_local_inet_address\">Местният адрес по IP е неизвестен</string>\n    <string name=\"ftp_server_url_menu_copy_url\">Копиране на URL</string>\n    <string name=\"ftp_server_url_menu_copy_password\">Копиране на парола</string>\n    <string name=\"ftp_server_add_tile_title\">Добавяне към бързите настройки</string>\n    <string name=\"ftp_server_add_tile_result_added\">Добавено „Сървър за FTP“ в бързите настройки</string>\n    <string name=\"ftp_server_add_tile_result_already_added\">Вече има „Сървър за FTP“ в бързите настройки</string>\n    <string name=\"ftp_server_add_tile_result_error\">Грешка при добавяне на „Сървър за FTP“ в бързите настройки</string>\n    <string name=\"ftp_server_configuration_title\">Настройки</string>\n    <string name=\"ftp_server_anonymous_login_title\">Анонимно вписване</string>\n    <string name=\"ftp_server_username_title\">Потребител</string>\n    <string name=\"ftp_server_password_title\">Парола</string>\n    <string name=\"ftp_server_port_title\">Порт</string>\n    <string name=\"ftp_server_home_directory_title\">Коренова папка</string>\n    <string name=\"ftp_server_writable_title\">Разрешаване на писане</string>\n    <string name=\"settings_title\">Настройки</string>\n    <string name=\"settings_interface_title\">Интерфейс</string>\n    <string name=\"settings_locale_title\">Език</string>\n    <string name=\"settings_theme_color_title\">Цвят на темата</string>\n    <string name=\"settings_theme_color_summary\">Цвят, който се ползва най-често в приложението</string>\n    <string name=\"settings_material_design_3_title\">Материален дизайн 3</string>\n    <string name=\"settings_night_mode_title\">Нощен режим</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>Спрямо системата</item>\n        <item>Изк.</item>\n        <item>Вкл.</item>\n        <item>Според времето</item>\n        <item>Според пестенето на батерия</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">Тъмен нощен режим</string>\n    <string name=\"settings_file_list_animation_title\">Анимиране на списъка на файлове</string>\n    <string name=\"settings_file_name_ellipsize_title\">Показване на дълги имена на файлове</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>Многоточие в началото</item>\n        <item>Многоточие в средата</item>\n        <item>Многоточие в края</item>\n        <item>Плъзгане</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">Поведение</string>\n    <string name=\"settings_default_directory_title\">Папка по подразбиране</string>\n    <string name=\"settings_standard_directories_title\">Стандартни папки</string>\n    <string name=\"settings_standard_directories_summary_empty\">Няма стандартни папки</string>\n    <string name=\"settings_bookmark_directory_screenshots\">Екранни снимки</string>\n    <string name=\"settings_bookmark_directories_title\">Отметнати папки</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">Няма отметнати папки</string>\n    <string name=\"settings_root_strategy_title\">Достъп от суперпотребителя</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"one\">В момента се извършва действие с %1$,d файл и промяната на режима за достъп до правата на суперпотребителя може да доведе до неочаквана грешка. Сигурни ли сте, че желаете да промените режима сега?</item>\n        <item quantity=\"other\">В момента се извършват действия с %1$,d файла и промяната на режима за достъп до правата на суперпотребителя може да доведе до неочаквани грешки. Сигурни ли сте, че желаете да промените режима сега?</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>Само нормален достъп</item>\n        <item>Автоматичен</item>\n        <item>Само достъп от суперпотребител</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">Кодиране на имената на архиви</string>\n    <string name=\"settings_open_apk_default_action_title\">Отваряне на пакет на Андроид</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>Инсталиране</item>\n        <item>Преглед на съдържанието</item>\n        <item>Питане за действие</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">Има опасност да направи приложението нестабилно под по-ранни издания на Андроид</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">Четене на отдалечени файлове за миниатюри</string>\n\n    <string name=\"about_title\">Относно</string>\n    <string name=\"about_version_title\">Версия</string>\n    <string name=\"about_github_title\">Преглед в GitHub</string>\n    <string name=\"about_licenses_title\">Лицензи</string>\n    <string name=\"about_privacy_policy_title\">Политика за поверителност</string>\n    <string name=\"about_author_title\">Автор</string>\n    <string name=\"about_author_name_title\">Hai Zhang</string>\n    <string name=\"about_author_github_title\">Следване в GitHub</string>\n    <string name=\"about_author_twitter_title\">Следване в Twitter</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ca/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">Fitxer</string>\n    <string name=\"file_type_name_apk\">Paquet Android</string>\n    <string name=\"file_type_name_archive\">Arxiu %1$s</string>\n    <string name=\"file_type_name_audio\">Àudio %1$s </string>\n    <string name=\"file_type_name_calendar\">Calendari %1$s</string>\n    <string name=\"file_type_name_certificate\">Certificat %1$s</string>\n    <string name=\"file_type_name_code\">Document %1$s</string>\n    <string name=\"file_type_name_contact\">Targeta de visita electrònica</string>\n    <string name=\"file_type_name_directory\">Carpeta</string>\n    <string name=\"file_type_name_document\">Document %1$s</string>\n    <string name=\"file_type_name_ebook\">Llibre electrònic %1$s </string>\n    <string name=\"file_type_name_email\">Missatge de correu electrònic %1$s</string>\n    <string name=\"file_type_name_font\">Font %1$s</string>\n    <string name=\"file_type_name_generic\">Fitxer %1$s</string>\n    <string name=\"file_type_name_image\">Imatge %1$s</string>\n    <string name=\"file_type_name_pdf\">Document PDF</string>\n    <string name=\"file_type_name_presentation\">Presentació %1$s </string>\n    <string name=\"file_type_name_spreadsheet\">Full de càlcul %1$s </string>\n    <string name=\"file_type_name_text\">Document %1$s </string>\n    <string name=\"file_type_name_text_plain\">Document de text pla</string>\n    <string name=\"file_type_name_video\">Vídeo %1$s </string>\n    <string name=\"file_type_name_word\">Document Word</string>\n    <string name=\"file_type_name_powerpoint\">Presentació PowerPoint</string>\n    <string name=\"file_type_name_excel\">Full de càlcul Excel</string>\n    <string name=\"file_type_name_posix_character_device\">Dispositiu de caràcters</string>\n    <string name=\"file_type_name_posix_block_device\">Bloca el dispositiu</string>\n    <string name=\"file_type_name_posix_fifo\">Canonada</string>\n    <string name=\"file_type_name_posix_symbolic_link\">Enllaç</string>\n    <string name=\"file_type_name_posix_socket\">Sòcol</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">Enllaç (trencat)</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ca/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <string name=\"app_name\">Material Files</string>\n\n    <string name=\"close\">Tanca</string>\n    <string name=\"copy\">Copia</string>\n    <string name=\"cut\">Retalla</string>\n    <string name=\"default_\">Per defecte</string>\n    <string name=\"delete\">Suprimeix</string>\n    <string name=\"discard\">Descarta</string>\n    <string name=\"edit\">Edita</string>\n    <string name=\"empty\">Buit</string>\n    <string name=\"empty_placeholder\">(Buit)</string>\n    <string name=\"error\">Error</string>\n    <string name=\"hide\">Amaga</string>\n    <string name=\"install\">Instal·la</string>\n    <string name=\"keep_editing\">Continua editant</string>\n    <string name=\"loading\">S\\'està carregant...</string>\n    <string name=\"maybe_later\">Potser més tard</string>\n    <string name=\"merge\">Combina</string>\n    <string name=\"none\">Cap</string>\n    <string name=\"open_navigation_drawer\">Obre el calaix de navegació</string>\n    <string name=\"paste\">Enganxa</string>\n    <string name=\"refresh\">Refresca</string>\n    <string name=\"reload\">Torna a carregar</string>\n    <string name=\"remove\">Elimina</string>\n    <string name=\"rename\">Canvia el nom</string>\n    <string name=\"replace\">Substitueix</string>\n    <string name=\"reset\">Reinicia</string>\n    <string name=\"retry\">Torna a provar</string>\n    <string name=\"save\">Desa</string>\n    <string name=\"search\">Cerca</string>\n    <string name=\"select\">Selecciona</string>\n    <string name=\"select_all\">Selecciona-ho tot</string>\n    <string name=\"share\">Comparteix</string>\n    <string name=\"show\">Mostra</string>\n    <string name=\"skip\">Omet</string>\n    <string name=\"stop\">Atura</string>\n    <string name=\"system_default\">Predeterminat del sistema</string>\n    <string name=\"unknown\">Desconegut</string>\n    <string name=\"view\">Visualitza</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">No s\\'ha trobat cap aplicació per gestionar aquesta acció</string>\n    <string name=\"open_settings\">Obre la configuració</string>\n    <string name=\"shortcut_created\">S\\'ha creat una drecera</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"one\">%1$,d byte</item>\n        <item quantity=\"other\">%1$,d bytes</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">L\\'aplicació necessita accés per gestionar tots els fitxers. Si us plau, permeteu l\\'accés a la configuració del sistema.</string>\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">L\\'aplicació necessita permís per accedir als fitxers. Feu clic a «PERMET» en el diàleg del sistema.</string>\n    <string name=\"storage_permission_permanently_denied_message\">L\\'aplicació necessita permís per accedir als fitxers. Si us plau, concedeix el permís “emmagatzematge” a la configuració del sistema.</string>\n    <!-- The allow button is in sentence case now. -->\n    <string name=\"notification_permission_rationale_message\">L\\'aplicació necessita permís per publicar notificacions sobre les operacions dels fitxers. Feu clic a «Permet» en el diàleg del sistema.</string>\n    <string name=\"notification_permission_permanently_denied_message\">L\\'aplicació necessita permís per publicar notificacions sobre les operacions dels fitxers. Atorgueu el permís de «Notificació» a la configuració del sistema.</string>\n\n    <string name=\"notification_channel_background_activity_start_name\">Accions en segon pla</string>\n    <string name=\"notification_channel_background_activity_start_description\">Fes accions mentre l\\'aplicació estigui en segon pla</string>\n    <string name=\"notification_channel_file_job_name\">Operacions de fitxers</string>\n    <string name=\"notification_channel_file_job_description\">Mostra i controla les operacions de fitxers</string>\n    <string name=\"notification_channel_ftp_server_name\">Servidor FTP</string>\n    <string name=\"notification_channel_ftp_server_description\">Mostra i controla el servidor FTP</string>\n\n    <string name=\"file_action_archive_password_title\">Es requereix una contrasenya</string>\n    <string name=\"file_action_archive_password_message_format\">«%1$s» està protegit amb contrasenya.</string>\n    <string name=\"file_action_archive_password_error_empty\">La contrasenya no pot ser buida</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"one\">Preparant la compressió de %1$,d fitxers (%2$s)</item>\n        <item quantity=\"other\">S\\'està preparant la compressió de %1$,d fitxers (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">S\\'està comprimint «%1$s» a «%2$s»</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"one\">Comprimint %1$,d fitxers a “%2$s”</item>\n        <item quantity=\"other\">S\\'estan comprimint %1$,d fitxers a «%2$s»</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"one\">Preparant la còpia de %1$,d fitxers (%2$s)</item>\n        <item quantity=\"other\">S\\'està preparant per a copiar %1$,d fitxers (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">S\\'està copiant «%1$s» a «%2$s»</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"one\">Copiant %1$,d fitxers a “%2$s”</item>\n        <item quantity=\"other\">S\\'estan copiant %1$,d fitxers a «%2$s»</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"one\">Preparant l\\'extracció de %1$,d fitxers (%2$s)</item>\n        <item quantity=\"other\">S\\'està preparant l\\'extracció de %1$,d fitxers (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">S\\'està extraient «%1$s» a «%2$s»</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"one\">Extraient %1$,d fitxers a “%2$s”</item>\n        <item quantity=\"other\">S\\'estan extraient %1$,d fitxers a «%2$s»</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"one\">Preparant per moure %1$,d fitxers (%2$s)</item>\n        <item quantity=\"other\">S\\'està preparant per moure %1$,d fitxers (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">S\\'està movent «%1$s» a «%2$s»</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"one\">Movent %1$,d fitxers a “%2$s”</item>\n        <item quantity=\"other\">S\\'estan movent %1$,d fitxers a «%2$s»</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s de %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d de %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"one\">Preparant la supressió de %1$,d fitxers</item>\n        <item quantity=\"other\">S\\'està preparant la supressió %1$,d fitxers</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">S\\'està suprimint «%1$s»</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"one\">Suprimint %1$,d fitxers</item>\n        <item quantity=\"other\">S\\'estan suprimint %1$,d fitxers</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"one\">Preparant el canvi de propietari per %1$,d fitxers</item>\n        <item quantity=\"other\">S\\'està preparant el canvi de propietari per a %1$,d fitxers</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">S\\'està canviant el propietari de «%1$s»</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"one\">Canviant el propietari per %1$,d fitxers</item>\n        <item quantity=\"other\">S\\'està canviant el propietari de %1$,d fitxers</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"one\">Preparant el canvi de grup per %1$,d fitxers</item>\n        <item quantity=\"other\">S\\'està preparant el canvi de grup per a %1$,d fitxers</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">S\\'està canviant el grup per «%1$s»</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"one\">Canviant el grup per %1$,d fitxers</item>\n        <item quantity=\"other\">S\\'està canviant el grup de %1$,d fitxers</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"one\">Preparant el canvi de mode per %1$,d fitxers</item>\n        <item quantity=\"other\">S\\'està preparant el mode de canvi per a %1$,d fitxers</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">S\\'està canviant el mode per a «%1$s»</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"one\">Canviant el mode per %1$,d fitxers</item>\n        <item quantity=\"other\">S\\'està canviant el mode de %1$,d fitxers</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Preparant el canvi de context SELinux per %1$,d fitxers</item>\n        <item quantity=\"other\">S\\'està preparant canviar el context de SELinux per a %1$,d fitxers</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">S\\'està canviant el context de SELinux per a «%1$s»</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Canviant el context SELinux per %1$,d fitxers</item>\n        <item quantity=\"other\">S\\'està canviant el context de SELinux per a %1$,d fitxers</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Preparant la restauració del context SELinux pel fitxer %1$,d </item>\n        <item quantity=\"other\">S\\'està preparant la restauració del context de SELinux per a %1$,d fitxers</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">S\\'està restaurant el context de SELinux per a «%1$s»</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">S\\'està restaurant el context SELinux per al fitxer %1$,d</item>\n        <item quantity=\"other\">S\\'està restaurant el context de SELinux per a %1$,d fitxers</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d de %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">S\\'està escrivint «%1$s»</string>\n    <string name=\"file_job_create_error_title\">S\\'ha produït un error en crear</string>\n    <string name=\"file_job_create_error_message_format\">S\\'ha produït un error en crear «%1$s».\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">No es pot copiar una carpeta en si mateixa</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">No es pot extreure una carpeta en si mateixa</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">No es pot moure una carpeta en si mateixa</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">La carpeta de destinació es troba dins de la carpeta d\\'origen.</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">No es pot copiar un fitxer sobre si mateix</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">No es pot extreure un fitxer sobre si mateix</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">No es pot moure un fitxer sobre si mateix</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">El fitxer d\\'origen seria sobreescrit per la destinació.</string>\n    <string name=\"file_job_replace_title_format\">Voleu reemplaçar el fitxer «%1$s»?</string>\n    <string name=\"file_job_replace_message_format\">Ja existeix un altre fitxer amb el mateix nom a «%1$s».\\nReemplaçar-lo sobreescriurà el seu contingut.</string>\n    <string name=\"file_job_replace_target_name\">Fitxer original</string>\n    <string name=\"file_job_replace_source_name\">Reemplaça per</string>\n    <string name=\"file_job_merge_title_format\">Voleu fusionar la carpeta «%1$s»?</string>\n    <string name=\"file_job_merge_copy_message_format\">La fusió demanarà confirmació abans de substituir qualsevol fitxer de la carpeta que entri en conflicte amb els fitxers que s\\'estan copiant.</string>\n    <string name=\"file_job_merge_extract_message_format\">La fusió demanarà confirmació abans de substituir qualsevol fitxer de la carpeta que entri en conflicte amb els fitxers que s\\'estan extraient.</string>\n    <string name=\"file_job_merge_move_message_format\">La fusió demanarà confirmació abans de substituir qualsevol fitxer de la carpeta que entri en conflicte amb els fitxers que s\\'estan movent.</string>\n    <string name=\"file_job_merge_target_name\">Carpeta original</string>\n    <string name=\"file_job_merge_source_name\">Fusiona amb</string>\n    <string name=\"file_job_conflict_show_name\">Selecciona un nom nou per la destinació</string>\n    <string name=\"file_job_conflict_name_hint\">Nom nou</string>\n    <string name=\"file_job_archive_error_title_format\">S\\'ha produït un error en comprimir «%1$s»</string>\n    <string name=\"file_job_archive_error_message_format\">S\\'ha produït un error en comprimir el fitxer a «%1$s».\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">S\\'ha produït un error en copiar «%1$s»</string>\n    <string name=\"file_job_copy_error_message_format\">S\\'ha produït un error en copiar el fitxer a «%1$s».\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">S\\'ha produït un error en extreure «%1$s»</string>\n    <string name=\"file_job_extract_error_message_format\">S\\'ha produït un error en extreure el fitxer a «%1$s».\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">S\\'ha produït un error en moure «%1$s»</string>\n    <string name=\"file_job_move_error_message_format\">S\\'ha produït un error en moure el fitxer a «%1$s».\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">S\\'ha produït un error en suprimir</string>\n    <string name=\"file_job_delete_error_message_format\">S\\'ha produït un error en suprimir «%1$s».\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">S\\'ha produït un error en canviar el nom de «%1$s»</string>\n    <string name=\"file_job_rename_error_message_format\">S\\'ha produït un error en canviar el nom del fitxer a «%1$s».\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">S\\'ha produït un error en canviar el propietari de «%1$s»</string>\n    <string name=\"file_job_set_owner_error_message_format\">S\\'ha produït un error en canviar el propietari a «%1$s».\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">S\\'ha produït un error en canviar el grup per «%1$s»</string>\n    <string name=\"file_job_set_group_error_message_format\">S\\'ha produït un error en canviar el grup a «%1$s».\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">S\\'ha produït un error en canviar el mode de «%1$s»</string>\n    <string name=\"file_job_set_mode_error_message_format\">S\\'ha produït un error en canviar el mode a «%1$s».\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">S\\'ha produït un error en canviar el context de SELinux per a «%1$s»</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">S\\'ha produït un error en canviar el context de SELinux a «%1$s».\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">S\\'ha produït un error en restaurar el context de SELinux</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">S\\'ha produït un error en restaurar el context de SELinux per a «%1$s».\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">S\\'ha produït un error en escriure</string>\n    <string name=\"file_job_write_error_message_format\">S\\'ha produït un error en escriure «%1$s».\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">S\\'ha produït un error en obtenir informació sobre «%1$s».</string>\n    <string name=\"file_job_remount_format\">Remunta «%1$s» com a lectura-escriptura</string>\n    <string name=\"file_job_remount_loading_format\">S\\'està tornant a muntar «%1$s» com a lectura-escriptura...</string>\n    <string name=\"file_job_remount_success_format\">S\\'ha tornat a muntar «%1$s» com a lectura-escriptura</string>\n    <string name=\"file_job_action_all\">Aplica aquesta acció a tots els fitxers</string>\n\n    <string name=\"file_open_apk_message\">Aquest fitxer és un paquet Android. Voleu instal·lar-lo, o veure\\'n el contingut?</string>\n    <string name=\"file_open_as_title\">Obre com a...</string>\n    <string name=\"file_open_as_title_format\">Obre «%1$s» com a</string>\n    <string name=\"file_open_as_type_text\">Text</string>\n    <string name=\"file_open_as_type_image\">Imatge</string>\n    <string name=\"file_open_as_type_audio\">Àudio</string>\n    <string name=\"file_open_as_type_video\">Vídeo</string>\n    <string name=\"file_open_as_type_directory\">Carpeta</string>\n    <string name=\"file_open_as_type_any\">Altres</string>\n    <string name=\"file_install_apk_from_background_title_format\">«%1$s» està a punt per instal·lar</string>\n    <string name=\"file_install_apk_from_background_text\">Toqueu per instal·lar</string>\n    <string name=\"file_open_from_background_title_format\">«%1$s» està a punt per obrir-se</string>\n    <string name=\"file_open_from_background_text\">Toqueu per obrir</string>\n    <string name=\"file_name_error_empty\">El nom del fitxer no pot estar buit</string>\n    <string name=\"file_name_error_invalid\">El nom del fitxer no és vàlid</string>\n    <string name=\"file_name_error_already_exists\">Ja existeix un fitxer amb aquest nom</string>\n    <string name=\"file_delete_message_file_format\">Voleu suprimir «%1$s»?</string>\n    <string name=\"file_delete_message_directory_format\">Voleu suprimir la carpeta «%1$s» i el seu contingut?</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"one\">Voleu suprimir el fitxer %1$,d?</item>\n        <item quantity=\"other\">Voleu suprimir els %1$,d fitxers?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"one\">Voleu suprimir %1$,d carpeta i el seu contingut?</item>\n        <item quantity=\"other\">Voleu suprimir %1$,d carpetes i el seu contingut?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"one\">Voleu suprimir %1$,d element?</item>\n        <item quantity=\"other\">Voleu suprimir %1$,d elements?</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">Crea un arxiu</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_create_archive_password\">Contrasenya (opcional)</string>\n    <string name=\"file_add_bookmark_success\">S\\'ha afegit el marcador</string>\n    <string name=\"file_create_file_title\">Fitxer nou</string>\n    <string name=\"file_create_directory_title\">Carpeta nova</string>\n\n    <string name=\"file_list_title\">Fitxers</string>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"one\">%1$,d carpeta</item>\n        <item quantity=\"other\">%1$,d carpetes</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"one\">%1$,d fitxer</item>\n        <item quantity=\"other\">%1$,d fitxers</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">,\\u0020</string>\n    <string name=\"file_list_path_error_empty\">El camí no pot estar buit</string>\n    <string name=\"file_list_path_error_invalid\">El camí no és vàlid</string>\n    <string name=\"file_list_action_view_sort\">Mostra i ordena</string>\n    <string name=\"file_list_action_view_list\">Llista</string>\n    <string name=\"file_list_action_view_grid\">Graella</string>\n    <string name=\"file_list_action_sort_by_name\">Nom</string>\n    <string name=\"file_list_action_sort_by_type\">Tipus</string>\n    <string name=\"file_list_action_sort_by_size\">Mida</string>\n    <string name=\"file_list_action_sort_by_last_modified\">Última modificació</string>\n    <string name=\"file_list_action_sort_order_ascending\"> Ascendent</string>\n    <string name=\"file_list_action_sort_directories_first\">Carpetes primer</string>\n    <string name=\"file_list_action_view_sort_path_specific\">Només per a aquesta carpeta</string>\n    <string name=\"file_list_action_new_task\">Finestra nova</string>\n    <string name=\"file_list_action_navigate_up\">Puja</string>\n    <string name=\"file_list_action_navigate_to\">Ves a</string>\n    <string name=\"file_list_action_show_hidden_files\">Mostra els fitxers ocults</string>\n    <string name=\"file_list_action_copy_path\">Copia el camí</string>\n    <string name=\"file_list_action_open_in_terminal\">Obre en el terminal</string>\n    <string name=\"file_list_action_add_bookmark\">Afegeix un marcador</string>\n    <string name=\"file_list_action_create_shortcut\">Crea una drecera</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">Obre en una finestra nova</string>\n    <string name=\"file_list_select_title_format\">%1$,d</string>\n    <string name=\"file_list_paste_move_title_format\">S\\'està movent %1$,d</string>\n    <string name=\"file_list_paste_copy_title_format\">S\\'està copiant %1$,d</string>\n    <string name=\"file_list_paste_extract_title_format\">S\\'està extraient %1$,d</string>\n    <string name=\"file_list_paste_action_extract_here\">Extreu aquí</string>\n    <string name=\"file_list_empty\">No hi ha cap fitxer</string>\n    <string name=\"file_list_action_create_file\">Fitxer</string>\n    <string name=\"file_list_action_create_directory\">Carpeta</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">Obre amb</string>\n    <string name=\"file_item_action_extract\">Extreu</string>\n    <string name=\"file_item_action_archive\">Comprimeix</string>\n    <string name=\"file_item_action_properties\">Propietats</string>\n\n    <string name=\"file_properties_title_format\">Propietats de «%1$s»</string>\n    <string name=\"file_properties_basic\">Bàsic</string>\n    <string name=\"file_properties_basic_name\">Nom</string>\n    <string name=\"file_properties_basic_type\">Tipus</string>\n    <string name=\"file_properties_basic_type_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">Enllaça a %1$s (%2$s)</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">Destinació de l\\'enllaç</string>\n    <string name=\"file_properties_basic_size\">Mida</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_contents\">Contingut</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"one\">%1$,d element, amb mida %2$s</item>\n        <item quantity=\"other\">%1$,d elements, en total %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">Última modificació</string>\n    <string name=\"file_properties_basic_parent_directory\">Carpeta pare</string>\n    <string name=\"file_properties_basic_archive_file\">Fitxer de l\\'arxiu</string>\n    <string name=\"file_properties_basic_archive_entry\">Entrada del fitxer</string>\n    <string name=\"file_properties_basic_free_space\">Espai lliure</string>\n    <string name=\"file_properties_image\">Imatge</string>\n    <string name=\"file_properties_media_dimensions\">Dimensions</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">Agafat el</string>\n    <string name=\"file_properties_media_coordinates\">Coordenades</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f, %2$.3f</string>\n    <string name=\"file_properties_media_address\">Ubicació</string>\n    <string name=\"file_properties_image_gps_altitude\">Altitud</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f m</string>\n    <string name=\"file_properties_image_equipment\">Càmera</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">Obertura</string>\n    <string name=\"file_properties_image_shutter_speed\">Velocitat de l\\'obturador</string>\n    <string name=\"file_properties_image_focal_length\">Longitud focal</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f mm</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">Equivalent ISO</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">Programari</string>\n    <string name=\"file_properties_image_description\">Descripció</string>\n    <string name=\"file_properties_image_artist\">Creador</string>\n    <string name=\"file_properties_image_copyright\">Copyright</string>\n    <string name=\"file_properties_audio\">Àudio</string>\n    <string name=\"file_properties_media_title\">Títol</string>\n    <string name=\"file_properties_audio_artist\">Artista</string>\n    <string name=\"file_properties_audio_album\">Àlbum</string>\n    <string name=\"file_properties_audio_album_artist\">Artista de l\\'àlbum</string>\n    <string name=\"file_properties_audio_composer\">Compositor</string>\n    <string name=\"file_properties_audio_disc_number\">Disc</string>\n    <string name=\"file_properties_audio_track_number\">Pista</string>\n    <string name=\"file_properties_audio_year\">Any</string>\n    <string name=\"file_properties_audio_genre\">Gènere</string>\n    <string name=\"file_properties_media_duration\">Durada</string>\n    <string name=\"file_properties_media_bit_rate\">Taxa de bits</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d kbps</string>\n    <string name=\"file_properties_audio_sample_rate\">Freqüència de mostratge</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Hz</string>\n    <string name=\"file_properties_video\">Vídeo</string>\n    <string name=\"file_properties_apk\">APK</string>\n    <string name=\"file_properties_apk_label\">Nom</string>\n    <string name=\"file_properties_apk_package_name\">Nom del paquet</string>\n    <string name=\"file_properties_apk_version\">Versió</string>\n    <string name=\"file_properties_apk_version_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_apk_min_sdk_version\">Versió mínima del sistema</string>\n    <string name=\"file_properties_apk_target_sdk_version\">Versió del sistema de destinació</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s (%2$s, %3$d)</string>\n    <string name=\"file_properties_apk_requested_permissions\">Permisos</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">No s\\'ha sol·licitat cap permís</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"one\">%1$,d permís sol·licitat</item>\n        <item quantity=\"other\">%1$,d permisos sol·licitats</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">Signatures</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">No hi ha cap signatura vàlida</string>\n    <string name=\"file_properties_apk_past_signature_digests\">Signatures antigues</string>\n    <string name=\"storage_file_system_root_title\">Arrel</string>\n    <string name=\"storage_list_title\">Emmagatzematge</string>\n    <string name=\"storage_list_empty\">Sense emmagatzematge</string>\n    <string name=\"storage_add_storage_title\">Afegeix l\\'emmagatzematge</string>\n    <string name=\"storage_add_storage_document_tree\">Emmagatzematge extern</string>\n    <string name=\"storage_add_storage_ftp_server\">Servidor FTP</string>\n    <string name=\"storage_add_storage_sftp_server\">Servidor SFTP</string>\n    <string name=\"storage_add_storage_smb_server\">Servidor SMB</string>\n    <string name=\"storage_add_storage_webdav_server\">Servidor WebDAV</string>\n    <string name=\"storage_edit_device_storage_title\">Edita l\\'emmagatzematge del dispositiu</string>\n    <string name=\"storage_edit_device_storage_name\">Nom</string>\n    <string name=\"storage_edit_device_storage_path\">Camí</string>\n    <string name=\"storage_add_document_tree_title\">Afegeix un emmagatzematge extern</string>\n    <string name=\"storage_edit_document_tree_title\">Edita l\\'emmagatzematge extern</string>\n    <string name=\"storage_edit_document_tree_name\">Nom</string>\n    <string name=\"storage_edit_document_tree_uri\">URI</string>\n    <string name=\"storage_edit_document_tree_path\">Camí</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">Edita el servidor FTP</string>\n    <string name=\"storage_edit_ftp_server_title_add\">Afegeix el servidor FTP</string>\n    <string name=\"storage_edit_ftp_server_host\">Nom d\\'amfitrió</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">Introduïu un nom d\\'amfitrió</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">El nom d\\'amfitrió no és vàlid</string>\n    <string name=\"storage_edit_ftp_server_port\">Port</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">El port no és vàlid</string>\n    <string name=\"storage_edit_ftp_server_path\">Camí</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">Es pot deixar buit</string>\n    <string name=\"storage_edit_ftp_server_name\">Nom</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">Usa el nom d\\'amfitrió</string>\n    <string name=\"storage_edit_ftp_server_protocol\">Protocol</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">Autenticació</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>Contrasenya</item>\n        <item>Anònim</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">Nom d\\'usuari</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">Introduïu un nom d\\'usuari</string>\n    <string name=\"storage_edit_ftp_server_password\">Contrasenya</string>\n    <string name=\"storage_edit_ftp_server_mode\">Mode</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>Actiu</item>\n        <item>Passiu</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">Codificació</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">Connecta i afegeix</string>\n    <string name=\"storage_edit_ftp_server_add\">Afegeix</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">Edita el servidor SFTP</string>\n    <string name=\"storage_edit_sftp_server_title_add\">Afegeix el servidor SFTP</string>\n    <string name=\"storage_edit_sftp_server_host\">Nom d\\'amfitrió</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">Introduïu un nom d\\'amfitrió</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">El nom d\\'amfitrió no és vàlid</string>\n    <string name=\"storage_edit_sftp_server_port\">Port</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">El port no és vàlid</string>\n    <string name=\"storage_edit_sftp_server_path\">Camí</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">Es pot deixar buit</string>\n    <string name=\"storage_edit_sftp_server_name\">Nom</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">Usa el nom d\\'amfitrió</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">Autenticació</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>Contrasenya</item>\n        <item>Clau pública</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">Nom d\\'usuari</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">Introduïu un nom d\\'usuari</string>\n    <string name=\"storage_edit_sftp_server_password\">Contrasenya</string>\n    <string name=\"storage_edit_sftp_server_private_key\">Clau privada</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">Obre el fitxer</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">Introduïu una clau privada</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">La clau privada no és vàlida</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">Contrasenya de la clau privada</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">Es pot deixar buit</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">La contrasenya de la clau privada no és vàlida</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">Connecta i afegeix</string>\n    <string name=\"storage_edit_sftp_server_add\">Afegeix</string>\n    <string name=\"storage_add_lan_smb_server_loading\">S\\'estan cercant servidors SMB...</string>\n    <string name=\"storage_add_lan_smb_server_add\">Afegeix manualment</string>\n    <string name=\"storage_edit_smb_server_title_edit\">Edita el servidor SMB</string>\n    <string name=\"storage_edit_smb_server_title_add\">Afegeix un servidor SMB</string>\n    <string name=\"storage_edit_smb_server_host\">Nom d\\'amfitrió</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">Introduïu un nom d\\'amfitrió</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">El nom d\\'amfitrió no és vàlid</string>\n    <string name=\"storage_edit_smb_server_port\">Port</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">El port no és vàlid</string>\n    <string name=\"storage_edit_smb_server_path\">Camí</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">Es pot deixar buit</string>\n    <string name=\"storage_edit_smb_server_name\">Nom</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">Usa el nom d\\'amfitrió</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">Autenticació</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>Contrasenya</item>\n        <item>Convidat</item>\n        <item>Anònim</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">Nom d\\'usuari</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">Introduïu un nom d\\'usuari</string>\n    <string name=\"storage_edit_smb_server_password\">Contrasenya</string>\n    <string name=\"storage_edit_smb_server_domain\">Domini</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">Connecta i afegeix</string>\n    <string name=\"storage_edit_smb_server_add\">Afegeix</string>\n    <string name=\"storage_edit_webdav_server_title_edit\">Edita el servidor WebDAV</string>\n    <string name=\"storage_edit_webdav_server_title_add\">Afegeix un servidor WebDAV</string>\n    <string name=\"storage_edit_webdav_server_host\">Nom de l\\'amfitrió</string>\n    <string name=\"storage_edit_webdav_server_host_error_empty\">Introduïu el nom de l\\'amfitrió</string>\n    <string name=\"storage_edit_webdav_server_host_error_invalid\">El nom d\\'amfitrió no és vàlid</string>\n    <string name=\"storage_edit_webdav_server_port\">Port</string>\n    <string name=\"storage_edit_webdav_server_port_error_invalid\">El port no és vàlid</string>\n    <string name=\"storage_edit_webdav_server_path\">Camí</string>\n    <string name=\"storage_edit_webdav_server_path_placeholder\">Es pot deixar buit</string>\n    <string name=\"storage_edit_webdav_server_name\">Nom</string>\n    <string name=\"storage_edit_webdav_server_name_placeholder\">Fes servir el nom de l\\'amfitrió</string>\n    <string name=\"storage_edit_webdav_server_protocol\">Protocol</string>\n    <string name=\"storage_edit_webdav_server_authentication_type\">Autenticació</string>\n    <string-array name=\"storage_edit_webdav_server_authentication_type_entries\">\n        <item>Contrasenya</item>\n        <item>Testimoni d\\'accés</item>\n        <item>Cap</item>\n    </string-array>\n    <string name=\"storage_edit_webdav_server_username\">Nom d\\'usuari</string>\n    <string name=\"storage_edit_webdav_server_username_error_empty\">Introduïu un nom d\\'usuari</string>\n    <string name=\"storage_edit_webdav_server_password\">Contrasenya</string>\n    <string name=\"storage_edit_webdav_server_access_token\">Testimoni d\\'accés</string>\n    <string name=\"storage_edit_webdav_server_access_token_error_empty\">Introduïu un testimoni d\\'accés</string>\n    <string name=\"storage_edit_webdav_server_connect_and_add\">Connecta i afegeix</string>\n    <string name=\"storage_edit_webdav_server_add\">Afegeix</string>\n\n    <string name=\"navigation_storage_subtitle_format\">%1$s lliures de %2$s</string>\n    <string name=\"navigation_add_storage\">Afegeix emmagatzematge...</string>\n    <string name=\"navigation_standard_directory_alarms\">Alarmes</string>\n    <string name=\"navigation_standard_directory_dcim\">DCIM</string>\n    <string name=\"navigation_standard_directory_documents\">Documents</string>\n    <string name=\"navigation_standard_directory_downloads\">Baixades</string>\n    <string name=\"navigation_standard_directory_movies\">Pel·lícules</string>\n    <string name=\"navigation_standard_directory_music\">Música</string>\n    <string name=\"navigation_standard_directory_notifications\">Notificacions</string>\n    <string name=\"navigation_standard_directory_pictures\">Imatges</string>\n    <string name=\"navigation_standard_directory_podcasts\">Podcasts</string>\n    <string name=\"navigation_standard_directory_ringtones\">Melodies</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">Carpeta de marcadors</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">Nom</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">Camí</string>\n    <string name=\"archive_viewer_title\">Visor d\\'arxius</string>\n    <string name=\"image_viewer_title\">Visor d\\'imatges</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"text_editor_title\">Editor de text</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">S\\'ha desat</string>\n    <string name=\"text_editor_reload_message\">Esteu segur que voleu tornar-ho a carregar? Es perdran els canvis no desats en aquest document.</string>\n    <string name=\"text_editor_encoding\">Codificació</string>\n    <string name=\"text_editor_close_message\">Esteu segur que voleu descartar els canvis no desats en aquest document?</string>\n\n    <string name=\"ftp_server_title\">Servidor FTP</string>\n    <string name=\"ftp_server_state_title\">Estat</string>\n    <string name=\"ftp_server_state_summary_starting\">S\\'està iniciant...</string>\n    <string name=\"ftp_server_state_summary_running\">S\\'està executant</string>\n    <string name=\"ftp_server_state_summary_stopping\">S\\'està aturant...</string>\n    <string name=\"ftp_server_state_summary_stopped\">No s\\'ha iniciat</string>\n    <string name=\"ftp_server_url_title\">URL</string>\n    <string name=\"ftp_server_url_summary_no_local_inet_address\">L\\'adreça IP local és desconeguda</string>\n    <string name=\"ftp_server_url_menu_copy_url\">Copia l\\'URL</string>\n    <string name=\"ftp_server_url_menu_copy_password\">Copia la contrasenya</string>\n    <string name=\"ftp_server_add_tile_title\">Afegeix a la configuració ràpida</string>\n    <string name=\"ftp_server_add_tile_result_added\">S\\'ha afegit «servidor FTP» a la configuració ràpida</string>\n    <string name=\"ftp_server_add_tile_result_already_added\">Ja s\\'ha afegit «Servidor FTP» a la configuració ràpida</string>\n    <string name=\"ftp_server_add_tile_result_error\">S\\'ha produït un error en afegir «servidor FTP» a la configuració ràpida</string>\n    <string name=\"ftp_server_configuration_title\">Configuració</string>\n    <string name=\"ftp_server_anonymous_login_title\">Inici de sessió anònim</string>\n    <string name=\"ftp_server_username_title\">Nom d\\'usuari</string>\n    <string name=\"ftp_server_password_title\">Contrasenya</string>\n    <string name=\"ftp_server_port_title\">Port</string>\n    <string name=\"ftp_server_home_directory_title\">Carpeta arrel</string>\n    <string name=\"ftp_server_writable_title\">Permet l\\'escriptura</string>\n    <string name=\"settings_title\">Configuració</string>\n    <string name=\"settings_interface_title\">Interfície</string>\n    <string name=\"settings_locale_title\">Idioma</string>\n    <string name=\"settings_theme_color_title\">Color del tema</string>\n    <string name=\"settings_theme_color_summary\">El color que apareix amb més freqüència a l\\'aplicació</string>\n    <string name=\"settings_material_design_3_title\">Material Design 3</string>\n    <string name=\"settings_night_mode_title\">Mode nocturn</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>Segueix el sistema</item>\n        <item>Desactivat</item>\n        <item>Actiu</item>\n        <item>Basat en el temps</item>\n        <item>Basat en l\\'estalvi de bateria</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">Mode nocturn negre</string>\n    <string name=\"settings_file_list_animation_title\">Animació de la llista de fitxers</string>\n    <string name=\"settings_file_name_ellipsize_title\">Mostra el nom de fitxer llarg</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>Escurça el principi</item>\n        <item>Escurça al mig</item>\n        <item>Escurça el final</item>\n        <item>Marquesina</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">Comportament</string>\n    <string name=\"settings_default_directory_title\">Carpeta predeterminada</string>\n    <string name=\"settings_standard_directories_title\">Carpetes estàndard</string>\n    <string name=\"settings_standard_directories_summary_empty\">No hi ha cap carpeta estàndard activada</string>\n    <string name=\"settings_bookmark_directory_screenshots\">Captures de pantalla</string>\n    <string name=\"settings_bookmark_directories_title\">Carpetes de marcadors</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">No hi ha cap carpeta de marcadors</string>\n    <string name=\"settings_root_strategy_title\">Mode d\\'accés root</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"one\">Hi ha %1$,d operació de fitxer executant-se ara mateix. Canviar el mode d\\'accés root podria provocar un error inesperat. Esteu segur que voleu canviar-lo ara?</item>\n        <item quantity=\"other\">Hi ha %1$,d operacions de fitxer executant-se ara mateix. Canviar el mode d\\'accés root podria provocar un error inesperat. Esteu segur que voleu canviar-lo ara?</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>Només amb accés normal</item>\n        <item>Automàtic</item>\n        <item>Només amb accés root</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">Codificació dels noms dels fitxers als arxius</string>\n    <string name=\"settings_open_apk_default_action_title\">Obre els paquets d\\'Android</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>Instal·la</item>\n        <item>Mostra el contingut</item>\n        <item>Demana què fer</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">Pot fer que l\\'aplicació sigui inestable en versions anteriors d\\'Android</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">Lectura remota de miniatures</string>\n\n    <string name=\"about_title\">Quant a</string>\n    <string name=\"about_version_title\">Versió</string>\n    <string name=\"about_github_title\">Mostra a GitHub</string>\n    <string name=\"about_licenses_title\">Llicències</string>\n    <string name=\"about_privacy_policy_title\">Política de confidencialitat</string>\n    <string name=\"about_author_title\">Autor</string>\n    <string name=\"about_author_name_title\">Hai Zhang</string>\n    <string name=\"about_author_github_title\">Segueix a GitHub</string>\n    <string name=\"about_author_twitter_title\">Segueix al Twitter</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-cs/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">Soubor</string>\n    <string name=\"file_type_name_apk\">Balíček Android</string>\n    <string name=\"file_type_name_archive\">%1$s archiv</string>\n    <string name=\"file_type_name_audio\">%1$s audio</string>\n    <string name=\"file_type_name_calendar\">%1$s kalendář</string>\n    <string name=\"file_type_name_certificate\">%1$s certifikát</string>\n    <string name=\"file_type_name_code\">%1$s dokument</string>\n    <string name=\"file_type_name_contact\">Elektronická vizitka</string>\n    <string name=\"file_type_name_directory\">Složka</string>\n    <string name=\"file_type_name_document\">%1$s dokument</string>\n    <string name=\"file_type_name_ebook\">%1$s e-kniha</string>\n    <string name=\"file_type_name_email\">%1$s e-mailová zpráva</string>\n    <string name=\"file_type_name_font\">%1$s font</string>\n    <string name=\"file_type_name_generic\">%1$s soubor </string>\n    <string name=\"file_type_name_image\">%1$s obrázek</string>\n    <string name=\"file_type_name_pdf\">Dokument PDF</string>\n    <string name=\"file_type_name_presentation\">%1$s prezentace</string>\n    <string name=\"file_type_name_spreadsheet\">%1$s tabulka</string>\n    <string name=\"file_type_name_text\">%1$s dokument</string>\n    <string name=\"file_type_name_text_plain\">Prostý textový dokument</string>\n    <string name=\"file_type_name_video\">%1$s video</string>\n    <string name=\"file_type_name_word\">Dokument Word</string>\n    <string name=\"file_type_name_powerpoint\">Prezentace PowerPoint</string>\n    <string name=\"file_type_name_excel\">Tabulka Excel</string>\n    <string name=\"file_type_name_posix_character_device\">Znakové zařízení</string>\n    <string name=\"file_type_name_posix_block_device\">Blokové zařízení</string>\n    <string name=\"file_type_name_posix_fifo\">Roura</string>\n    <string name=\"file_type_name_posix_symbolic_link\">Odkaz</string>\n    <string name=\"file_type_name_posix_socket\">Soket</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">Odkaz (rozbitý)</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-cs/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <string name=\"app_name\">Material Files</string>\n\n    <string name=\"close\">Zavřít</string>\n    <string name=\"copy\">Kopírovat</string>\n    <string name=\"cut\">Vyjmout</string>\n    <string name=\"default_\">Výchozí</string>\n    <string name=\"delete\">Smazat</string>\n    <string name=\"discard\">Zrušit</string>\n    <string name=\"edit\">Upravit</string>\n    <string name=\"empty\">Prázdný</string>\n    <string name=\"empty_placeholder\">(Prázdný)</string>\n    <string name=\"error\">Chyba</string>\n    <string name=\"hide\">Skrýt</string>\n    <string name=\"install\">Nainstalovat</string>\n    <string name=\"keep_editing\">Pokračovat v úpravách</string>\n    <string name=\"loading\">Načítání…</string>\n    <string name=\"maybe_later\">Možná později</string>\n    <string name=\"merge\">Sloučit</string>\n    <string name=\"none\">Nic</string>\n    <string name=\"open_navigation_drawer\">Otevřít navigační panel</string>\n    <string name=\"paste\">Vložit</string>\n    <string name=\"refresh\">Obnovit</string>\n    <string name=\"reload\">Obnovit</string>\n    <string name=\"remove\">Odstranit</string>\n    <string name=\"rename\">Přejmenovat</string>\n    <string name=\"replace\">Nahradit</string>\n    <string name=\"reset\">Resetovat</string>\n    <string name=\"retry\">Zkusit znovu</string>\n    <string name=\"save\">Uložit</string>\n    <string name=\"search\">Hledat</string>\n    <string name=\"select\">Vybrat</string>\n    <string name=\"select_all\">Vybrat vše</string>\n    <string name=\"share\">Sdílet</string>\n    <string name=\"show\">Ukázat</string>\n    <string name=\"skip\">Přeskočit</string>\n    <string name=\"stop\">Zastavit</string>\n    <string name=\"system_default\">Výchozí systému</string>\n    <string name=\"unknown\">Neznámý</string>\n    <string name=\"view\">Zobrazení</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">Nebyla nalezena vhodná aplikace pro tuto akci</string>\n    <string name=\"open_settings\">Otevřít nastavení</string>\n    <string name=\"shortcut_created\">Zkratka byla vytvořena</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"one\">%1$,d byte</item>\n        <item quantity=\"few\">%1$,d byty</item>\n        <item quantity=\"many\">%1$,d bytů</item>\n        <item quantity=\"other\">%1$,d bytů</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">Aplikace potřebuje přístup k práci se soubory. Povolte přístup v dalším systémovém dialogu, prosím.</string>\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">Aplikace vyžaduje povolení přístupu k souborům. V dalším dialogu klikněte na \\\"POVOLIT\\\", prosím.</string>\n    <string name=\"storage_permission_permanently_denied_message\">Aplikace potřebuje povolení přístupu k souborům. Prosím udělte povolení \\\"Úložiště\\\" v nastavení.</string>\n    <!-- The allow button is in sentence case now. -->\n    <string name=\"notification_permission_rationale_message\">Aplikace potřebuje oprávnění pro posílání oznámení o operacích se soubory. Prosíme vyberte \\\"Povolit\\\" v následující výzvě systému.</string>\n    <string name=\"notification_permission_permanently_denied_message\">Aplikace potřebuje oprávnění pro posílání oznámení o operacích se soubory. Prosíme udělte oprávnění \\\"Oznámení\\\" v nastavení systému.</string>\n\n    <string name=\"notification_channel_background_activity_start_name\">Akce probíhají v pozadí</string>\n    <string name=\"notification_channel_background_activity_start_description\">Podnikněte kroky, když aplikace běžela na pozadí</string>\n    <string name=\"notification_channel_file_job_name\">Operace se souborem</string>\n    <string name=\"notification_channel_file_job_description\">Zobrazit a ovládat operace se souborem</string>\n    <string name=\"notification_channel_ftp_server_name\">Server FTP</string>\n    <string name=\"notification_channel_ftp_server_description\">Zobrazit a kontrolovat FTP server</string>\n\n    <string name=\"file_action_archive_password_title\">Heslo vyžadováno</string>\n    <string name=\"file_action_archive_password_message_format\">\\\"%1$s\\\" je chráněno heslem.</string>\n    <string name=\"file_action_archive_password_error_empty\">Heslo nemůže být prázdné</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"one\">Příprava ke kompresi %1$,d souboru (%2$s)</item>\n        <item quantity=\"few\">Příprava ke kompresi %1$,d souborů (%2$s)</item>\n        <item quantity=\"many\">Příprava ke kompresi %1$,d souborů (%2$s)</item>\n        <item quantity=\"other\">Příprava ke kompresi %1$,d souborů (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">Komprese \\\"%1$s\\\" do \\\"%2$s\\\"</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"one\">Komprese %1$,d souboru do \\\"%2$s\\\"</item>\n        <item quantity=\"few\">Komprese %1$,d souborů do \\\"%2$s\\\"</item>\n        <item quantity=\"many\">Komprese %1$,d souborů do \\\"%2$s\\\"</item>\n        <item quantity=\"other\">Komprese %1$,d souborů do \\\"%2$s\\\"</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"one\">Příprava pro kopírování %1$,d souboru (%2$s)</item>\n        <item quantity=\"few\">Příprava pro kopírování %1$,d souborů (%2$s)</item>\n        <item quantity=\"many\">Příprava pro kopírování %1$,d souborů (%2$s)</item>\n        <item quantity=\"other\">Příprava pro kopírování %1$,d souborů (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">Kopírování \\\"%1$s\\\" do \\\"%2$s\\\"</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"one\">Kopírování %1$,d souboru do \\\"%2$s\\\"</item>\n        <item quantity=\"few\">Kopírování %1$,d souborů do \\\"%2$s\\\"</item>\n        <item quantity=\"many\">Kopírování %1$,d souborů do \\\"%2$s\\\"</item>\n        <item quantity=\"other\">Kopírování %1$,d souborů do \\\"%2$s\\\"</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"one\">Příprava k rozbalení %1$,d souboru (%2$s)</item>\n        <item quantity=\"few\">Příprava k rozbalení %1$,d souborů (%2$s)</item>\n        <item quantity=\"many\">Příprava k rozbalení %1$,d souborů (%2$s)</item>\n        <item quantity=\"other\">Příprava k rozbalení %1$,d souborů (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">Rozbalování \\\"%1$s\\\" do \\\"%2$s\\\"</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"one\">Rozbalování %1$,d souboru do \\\"%2$s\\\"</item>\n        <item quantity=\"few\">Rozbalování %1$,d souborů do \\\"%2$s\\\"</item>\n        <item quantity=\"many\">Rozbalování %1$,d souborů do \\\"%2$s\\\"</item>\n        <item quantity=\"other\">Rozbalování %1$,d souborů do \\\"%2$s\\\"</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"one\">Příprava k přesunu %1$,d souboru (%2$s)</item>\n        <item quantity=\"few\">Příprava k přesunu %1$,d souborů (%2$s)</item>\n        <item quantity=\"many\">Příprava k přesunu %1$,d souborů (%2$s)</item>\n        <item quantity=\"other\">Příprava k přesunu %1$,d souborů (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">Přesouvání \\\"%1$s\\\" do \\\"%2$s\\\"</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"one\">Přesouvání %1$,d souboru do \\\"%2$s\\\"</item>\n        <item quantity=\"few\">Přesouvání %1$,d souborů do \\\"%2$s\\\"</item>\n        <item quantity=\"many\">Přesouvání %1$,d souborů do \\\"%2$s\\\"</item>\n        <item quantity=\"other\">Přesouvání %1$,d souborů do \\\"%2$s\\\"</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s/%2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"one\">Připrava ke smazání %1$,d souboru</item>\n        <item quantity=\"few\">Příprava ke smazání %1$,d souborů</item>\n        <item quantity=\"many\">Příprava ke smazání %1$,d souborů</item>\n        <item quantity=\"other\">Příprava ke smazání %1$,d souborů</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">Mazání \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"one\">Mazání %1$,d souboru</item>\n        <item quantity=\"few\">Mazání %1$,d souborů</item>\n        <item quantity=\"many\">Mazání %1$,d souborů</item>\n        <item quantity=\"other\">Mazání %1$,d souborů</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"one\">Příprava pro změnu majitele %1$,d souboru</item>\n        <item quantity=\"few\">Příprava pro změnu majitele %1$,d soubory</item>\n        <item quantity=\"many\">Příprava pro změnu majitele %1$,d souborů</item>\n        <item quantity=\"other\">Příprava pro změnu majitele %1$,d souborů</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">Měním majitele pro \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"one\">Změna majitele pro %1$,d soubor</item>\n        <item quantity=\"few\">Změna majitele pro %1$,d soubory</item>\n        <item quantity=\"many\">Změna majitele pro %1$,d souborů</item>\n        <item quantity=\"other\">Změna majitele pro %1$,d souborů</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"one\">Příprava pro změnu skupiny pro %1$,d soubor</item>\n        <item quantity=\"few\">Příprava pro změnu skupiny pro %1$,d soubory</item>\n        <item quantity=\"many\">Příprava pro změnu skupiny pro %1$,d souborů</item>\n        <item quantity=\"other\">Příprava pro změnu skupiny pro %1$,d souborů</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">Změna skupiny pro \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"one\">Změna skupiny pro %1$,d souborů</item>\n        <item quantity=\"few\">Změna skupiny pro %1$,d soubory</item>\n        <item quantity=\"many\">Změna skupiny pro %1$,d souborů</item>\n        <item quantity=\"other\">Změna skupiny pro %1$,d souborů</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"one\">Příprava ke změně režimu pro %1$,d soubor</item>\n        <item quantity=\"few\">Příprava ke změně režimu pro %1$,d soubory</item>\n        <item quantity=\"many\">Příprava ke změně režimu pro %1$,d souborů</item>\n        <item quantity=\"other\">Příprava ke změně režimu pro %1$,d souborů</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">Změna režimu pro \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"one\">Změna režimu pro %1$,d soubor</item>\n        <item quantity=\"few\">Změna režimu pro %1$,d soubory</item>\n        <item quantity=\"many\">Změna režimu pro %1$,d souborů</item>\n        <item quantity=\"other\">Změna režimu pro %1$,d souborů</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Příprava ke změně kontextu SELinuxu pro %1$,d soubor</item>\n        <item quantity=\"few\">Příprava ke změně kontextu SELinuxu pro %1$,d soubory</item>\n        <item quantity=\"many\">Příprava ke změně kontextu SELinuxu pro %1$,d souborů</item>\n        <item quantity=\"other\">Příprava ke změně kontextu SELinuxu pro %1$,d souborů</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">Změna kontextu SELinuxu pro \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Změna kontextu SELinuxu pro %1$,d soubor</item>\n        <item quantity=\"few\">Změna kontextu SELinuxu pro %1$,d soubry</item>\n        <item quantity=\"many\">Změna kontextu SELinuxu pro %1$,d souborů</item>\n        <item quantity=\"other\">Změna kontextu SELinuxu pro %1$,d souborů</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Příprava k obnově kontextu SELinuxu pro %1$,d soubor</item>\n        <item quantity=\"few\">Příprava k obnově kontextu SELinuxu pro %1$,d soubory</item>\n        <item quantity=\"many\">Příprava k obnově kontextu SELinuxu pro %1$,d souborů</item>\n        <item quantity=\"other\">Příprava k obnově kontextu SELinuxu pro %1$,d souborů</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">Obnovování kontextu SELinuxu pro \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Obnovování kontextu SELinuxu pro %1$,d soubor</item>\n        <item quantity=\"few\">Obnovování kontextu SELinuxu pro %1$,d soubory</item>\n        <item quantity=\"many\">Obnovování kontextu SELinuxu pro %1$,d souborů</item>\n        <item quantity=\"other\">Obnovování kontextu SELinuxu pro %1$,d souborů</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">Zapisování do \\\"%1$s\\\"</string>\n    <string name=\"file_job_create_error_title\">Chyba při vytváření</string>\n    <string name=\"file_job_create_error_message_format\">Nastala chyba při vytváření \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">Nelze kopírovat složku do sebe</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">Nelze rozbalit složku do sebe</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">Nelze přesunout složku do stejné složky</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">Cílová složka se nachází uvnitř zdrojové složky.</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">Nelze zkopírovat soubor přes sebe</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">Nelze rozbalit soubor přes sebe</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">Nelze přesunout soubor přes sebe</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">Zdrojový soubor by byl přepsán cílovým souborem.</string>\n    <string name=\"file_job_replace_title_format\">Nahradit soubor \\\"%1$s\\\"?</string>\n    <string name=\"file_job_replace_message_format\">V \\\"%1$s\\\" již existuje soubor se stejným jménem.\\nNahrazením se přepíše jeho obsah.</string>\n    <string name=\"file_job_replace_target_name\">Původní soubor</string>\n    <string name=\"file_job_replace_source_name\">Nahradit</string>\n    <string name=\"file_job_merge_title_format\">Sloučit složku \\\"%1$s\\\"?</string>\n    <string name=\"file_job_merge_copy_message_format\">Před nahrazením souborů ve složce, které jsou v rozporu s kopírovanými soubory, bude požádáno o potvrzení o sloučení.</string>\n    <string name=\"file_job_merge_extract_message_format\">Před nahrazením souborů ve složce, které jsou v rozporu s extrahovanými soubory, bude požádáno o potvrzení o sloučení.</string>\n    <string name=\"file_job_merge_move_message_format\">Před nahrazením souborů ve složce, které jsou v rozporu s přesouvanými soubory, bude požádáno o potvrzení o sloučení.</string>\n    <string name=\"file_job_merge_target_name\">Původní složka</string>\n    <string name=\"file_job_merge_source_name\">Sloučit s</string>\n    <string name=\"file_job_conflict_show_name\">Zvolte nové jméno destinace</string>\n    <string name=\"file_job_conflict_name_hint\">Nový název</string>\n    <string name=\"file_job_archive_error_title_format\">Chyba při kompresi \\\"%1$s\\\"</string>\n    <string name=\"file_job_archive_error_message_format\">Nastala chyba při kompresi souboru do \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">Chyba při kopírování \\\"%1$s\\\"</string>\n    <string name=\"file_job_copy_error_message_format\">Nastala chyba při kopírování souboru do \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">Chyba při rozbalování \\\"%1$s\\\"</string>\n    <string name=\"file_job_extract_error_message_format\">Nastala chyba při rozbalování souboru do \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">Chyba při přesouvání \\\"%1$s\\\"</string>\n    <string name=\"file_job_move_error_message_format\">Nastala chyba při přesouvání souboru do \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">Chyba při mazání</string>\n    <string name=\"file_job_delete_error_message_format\">Nastala chyba při mazání \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">Chyba při přejmenovávání \\\"%1$s\\\"</string>\n    <string name=\"file_job_rename_error_message_format\">Nastala chyba při přejmenovávání souboru na \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">Chyba při změně majitele \\\"%1$s\\\"</string>\n    <string name=\"file_job_set_owner_error_message_format\">Nastala chyba při změně majitele na \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">Chyba při změně skupiny pro \\\"%1$s\\\"</string>\n    <string name=\"file_job_set_group_error_message_format\">Nastala chyba při změně skupiny na \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">Chyba při změně režimu pro \\\"%1$s\\\"</string>\n    <string name=\"file_job_set_mode_error_message_format\">Nastala chyba při změně režimu na \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">Chyba při změně kontextu SELinuxu pro \\\"%1$s\\\"</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">Nastala chyba při změně kontextu SELinuxu na \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">Chyba při obnově kontextu SELinuxu</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">Nastala chyba při obnově kontextu SELinuxu pro \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">Chyba při zapisování</string>\n    <string name=\"file_job_write_error_message_format\">Nastala chyba při zapisování \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">Nastala chyba při získávání informací o \\\"%1$s\\\".</string>\n    <string name=\"file_job_remount_format\">Nastavit \\\"%1$s\\\" jako čtení-zápis</string>\n    <string name=\"file_job_remount_loading_format\">Nastavování \\\"%1$s\\\" jako čtení-zápis</string>\n    <string name=\"file_job_remount_success_format\">\\\"%1$s\\\" bylo nastaveno jako čtení-zápis</string>\n    <string name=\"file_job_action_all\">Použít tuto akci na všechny soubory</string>\n\n    <string name=\"file_open_apk_message\">Tento soubor je balíčkem pro systém Android. Chcete jej nainstalovat nebo zobrazit jeho obsah?</string>\n    <string name=\"file_open_as_title\">Otevřít jako…</string>\n    <string name=\"file_open_as_title_format\">Otevřít \\\"%1$s\\\" jako</string>\n    <string name=\"file_open_as_type_text\">Text</string>\n    <string name=\"file_open_as_type_image\">Obrázek</string>\n    <string name=\"file_open_as_type_audio\">Zvuk</string>\n    <string name=\"file_open_as_type_video\">Video</string>\n    <string name=\"file_open_as_type_directory\">Složka</string>\n    <string name=\"file_open_as_type_any\">Jiné</string>\n    <string name=\"file_install_apk_from_background_title_format\">\\\"%1$s\\\" je připraveno na instalaci</string>\n    <string name=\"file_install_apk_from_background_text\">Klepněte pro instalaci</string>\n    <string name=\"file_open_from_background_title_format\">\\\"%1$s\\\" je připraveno na otevření</string>\n    <string name=\"file_open_from_background_text\">Klepněte pro otevření</string>\n    <string name=\"file_name_error_empty\">Název souboru nemůže být prázdný</string>\n    <string name=\"file_name_error_invalid\">Neplatný název souboru</string>\n    <string name=\"file_name_error_already_exists\">Soubor s tímto názvem již existuje</string>\n    <string name=\"file_replace_message_format\">Nahradit „%1$s“?</string>\n    <string name=\"file_delete_message_file_format\">Smazat \\\"%1$s\\\"?</string>\n    <string name=\"file_delete_message_directory_format\">Smazat složku \\\"%1$s\\\" a její obsah?</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"one\">Odstranit %1$,d soubor?</item>\n        <item quantity=\"few\">Odstranit %1$,d soubory?</item>\n        <item quantity=\"many\">Odstranit %1$,d souborů?</item>\n        <item quantity=\"other\">Odstranit %1$,d souborů?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"one\">Odstranit %1$,d složku a její obsah?</item>\n        <item quantity=\"few\">Odstranit %1$,d složky a jejich obsahy?</item>\n        <item quantity=\"many\">Odstranit %1$,d složek a jejich obsahy?</item>\n        <item quantity=\"other\">Odstranit %1$,d složek a jejich obsahy?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"one\">Odstranit %1$,d položku?</item>\n        <item quantity=\"few\">Odstranit %1$,d položky?</item>\n        <item quantity=\"many\">Odstranit %1$,d položek?</item>\n        <item quantity=\"other\">Odstranit %1$,d položek?</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">Vytvořit archiv</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_create_archive_password\">Heslo (volitelné)</string>\n    <string name=\"file_add_bookmark_success\">Záložka přidána</string>\n    <string name=\"file_create_file_title\">Nový soubor</string>\n    <string name=\"file_create_directory_title\">Nová složka</string>\n\n    <string name=\"file_list_title\">Soubory</string>\n    <string name=\"file_list_title_create_file\">Uložit soubor</string>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"one\">%1$,d složka</item>\n        <item quantity=\"few\">%1$,d složky</item>\n        <item quantity=\"many\">%1$,d složek</item>\n        <item quantity=\"other\">%1$,d složek</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"one\">%1$,d soubor</item>\n        <item quantity=\"few\">%1$,d soubory</item>\n        <item quantity=\"many\">%1$,d souborů</item>\n        <item quantity=\"other\">%1$,d souborů</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">,\\u0020</string>\n    <string name=\"file_list_path_error_empty\">Cesta nemůže být prázná</string>\n    <string name=\"file_list_path_error_invalid\">Neplatná cesta</string>\n    <string name=\"file_list_action_view_sort\">Zobrazení a seřazení</string>\n    <string name=\"file_list_action_view_list\">Seznam</string>\n    <string name=\"file_list_action_view_grid\">Mřížka</string>\n    <string name=\"file_list_action_sort_by_name\">Název</string>\n    <string name=\"file_list_action_sort_by_type\">Typ</string>\n    <string name=\"file_list_action_sort_by_size\">Velikost</string>\n    <string name=\"file_list_action_sort_by_last_modified\">Naposledy změněno</string>\n    <string name=\"file_list_action_sort_order_ascending\">Vzestupně</string>\n    <string name=\"file_list_action_sort_directories_first\">Složky jako první</string>\n    <string name=\"file_list_action_view_sort_path_specific\">Jen pro tuto složku</string>\n    <string name=\"file_list_action_new_task\">Nové okno</string>\n    <string name=\"file_list_action_navigate_up\">Nahoru</string>\n    <string name=\"file_list_action_navigate_to\">Jít na</string>\n    <string name=\"file_list_action_show_hidden_files\">Zobrazit skryté soubory</string>\n    <string name=\"file_list_action_copy_path\">Zkopírovat cestu</string>\n    <string name=\"file_list_action_open_in_terminal\">Otevřít v příkazovém řádku</string>\n    <string name=\"file_list_action_add_bookmark\">Přidat záložku</string>\n    <string name=\"file_list_action_create_shortcut\">Vytvořit zástupce</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">Otevřít v novém okně</string>\n    <string name=\"file_list_create_file_name_hint\">Název souboru</string>\n    <string name=\"file_list_paste_move_title_format\">Přesouvání %1$,d</string>\n    <string name=\"file_list_paste_copy_title_format\">Kopírování %1$,d</string>\n    <string name=\"file_list_paste_extract_title_format\">Rozbalování %1$,d</string>\n    <string name=\"file_list_paste_action_extract_here\">Rozbalit zde</string>\n    <string name=\"file_list_empty\">Žádné soubory</string>\n    <string name=\"file_list_action_create_file\">Soubor</string>\n    <string name=\"file_list_action_create_directory\">Složka</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">Otevřít v</string>\n    <string name=\"file_item_action_extract\">Rozbalit</string>\n    <string name=\"file_item_action_archive\">Zkomprimovat</string>\n    <string name=\"file_item_action_properties\">Vlastnosti</string>\n\n    <string name=\"file_properties_title_format\">Vlastnosti \\\"%1$s\\\"</string>\n    <string name=\"file_properties_basic\">Základní</string>\n    <string name=\"file_properties_basic_name\">Název</string>\n    <string name=\"file_properties_basic_type\">Typ</string>\n    <string name=\"file_properties_basic_type_format\">%1$s(%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">Odkaz k %1$s(%2$s)</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">Cíl odkazu</string>\n    <string name=\"file_properties_basic_size\">Velikost</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s(%2$s)</string>\n    <string name=\"file_properties_basic_contents\">Obsah</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"one\">%1$,d položka, s velikostí %2$s</item>\n        <item quantity=\"few\">%1$,d položky, celkem %2$s</item>\n        <item quantity=\"many\">%1$,d položek, celkem %2$s</item>\n        <item quantity=\"other\">%1$,d položek, celkem %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">Změněno</string>\n    <string name=\"file_properties_basic_parent_directory\">Mateřská složka</string>\n    <string name=\"file_properties_basic_archive_file\">Archivovaný soubor</string>\n    <string name=\"file_properties_basic_archive_entry\">Archivovaná položka</string>\n    <string name=\"file_properties_basic_free_space\">Volné místo</string>\n    <string name=\"file_properties_permission\">Oprávnění</string>\n    <string name=\"file_properties_permission_owner\">Vlastník</string>\n    <string name=\"file_properties_permission_group\">Skupina</string>\n    <string name=\"file_properties_permission_set_owner_title\">Změnit vlastníka</string>\n    <string name=\"file_properties_permission_set_group_title\">Změnit skupinu</string>\n    <string name=\"file_properties_permission_set_principal_filter_hint\">Zadejte název nebo ID</string>\n    <string name=\"file_properties_permission_mode\">Režim</string>\n    <string name=\"file_properties_permission_set_mode_title\">Změnit režim</string>\n    <string name=\"file_properties_permission_set_mode_others\">Ostatní</string>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_file\">\n        <item>Čtení</item>\n        <item>Zápis</item>\n        <item>Spuštění</item>\n    </string-array>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_directory\">\n        <item>Čtení</item>\n        <item>Zápis</item>\n        <item>Vyhledání</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_special\">Speciální</string>\n    <string-array name=\"file_properties_permission_set_mode_special_mode_bits\">\n        <item>Nastavit UID</item>\n        <item>Nastavit GID</item>\n        <item>Sticky bit</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_selinux_context_restore\">Obnovit</string>\n    <string name=\"file_properties_image\">Obrázek</string>\n    <string name=\"file_properties_media_dimensions\">Rozměry</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">Pořízeno na</string>\n    <string name=\"file_properties_media_coordinates\">Souřadnice</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f, %2$.3f</string>\n    <string name=\"file_properties_media_address\">Lokace</string>\n    <string name=\"file_properties_image_gps_altitude\">Nadmořská výška</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f m</string>\n    <string name=\"file_properties_image_equipment\">Kamera</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">Clona</string>\n    <string name=\"file_properties_image_shutter_speed\">Rychlost závěrky</string>\n    <string name=\"file_properties_image_focal_length\">Ohnisková vzálenost</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f mm</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">Ekvivalent ISO</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">Software</string>\n    <string name=\"file_properties_image_description\">Popis</string>\n    <string name=\"file_properties_image_artist\">Tvůrce</string>\n    <string name=\"file_properties_image_copyright\">Copyright</string>\n    <string name=\"file_properties_audio\">Zvuk</string>\n    <string name=\"file_properties_media_title\">Název</string>\n    <string name=\"file_properties_audio_artist\">Umělec</string>\n    <string name=\"file_properties_audio_album\">Album</string>\n    <string name=\"file_properties_audio_album_artist\">Interpret alba</string>\n    <string name=\"file_properties_audio_composer\">Skladatel</string>\n    <string name=\"file_properties_audio_disc_number\">Disk</string>\n    <string name=\"file_properties_audio_track_number\">Skladba</string>\n    <string name=\"file_properties_audio_year\">Rok</string>\n    <string name=\"file_properties_audio_genre\">Žánr</string>\n    <string name=\"file_properties_media_duration\">Doba</string>\n    <string name=\"file_properties_media_bit_rate\">Přenosová rychlost</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d kbps</string>\n    <string name=\"file_properties_audio_sample_rate\">Frekvence vzorkování</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Hz</string>\n    <string name=\"file_properties_video\">Video</string>\n    <string name=\"file_properties_apk\">APK</string>\n    <string name=\"file_properties_apk_label\">Název</string>\n    <string name=\"file_properties_apk_package_name\">Název balíčku</string>\n    <string name=\"file_properties_apk_version\">Verze</string>\n    <string name=\"file_properties_apk_version_format\">%1$s(%2$d)</string>\n    <string name=\"file_properties_apk_min_sdk_version\">Minimální verze systému</string>\n    <string name=\"file_properties_apk_target_sdk_version\">Doporučená verze systému</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s (%2$s, %3$d)</string>\n    <string name=\"file_properties_apk_requested_permissions\">Oprávnění</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">Nebyla vyžádána žádná oprávnění</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"one\">%1$,d požádané oprávnění</item>\n        <item quantity=\"few\">%1$,d požádané oprávnění</item>\n        <item quantity=\"many\">%1$,d požádaných oprávnění</item>\n        <item quantity=\"other\">%1$,d požádaných oprávnění</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">Podpisy</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">Žádné platné podpisy</string>\n    <string name=\"file_properties_apk_past_signature_digests\">Staré podpisy</string>\n    <string name=\"file_properties_checksum\">Kontrolní součty</string>\n    <string name=\"file_properties_checksum_compare\">Porovnat</string>\n    <string name=\"file_properties_checksum_compare_placeholder\">Jakýkoli kontrolní součet</string>\n    <string name=\"file_properties_checksum_compare_no_match\">Žádný odpovídající kontrolní součet</string>\n    <string name=\"storage_file_system_root_title\">Root</string>\n    <string name=\"storage_list_title\">Úložiště</string>\n    <string name=\"storage_list_empty\">Žádné úložiště</string>\n    <string name=\"storage_add_storage_title\">Přidat úložiště</string>\n    <string name=\"storage_add_storage_document_tree\">Externí úložiště</string>\n    <string name=\"storage_add_storage_ftp_server\">Server FTP</string>\n    <string name=\"storage_add_storage_sftp_server\">Server SFTP</string>\n    <string name=\"storage_add_storage_smb_server\">Server SMB</string>\n    <string name=\"storage_add_storage_webdav_server\">Server WebDAV</string>\n    <string name=\"storage_edit_device_storage_title\">Upravit úložiště zařízení</string>\n    <string name=\"storage_edit_device_storage_name\">Název</string>\n    <string name=\"storage_edit_device_storage_path\">Cesta</string>\n    <string name=\"storage_add_document_tree_title\">Přidat externí úložiště</string>\n    <string name=\"storage_edit_document_tree_title\">Upravit externí úložiště</string>\n    <string name=\"storage_edit_document_tree_name\">Název</string>\n    <string name=\"storage_edit_document_tree_uri\">URI</string>\n    <string name=\"storage_edit_document_tree_path\">Cesta</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">Upravit server FTP</string>\n    <string name=\"storage_edit_ftp_server_title_add\">Přidat server FTP</string>\n    <string name=\"storage_edit_ftp_server_host\">Název hostitele</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">Zadejte název hostitele</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">Neplatný název hostitele</string>\n    <string name=\"storage_edit_ftp_server_port\">Port</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">Neplatný port</string>\n    <string name=\"storage_edit_ftp_server_path\">Cesta</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">Může zůstat prázdná</string>\n    <string name=\"storage_edit_ftp_server_name\">Název</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">Použít název hostitele</string>\n    <string name=\"storage_edit_ftp_server_protocol\">Protokol</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">Oveření</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>Heslo</item>\n        <item>Anonymní</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">Uživatelské jméno</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">Zadejte uživatelské jméno</string>\n    <string name=\"storage_edit_ftp_server_password\">Heslo</string>\n    <string name=\"storage_edit_ftp_server_mode\">Režim</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>Aktivní</item>\n        <item>Pasivní</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">Kódování</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">Připojit a přidat</string>\n    <string name=\"storage_edit_ftp_server_add\">Přidat</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">Upravit server SFTP</string>\n    <string name=\"storage_edit_sftp_server_title_add\">Přidat server SFTP</string>\n    <string name=\"storage_edit_sftp_server_host\">Název hostitele</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">Vložit název hostitele</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">Neplatný název hostitele</string>\n    <string name=\"storage_edit_sftp_server_port\">Port</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">Neplatný port</string>\n    <string name=\"storage_edit_sftp_server_path\">Cesta</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">Může být prázdné</string>\n    <string name=\"storage_edit_sftp_server_name\">Název</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">Použít název hostitele</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">Ověření</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>Heslo</item>\n        <item>Veřejný klíč</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">Uživatelské jméno</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">Vložit uživatelské jméno</string>\n    <string name=\"storage_edit_sftp_server_password\">Heslo</string>\n    <string name=\"storage_edit_sftp_server_private_key\">Soukromý klíč</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">Otevřít soubor</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">Vložit soukromý klíč</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">Neplatný soukromý klíč</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">Heslo soukromého kliče</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">Může zůstat prázné</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">Neplatné heslo soukromého klíče</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">Připojit se a přidat</string>\n    <string name=\"storage_edit_sftp_server_add\">Přidat</string>\n    <string name=\"storage_add_lan_smb_server_loading\">Hledání SMB serverů…</string>\n    <string name=\"storage_add_lan_smb_server_add\">Přidat manuálně</string>\n    <string name=\"storage_edit_smb_server_title_edit\">Upravit server SMB</string>\n    <string name=\"storage_edit_smb_server_title_add\">Přidat server SMB</string>\n    <string name=\"storage_edit_smb_server_host\">Název hostitele</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">Vložit název hostitele</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">Neplatný název hostitele</string>\n    <string name=\"storage_edit_smb_server_port\">Port</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">Neplatný port</string>\n    <string name=\"storage_edit_smb_server_path\">Cesta</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">Může být prázdné</string>\n    <string name=\"storage_edit_smb_server_name\">Název</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">Použít název hostitele</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">Ověření</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>Heslo</item>\n        <item>Host</item>\n        <item>Anonymní</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">Uživatelské jméno</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">Vložit uživatelské jméno</string>\n    <string name=\"storage_edit_smb_server_password\">Heslo</string>\n    <string name=\"storage_edit_smb_server_domain\">Doména</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">Připojit se a přidat</string>\n    <string name=\"storage_edit_smb_server_add\">Přidat</string>\n    <string name=\"storage_edit_webdav_server_title_edit\">Upravit server WebDAV</string>\n    <string name=\"storage_edit_webdav_server_title_add\">Přidat server WebDAV</string>\n    <string name=\"storage_edit_webdav_server_host\">Název hostitele</string>\n    <string name=\"storage_edit_webdav_server_host_error_empty\">Zadejte název hostitele</string>\n    <string name=\"storage_edit_webdav_server_host_error_invalid\">Neplatný název hostitele</string>\n    <string name=\"storage_edit_webdav_server_port\">Port</string>\n    <string name=\"storage_edit_webdav_server_port_error_invalid\">Neplatný port</string>\n    <string name=\"storage_edit_webdav_server_path\">Cesta</string>\n    <string name=\"storage_edit_webdav_server_path_placeholder\">Může být vynechána</string>\n    <string name=\"storage_edit_webdav_server_name\">Název</string>\n    <string name=\"storage_edit_webdav_server_name_placeholder\">Použít název hostitele</string>\n    <string name=\"storage_edit_webdav_server_protocol\">Protokol</string>\n    <string name=\"storage_edit_webdav_server_authentication_type\">Ověření</string>\n    <string-array name=\"storage_edit_webdav_server_authentication_type_entries\">\n        <item>Heslo</item>\n        <item>Přístupový token</item>\n        <item>Žádné</item>\n    </string-array>\n    <string name=\"storage_edit_webdav_server_username\">Uživatelské jméno</string>\n    <string name=\"storage_edit_webdav_server_username_error_empty\">Zadejte uživatelské jméno</string>\n    <string name=\"storage_edit_webdav_server_password\">Heslo</string>\n    <string name=\"storage_edit_webdav_server_access_token\">Přístupový token</string>\n    <string name=\"storage_edit_webdav_server_access_token_error_empty\">Zadejte přístupový token</string>\n    <string name=\"storage_edit_webdav_server_connect_and_add\">Připojit a přidat</string>\n    <string name=\"storage_edit_webdav_server_add\">Přidat</string>\n\n    <string name=\"navigation_storage_subtitle_format\">%1$svolných z %2$s</string>\n    <string name=\"navigation_add_storage\">Přidat úložiště…</string>\n    <string name=\"navigation_standard_directory_alarms\">Budíky</string>\n    <string name=\"navigation_standard_directory_dcim\">DCMI</string>\n    <string name=\"navigation_standard_directory_documents\">Dokumenty</string>\n    <string name=\"navigation_standard_directory_downloads\">Stahování</string>\n    <string name=\"navigation_standard_directory_movies\">Filmy</string>\n    <string name=\"navigation_standard_directory_music\">Hudba</string>\n    <string name=\"navigation_standard_directory_notifications\">Oznámení</string>\n    <string name=\"navigation_standard_directory_pictures\">Obrázky</string>\n    <string name=\"navigation_standard_directory_podcasts\">Podcasty</string>\n    <string name=\"navigation_standard_directory_ringtones\">Vyzvánění</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">Založená složka</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">Název</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">Cesta</string>\n    <string name=\"archive_viewer_title\">Prohlížeč archivů</string>\n    <string name=\"image_viewer_title\">Prohlížeč obrázků</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"save_as_title\">Uložit jako</string>\n    <string name=\"save_as_error\">Chyba při ukládání souboru</string>\n    <string name=\"save_as_success_format\">„%1$s“ byl uložen do „%2$s“</string>\n    <string name=\"text_editor_title\">Textový editor</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">Uloženo</string>\n    <string name=\"text_editor_reload_message\">Jste si jisti, že chcete dokument znovu načíst? Neuložené změny budou ztraceny.</string>\n    <string name=\"text_editor_encoding\">Kódování</string>\n    <string name=\"text_editor_close_message\">Jste si jisti, že chcete zahodit neuložené změny v tomto dokumentu?</string>\n\n    <string name=\"ftp_server_title\">Server FTP</string>\n    <string name=\"ftp_server_state_title\">Stav</string>\n    <string name=\"ftp_server_state_summary_starting\">Spouštění…</string>\n    <string name=\"ftp_server_state_summary_running\">Běží</string>\n    <string name=\"ftp_server_state_summary_stopping\">Zastavování…</string>\n    <string name=\"ftp_server_state_summary_stopped\">Není spuštěno</string>\n    <string name=\"ftp_server_url_title\">URL</string>\n    <string name=\"ftp_server_url_summary_no_local_inet_address\">Místní IP adresa je neznámá</string>\n    <string name=\"ftp_server_url_menu_copy_url\">Kopírovat URL</string>\n    <string name=\"ftp_server_url_menu_copy_password\">Kopírovat heslo</string>\n    <string name=\"ftp_server_add_tile_title\">Přidat do rychlých nastavení</string>\n    <string name=\"ftp_server_add_tile_result_added\">\\\"Server FTP\\\" byl přidán do rychlých nastavení</string>\n    <string name=\"ftp_server_add_tile_result_already_added\">\\\"Server FTP\\\" již byl přidán do rychlých nastavení</string>\n    <string name=\"ftp_server_add_tile_result_error\">Chyba při přidávání \\\"Server FTP\\\" do rychlých nastavení</string>\n    <string name=\"ftp_server_configuration_title\">Konfigurace</string>\n    <string name=\"ftp_server_anonymous_login_title\">Anonymní přihlášení</string>\n    <string name=\"ftp_server_username_title\">Uživatelské jméno</string>\n    <string name=\"ftp_server_password_title\">Heslo</string>\n    <string name=\"ftp_server_port_title\">Port</string>\n    <string name=\"ftp_server_home_directory_title\">Složka Root</string>\n    <string name=\"ftp_server_writable_title\">Povolit zapisování</string>\n    <string name=\"settings_title\">Nastavení</string>\n    <string name=\"settings_interface_title\">Rozhraní</string>\n    <string name=\"settings_locale_title\">Jazyk</string>\n    <string name=\"settings_theme_color_title\">Barva motivu</string>\n    <string name=\"settings_theme_color_summary\">Barva, která se v aplikaci vyskytuje nejvíce</string>\n    <string name=\"settings_material_design_3_title\">Material Design 3</string>\n    <string name=\"settings_night_mode_title\">Noční režim</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>Podle systému</item>\n        <item>Vypnuto</item>\n        <item>Zapnuto</item>\n        <item>Podle času</item>\n        <item>Podle spořiče baterie</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">Režim černá noc</string>\n    <string name=\"settings_file_list_animation_title\">Animace při zobrazení souborů</string>\n    <string name=\"settings_file_name_ellipsize_title\">Způsob zobrazení dlouhého názvu souboru</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>Zobrazit od konce</item>\n        <item>Zobrazit začátek a konec</item>\n        <item>Zobrazit od začátku</item>\n        <item>Posouvání názvu</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">Chování</string>\n    <string name=\"settings_default_directory_title\">Výchozí složka</string>\n    <string name=\"settings_standard_directories_title\">Standartní složky</string>\n    <string name=\"settings_standard_directories_summary_empty\">Nejsou povoleny žádné standartní složky</string>\n    <string name=\"settings_bookmark_directory_screenshots\">Snímky obrazovky</string>\n    <string name=\"settings_bookmark_directories_title\">Založené složky</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">Žádné založené složky</string>\n    <string name=\"settings_root_strategy_title\">Režim přístupu root</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"one\">Právě probíhá %1$,d souborová operace a změna režimu přístupu root by mohla mít za následek nečekanou chybu. Jste si jisit, že ho chcete změnit nyní?</item>\n        <item quantity=\"few\">Právě probíhá %1$,d souborové operace a změna režimu přístupu root by mohla mít za následek nečekanou chybu. Jste si jisti, že ho chcete změnit nyní?</item>\n        <item quantity=\"many\">Právě probíhá %1$,d souborových operací a změna režimu přístupu root by mohla mít za následek nečekanou chybu. Jste si jisti, že ho chcete změnit nyní?</item>\n        <item quantity=\"other\">Právě probíhá %1$,d souborových operací a změna režimu přístupu root by mohla mít za následek nečekanou chybu. Jste si jisti, že ho chcete změnit nyní?</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>Pouze normální přístup</item>\n        <item>Automatický</item>\n        <item>Pouze přístup root</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">Kódování názvu archivovaného souboru</string>\n    <string name=\"settings_open_apk_default_action_title\">Otevírání balíčku Android</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>Nainstalovat</item>\n        <item>Zobrazit obsah</item>\n        <item>Zeptat se, co udělat</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_title\">Zobrazit náhled pro PDF soubory</string>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">Aplikace může být nestabilní na starších verzích systému Android.</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">Přečíst vzdálené soubory pro náhled</string>\n\n    <string name=\"about_title\">O aplikaci</string>\n    <string name=\"about_version_title\">Verze</string>\n    <string name=\"about_github_title\">Podívat se na GitHubu</string>\n    <string name=\"about_licenses_title\">Licence</string>\n    <string name=\"about_privacy_policy_title\">Zásady ochrany soukromí</string>\n    <string name=\"about_author_title\">Autor</string>\n    <string name=\"about_author_name_title\">Hai Zhang</string>\n    <string name=\"about_author_github_title\">Sledovat na GitHubu</string>\n    <string name=\"about_author_twitter_title\">Sledovat na Twitteru</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-de/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">Datei</string>\n    <string name=\"file_type_name_apk\">APK-Datei</string>\n    <string name=\"file_type_name_archive\">%1$s Archiv</string>\n    <string name=\"file_type_name_audio\">%1$s Audio</string>\n    <string name=\"file_type_name_calendar\">%1$s Kalender</string>\n    <string name=\"file_type_name_certificate\">%1$s Zertifikat</string>\n    <string name=\"file_type_name_code\">%1$s Dokument</string>\n    <string name=\"file_type_name_contact\">Elektronische Visitenkarte</string>\n    <string name=\"file_type_name_directory\">Verzeichnis</string>\n    <string name=\"file_type_name_document\">%1$s Dokument</string>\n    <string name=\"file_type_name_ebook\">%1$s E-Book</string>\n    <string name=\"file_type_name_email\">%1$s E-Mail-Nachricht</string>\n    <string name=\"file_type_name_font\">%1$s Schrift</string>\n    <string name=\"file_type_name_generic\">%1$s Datei</string>\n    <string name=\"file_type_name_image\">%1$s Bild</string>\n    <string name=\"file_type_name_pdf\">PDF-Dokument</string>\n    <string name=\"file_type_name_presentation\">%1$s Präsentation</string>\n    <string name=\"file_type_name_spreadsheet\">%1$s Tabelle</string>\n    <string name=\"file_type_name_text\">%1$s Dokument</string>\n    <string name=\"file_type_name_text_plain\">Textdatei</string>\n    <string name=\"file_type_name_video\">%1$s Video</string>\n    <string name=\"file_type_name_word\">Word-Dokument</string>\n    <string name=\"file_type_name_powerpoint\">PowerPoint-Präsentation</string>\n    <string name=\"file_type_name_excel\">Excel-Tabelle</string>\n    <string name=\"file_type_name_posix_character_device\">Zeichengerät</string>\n    <string name=\"file_type_name_posix_block_device\">Blockgerät</string>\n    <string name=\"file_type_name_posix_fifo\">Leitung</string>\n    <string name=\"file_type_name_posix_symbolic_link\">Link</string>\n    <string name=\"file_type_name_posix_socket\">Socket</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">Link (fehlerhaft)</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-de/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <string name=\"app_name\">Material Files</string>\n\n    <string name=\"close\">Schließen</string>\n    <string name=\"copy\">Kopieren</string>\n    <string name=\"cut\">Ausschneiden</string>\n    <string name=\"default_\">Standard</string>\n    <string name=\"delete\">Löschen</string>\n    <string name=\"discard\">Verwerfen</string>\n    <string name=\"edit\">Bearbeiten</string>\n    <string name=\"empty\">Leer</string>\n    <string name=\"empty_placeholder\">(Leer)</string>\n    <string name=\"error\">Fehler</string>\n    <string name=\"hide\">Ausblenden</string>\n    <string name=\"install\">Installieren</string>\n    <string name=\"keep_editing\">Weiter bearbeiten</string>\n    <string name=\"loading\">Laden…</string>\n    <string name=\"maybe_later\">Vielleicht später</string>\n    <string name=\"merge\">Zusammenführen</string>\n    <string name=\"none\">Nichts</string>\n    <string name=\"open_navigation_drawer\">Navigationsmenü öffnen</string>\n    <string name=\"paste\">Einfügen</string>\n    <string name=\"refresh\">Aktualisieren</string>\n    <string name=\"reload\">Neu laden</string>\n    <string name=\"remove\">Entfernen</string>\n    <string name=\"rename\">Umbenennen</string>\n    <string name=\"replace\">Ersetzen</string>\n    <string name=\"reset\">Zurücksetzen</string>\n    <string name=\"retry\">Erneut versuchen</string>\n    <string name=\"save\">Speichern</string>\n    <string name=\"search\">Suchen</string>\n    <string name=\"select\">Auswählen</string>\n    <string name=\"select_all\">Alles auswählen</string>\n    <string name=\"share\">Teilen</string>\n    <string name=\"show\">Anzeigen</string>\n    <string name=\"skip\">Überspringen</string>\n    <string name=\"stop\">Stopp</string>\n    <string name=\"system_default\">Systemvorgabe</string>\n    <string name=\"unknown\">Unbekannt</string>\n    <string name=\"view\">Anzeigen</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">Keine passende Anwendung gefunden</string>\n    <string name=\"open_settings\">Einstellungen öffnen</string>\n    <string name=\"shortcut_created\">Verknüpfung erstellt</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"one\">%1$,d Byte</item>\n        <item quantity=\"other\">%1$,d Bytes</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">App benötigt Berechtigungen für den Zugriff auf Dateien. Bitte den Zugriff in den Systemeinstellungen erlauben.</string>\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">App benötigt Berechtigungen für den Zugriff auf Dateien. Bitte klicken Sie im nächsten Dialog auf \\\"Erlauben\\\".</string>\n    <string name=\"storage_permission_permanently_denied_message\">App benötigt Berechtigungen für den Zugriff auf Dateien. Bitte erteilen Sie die \\\"Speicher\\\"-Berechtigung in den Systemeinstellungen.</string>\n    <!-- The allow button is in sentence case now. -->\n    <string name=\"notification_permission_rationale_message\">Die App benötigt für Benachrichtigungen über Dateivorgänge die erforderlichen Berechtigungen. Bitte klicken Sie im kommenden Systemdialog auf „Zulassen“.</string>\n    <string name=\"notification_permission_permanently_denied_message\">Die App benötigt für Benachrichtigungen über Dateivorgänge die erforderlichen Berechtigungen. Bitte erteilen Sie in den Systemeinstellungen die Berechtigung „Benachrichtigung“.</string>\n\n    <string name=\"notification_channel_background_activity_start_name\">Aktionen im Hintergrund</string>\n    <string name=\"notification_channel_background_activity_start_description\">Aktionen ausführen während die App im Hintergrund läuft</string>\n    <string name=\"notification_channel_file_job_name\">Dateioperationen</string>\n    <string name=\"notification_channel_file_job_description\">Anzeigen und Steuern von Dateioperationen</string>\n    <string name=\"notification_channel_ftp_server_name\">FTP-Server</string>\n    <string name=\"notification_channel_ftp_server_description\">Anzeigen und Steuern des FTP-Servers</string>\n\n    <string name=\"file_action_archive_password_title\">Passwort erforderlich</string>\n    <string name=\"file_action_archive_password_message_format\">“%1$s” ist passwortgeschützt.</string>\n    <string name=\"file_action_archive_password_error_empty\">Passwort darf nicht leer sein</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"one\">Bereite Komprimierung für %1$,d Datei vor (%2$s)</item>\n        <item quantity=\"other\">Bereite Komprimierung für %1$,d Dateien vor (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">Komprimiere \\\"%1$s\\\" nach \\\"%2$s\\\"</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"one\">Komprimiere %1$,d Datei nach \\\"%2$s\\\"</item>\n        <item quantity=\"other\">Komprimiere %1$,d Dateien nach \\\"%2$s\\\"</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"one\">Bereite Kopieren von %1$,d Datei vor (%2$s)</item>\n        <item quantity=\"other\">Bereite Kopieren von %1$,d Dateien vor (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">Kopiere \\\"%1$s\\\" nach \\\"%2$s\\\"</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"one\">Kopiere %1$,d Datei nach \\\"%2$s\\\"</item>\n        <item quantity=\"other\">Kopiere %1$,d Dateien nach \\\"%2$s\\\"</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"one\">Bereite Extrahieren für %1$,d Datei vor (%2$s)</item>\n        <item quantity=\"other\">Bereite Extrahieren für %1$,d Dateien vor (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">Extrahiere \\\"%1$s\\\" nach \\\"%2$s\\\"</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"one\">Extrahiere %1$,d Datei nach \\\"%2$s”</item>\n        <item quantity=\"other\">Extrahiere %1$,d Dateien nach \\\"%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"one\">Bereite Verschieben von %1$,d Datei vor (%2$s)</item>\n        <item quantity=\"other\">Bereite Verschieben von %1$,d Dateien vor (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">Verschiebe \\\"%1$s\\\" nach \\\"%2$s\\\"</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"one\">Verschiebe %1$,d Datei nach \\\"%2$s\\\"</item>\n        <item quantity=\"other\">Verschiebe %1$,d Dateien nach \\\"%2$s\\\"</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"one\">Bereite Löschen von %1$,d Datei vor</item>\n        <item quantity=\"other\">Bereite Löschen von %1$,d Dateien vor</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">Lösche \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"one\">Lösche %1$,d Datei</item>\n        <item quantity=\"other\">Lösche %1$,d Dateien</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"one\">Bereite Änderung des Besitzers für %1$,d Datei vor</item>\n        <item quantity=\"other\">Bereite Änderung des Besitzers für %1$,d Dateien vor</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">Ändere Besitzer für \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"one\">Ändere Besitzer für %1$,d Datei</item>\n        <item quantity=\"other\">Ändere Besitzer für %1$,d Dateien</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"one\">Bereite Änderung der Gruppe für %1$,d Datei vor</item>\n        <item quantity=\"other\">Bereite Änderung der Gruppe für %1$,d Dateien vor</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">Ändere Gruppe für \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"one\">Ändere Gruppe für %1$,d Datei</item>\n        <item quantity=\"other\">Ändere Gruppe für %1$,d Dateien</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"one\">Bereite Änderung des Modus für %1$,d Datei vor</item>\n        <item quantity=\"other\">Bereite Änderung des Modus für %1$,d Dateien vor</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">Ändere Modus für \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"one\">Ändere Modus für %1$,d Datei</item>\n        <item quantity=\"other\">Ändere Modus für %1$,d Dateien</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Bereite Änderung des SELinux Kontexts von %1$,d Datei vor</item>\n        <item quantity=\"other\">Bereite Änderung des SELinux Kontexts von %1$,d Dateien vor</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">Ändere SELinux Kontext von \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Ändere SELinux Kontext von %1$,d Datei</item>\n        <item quantity=\"other\">Ändere SELinux Kontext von %1$,d Dateien</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Bereite Wiederherstellung des SELinux Kontexts von %1$,d Datei vor</item>\n        <item quantity=\"other\">Bereite Wiederherstellung des SELinux Kontexts von %1$,d Dateien vor</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">Stelle SELinux Kontext von \\\"%1$s\\\" wieder her</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Stelle SELinux Kontext von %1$,d Datei wieder her</item>\n        <item quantity=\"other\">Stelle SELinux Kontext von %1$,d Dateien wieder her</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">Schreibe \\\"%1$s\\\"</string>\n    <string name=\"file_job_create_error_title\">Fehler beim Erstellen</string>\n    <string name=\"file_job_create_error_message_format\">Es ist ein Fehler beim Erstellen der Datei \\\"%1$s\\\" aufgetreten.\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">Es ist nicht möglich, einen Ordner in sich selbst zu kopieren</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">Es ist nicht möglich, einen Ordner in sich selbst zu extrahieren</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">Es ist nicht möglich, einen Ordner in sich selbst zu verschieben</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">Der Zielordner ist innerhalb des Quellordners</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">Kopieren nicht möglich</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">Extrahieren nicht möglich</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">Verschieben nicht möglich</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">Die Quelldatei würde durch das Ziel überschrieben werden.</string>\n    <string name=\"file_job_replace_title_format\">Datei \\\"%1$s\\\" überschreiben?</string>\n    <string name=\"file_job_replace_message_format\">Eine andere Datei mit demselben Namen existiert bereits in \\\"%1$s\\\".\\nDer Inhalt der Datei wird überschrieben.</string>\n    <string name=\"file_job_replace_target_name\">Ursprungsdatei</string>\n    <string name=\"file_job_replace_source_name\">Ersetzen mit</string>\n    <string name=\"file_job_merge_title_format\">Ordner \\\"%1$s\\\" zusammenführen?</string>\n    <string name=\"file_job_merge_copy_message_format\">Beim Zusammenführen wird nach einer Bestätigung gefragt, bevor Dateien im Ordner ersetzt werden, die im Konflikt mit den zu kopierenden Dateien stehen.</string>\n    <string name=\"file_job_merge_extract_message_format\">Beim Zusammenführen wird nach einer Bestätigung gefragt, bevor Dateien im Ordner ersetzt werden, die mit den zu extrahierenden Dateien im Konflikt stehen.</string>\n    <string name=\"file_job_merge_move_message_format\">Beim Zusammenführen wird nach einer Bestätigung gefragt, bevor Dateien im Ordner ersetzt werden, die im Konflikt mit den zu verschiebenden Dateien stehen.</string>\n    <string name=\"file_job_merge_target_name\">Ursprungsordner</string>\n    <string name=\"file_job_merge_source_name\">Zusammenführen mit</string>\n    <string name=\"file_job_conflict_show_name\">Einen neuen Namen für das Ziel angeben</string>\n    <string name=\"file_job_conflict_name_hint\">Neuer Name</string>\n    <string name=\"file_job_archive_error_title_format\">Fehler beim Komprimieren von \\\"%1$s\\\"</string>\n    <string name=\"file_job_archive_error_message_format\">Es ist ein Fehler beim Komprimieren der Datei nach \\\"%1$s\\\" aufgetreten.\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">Fehler beim Kopieren von \\\"%1$s\\\"</string>\n    <string name=\"file_job_copy_error_message_format\">Es ist ein Fehler beim Kopieren der Datei nach \\\"%1$s\\\" aufgetreten.\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">Fehler beim Extrahieren von \\\"%1$s\\\"</string>\n    <string name=\"file_job_extract_error_message_format\">Es ist ein Fehler beim Extrahieren der Datei nach \\\"%1$s\\\" aufgetreten.\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">Fehler beim Verschieben von \\\"%1$s\\\"</string>\n    <string name=\"file_job_move_error_message_format\">Es ist ein Fehler beim Verschieben der Datei nach \\\"%1$s\\\" aufgetreten.\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">Fehler beim Löschen</string>\n    <string name=\"file_job_delete_error_message_format\">Es ist ein Fehler beim Löschen von \\\"%1$s\\\" aufgetreten.\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">Fehler beim Umbenennen von \\\"%1$s\\\"</string>\n    <string name=\"file_job_rename_error_message_format\">Es ist ein Fehler beim Umbenennen der Datei nach \\\"%1$s\\\" aufgetreten.\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">Fehler beim Ändern des Besitzers von \\\"%1$s\\\"</string>\n    <string name=\"file_job_set_owner_error_message_format\">Es ist ein Fehler beim Ändern des Besitzers nach \\\"%1$s\\\" aufgetreten.\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">Fehler beim Ändern der Gruppe \\\"%1$s\\\"</string>\n    <string name=\"file_job_set_group_error_message_format\">Es ist ein Fehler beim Ändern der Gruppe nach \\\"%1$s\\\" aufgetreten.\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">Fehler beim Ändern des Modus von \\\"%1$s\\\"</string>\n    <string name=\"file_job_set_mode_error_message_format\">Es ist ein Fehler beim Ändern des Modus nach \\\"%1$s\\\" aufgetreten.\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">Fehler beim Ändern des SELinux Kontexts von \\\"%1$s\\\"</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">Es ist ein Fehler beim Ändern des SELinux Kontexts nach \\\"%1$s\\\" aufgetreten.\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">Fehler beim Wiederherstellen des SELinux Kontexts</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">Es ist ein Fehler beim Wiederherstellen des SELinux Kontexts von \\\"%1$s\\\" aufgetreten.\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">Fehler beim Schreiben</string>\n    <string name=\"file_job_write_error_message_format\">Es ist ein Fehler beim Schreiben von \\\"%1$s\\\" aufgetreten.\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">Es ist ein Fehler beim Lesen von Informationen über \\\"%1$s\\\" aufgetreten.</string>\n    <string name=\"file_job_remount_format\">Erneutes Einhängen von \\\"%1$s\\\" mit Schreibrechten</string>\n    <string name=\"file_job_remount_loading_format\">Hänge \\\"%1$s\\\" mit Schreibrechten neu ein…</string>\n    <string name=\"file_job_remount_success_format\">\\\"%1$s\\\" wurde mit Schreibrechten erneut eingehängt</string>\n    <string name=\"file_job_action_all\">Diese Aktion für alle Dateien durchführen</string>\n\n    <string name=\"file_open_apk_message\">Diese Datei ist eine APK-Datei. Möchten Sie sie installieren oder ihren Inhalt ansehen?</string>\n    <string name=\"file_open_as_title\">Öffnen als…</string>\n    <string name=\"file_open_as_title_format\">Öffne \\\"%1$s\\\" als</string>\n    <string name=\"file_open_as_type_text\">Text</string>\n    <string name=\"file_open_as_type_image\">Bild</string>\n    <string name=\"file_open_as_type_audio\">Audio</string>\n    <string name=\"file_open_as_type_video\">Video</string>\n    <string name=\"file_open_as_type_directory\">Ordner</string>\n    <string name=\"file_open_as_type_any\">Andere</string>\n    <string name=\"file_install_apk_from_background_title_format\">\\\"%1$s\\\" ist bereit zum Installieren</string>\n    <string name=\"file_install_apk_from_background_text\">Tippe zum Installieren</string>\n    <string name=\"file_open_from_background_title_format\">\\\"%1$s\\\" ist bereit zum Öffnen</string>\n    <string name=\"file_open_from_background_text\">Tippe zum Öffnen</string>\n    <string name=\"file_name_error_empty\">Dateiname darf nicht leer sein</string>\n    <string name=\"file_name_error_invalid\">Ungültiger Dateiname</string>\n    <string name=\"file_name_error_already_exists\">Eine Datei mit diesem Namen existiert bereits</string>\n    <string name=\"file_replace_message_format\">\\\"%1$s\\\" ersetzten?</string>\n    <string name=\"file_delete_message_file_format\">“%1$s” löschen?</string>\n    <string name=\"file_delete_message_directory_format\">Ordner “%1$s” und dessen Inhalt löschen?</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"one\">%1$,d Datei löschen?</item>\n        <item quantity=\"other\">%1$,d Dateien löschen?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"one\">%1$,d Ordner und dessen Inhalt löschen?</item>\n        <item quantity=\"other\">%1$,d Ordner und deren Inhalte löschen?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"one\">%1$,d Eintrag löschen?</item>\n        <item quantity=\"other\">%1$,d Einträge löschen?</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">Archiv erstellen</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_create_archive_password\">Passwort (optional)</string>\n    <string name=\"file_add_bookmark_success\">Lesezeichen hinzugefügt</string>\n    <string name=\"file_create_file_title\">Neue Datei</string>\n    <string name=\"file_create_directory_title\">Neuer Ordner</string>\n\n    <string name=\"file_list_title\">Dateien</string>\n    <plurals name=\"file_list_title_open_file\">\n        <item quantity=\"one\">Wähle eine Datei aus</item>\n        <item quantity=\"other\">Wähle Dateien aus</item>\n    </plurals>\n    <string name=\"file_list_title_create_file\">Datei speichern</string>\n    <plurals name=\"file_list_title_open_directory\">\n        <item quantity=\"one\">Wähle einen Ordner aus</item>\n        <item quantity=\"other\">Wähle Ordner aus</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"one\">%1$,d Ordner</item>\n        <item quantity=\"other\">%1$,d Ordner</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"one\">%1$,d Datei</item>\n        <item quantity=\"other\">%1$,d Dateien</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">,\\u0020</string>\n    <string name=\"file_list_path_error_empty\">Pfad darf nicht leer sein</string>\n    <string name=\"file_list_path_error_invalid\">Ungültiger Pfad</string>\n    <string name=\"file_list_action_view_sort\">Anzeigen und Sortieren</string>\n    <string name=\"file_list_action_view_list\">Liste</string>\n    <string name=\"file_list_action_view_grid\">Raster</string>\n    <string name=\"file_list_action_sort_by_name\">Name</string>\n    <string name=\"file_list_action_sort_by_type\">Typ</string>\n    <string name=\"file_list_action_sort_by_size\">Größe</string>\n    <string name=\"file_list_action_sort_by_last_modified\">Zuletzt geändert</string>\n    <string name=\"file_list_action_sort_order_ascending\">Aufsteigend</string>\n    <string name=\"file_list_action_sort_directories_first\">Ordner zuerst</string>\n    <string name=\"file_list_action_view_sort_path_specific\">Nur für diesen Ordner</string>\n    <string name=\"file_list_action_new_task\">Neues Fenster</string>\n    <string name=\"file_list_action_navigate_up\">Gehe hoch</string>\n    <string name=\"file_list_action_navigate_to\">Gehe zu</string>\n    <string name=\"file_list_action_show_hidden_files\">Versteckte Dateien anzeigen</string>\n    <string name=\"file_list_action_copy_path\">Pfad kopieren</string>\n    <string name=\"file_list_action_open_in_terminal\">Im Terminal öffnen</string>\n    <string name=\"file_list_action_add_bookmark\">Lesezeichen hinzufügen</string>\n    <string name=\"file_list_action_create_shortcut\">Verknüpfung erstellen</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">In neuem Fenster öffnen</string>\n    <string name=\"file_list_select_title_format\">%1$,d</string>\n    <string name=\"file_list_create_file_name_hint\">Dateiname</string>\n    <string name=\"file_list_open_current_directory_format\">\\\"%1$s\\\" auswählen</string>\n    <string name=\"file_list_paste_move_title_format\">Verschiebe %1$,d</string>\n    <string name=\"file_list_paste_copy_title_format\">Kopiere %1$,d</string>\n    <string name=\"file_list_paste_extract_title_format\">Extrahiere %1$,d</string>\n    <string name=\"file_list_paste_action_extract_here\">Extrahiere hier</string>\n    <string name=\"file_list_empty\">Keine Dateien</string>\n    <string name=\"file_list_action_create_file\">Datei</string>\n    <string name=\"file_list_action_create_directory\">Ordner</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">Öffnen mit</string>\n    <string name=\"file_item_action_extract\">Extrahieren</string>\n    <string name=\"file_item_action_archive\">Komprimieren </string>\n    <string name=\"file_item_action_properties\">Eigenschaften</string>\n\n    <string name=\"file_properties_title_format\">Eigenschaften von \\\"%1$s\\\"</string>\n    <string name=\"file_properties_basic\">Allgemein</string>\n    <string name=\"file_properties_basic_name\">Name</string>\n    <string name=\"file_properties_basic_type\">Typ</string>\n    <string name=\"file_properties_basic_type_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">Link zu %1$s (%2$s)</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">Verknüpfungsziel</string>\n    <string name=\"file_properties_basic_size\">Größe</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_contents\">Inhalt</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"one\">%1$,d Eintrag der Größe %2$s</item>\n        <item quantity=\"other\">%1$,d Einträge, %2$s gesamt</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">Zuletzt geändert</string>\n    <string name=\"file_properties_basic_parent_directory\">Übergeordneter Ordner</string>\n    <string name=\"file_properties_basic_archive_file\">Archiviere Datei</string>\n    <string name=\"file_properties_basic_archive_entry\">Archiviere Eintrag</string>\n    <string name=\"file_properties_basic_free_space\">Freier Speicher</string>\n    <string name=\"file_properties_permission\">Berechtigungen</string>\n    <string name=\"file_properties_permission_owner\">Besitzer</string>\n    <string name=\"file_properties_permission_group\">Gruppe</string>\n    <string name=\"file_properties_permission_principal_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_permission_set_owner_title\">Besitzer ändern</string>\n    <string name=\"file_properties_permission_set_group_title\">Gruppe ändern</string>\n    <string name=\"file_properties_permission_set_principal_filter_hint\">Name oder ID eingeben</string>\n    <string name=\"file_properties_permission_set_principal_system\">System</string>\n    <string name=\"file_properties_permission_mode\">Modus</string>\n    <string name=\"file_properties_permission_mode_format\">%1$s (%2$04o)</string>\n    <string name=\"file_properties_permission_set_mode_title\">Modus ändern</string>\n    <string name=\"file_properties_permission_set_mode_others\">Andere</string>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_file\">\n        <item>Lesen</item>\n        <item>Schreiben</item>\n        <item>Ausführen</item>\n    </string-array>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_directory\">\n        <item>Lesen</item>\n        <item>Schreiben</item>\n        <item>Suchen</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_special\">Besondere</string>\n    <string-array name=\"file_properties_permission_set_mode_special_mode_bits\">\n        <item>UID festlegen</item>\n        <item>GID festlegen</item>\n        <item>Sticky Bit</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_uppercase_x\">\\\"Ausführen\\\" nicht zu den enthaltenen Dateien hinzufügen</string>\n    <string name=\"file_properties_permission_selinux_context\">SELinux Kontext</string>\n    <string name=\"file_properties_permission_set_selinux_context_title\">SELinux Kontext ändern</string>\n    <string name=\"file_properties_permission_set_selinux_context_restore\">Zurücksetzen</string>\n    <string name=\"file_properties_permission_recursive\">Auf enthaltene Dateien anwenden</string>\n    <string name=\"file_properties_image\">Bild</string>\n    <string name=\"file_properties_media_dimensions\">Abmessungen</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">Aufgenommen am</string>\n    <string name=\"file_properties_media_coordinates\">Koordinaten</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f, %2$.3f</string>\n    <string name=\"file_properties_media_address\">Ort</string>\n    <string name=\"file_properties_image_gps_altitude\">Höhe</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f m</string>\n    <string name=\"file_properties_image_equipment\">Kamera</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">Blende</string>\n    <string name=\"file_properties_image_shutter_speed\">Verschlusszeit</string>\n    <string name=\"file_properties_image_focal_length\">Brennweite</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f mm</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">ISO Äquivalent</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">Software</string>\n    <string name=\"file_properties_image_description\">Beschreibung</string>\n    <string name=\"file_properties_image_artist\">Ersteller</string>\n    <string name=\"file_properties_image_copyright\">Copyright</string>\n    <string name=\"file_properties_audio\">Audio</string>\n    <string name=\"file_properties_media_title\">Titel</string>\n    <string name=\"file_properties_audio_artist\">Künstler</string>\n    <string name=\"file_properties_audio_album\">Album</string>\n    <string name=\"file_properties_audio_album_artist\">Albumkünstler</string>\n    <string name=\"file_properties_audio_composer\">Komponist</string>\n    <string name=\"file_properties_audio_disc_number\">Disk</string>\n    <string name=\"file_properties_audio_track_number\">Spur</string>\n    <string name=\"file_properties_audio_year\">Jahr</string>\n    <string name=\"file_properties_audio_genre\">Genre</string>\n    <string name=\"file_properties_media_duration\">Länge</string>\n    <string name=\"file_properties_media_bit_rate\">Bitrate</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d kbps</string>\n    <string name=\"file_properties_audio_sample_rate\">Abtastrate</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Hz</string>\n    <string name=\"file_properties_video\">Video</string>\n    <string name=\"file_properties_apk\">APK</string>\n    <string name=\"file_properties_apk_label\">Name</string>\n    <string name=\"file_properties_apk_package_name\">Paketname</string>\n    <string name=\"file_properties_apk_version\">Version</string>\n    <string name=\"file_properties_apk_version_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_apk_min_sdk_version\">Minimale Systemversion</string>\n    <string name=\"file_properties_apk_target_sdk_version\">Ziel-Systemversion</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s (%2$s, %3$d)</string>\n    <string name=\"file_properties_apk_requested_permissions\">Berechtigungen</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">Keine Berechtigungen angefordert</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"one\">%1$,d Berechtigung angefordert</item>\n        <item quantity=\"other\">%1$,d Berechtigungen angefordert</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">Signaturen</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">Keine gültigen Signaturen</string>\n    <string name=\"file_properties_apk_past_signature_digests\">Alte Signaturen</string>\n    <string name=\"file_properties_checksum\">Prüfsummen</string>\n    <string name=\"file_properties_checksum_compare\">Vergleichen</string>\n    <string name=\"file_properties_checksum_compare_placeholder\">Jede Prüfsumme</string>\n    <string name=\"file_properties_checksum_compare_match_format\">Entspricht %1$sPrüfsumme</string>\n    <string name=\"file_properties_checksum_compare_prefix_match_format\">Entspricht dem Prefix der %1$s Prüfsumme</string>\n    <string name=\"file_properties_checksum_compare_no_match\">Keine passende Prüfsumme gefunden</string>\n    <string name=\"storage_file_system_root_title\">Wurzel</string>\n    <string name=\"storage_list_title\">Speicher</string>\n    <string name=\"storage_list_empty\">Kein Speicher</string>\n    <string name=\"storage_add_storage_title\">Speicher hinzufügen</string>\n    <string name=\"storage_add_storage_document_tree\">Externer Speicher</string>\n    <string name=\"storage_add_storage_ftp_server\">FTP-Server</string>\n    <string name=\"storage_add_storage_sftp_server\">SFTP-Server</string>\n    <string name=\"storage_add_storage_smb_server\">SMB-Server</string>\n    <string name=\"storage_add_storage_webdav_server\">WebDAV-Server</string>\n    <string name=\"storage_edit_device_storage_title\">Gerätespeicher bearbeiten</string>\n    <string name=\"storage_edit_device_storage_name\">Name</string>\n    <string name=\"storage_edit_device_storage_path\">Pfad</string>\n    <string name=\"storage_add_external_storage_shortcut_title\">Shortcut für externen Speicher hinzufügen</string>\n    <string name=\"storage_edit_external_storage_shortcut_title\">Schortcut für externen Speicher bearbeiten</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id\">Speichervolumen</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id_error_empty\">Speichervolumen eingeben</string>\n    <string name=\"storage_edit_external_storage_shortcut_path\">Pfad</string>\n    <string name=\"storage_add_document_tree_title\">Externen Speicher hinzufügen</string>\n    <string name=\"storage_edit_document_tree_title\">Externen Speicher bearbeiten</string>\n    <string name=\"storage_edit_document_tree_name\">Name</string>\n    <string name=\"storage_edit_document_tree_uri\">URI</string>\n    <string name=\"storage_edit_document_tree_path\">Pfad</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">FTP-Server bearbeiten</string>\n    <string name=\"storage_edit_ftp_server_title_add\">FTP-Server hinzufügen</string>\n    <string name=\"storage_edit_ftp_server_host\">Hostname</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">Hostname eingeben</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">Ungültiger Hostname</string>\n    <string name=\"storage_edit_ftp_server_port\">Port</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">Ungültiger Port</string>\n    <string name=\"storage_edit_ftp_server_path\">Pfad</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">Kann leer gelassen werden</string>\n    <string name=\"storage_edit_ftp_server_name\">Name</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">Hostname verwenden</string>\n    <string name=\"storage_edit_ftp_server_protocol\">Protokoll</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">Authentifizierung</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>Passwort</item>\n        <item>Anonym</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">Benutzername</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">Benutzername eingeben</string>\n    <string name=\"storage_edit_ftp_server_password\">Passwort</string>\n    <string name=\"storage_edit_ftp_server_mode\">Modus</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>Aktiv</item>\n        <item>Passiv</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">Kodierung</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">Verbinden und hinzufügen</string>\n    <string name=\"storage_edit_ftp_server_add\">Hinzufügen</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">SFTP-Server bearbeiten</string>\n    <string name=\"storage_edit_sftp_server_title_add\">SFTP-Server hinzufügen</string>\n    <string name=\"storage_edit_sftp_server_host\">Hostname</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">Hostnamen eingeben</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">Ungültiger Hostname</string>\n    <string name=\"storage_edit_sftp_server_port\">Port</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">Ungültiger Port</string>\n    <string name=\"storage_edit_sftp_server_path\">Pfad</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">Kann leer gelassen werden</string>\n    <string name=\"storage_edit_sftp_server_name\">Name</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">Hostname verwenden</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">Authentifizierung</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>Passwort</item>\n        <item>Öffentlicher Schlüssel</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">Benutzername</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">Benutzernamen eingeben</string>\n    <string name=\"storage_edit_sftp_server_password\">Passwort</string>\n    <string name=\"storage_edit_sftp_server_private_key\">Privater Schlüssel</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">Datei öffnen</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">Privaten Schlüssel eingeben</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">Ungültiger privater Schlüssel</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">Passwort des privaten Schlüssels</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">Kann leer gelassen werden</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">Passwort des privaten Schlüssels ungültig</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">Verbinden und hinzufügen</string>\n    <string name=\"storage_edit_sftp_server_add\">Hinzufügen</string>\n    <string name=\"storage_add_lan_smb_server_loading\">Suche nach SMB-Servern…</string>\n    <string name=\"storage_add_lan_smb_server_add\">Manuell hinzufügen</string>\n    <string name=\"storage_edit_smb_server_title_edit\">SMB-Server bearbeiten</string>\n    <string name=\"storage_edit_smb_server_title_add\">SMB-Server hinzufügen</string>\n    <string name=\"storage_edit_smb_server_host\">Hostname</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">Benutzernamen eingeben</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">Ungültiger Hostname</string>\n    <string name=\"storage_edit_smb_server_port\">Port</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">Ungültiger Port</string>\n    <string name=\"storage_edit_smb_server_path\">Pfad</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">Kann leer gelassen werden</string>\n    <string name=\"storage_edit_smb_server_name\">Name</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">Hostname verwenden</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">Authentifizierung</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>Passwort</item>\n        <item>Gast</item>\n        <item>Anonym</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">Benutzername</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">Benutzernamen eingeben</string>\n    <string name=\"storage_edit_smb_server_password\">Passwort</string>\n    <string name=\"storage_edit_smb_server_domain\">Domain</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">Verbinden und hinzufügen</string>\n    <string name=\"storage_edit_smb_server_add\">Hinzufügen</string>\n    <string name=\"storage_edit_webdav_server_title_edit\">WebDAV-Server bearbeiten</string>\n    <string name=\"storage_edit_webdav_server_title_add\">WebDAV-Server hinzufügen</string>\n    <string name=\"storage_edit_webdav_server_host\">Hostname</string>\n    <string name=\"storage_edit_webdav_server_host_error_empty\">Hostname eingeben</string>\n    <string name=\"storage_edit_webdav_server_host_error_invalid\">Ungültiger Hostname</string>\n    <string name=\"storage_edit_webdav_server_port\">Port</string>\n    <string name=\"storage_edit_webdav_server_port_error_invalid\">Ungültiger Port</string>\n    <string name=\"storage_edit_webdav_server_path\">Pfad</string>\n    <string name=\"storage_edit_webdav_server_path_placeholder\">Kann leer gelassen werden</string>\n    <string name=\"storage_edit_webdav_server_name\">Name</string>\n    <string name=\"storage_edit_webdav_server_name_placeholder\">Hostname verwenden</string>\n    <string name=\"storage_edit_webdav_server_protocol\">Protokoll</string>\n    <string name=\"storage_edit_webdav_server_authentication_type\">Authentifizierung</string>\n    <string-array name=\"storage_edit_webdav_server_authentication_type_entries\">\n        <item>Passwort</item>\n        <item>Zugangstoken</item>\n        <item>Nichts</item>\n    </string-array>\n    <string name=\"storage_edit_webdav_server_username\">Benutzername</string>\n    <string name=\"storage_edit_webdav_server_username_error_empty\">Benutzernamen eingeben</string>\n    <string name=\"storage_edit_webdav_server_password\">Passwort</string>\n    <string name=\"storage_edit_webdav_server_access_token\">Zugangstoken</string>\n    <string name=\"storage_edit_webdav_server_access_token_error_empty\">Zugangstoken eingeben</string>\n    <string name=\"storage_edit_webdav_server_connect_and_add\">Verbinden und hinzufügen</string>\n    <string name=\"storage_edit_webdav_server_add\">Hinzufügen</string>\n\n    <string name=\"navigation_storage_subtitle_format\">%1$s frei von %2$s</string>\n    <string name=\"navigation_add_storage\">Speicher hinzufügen…</string>\n    <string name=\"navigation_standard_directory_alarms\">Alarme</string>\n    <string name=\"navigation_standard_directory_dcim\">DCIM</string>\n    <string name=\"navigation_standard_directory_documents\">Dokumente</string>\n    <string name=\"navigation_standard_directory_downloads\">Downloads</string>\n    <string name=\"navigation_standard_directory_movies\">Videos</string>\n    <string name=\"navigation_standard_directory_music\">Musik</string>\n    <string name=\"navigation_standard_directory_notifications\">Benachrichtigungen</string>\n    <string name=\"navigation_standard_directory_pictures\">Bilder</string>\n    <string name=\"navigation_standard_directory_podcasts\">Podcasts</string>\n    <string name=\"navigation_standard_directory_ringtones\">Klingeltöne</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">Lesezeichenordner</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">Name</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">Pfad</string>\n    <string name=\"archive_viewer_title\">Archivbetrachter</string>\n    <string name=\"image_viewer_title\">Bildbetrachter</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"save_as_title\">Speichern als</string>\n    <string name=\"save_as_error\">Fehler beim Speichern der Datei</string>\n    <string name=\"save_as_success_format\">\\\"%1$s\\\" wurde als \\\"%2$s\\\" gespeichert</string>\n    <string name=\"text_editor_title\">Textbearbeitung</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">Gespeichert</string>\n    <string name=\"text_editor_reload_message\">Soll wirklich neu geladen werden? Alle nicht gespeicherten Änderungen gehen verloren.</string>\n    <string name=\"text_editor_encoding\">Kodierung</string>\n    <string name=\"text_editor_close_message\">Sollen alle ungespeicherten Änderungen verworfen werden?</string>\n\n    <string name=\"ftp_server_title\">FTP-Server</string>\n    <string name=\"ftp_server_state_title\">Status</string>\n    <string name=\"ftp_server_state_summary_starting\">Startet…</string>\n    <string name=\"ftp_server_state_summary_running\">Läuft</string>\n    <string name=\"ftp_server_state_summary_stopping\">Beendet…</string>\n    <string name=\"ftp_server_state_summary_stopped\">Nicht gestartet</string>\n    <string name=\"ftp_server_url_title\">URL</string>\n    <string name=\"ftp_server_url_summary_no_local_inet_address\">Die Lokale IP-Adresse ist unbekannt</string>\n    <string name=\"ftp_server_url_menu_copy_url\">Kopiere URL</string>\n    <string name=\"ftp_server_url_menu_copy_password\">Kopiere Passwort</string>\n    <string name=\"ftp_server_add_tile_title\">Zu  den Schnell-Einstellungen hinzufügen</string>\n    <string name=\"ftp_server_add_tile_result_added\">\\\"FTP-Server\\\" wurde zu den Schnelleinstellungen hinzugefügt</string>\n    <string name=\"ftp_server_add_tile_result_already_added\">\\\"FTP-Server\\\" wurde bereits zu den Schnelleinstellungen hinzugefügt</string>\n    <string name=\"ftp_server_add_tile_result_error\">Fehler beim Hinzufügen von \\\"FTP-Server\\\" zu den Schnelleinstellungen</string>\n    <string name=\"ftp_server_configuration_title\">Konfiguration</string>\n    <string name=\"ftp_server_anonymous_login_title\">Anonymer Login</string>\n    <string name=\"ftp_server_username_title\">Benutzername</string>\n    <string name=\"ftp_server_password_title\">Passwort</string>\n    <string name=\"ftp_server_port_title\">Port</string>\n    <string name=\"ftp_server_home_directory_title\">Stammverzeichnis</string>\n    <string name=\"ftp_server_writable_title\">Erlaube Schreiben</string>\n    <string name=\"settings_title\">Einstellungen</string>\n    <string name=\"settings_interface_title\">Benutzeroberfläche</string>\n    <string name=\"settings_locale_title\">Sprache</string>\n    <string name=\"settings_theme_color_title\">Themenfarbe</string>\n    <string name=\"settings_theme_color_summary\">Die am häufigsten vorkommende Farbe in der App</string>\n    <string name=\"settings_material_design_3_title\">Material Design 3</string>\n    <string name=\"settings_night_mode_title\">Nachtmodus</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>Nutze Systemeinstellung</item>\n        <item>Aus</item>\n        <item>An</item>\n        <item>Zeitbasiert</item>\n        <item>Basierend auf dem Batteriesparmodus</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">Schwarzer Nachtmodus</string>\n    <string name=\"settings_file_list_animation_title\">Dateiliste animieren</string>\n    <string name=\"settings_file_name_ellipsize_title\">Langen Dateinamen anzeigen</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>Den Anfang kürzen</item>\n        <item>Die Mitte kürzen</item>\n        <item>Das Ende kürzen</item>\n        <item>Laufschrift</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">Verhalten</string>\n    <string name=\"settings_default_directory_title\">Startordner</string>\n    <string name=\"settings_standard_directories_title\">Standardordner</string>\n    <string name=\"settings_standard_directories_summary_empty\">Keine Standardordner aktiviert</string>\n    <string name=\"settings_bookmark_directory_screenshots\">Screenshots</string>\n    <string name=\"settings_bookmark_directories_title\">Lesezeichenordner</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">Keine Lesezeichenordner</string>\n    <string name=\"settings_root_strategy_title\">Root Zugriffsmodus</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"one\">Es läuft gerade %1$,d Dateioperation. Ändern des Zugriffsmodus könnte unerwartete Fehler hervorrufen. Zugriffsmodus dennoch ändern?</item>\n        <item quantity=\"other\">Es laufen gerade %1$,d Dateioperationen. Ändern des Zugriffsmodus könnte unerwartete Fehler hervorrufen. Zugriffsmodus dennoch ändern?</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>Nur normaler Zugriff</item>\n        <item>Automatisch</item>\n        <item>Nur Root-Zugriff</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">Zeichenkodierung des Dateinamens im Archiv</string>\n    <string name=\"settings_open_apk_default_action_title\">Öffnen einer APK-Datei</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>Installieren</item>\n        <item>Inhalt anzeigen</item>\n        <item>Nachfragen</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_title\">Miniaturansicht für PDF-Dateien anzeigen</string>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">Kann die App auf älteren Versionen von Android instabil machen</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">Remote-Dateien für Miniaturansichten lesen</string>\n\n    <string name=\"about_title\">Über</string>\n    <string name=\"about_version_title\">Version</string>\n    <string name=\"about_github_title\">Auf GitHub anzeigen</string>\n    <string name=\"about_licenses_title\">Lizenzen</string>\n    <string name=\"about_privacy_policy_title\">Datenschutzerklärung</string>\n    <string name=\"about_author_title\">Autor</string>\n    <string name=\"about_author_name_title\">Hai Zhang</string>\n    <string name=\"about_author_github_title\">Auf GitHub folgen</string>\n    <string name=\"about_author_twitter_title\">Auf Twitter folgen</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-el/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">Αρχείο</string>\n    <string name=\"file_type_name_apk\">Πακέτο Android</string>\n    <string name=\"file_type_name_archive\">%1$s αρχειοθήκη</string>\n    <string name=\"file_type_name_audio\">%1$s ήχος</string>\n    <string name=\"file_type_name_calendar\">%1$s ημερολόγιο</string>\n    <string name=\"file_type_name_certificate\">%1$s πιστοποιητικό</string>\n    <string name=\"file_type_name_code\">%1$s έγγραφο</string>\n    <string name=\"file_type_name_contact\">Ηλεκτρονική επαγγελματική κάρτα</string>\n    <string name=\"file_type_name_directory\">Φάκελος</string>\n    <string name=\"file_type_name_document\">%1$s έγγραφο</string>\n    <string name=\"file_type_name_ebook\">%1$s ηλεκτρ-βιβλίο</string>\n    <string name=\"file_type_name_email\">%1$s μήνυμα email</string>\n    <string name=\"file_type_name_font\">%1$s γραμματοσειρά</string>\n    <string name=\"file_type_name_generic\">%1$s αρχείο</string>\n    <string name=\"file_type_name_image\">%1$s εικόνα</string>\n    <string name=\"file_type_name_pdf\">Έγγραφο PDF</string>\n    <string name=\"file_type_name_presentation\">%1$s παρουσίαση</string>\n    <string name=\"file_type_name_spreadsheet\">%1$s υπολογιστικό φύλλο</string>\n    <string name=\"file_type_name_text\">%1$s έγγραφο</string>\n    <string name=\"file_type_name_text_plain\">Έγγραφο απλού κειμένου</string>\n    <string name=\"file_type_name_video\">%1$s βίντεο</string>\n    <string name=\"file_type_name_word\">Έγγραφο Word</string>\n    <string name=\"file_type_name_powerpoint\">Παρουσίαση PowerPoint</string>\n    <string name=\"file_type_name_excel\">Υπολογιστικό φύλλο Excel</string>\n    <string name=\"file_type_name_posix_character_device\">Χαρακτήρας συσκευής</string>\n    <string name=\"file_type_name_posix_block_device\">Αποκλεισμός συσκευής</string>\n    <string name=\"file_type_name_posix_fifo\">Pipe</string>\n    <string name=\"file_type_name_posix_symbolic_link\">Σύνδεσμος</string>\n    <string name=\"file_type_name_posix_socket\">Υποδοχή</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">Σύνδεσμος (κατεστραμμένος)</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-el/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <string name=\"app_name\">Αρχεία Υλικού</string>\n\n    <string name=\"close\">Κλείσιμο</string>\n    <string name=\"copy\">Αντιγραφή</string>\n    <string name=\"cut\">Αποκοπή</string>\n    <string name=\"default_\">Προεπιλογή</string>\n    <string name=\"delete\">Διαγραφή</string>\n    <string name=\"discard\">Απόρριψη</string>\n    <string name=\"edit\">Επεξεργασία</string>\n    <string name=\"empty\">Κενό</string>\n    <string name=\"empty_placeholder\">(Κενό)</string>\n    <string name=\"error\">Σφάλμα</string>\n    <string name=\"hide\">Απόκρυψη</string>\n    <string name=\"install\">Εγκατάσταση</string>\n    <string name=\"keep_editing\">Συνέχεια επεξεργασίας</string>\n    <string name=\"loading\">Φόρτωση…</string>\n    <string name=\"maybe_later\">Ίσως αργότερα</string>\n    <string name=\"merge\">Συγχώνευση</string>\n    <string name=\"none\">Χωρίς</string>\n    <string name=\"open_navigation_drawer\">Άνοιγμα συρταριού πλοήγησης</string>\n    <string name=\"paste\">Επικόλληση</string>\n    <string name=\"refresh\">Ανανέωση</string>\n    <string name=\"reload\">Επαναφόρτωση</string>\n    <string name=\"remove\">Αφαίρεση</string>\n    <string name=\"rename\">Μετονομασία</string>\n    <string name=\"replace\">Αντικατάσταση</string>\n    <string name=\"reset\">Επαναφορά</string>\n    <string name=\"retry\">Επανάληψη</string>\n    <string name=\"save\">Αποθήκευση</string>\n    <string name=\"search\">Αναζήτηση</string>\n    <string name=\"select\">Επιλογή</string>\n    <string name=\"select_all\">Επιλογή όλων</string>\n    <string name=\"share\">Κοινή χρήση</string>\n    <string name=\"show\">Εμφάνιση</string>\n    <string name=\"skip\">Παράλειψη</string>\n    <string name=\"stop\">Να σταματήσει</string>\n    <string name=\"system_default\">Προεπιλογή συστήματος</string>\n    <string name=\"unknown\">Άγνωστο</string>\n    <string name=\"view\">Προβολή</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">Δεν βρέθηκε εφαρμογή για να χειριστεί αυτή την ενέργεια</string>\n    <string name=\"open_settings\">Άνοιγμα ρυθμίσεων</string>\n    <string name=\"shortcut_created\">Δημιουργήθηκε συντόμευση</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"one\">%1$,d byte</item>\n        <item quantity=\"other\">%1$,d byte</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">Η εφαρμογή χρειάζεται πρόσβαση για τη διαχείριση όλων των αρχείων. Παρακαλούμε επιτρέψτε την πρόσβαση στην ρύθμιση συστήματος που ακολουθεί.</string>\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">Η εφαρμογή χρειάζεται άδεια πρόσβασης στα αρχεία. Παρακαλούμε πατήστε “Αποδοχή” στον διάλογο συστήματος που ακολουθεί.</string>\n    <string name=\"storage_permission_permanently_denied_message\">Η εφαρμογή χρειάζεται άδεια πρόσβασης στα αρχεία. Παρακαλούμε παραχωρήστε την άδεια “Αποθηκευτικός Χώρος” στις ρυθμίσεις συστήματος.</string>\n    <!-- The allow button is in sentence case now. -->\n    <string name=\"notification_permission_rationale_message\">Η εφαρμογή χρειάζεται άδεια εμφάνισης ειδοποιήσεων για λειτουργίες αρχείων. Παρακαλούμε πατήστε “Αποδοχή” στον διάλογο συστήματος που ακολουθεί.</string>\n    <string name=\"notification_permission_permanently_denied_message\">Η εφαρμογή χρειάζεται άδεια εμφάνισης ειδοποιήσεων για λειτουργίες αρχείων. Παρακαλούμε παραχωρήστε την άδεια “Ειδοποιήσεις” στις ρυθμίσεις συστήματος.</string>\n\n    <string name=\"notification_channel_background_activity_start_name\">Ενέργειες στο παρασκήνιο</string>\n    <string name=\"notification_channel_background_activity_start_description\">Να προβαίνει σε ενέργειες ενώ η εφαρμογή βρίσκεται στο παρασκήνιο</string>\n    <string name=\"notification_channel_file_job_name\">Λειτουργίες αρχείων</string>\n    <string name=\"notification_channel_file_job_description\">Προβολή και έλεγχος λειτουργιών αρχείων</string>\n    <string name=\"notification_channel_ftp_server_name\">Διακομιστής FTP</string>\n    <string name=\"notification_channel_ftp_server_description\">Προβολή και έλεγχος του διακομιστή FTP</string>\n\n    <string name=\"file_action_archive_password_title\">Απαιτείται κωδικός</string>\n    <string name=\"file_action_archive_password_message_format\">Το “%1$s” προστατεύεται από κωδικό.</string>\n    <string name=\"file_action_archive_password_error_empty\">Ο κωδικός δεν μπορεί να είναι άδειος</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"one\">Προετοιμασία για συμπίεση %1$,d αρχείο (%2$s)</item>\n        <item quantity=\"other\">Προετοιμασία για συμπίεση %1$,d αρχείων (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">Συμπίεση “%1$s” σε “%2$s”</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"one\">Συμπίεση %1$,d αρχείο σε “%2$s”</item>\n        <item quantity=\"other\">Συμπίεση %1$,d αρχείων σε “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"one\">Προετοιμασία αντιγραφής %1$,d αρχείο (%2$s)</item>\n        <item quantity=\"other\">Προετοιμασία αντιγραφής %1$,d αρχείων (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">Αντιγραφή “%1$s” σε “%2$s”</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"one\">Αντιγραφή %1$,d αρχείο σε “%2$s”</item>\n        <item quantity=\"other\">Αντιγραφή %1$,d αρχείων σε “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"one\">Προετοιμασία για εξαγωγή %1$,d αρχείο (%2$s)</item>\n        <item quantity=\"other\">Προετοιμασία για εξαγωγή %1$,d αρχείων (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">Εξαγωγή “%1$s” σε “%2$s”</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"one\">Εξαγωγή %1$,d αρχείο σε “%2$s”</item>\n        <item quantity=\"other\">Εξαγωγή %1$,d αρχείων σε “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"one\">Προετοιμασία για μετακίνηση %1$,d αρχείο (%2$s)</item>\n        <item quantity=\"other\">Προετοιμασία για μετακίνηση %1$,d αρχείων (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">Μετακίνηση “%1$s” σε “%2$s”</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"one\">Μετακίνηση %1$,d αρχείο σε “%2$s”</item>\n        <item quantity=\"other\">Μετακίνηση %1$,d αρχείων σε “%2$s”</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"one\">Προετοιμασία για διαγραφή %1$,d αρχείο</item>\n        <item quantity=\"other\">Προετοιμασία για διαγραφή %1$,d αρχείων</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">Διαγραφή “%1$s”</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"one\">Διαγραφή %1$,d αρχείο</item>\n        <item quantity=\"other\">Διαγραφή %1$,d αρχείων</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"one\">Προετοιμασία αλλαγής κατόχου για %1$,d αρχείο</item>\n        <item quantity=\"other\">Προετοιμασία αλλαγής κατόχου για %1$,d αρχεία</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">Αλλαγή κατόχου για “%1$s”</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"one\">Αλλαγή κατόχου για %1$,d αρχείο</item>\n        <item quantity=\"other\">Αλλαγή κατόχου για %1$,d αρχεία</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"one\">Προετοιμασία αλλαγής ομάδας για %1$,d αρχείο</item>\n        <item quantity=\"other\">Προετοιμασία αλλαγής ομάδας για %1$,d αρχεία</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">Αλλαγή ομάδας για “%1$s”</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"one\">Αλλαγή ομάδας για %1$,d αρχείο</item>\n        <item quantity=\"other\">Αλλαγή ομάδας για %1$,d αρχεία</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"one\">Προετοιμασία αλλαγής λειτουργίας για %1$,d αρχείο</item>\n        <item quantity=\"other\">Προετοιμασία αλλαγής λειτουργίας για %1$,d αρχεία</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">Αλλαγή λειτουργίας για “%1$s”</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"one\">Αλλαγή λειτουργίας για %1$,d αρχείο</item>\n        <item quantity=\"other\">Αλλαγή λειτουργίας για %1$,d αρχεία</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Προετοιμασία αλλαγής SELinux context για %1$,d αρχείο</item>\n        <item quantity=\"other\">Προετοιμασία αλλαγής SELinux context για %1$,d αρχεία</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">Αλλαγή SELinux context για “%1$s”</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Αλλαγή SELinux context για %1$,d αρχείο</item>\n        <item quantity=\"other\">Αλλαγή SELinux context για %1$,d αρχεία</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Προετοιμασία επαναφοράς SELinux context για %1$,d αρχείο</item>\n        <item quantity=\"other\">Προετοιμασία επαναφοράς SELinux context για %1$,d αρχεία</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">Επαναφορά SELinux context για “%1$s”</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Επαναφορά SELinux context για %1$,d αρχείο</item>\n        <item quantity=\"other\">Επαναφορά SELinux context για %1$,d αρχεία</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">Εγγραφή “%1$s”</string>\n    <string name=\"file_job_create_error_title\">Σφάλμα κατά τη δημιουργία</string>\n    <string name=\"file_job_create_error_message_format\">Υπήρξε σφάλμα δημιουργίας “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">Αδύνατη η αντιγραφή ενός φακέλου στον εαυτό του</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">Αδύνατη η εξαγωγή φακέλου στον εαυτό του</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">Αδύνατη η μετακίνηση φακέλου στον εαυτό του</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">Ο φάκελος προορισμού βρίσκεται μέσα στο φάκελο προέλευσης.</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">Αδύνατη η αντιγραφή ενός αρχείου στον εαυτό του</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">Αδύνατη η εξαγωγή ενός αρχείου στον εαυτό του</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">Αδύνατη η μετακίνηση ενός αρχείου στον εαυτό του</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">Το αρχείο προέλευσης θα αντικαθιστόταν από τον προορισμό.</string>\n    <string name=\"file_job_replace_title_format\">Αντικατάσταση αρχείου “%1$s”;</string>\n    <string name=\"file_job_replace_message_format\">Υπάρχει ήδη ένα άλλο αρχείο με το ίδιο όνομα στο “%1$s”.\\nΗ αντικατάστασή του θα αντικαταστήσει το περιεχόμενό του.</string>\n    <string name=\"file_job_replace_target_name\">Αρχικό αρχείο</string>\n    <string name=\"file_job_replace_source_name\">Αντικατάσταση με</string>\n    <string name=\"file_job_merge_title_format\">Συγχώνευση φακέλου “%1$s”;</string>\n    <string name=\"file_job_merge_copy_message_format\">Η συγχώνευση θα ζητήσει επιβεβαίωση πριν από την αντικατάσταση τυχόν αρχείων στο φάκελο που έρχονται σε σύγκρουση με τα αρχεία που αντιγράφονται.</string>\n    <string name=\"file_job_merge_extract_message_format\">Η συγχώνευση θα ζητήσει επιβεβαίωση πριν από την αντικατάσταση τυχόν αρχείων στο φάκελο που έρχονται σε σύγκρουση με τα αρχεία που εξάγονται.</string>\n    <string name=\"file_job_merge_move_message_format\">Η συγχώνευση θα ζητήσει επιβεβαίωση πριν αντικαταστήσει τυχόν αρχεία στο φάκελο που έρχονται σε σύγκρουση με τα αρχεία που μετακινούνται.</string>\n    <string name=\"file_job_merge_target_name\">Αρχικός φάκελος</string>\n    <string name=\"file_job_merge_source_name\">Συγχώνευση με</string>\n    <string name=\"file_job_conflict_show_name\">Επιλέξτε ένα νέο όνομα για τον προορισμό</string>\n    <string name=\"file_job_conflict_name_hint\">Νέο όνομα</string>\n    <string name=\"file_job_archive_error_title_format\">Σφάλμα κατά τη συμπίεση “%1$s”</string>\n    <string name=\"file_job_archive_error_message_format\">Υπήρξε σφάλμα συμπίεσης του αρχείου σε “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">Σφάλμα κατά την αντιγραφή “%1$s”</string>\n    <string name=\"file_job_copy_error_message_format\">Υπήρξε σφάλμα κατά την αντιγραφή του αρχείου στο “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">Σφάλμα κατά την εξαγωγή “%1$s”</string>\n    <string name=\"file_job_extract_error_message_format\">Υπήρξε σφάλμα κατά την εξαγωγή του αρχείου στο “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">Σφάλμα κατά τη μετακίνηση “%1$s”</string>\n    <string name=\"file_job_move_error_message_format\">Υπήρξε σφάλμα κατά τη μετακίνηση του αρχείου στο “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">Σφάλμα κατά τη διαγραφή</string>\n    <string name=\"file_job_delete_error_message_format\">Υπήρξε σφάλμα κατά τη διαγραφή “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">Σφάλμα κατά τη μετονομασία “%1$s”</string>\n    <string name=\"file_job_rename_error_message_format\">Υπήρξε σφάλμα κατά τη μετονομασία του αρχείου σε “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">Σφάλμα κατά την αλλαγή κατόχου για “%1$s”</string>\n    <string name=\"file_job_set_owner_error_message_format\">Υπήρξε ένα σφάλμα αλλαγής του κατόχου σε “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">Σφάλμα κατά την αλλαγή ομάδας για “%1$s”</string>\n    <string name=\"file_job_set_group_error_message_format\">Υπήρξε σφάλμα στην αλλαγή της ομάδας σε “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">Σφάλμα κατά την αλλαγή λειτουργίας για “%1$s”</string>\n    <string name=\"file_job_set_mode_error_message_format\">Υπήρξε σφάλμα κατά την αλλαγή του τρόπου λειτουργίας σε “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">Σφάλμα κατά την αλλαγή SELinux context για “%1$s”</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">Υπήρξε σφάλμα κατά την αλλαγή του SELinux context σε “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">Σφάλμα κατά την επαναφορά SELinux context</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">Υπήρξε σφάλμα κατά την επαναφορά του SELinux context για “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">Σφάλμα κατά την εγγραφή</string>\n    <string name=\"file_job_write_error_message_format\">Υπήρξε σφάλμα κατά την εγγραφή “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">Υπήρξε σφάλμα στη λήψη πληροφοριών για “%1$s”.</string>\n    <string name=\"file_job_remount_format\">Επαναπροσάρτηση “%1$s” ως read-write</string>\n    <string name=\"file_job_remount_loading_format\">Γίνεται επαναπροσάρτηση “%1$s” ως read-write…</string>\n    <string name=\"file_job_remount_success_format\">“%1$s” έχει επαναπροσαρτηθεί ως read-write</string>\n    <string name=\"file_job_action_all\">Εφαρμογή αυτής της ενέργειας σε όλα τα αρχεία</string>\n\n    <string name=\"file_open_apk_message\">Αυτό το αρχείο είναι ένα πακέτο Android. Θέλετε να το εγκαταστήσετε ή να προβάλετε τα περιεχόμενά του;</string>\n    <string name=\"file_open_as_title\">Άνοιγμα ως…</string>\n    <string name=\"file_open_as_title_format\">Άνοιγμα “%1$s” ως</string>\n    <string name=\"file_open_as_type_text\">Κείμενο</string>\n    <string name=\"file_open_as_type_image\">Εικόνα</string>\n    <string name=\"file_open_as_type_audio\">Ήχος</string>\n    <string name=\"file_open_as_type_video\">Βίντεο</string>\n    <string name=\"file_open_as_type_directory\">Φάκελος</string>\n    <string name=\"file_open_as_type_any\">Άλλο</string>\n    <string name=\"file_install_apk_from_background_title_format\">“%1$s” είναι έτοιμο για εγκατάσταση</string>\n    <string name=\"file_install_apk_from_background_text\">Πατήστε για εγκατάσταση</string>\n    <string name=\"file_open_from_background_title_format\">“%1$s” είναι έτοιμο για άνοιγμα</string>\n    <string name=\"file_open_from_background_text\">Πατήστε για άνοιγμα</string>\n    <string name=\"file_name_error_empty\">Το όνομα αρχείου δεν μπορεί να είναι κενό</string>\n    <string name=\"file_name_error_invalid\">Άκυρο όνομα αρχείου</string>\n    <string name=\"file_name_error_already_exists\">Υπάρχει ήδη ένα αρχείο με αυτό το όνομα</string>\n    <string name=\"file_replace_message_format\">Να αντικατασταθεί “%1$s”;</string>\n    <string name=\"file_delete_message_file_format\">Διαγραφή “%1$s”?</string>\n    <string name=\"file_delete_message_directory_format\">Διαγραφή φακέλου “%1$s” και των περιεχομένων του;</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"one\">Διαγραφή %1$,d αρχείο?</item>\n        <item quantity=\"other\">Διαγραφή %1$,d αρχεία?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"one\">Διαγραφή %1$,d φακέλου και του περιεχομένου του;</item>\n        <item quantity=\"other\">Διαγραφή %1$,d φακέλων και των περιεχομένων τους;</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"one\">Διαγραφή %1$,d στοιχείου;</item>\n        <item quantity=\"other\">Διαγραφή %1$,d στοιχείων;</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">Δημιουργία αρχειοθήκης</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_create_archive_password\">Κωδικός (προαιρετικά)</string>\n    <string name=\"file_add_bookmark_success\">Προστέθηκε σελιδοδείκτης</string>\n    <string name=\"file_create_file_title\">Νέο αρχείο</string>\n    <string name=\"file_create_directory_title\">Νέος φάκελος</string>\n\n    <string name=\"file_list_title\">Αρχεία</string>\n    <plurals name=\"file_list_title_open_file\">\n        <item quantity=\"one\">Επιλέξτε ένα αρχείο</item>\n        <item quantity=\"other\">Επιλέξτε αρχεία</item>\n    </plurals>\n    <string name=\"file_list_title_create_file\">Αποθήκευση αρχείου</string>\n    <plurals name=\"file_list_title_open_directory\">\n        <item quantity=\"one\">Επιλέξτε έναν φάκελο</item>\n        <item quantity=\"other\">Επιλέξτε φακέλους</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"one\">%1$,d φάκελος</item>\n        <item quantity=\"other\">%1$,d φάκελοι</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"one\">%1$,d αρχείο</item>\n        <item quantity=\"other\">%1$,d αρχεία</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">,\\u0020</string>\n    <string name=\"file_list_path_error_empty\">Η διαδρομή δεν μπορεί να είναι κενή</string>\n    <string name=\"file_list_path_error_invalid\">Άκυρη διαδρομή</string>\n    <string name=\"file_list_action_view_sort\">Προβολή και ταξινόμηση</string>\n    <string name=\"file_list_action_view_list\">Λίστα</string>\n    <string name=\"file_list_action_view_grid\">Πλέγμα</string>\n    <string name=\"file_list_action_sort_by_name\">Όνομα</string>\n    <string name=\"file_list_action_sort_by_type\">Τύπος</string>\n    <string name=\"file_list_action_sort_by_size\">Μέγεθος</string>\n    <string name=\"file_list_action_sort_by_last_modified\">Τελευταία τροποποίηση</string>\n    <string name=\"file_list_action_sort_order_ascending\">Αύξουσα</string>\n    <string name=\"file_list_action_sort_directories_first\">Πρώτα οι φάκελοι</string>\n    <string name=\"file_list_action_view_sort_path_specific\">Μόνο για αυτό τον φάκελο</string>\n    <string name=\"file_list_action_new_task\">Νέο παράθυρο</string>\n    <string name=\"file_list_action_navigate_up\">Μετάβαση επάνω</string>\n    <string name=\"file_list_action_navigate_to\">Μετάβαση σε</string>\n    <string name=\"file_list_action_show_hidden_files\">Εμφάνιση κρυφών αρχείων</string>\n    <string name=\"file_list_action_copy_path\">Αντιγραφή διαδρομής</string>\n    <string name=\"file_list_action_open_in_terminal\">Άνοιγμα στο τερματικό</string>\n    <string name=\"file_list_action_add_bookmark\">Προσθήκη σελιδοδείκτη</string>\n    <string name=\"file_list_action_create_shortcut\">Δημιουργία συντόμευσης</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">Άνοιγμα σε νέο παράθυρο</string>\n    <string name=\"file_list_select_title_format\">%1$,d</string>\n    <string name=\"file_list_create_file_name_hint\">Όνομα αρχείου</string>\n    <string name=\"file_list_open_current_directory_format\">Επιλογή “%1$s”</string>\n    <string name=\"file_list_paste_move_title_format\">Μετακίνηση %1$,d</string>\n    <string name=\"file_list_paste_copy_title_format\">Αντιγραφή %1$,d</string>\n    <string name=\"file_list_paste_extract_title_format\">Εξαγωγή %1$,d</string>\n    <string name=\"file_list_paste_action_extract_here\">Εξαγωγή εδώ</string>\n    <string name=\"file_list_empty\">Χωρίς αρχεία</string>\n    <string name=\"file_list_action_create_file\">Αρχείο</string>\n    <string name=\"file_list_action_create_directory\">Φάκελος</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">Άνοιγμα με</string>\n    <string name=\"file_item_action_extract\">Εξαγωγή</string>\n    <string name=\"file_item_action_archive\">Συμπίεση</string>\n    <string name=\"file_item_action_properties\">Ιδιότητες</string>\n\n    <string name=\"file_properties_title_format\">Ιδιότητες “%1$s”</string>\n    <string name=\"file_properties_basic\">Βασικές</string>\n    <string name=\"file_properties_basic_name\">Όνομα</string>\n    <string name=\"file_properties_basic_type\">Τύπος</string>\n    <string name=\"file_properties_basic_type_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">Σύνδεση με %1$s (%2$s)</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">Προορισμός Σύνδεσης</string>\n    <string name=\"file_properties_basic_size\">Μέγεθος</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_contents\">Περιεχόμενα</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"one\">%1$,d στοιχείο, με μέγεθος %2$s</item>\n        <item quantity=\"other\">%1$,d στοιχεία, συνολικά %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">Τελευταία Τροποποίηση</string>\n    <string name=\"file_properties_basic_parent_directory\">Γονικός Φάκελος</string>\n    <string name=\"file_properties_basic_archive_file\">Αρχειοθέτηση Αρχείου</string>\n    <string name=\"file_properties_basic_archive_entry\">Καταχώρηση Αρχειοθήκης</string>\n    <string name=\"file_properties_basic_free_space\">Ελεύθερος Χώρος</string>\n    <string name=\"file_properties_permission\">Δικαιώματα</string>\n    <string name=\"file_properties_permission_owner\">Κάτοχος</string>\n    <string name=\"file_properties_permission_group\">Ομάδα</string>\n    <string name=\"file_properties_permission_principal_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_permission_set_owner_title\">Αλλαγή κατόχου</string>\n    <string name=\"file_properties_permission_set_group_title\">Αλλαγή ομάδας</string>\n    <string name=\"file_properties_permission_set_principal_filter_hint\">Εισάγετε ένα όνομα ή αναγνωριστικό</string>\n    <string name=\"file_properties_permission_set_principal_system\">Σύστημα</string>\n    <string name=\"file_properties_permission_mode\">Λειτουργία</string>\n    <string name=\"file_properties_permission_mode_format\">%1$s (%2$04o)</string>\n    <string name=\"file_properties_permission_set_mode_title\">Αλλαγή λειτουργίας</string>\n    <string name=\"file_properties_permission_set_mode_others\">Άλλα</string>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_file\">\n        <item>Ανάγνωση</item>\n        <item>Εγγραφή</item>\n        <item>Εκτέλεση</item>\n    </string-array>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_directory\">\n        <item>Ανάγνωση</item>\n        <item>Εγγραφή</item>\n        <item>Αναζήτηση</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_special\">Ειδικά</string>\n    <string-array name=\"file_properties_permission_set_mode_special_mode_bits\">\n        <item>Ορισμός UID</item>\n        <item>Ορισμός GID</item>\n        <item>Sticky bit</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_uppercase_x\">Χωρίς προσθήκη “Εκτέλεση“ για τα εσώκλειστα αρχεία</string>\n    <string name=\"file_properties_permission_selinux_context\">SELinux Context</string>\n    <string name=\"file_properties_permission_set_selinux_context_title\">Αλλαγή SELinux context</string>\n    <string name=\"file_properties_permission_set_selinux_context_restore\">Επαναφορά</string>\n    <string name=\"file_properties_permission_recursive\">Εφαρμογή στα εσώκλειστα αρχεία</string>\n    <string name=\"file_properties_image\">Εικόνα</string>\n    <string name=\"file_properties_media_dimensions\">Διαστάσεις</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">Λήφθηκε στις</string>\n    <string name=\"file_properties_media_coordinates\">Συντεταγμένες</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f, %2$.3f</string>\n    <string name=\"file_properties_media_address\">Τοποθεσία</string>\n    <string name=\"file_properties_image_gps_altitude\">Υψόμετρο</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f m</string>\n    <string name=\"file_properties_image_equipment\">Κάμερα</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">Διάφραγμα</string>\n    <string name=\"file_properties_image_shutter_speed\">Ταχύτητα κλείστρου</string>\n    <string name=\"file_properties_image_focal_length\">Εστιακό μήκος</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f mm</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">ISO ισοδύναμο</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">Λογισμικό</string>\n    <string name=\"file_properties_image_description\">Περιγραφή</string>\n    <string name=\"file_properties_image_artist\">Δημιουργός</string>\n    <string name=\"file_properties_image_copyright\">Πνευματικά δικαιώματα</string>\n    <string name=\"file_properties_audio\">Ήχος</string>\n    <string name=\"file_properties_media_title\">Τίτλος</string>\n    <string name=\"file_properties_audio_artist\">Καλλιτέχνης</string>\n    <string name=\"file_properties_audio_album\">Άλμπουμ</string>\n    <string name=\"file_properties_audio_album_artist\">Καλλιτέχνης άλμπουμ</string>\n    <string name=\"file_properties_audio_composer\">Συνθέτης</string>\n    <string name=\"file_properties_audio_disc_number\">Δίσκος</string>\n    <string name=\"file_properties_audio_track_number\">Κομμάτι</string>\n    <string name=\"file_properties_audio_year\">Έτος</string>\n    <string name=\"file_properties_audio_genre\">Είδος</string>\n    <string name=\"file_properties_media_duration\">Διάρκεια</string>\n    <string name=\"file_properties_media_bit_rate\">Ρυθμός μετάδοσης</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d kbps</string>\n    <string name=\"file_properties_audio_sample_rate\">Ρυθμός δειγματοληψίας</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Hz</string>\n    <string name=\"file_properties_video\">Βίντεο</string>\n    <string name=\"file_properties_apk\">APK</string>\n    <string name=\"file_properties_apk_label\">Όνομα</string>\n    <string name=\"file_properties_apk_package_name\">Όνομα πακέτου</string>\n    <string name=\"file_properties_apk_version\">Έκδοση</string>\n    <string name=\"file_properties_apk_version_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_apk_min_sdk_version\">Ελάχιστη έκδοση συστήματος</string>\n    <string name=\"file_properties_apk_target_sdk_version\">Στόχος έκδοσης συστήματος</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s (%2$s, %3$d)</string>\n    <string name=\"file_properties_apk_requested_permissions\">Δικαιώματα</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">Δεν ζητήθηκαν δικαιώματα</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"one\">%1$,d απαιτούμενο δικαίωμα</item>\n        <item quantity=\"other\">%1$,d δικαιώματα ζητήθηκαν</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">Υπογραφές</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">Χωρίς έγκυρες υπογραφές</string>\n    <string name=\"file_properties_apk_past_signature_digests\">Παλιές υπογραφές</string>\n    <string name=\"file_properties_checksum\">Αθροίσματα ελέγχου</string>\n    <string name=\"file_properties_checksum_compare\">Σύγκριση</string>\n    <string name=\"file_properties_checksum_compare_placeholder\">Οποιοδήποτε άθροισμα ελέγχου</string>\n    <string name=\"file_properties_checksum_compare_match_format\">Ταιρίαζει με το άθροισμα ελέγχου %1$s</string>\n    <string name=\"file_properties_checksum_compare_prefix_match_format\">Ταιριάζει με το πρόθεμα αθροίσματος ελέγχου %1$s</string>\n    <string name=\"file_properties_checksum_compare_no_match\">Δεν βρέθηκε άθροισμα ελέγχου που ταιριάζει</string>\n    <string name=\"storage_file_system_root_title\">Ρίζα</string>\n    <string name=\"storage_list_title\">Χώρος αποθήκευσης</string>\n    <string name=\"storage_list_empty\">Χωρίς χώρο αποθήκευσης</string>\n    <string name=\"storage_add_storage_title\">Προσθήκη χώρου αποθήκευσης</string>\n    <string name=\"storage_add_storage_document_tree\">Εξωτερικός χώρος αποθήκευσης</string>\n    <string name=\"storage_add_storage_ftp_server\">Διακομιστής FTP</string>\n    <string name=\"storage_add_storage_sftp_server\">Διακομιστής SFTP</string>\n    <string name=\"storage_add_storage_smb_server\">Διακομιστής SMB</string>\n    <string name=\"storage_add_storage_webdav_server\">Διακομιστής WebDAV</string>\n    <string name=\"storage_edit_device_storage_title\">Επεξεργασία χώρου αποθήκευσης συσκευής</string>\n    <string name=\"storage_edit_device_storage_name\">Όνομα</string>\n    <string name=\"storage_edit_device_storage_path\">Διαδρομή</string>\n    <string name=\"storage_add_external_storage_shortcut_title\">Προσθήκη συντόμευσης εξωτερικού χώρου αποθήκευσης</string>\n    <string name=\"storage_edit_external_storage_shortcut_title\">Επεξεργασία συντόμευσης εξωτερικού χώρου αποθήκευσης</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id\">Τόμος χώρου αποθήκευσης</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id_error_empty\">Εισάγετε έναν τόμο χώρου αποθήκευσης</string>\n    <string name=\"storage_edit_external_storage_shortcut_path\">Διαδρομή</string>\n    <string name=\"storage_add_document_tree_title\">Προσθήκη εξωτερικού χώρου αποθήκευσης</string>\n    <string name=\"storage_edit_document_tree_title\">Επεξεργασία εξωτερικού χώρου αποθήκευσης</string>\n    <string name=\"storage_edit_document_tree_name\">Όνομα</string>\n    <string name=\"storage_edit_document_tree_uri\">URI</string>\n    <string name=\"storage_edit_document_tree_path\">Διαδρομή</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">Επεξεργασία διακομιστή FTP</string>\n    <string name=\"storage_edit_ftp_server_title_add\">Προσθήκη διακομιστή FTP</string>\n    <string name=\"storage_edit_ftp_server_host\">Όνομα υπολογιστή</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">Εισάγετε ένα όνομα υπολογιστή</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">Μη έγκυρο όνομα υπολογιστή</string>\n    <string name=\"storage_edit_ftp_server_port\">Θύρα</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">Μη έγκυρη θύρα</string>\n    <string name=\"storage_edit_ftp_server_path\">Διαδρομή</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">Μπορεί να μείνει κενό</string>\n    <string name=\"storage_edit_ftp_server_name\">Όνομα</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">Χρήση ονόματος υπολογιστή</string>\n    <string name=\"storage_edit_ftp_server_protocol\">Πρωτόκολλο</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">Αυθεντικοποίηση</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>Κωδικός</item>\n        <item>Ανώνυμα</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">Όνομα χρήστη</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">Εισάγετε ένα όνομα χρήστη</string>\n    <string name=\"storage_edit_ftp_server_password\">Κωδικός</string>\n    <string name=\"storage_edit_ftp_server_mode\">Λειτουργία</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>Ενεργός</item>\n        <item>Παθητικός</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">Κωδικοποίηση</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">Σύνδεση και προσθήκη</string>\n    <string name=\"storage_edit_ftp_server_add\">Προσθήκη</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">Επεξεργασία διακομιστή SFTP</string>\n    <string name=\"storage_edit_sftp_server_title_add\">Προσθήκη διακομιστή SFTP</string>\n    <string name=\"storage_edit_sftp_server_host\">Όνομα υπολογιστή</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">Εισάγετε ένα όνομα υπολογιστή</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">Μη έγκυρο όνομα υπολογιστή</string>\n    <string name=\"storage_edit_sftp_server_port\">Θύρα</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">Μη έγκυρη θύρα</string>\n    <string name=\"storage_edit_sftp_server_path\">Διαδρομή</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">Μπορεί να μείνει κενό</string>\n    <string name=\"storage_edit_sftp_server_name\">Όνομα</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">Χρήση ονόματος υπολογιστή</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">Αυθεντικοποίηση</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>Κωδικός</item>\n        <item>Δημόσιο κλειδί</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">Όνομα χρήστη</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">Εισάγετε ένα όνομα χρήστη</string>\n    <string name=\"storage_edit_sftp_server_password\">Κωδικός</string>\n    <string name=\"storage_edit_sftp_server_private_key\">Ιδιωτικό κλειδί</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">Άνοιγμα αρχείου</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">Εισάγετε ένα ιδιωτικό κλειδί</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">Μη έγκυρο ιδιωτικό κλειδί</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">Κωδικός ιδιωτικού κλειδιού</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">Μπορεί να μείνει κενό</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">Μη έγκυρος κωδικός ιδιωτικού κλειδιού</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">Σύνδεση και προσθήκη</string>\n    <string name=\"storage_edit_sftp_server_add\">Προσθήκη</string>\n    <string name=\"storage_add_lan_smb_server_loading\">Αναζήτηση για διακομιστές SMB…</string>\n    <string name=\"storage_add_lan_smb_server_add\">Χειροκίνητη προσθήκη</string>\n    <string name=\"storage_edit_smb_server_title_edit\">Επεξεργασία διακομιστή SMB</string>\n    <string name=\"storage_edit_smb_server_title_add\">Προσθήκη διακομιστή SMB</string>\n    <string name=\"storage_edit_smb_server_host\">Όνομα υπολογιστή</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">Εισάγετε ένα όνομα υπολογιστή</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">Μη έγκυρο όνομα υπολογιστή</string>\n    <string name=\"storage_edit_smb_server_port\">Θύρα</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">Μη έγκυρη θύρα</string>\n    <string name=\"storage_edit_smb_server_path\">Διαδρομή</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">Μπορεί να μείνει κενό</string>\n    <string name=\"storage_edit_smb_server_name\">Όνομα</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">Χρήση ονόματος υπολογιστή</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">Αυθεντικοποίηση</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>Κωδικός</item>\n        <item>Επισκέπτης</item>\n        <item>Ανώνυμα</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">Όνομα χρήστη</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">Εισάγετε ένα όνομα χρήστη</string>\n    <string name=\"storage_edit_smb_server_password\">Κωδικός</string>\n    <string name=\"storage_edit_smb_server_domain\">Τομέας</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">Σύνδεση και προσθήκη</string>\n    <string name=\"storage_edit_smb_server_add\">Προσθήκη</string>\n    <string name=\"storage_edit_webdav_server_title_edit\">Επεξεργασία διακομιστή WebDAV</string>\n    <string name=\"storage_edit_webdav_server_title_add\">Προσθήκη διακομιστή WebDAV</string>\n    <string name=\"storage_edit_webdav_server_host\">Όνομα υπολογιστή</string>\n    <string name=\"storage_edit_webdav_server_host_error_empty\">Εισάγετε ένα όνομα υπολογιστή</string>\n    <string name=\"storage_edit_webdav_server_host_error_invalid\">Μη έγκυρο όνομα υπολογιστή</string>\n    <string name=\"storage_edit_webdav_server_port\">Θύρα</string>\n    <string name=\"storage_edit_webdav_server_port_error_invalid\">Μη έγκυρη θύρα</string>\n    <string name=\"storage_edit_webdav_server_path\">Διαδρομή</string>\n    <string name=\"storage_edit_webdav_server_path_placeholder\">Μπορεί να μείνει κενό</string>\n    <string name=\"storage_edit_webdav_server_name\">Όνομα</string>\n    <string name=\"storage_edit_webdav_server_name_placeholder\">Χρήση ονόματος υπολογιστή</string>\n    <string name=\"storage_edit_webdav_server_protocol\">Πρωτόκολλο</string>\n    <string name=\"storage_edit_webdav_server_authentication_type\">Αυθεντικοποίηση</string>\n    <string-array name=\"storage_edit_webdav_server_authentication_type_entries\">\n        <item>Κωδικός</item>\n        <item>Διακριτικό πρόσβασης</item>\n        <item>Κανένα</item>\n    </string-array>\n    <string name=\"storage_edit_webdav_server_username\">Όνομα χρήστη</string>\n    <string name=\"storage_edit_webdav_server_username_error_empty\">Εισάγετε ένα όνομα χρήστη</string>\n    <string name=\"storage_edit_webdav_server_password\">Κωδικός</string>\n    <string name=\"storage_edit_webdav_server_access_token\">Διακριτικό πρόσβασης</string>\n    <string name=\"storage_edit_webdav_server_access_token_error_empty\">Εισάγετε ένα διακριτικό πρόσβασης</string>\n    <string name=\"storage_edit_webdav_server_connect_and_add\">Σύνδεση και προσθήκη</string>\n    <string name=\"storage_edit_webdav_server_add\">Προσθήκη</string>\n\n    <string name=\"navigation_storage_subtitle_format\">%1$s ελεύθερα από %2$s</string>\n    <string name=\"navigation_add_storage\">Προσθήκη χώρου αποθήκευσης...</string>\n    <string name=\"navigation_standard_directory_alarms\">Ξυπνητήρια</string>\n    <string name=\"navigation_standard_directory_dcim\">DCIM</string>\n    <string name=\"navigation_standard_directory_documents\">Έγγραφα</string>\n    <string name=\"navigation_standard_directory_downloads\">Λήψεις</string>\n    <string name=\"navigation_standard_directory_movies\">Ταινίες</string>\n    <string name=\"navigation_standard_directory_music\">Μουσική</string>\n    <string name=\"navigation_standard_directory_notifications\">Ειδοποιήσεις</string>\n    <string name=\"navigation_standard_directory_pictures\">Εικόνες</string>\n    <string name=\"navigation_standard_directory_podcasts\">Podcast</string>\n    <string name=\"navigation_standard_directory_ringtones\">Ήχοι κλήσεων</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">Φάκελος σελιδοδείκτης</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">Όνομα</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">Διαδρομή</string>\n    <string name=\"archive_viewer_title\">Προβολέας αρχειοθήκης</string>\n    <string name=\"image_viewer_title\">Προβολέας εικόνων</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"save_as_title\">Αποθήκευση ως</string>\n    <string name=\"save_as_error\">Σφάλμα κατά την αποθήκευση του αρχείου</string>\n    <string name=\"save_as_success_format\">“%1$s” αποθηκεύτηκε στο “%2$s”</string>\n    <string name=\"text_editor_title\">Επεξεργαστής κειμένου</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">Αποθηκεύτηκε</string>\n    <string name=\"text_editor_reload_message\">Σίγουρα θέλετε να επαναλάβετε τη φόρτωση; Οι μη αποθηκευμένες αλλαγές σε αυτό το έγγραφο θα χαθούν.</string>\n    <string name=\"text_editor_encoding\">Κωδικοποίηση</string>\n    <string name=\"text_editor_close_message\">Σίγουρα θέλετε να απορρίψετε τις μη αποθηκευμένες αλλαγές σε αυτό το έγγραφο;</string>\n\n    <string name=\"ftp_server_title\">Διακομιστής FTP</string>\n    <string name=\"ftp_server_state_title\">Κατάσταση</string>\n    <string name=\"ftp_server_state_summary_starting\">Εκκίνηση…</string>\n    <string name=\"ftp_server_state_summary_running\">Εκτελείται</string>\n    <string name=\"ftp_server_state_summary_stopping\">Διακοπή…</string>\n    <string name=\"ftp_server_state_summary_stopped\">Δεν ξεκίνησε</string>\n    <string name=\"ftp_server_url_title\">URL</string>\n    <string name=\"ftp_server_url_summary_no_local_inet_address\">Η τοπική διεύθυνση IP είναι άγνωστη</string>\n    <string name=\"ftp_server_url_menu_copy_url\">Αντιγραφή URL</string>\n    <string name=\"ftp_server_url_menu_copy_password\">Αντιγραφή κωδικού</string>\n    <string name=\"ftp_server_add_tile_title\">Προσθήκη στις Γρήγορες Ρυθμίσεις</string>\n    <string name=\"ftp_server_add_tile_result_added\">“Διακομιστής FTP” έχει προστεθεί στις Γρήγορες Ρυθμίσεις</string>\n    <string name=\"ftp_server_add_tile_result_already_added\">“Διακομιστής FTP” έχει ήδη προστεθεί στις Γρήγορες Ρυθμίσεις</string>\n    <string name=\"ftp_server_add_tile_result_error\">Σφάλμα κατά την προσθήκη “Διακομιστής FTP” στις Γρήγορες Ρυθμίσεις</string>\n    <string name=\"ftp_server_configuration_title\">Διαμόρφωση</string>\n    <string name=\"ftp_server_anonymous_login_title\">Ανώνυμη σύνδεση</string>\n    <string name=\"ftp_server_username_title\">Όνομα χρήστη</string>\n    <string name=\"ftp_server_password_title\">Κωδικός</string>\n    <string name=\"ftp_server_port_title\">Θύρα</string>\n    <string name=\"ftp_server_home_directory_title\">Φάκελος ρίζας</string>\n    <string name=\"ftp_server_writable_title\">Αποδοχή εγγραφής</string>\n    <string name=\"settings_title\">Ρυθμίσεις</string>\n    <string name=\"settings_interface_title\">Διεπαφή</string>\n    <string name=\"settings_locale_title\">Γλώσσα</string>\n    <string name=\"settings_theme_color_title\">Χρώμα θέματος</string>\n    <string name=\"settings_theme_color_summary\">Χρώμα που εμφανίζεται συχνότερα στην εφαρμογή</string>\n    <string name=\"settings_material_design_3_title\">Σχεδίαση Υλικού 3</string>\n    <string name=\"settings_night_mode_title\">Νυχτερινή λειτουργία</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>Ακολουθεί το σύστημα</item>\n        <item>Κλειστό</item>\n        <item>Ανοιχτό</item>\n        <item>Βάσει ώρας</item>\n        <item>Βάσει εξοικονόμησης μπαταρίας</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">Μαύρη νυχτερινή λειτουργία</string>\n    <string name=\"settings_file_list_animation_title\">Κινούμενη εικόνα λίστας αρχείων</string>\n    <string name=\"settings_file_name_ellipsize_title\">Εμφάνιση μακροσκελούς ονόματος αρχείου</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>Ελλειψομετρία στην αρχή</item>\n        <item>Ελλειψομετρία στη μέση</item>\n        <item>Ελλειψομετρία στο τέλος</item>\n        <item>Μαρκίζα</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">Συμπεριφορά</string>\n    <string name=\"settings_default_directory_title\">Προεπιλεγμένος φάκελος</string>\n    <string name=\"settings_standard_directories_title\">Τυπικοί φάκελοι</string>\n    <string name=\"settings_standard_directories_summary_empty\">Χωρίς ενεργούς τυπικούς φακέλους</string>\n    <string name=\"settings_bookmark_directory_screenshots\">Στιγμιότυπα οθόνης</string>\n    <string name=\"settings_bookmark_directories_title\">Φάκελοι σελιδοδείκτες</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">Χωρίς φακέλους σελιδοδείκτες</string>\n    <string name=\"settings_root_strategy_title\">Λειτουργία πρόσβασης ρίζας</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"one\">Έχετε %1$,d αρχείο σε εκτέλεση και αλλαγή της λειτουργίας πρόσβασης root μπορεί να προκαλέσει τώρα μη αναμενόμενο σφάλμα. Είστε βέβαιοι ότι θέλετε να την αλλάξετε;</item>\n        <item quantity=\"other\">Έχετε %1$,d λειτουργίες αρχείων που εκτελούνται και η αλλαγή της λειτουργίας πρόσβασης ρίζας τώρα ενδέχεται να προκαλέσει μη αναμενόμενο σφάλμα. Είστε βέβαιοι ότι θέλετε να την αλλάξετε;</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>Μόνο κανονική πρόσβαση</item>\n        <item>Αυτόματη</item>\n        <item>Μόνο πρόσβαση ρίζας</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">Κωδικοποίηση ονόματος αρχείου αρχειοθήκης</string>\n    <string name=\"settings_open_apk_default_action_title\">Άνοιγμα πακέτου Android</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>Εγκατάσταση</item>\n        <item>Προβολή περιεχομένων</item>\n        <item>Ερώτηση για ενέργεια</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_title\">Εμφάνιση μικρογραφίας για έγγραφα PDF</string>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">Μπορεί να κάνει την εφαρμογή ασταθή σε παλαιότερες εκδόσεις Android</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">Ανάγνωση απόμακρων αρχείων για μικρογραφία</string>\n\n    <string name=\"about_title\">Σχετικά</string>\n    <string name=\"about_version_title\">Έκδοση</string>\n    <string name=\"about_github_title\">Προβολή στο GitHub</string>\n    <string name=\"about_licenses_title\">Άδειες</string>\n    <string name=\"about_privacy_policy_title\">Πολιτική απορρήτου</string>\n    <string name=\"about_author_title\">Συντάκτης</string>\n    <string name=\"about_author_name_title\">Hai Zhang</string>\n    <string name=\"about_author_github_title\">Ακολουθήστε στο GitHub</string>\n    <string name=\"about_author_twitter_title\">Ακολουθήστε στο Twitter</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-es/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">Archivo</string>\n    <string name=\"file_type_name_apk\">Paquete de Android</string>\n    <string name=\"file_type_name_archive\">%1$sarchivo</string>\n    <string name=\"file_type_name_audio\">%1$s audio</string>\n    <string name=\"file_type_name_calendar\">%1$s calendario</string>\n    <string name=\"file_type_name_certificate\">%1$s certificado</string>\n    <string name=\"file_type_name_code\">%1$s documento</string>\n    <string name=\"file_type_name_contact\">Tarjeta de negocios electrónica</string>\n    <string name=\"file_type_name_directory\">Carpeta</string>\n    <string name=\"file_type_name_document\">%1$s documento</string>\n    <string name=\"file_type_name_ebook\">%1$s libro electrónico</string>\n    <string name=\"file_type_name_email\">%1$smensaje de correo electrónico</string>\n    <string name=\"file_type_name_font\">%1$sfuente</string>\n    <string name=\"file_type_name_generic\">%1$sarchivo</string>\n    <string name=\"file_type_name_image\">%1$simagen</string>\n    <string name=\"file_type_name_pdf\">Documento PDF</string>\n    <string name=\"file_type_name_presentation\">%1$spresentación</string>\n    <string name=\"file_type_name_spreadsheet\">%1$shoja de cálculo</string>\n    <string name=\"file_type_name_text\">%1$sdocumento</string>\n    <string name=\"file_type_name_text_plain\">Documento de texto plano</string>\n    <string name=\"file_type_name_video\">%1$s video</string>\n    <string name=\"file_type_name_word\">Documento de Word</string>\n    <string name=\"file_type_name_powerpoint\">Presentación de PowerPoint</string>\n    <string name=\"file_type_name_excel\">Hoja de cálculo de Excel</string>\n    <string name=\"file_type_name_posix_character_device\">Dispositivo de carácteres</string>\n    <string name=\"file_type_name_posix_block_device\">Dispositivo de bloque</string>\n    <string name=\"file_type_name_posix_fifo\">Tubería</string>\n    <string name=\"file_type_name_posix_symbolic_link\">Enlace</string>\n    <string name=\"file_type_name_posix_socket\">Socket</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">Enlace (roto)</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-es/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <string name=\"app_name\">Material Files</string>\n\n    <string name=\"close\">Cerrar</string>\n    <string name=\"copy\">Copiar</string>\n    <string name=\"cut\">Cortar</string>\n    <string name=\"default_\">Predeterminado</string>\n    <string name=\"delete\">Eliminar</string>\n    <string name=\"discard\">Descartar</string>\n    <string name=\"edit\">Editar</string>\n    <string name=\"empty\">Vacío</string>\n    <string name=\"empty_placeholder\">(Vacío)</string>\n    <string name=\"error\">Error</string>\n    <string name=\"hide\">Ocultar</string>\n    <string name=\"install\">Instalar</string>\n    <string name=\"keep_editing\">Continuar editando</string>\n    <string name=\"loading\">Cargando…</string>\n    <string name=\"maybe_later\">Quizás más tarde</string>\n    <string name=\"merge\">Fusionar</string>\n    <string name=\"none\">Ninguno</string>\n    <string name=\"open_navigation_drawer\">Abrir barra de navegación</string>\n    <string name=\"paste\">Pegar</string>\n    <string name=\"refresh\">Actualizar</string>\n    <string name=\"reload\">Recargar</string>\n    <string name=\"remove\">Remover</string>\n    <string name=\"rename\">Renombrar</string>\n    <string name=\"replace\">Reemplazar</string>\n    <string name=\"reset\">Restrablecer</string>\n    <string name=\"retry\">Reintentar</string>\n    <string name=\"save\">Guardar</string>\n    <string name=\"search\">Buscar</string>\n    <string name=\"select\">Seleccionar</string>\n    <string name=\"select_all\">Seleccionar todo</string>\n    <string name=\"share\">Compartir</string>\n    <string name=\"show\">Mostrar</string>\n    <string name=\"skip\">Saltar</string>\n    <string name=\"stop\">Detener</string>\n    <string name=\"system_default\">Predeterminado del sistema</string>\n    <string name=\"unknown\">Desconocido</string>\n    <string name=\"view\">Ver</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">No se encontró ninguna aplicación para esta acción</string>\n    <string name=\"open_settings\">Abrir ajustes</string>\n    <string name=\"shortcut_created\">Acceso directo creado</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"one\">%1$,d byte</item>\n        <item quantity=\"many\">%1$,d bytes</item>\n        <item quantity=\"other\">%1$,d bytes</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">La aplicación necesita administrar todos los archivos. Por favor permita el acceso en la siguiente configuración del sistema.</string>\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">La aplicación necesita permiso para acceder a los archivos. Por favor, aprieta “PERMITIR” en el diálogo del sistema.</string>\n    <string name=\"storage_permission_permanently_denied_message\">La aplicación necesita permiso para acceder a los archivos. Por favor, concede el permiso de “Almacenamiento” en la configuración del sistema.</string>\n    <!-- The allow button is in sentence case now. -->\n    <string name=\"notification_permission_rationale_message\">Esta app necesita permiso para mostrar notificaciones acerca de las operaciones de los archivos. Por favor presiona “Permitir” en el diálogo del sistema a continuación.</string>\n    <string name=\"notification_permission_permanently_denied_message\">Esta app necesita permiso para mostrar notificaciones acerca de las operaciones de los archivos. Por favor permite el permiso de “Notificaciones” en los ajustes del sistema.</string>\n\n    <string name=\"notification_channel_background_activity_start_name\">Acciones en segundo plano</string>\n    <string name=\"notification_channel_background_activity_start_description\">Realiza acciones mientras la aplicación está en segundo plano</string>\n    <string name=\"notification_channel_file_job_name\">Operaciones de archivos</string>\n    <string name=\"notification_channel_file_job_description\">Muestra y controla las operaciones de archivos</string>\n    <string name=\"notification_channel_ftp_server_name\">Servidor FTP</string>\n    <string name=\"notification_channel_ftp_server_description\">Muestra y controla el servidor FTP</string>\n\n    <string name=\"file_action_archive_password_title\">Se requiere contraseña</string>\n    <string name=\"file_action_archive_password_message_format\">“%1$s” está protegido por una contraseña.</string>\n    <string name=\"file_action_archive_password_error_empty\">La contraseña no puede estar vacía</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"one\">Preparando para comprimir %1$,d archivos (%2$s)</item>\n        <item quantity=\"many\">Preparándose para comprimir %1$,d archivos (%2$s)</item>\n        <item quantity=\"other\">Preparándose para comprimir %1$,d archivos (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">Comprimiendo “%1$s” en “%2$s”</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"one\">Comprimiendo %1$,d archivos en \\\"%2$s\\\"</item>\n        <item quantity=\"many\">Comprimiendo %1$,d archivos en \\\"%2$s\\\"</item>\n        <item quantity=\"other\">Comprimiendo %1$,d archivos en “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"one\">Preparando para copiar %1$,d archivos (%2$s)</item>\n        <item quantity=\"many\">Preparándose para copiar %1$,d archivos (%2$s)</item>\n        <item quantity=\"other\">Preparándose para copiar %1$,d archivos (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">Copiando “%1$s” a “%2$s”</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"one\">Copiando %1$,d archivos a \\\"%2$s\\\"</item>\n        <item quantity=\"many\">Copiando %1$,d archivos a \\\"%2$s\\\"</item>\n        <item quantity=\"other\">Copiando %1$,d archivos a “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"one\">Preparando para extraer %1$,d archivos (%2$s)</item>\n        <item quantity=\"many\">Preparando para extraer %1$,d archivos (%2$s)</item>\n        <item quantity=\"other\">Preparando para extraer %1$,d archivos (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">Extrayendo “%1$s” a “%2$s”</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"one\">Extrayendo %1$,d archivos a \\\"%2$s\\\"</item>\n        <item quantity=\"many\">Extrayendo %1$,d archivos a \\\"%2$s\\\"</item>\n        <item quantity=\"other\">Extrayendo %1$,d archivos a “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"one\">Preparando para mover %1$,d archivos (%2$s)</item>\n        <item quantity=\"many\">Preparando para mover %1$,d archivos (%2$s)</item>\n        <item quantity=\"other\">Preparando para mover %1$,d archivos (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">Moviendo “%1$s” a “%2$s”</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"one\">Moviendo %1$,d archivos a \\\"%2$s\\\"</item>\n        <item quantity=\"many\">Moviendo %1$,d archivos a \\\"%2$s\\\"</item>\n        <item quantity=\"other\">Moviendo %1$,d archivos a “%2$s”</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"one\">Preparando para eliminar %1$,d archivos</item>\n        <item quantity=\"many\">Preparando para eliminar %1$,d archivos</item>\n        <item quantity=\"other\">Preparando para eliminar %1$,d archivos</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">Eliminando “%1$s”</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"one\">Eliminando %1$,d archivos</item>\n        <item quantity=\"many\">Eliminando %1$,d archivos</item>\n        <item quantity=\"other\">Eliminando %1$,d archivos</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"one\">Preparando para cambiar el propietario de %1$,d archivos</item>\n        <item quantity=\"many\">Preparando para cambiar el propietario de %1$,d archivos</item>\n        <item quantity=\"other\">Preparando para cambiar el propietario de %1$,d archivos</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">Cambiando el propietario de “%1$s”</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"one\">Cambiando el propietario de %1$,d archivos</item>\n        <item quantity=\"many\">Cambiando el propietario de %1$,d archivos</item>\n        <item quantity=\"other\">Cambiando el propietario de %1$,d archivos</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"one\">Preparando para cambiar el grupo de %1$,d archivos</item>\n        <item quantity=\"many\">Preparando para cambiar el grupo de %1$,d archivos</item>\n        <item quantity=\"other\">Preparando para cambiar el grupo de %1$,d archivos</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">Cambiando el grupo de “%1$s”</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"one\">Cambiando el grupo de %1$,d archivos</item>\n        <item quantity=\"many\">Cambiando el grupo de %1$,d archivos</item>\n        <item quantity=\"other\">Cambiando el grupo de %1$,d archivos</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"one\">Preparando para cambiar el modo de %1$,d archivos</item>\n        <item quantity=\"many\">Preparando para cambiar el modo de %1$,d archivos</item>\n        <item quantity=\"other\">Preparando para cambiar el modo de %1$,d archivos</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">Cambiando el modo de “%1$s”</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"one\">Cambiando el modo de %1$,d archivos</item>\n        <item quantity=\"many\">Cambiando el modo de %1$,d archivos</item>\n        <item quantity=\"other\">Cambiando el modo de %1$,d archivos</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Preparándose para cambiar el contexto de SELinux de %1$,d archivos</item>\n        <item quantity=\"many\">Preparándose para cambiar el contexto de SELinux de %1$,d archivos</item>\n        <item quantity=\"other\">Preparándose para cambiar el contexto de SELinux de %1$,d archivos</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">Cambiando el contexto SELinux de “%1$s”</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Cambiando el contexto de SELinux de %1$,d archivos</item>\n        <item quantity=\"many\">Cambiando el contexto de SELinux de %1$,d archivos</item>\n        <item quantity=\"other\">Cambiando el contexto de SELinux de %1$,d archivos</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Preparándose para restaurar el contexto de SELinux de %1$,d archivos</item>\n        <item quantity=\"many\">Preparándose para restaurar el contexto de SELinux de %1$,d archivos</item>\n        <item quantity=\"other\">Preparándose para restaurar el contexto de SELinux de %1$,d archivos</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">Restaurando el contexto de SELinux de “%1$s”</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Restaurando el contexto de SELinux de %1$,d archivos</item>\n        <item quantity=\"many\">Restaurando el contexto de SELinux de %1$,d archivos</item>\n        <item quantity=\"other\">Restaurando el contexto de SELinux de %1$,d archivos</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">Escribiendo “%1$s”</string>\n    <string name=\"file_job_create_error_title\">Error durante la creación</string>\n    <string name=\"file_job_create_error_message_format\">Hubo un error creando “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">No se puede copiar una carpeta dentro de sí misma</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">No se puede extraer una carpeta dentro de sí misma</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">No se puede mover una carpeta dentro de sí misma</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">La carpeta de destino está dentro de la carpeta de origen.</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">No se puede copiar un archivo sobre sí mismo</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">No se puede extraer un archivo sobre sí mismo</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">No se puede mover un archivo sobre sí mismo</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">El archivo de origen será sobreescrito por el de destino</string>\n    <string name=\"file_job_replace_title_format\">¿Reemplazar el archivo “%1$s”?</string>\n    <string name=\"file_job_replace_message_format\">Existe otro archivo con el mismo nombre en “%1$s”.\\nReemplazarlo va a sobreescribir su contenido.</string>\n    <string name=\"file_job_replace_target_name\">Archivo original</string>\n    <string name=\"file_job_replace_source_name\">Reemplazar con</string>\n    <string name=\"file_job_merge_title_format\">¿Fusionar la carpeta “%1$s”?</string>\n    <string name=\"file_job_merge_copy_message_format\">Fusionando se le preguntará por confirmación antes de reemplazar cualquier archivo en la carpeta que entre en conflicto con los archivos que se estén copiando.</string>\n    <string name=\"file_job_merge_extract_message_format\">Fusionando se le preguntará por confirmación antes de reemplazar cualquier archivo en la carpeta que entre en conflicto con los archivos que se estén extrayendo.</string>\n    <string name=\"file_job_merge_move_message_format\">Fusionando se le preguntará por confirmación antes de reemplazar cualquier archivo en la carpeta que entre en conflicto con los archivos que se estén moviendo.</string>\n    <string name=\"file_job_merge_target_name\">Carpeta original</string>\n    <string name=\"file_job_merge_source_name\">Fusionar con</string>\n    <string name=\"file_job_conflict_show_name\">Elegir un nuevo nombre para el destino</string>\n    <string name=\"file_job_conflict_name_hint\">Nombre nuevo</string>\n    <string name=\"file_job_archive_error_title_format\">Error al comprimir “%1$s”</string>\n    <string name=\"file_job_archive_error_message_format\">Hubo un error comprimiendo el archivo en “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">Error al copiar “%1$s”</string>\n    <string name=\"file_job_copy_error_message_format\">Hubo un error copiando el archivo en “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">Error al extraer “%1$s”</string>\n    <string name=\"file_job_extract_error_message_format\">Hubo un error extrayendo el archivo en “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">Error al mover “%1$s”</string>\n    <string name=\"file_job_move_error_message_format\">Hubo un error movimiento el archivo en “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">Error al eliminar</string>\n    <string name=\"file_job_delete_error_message_format\">Hubo un error eliminando “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">Error al renombrar “%1$s”</string>\n    <string name=\"file_job_rename_error_message_format\">Hubo un error renombrando “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">Error al cambiar el propietario de “%1$s”</string>\n    <string name=\"file_job_set_owner_error_message_format\">Hubo un error cambiando el propietario de “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">Error al cambiar el grupo de “%1$s”</string>\n    <string name=\"file_job_set_group_error_message_format\">Hubo un error cambiando el grupo de “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">Error al cambiar el modo de “%1$s”</string>\n    <string name=\"file_job_set_mode_error_message_format\">Hubo un error cambiando el modo de “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">Error al cambiar el contexto de SELinux de “%1$s”</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">Hubo un error cambiando el contexto de SELinux de “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">Error al restaurar el contexto de SELinux</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">Hubo un error restaurando el contexto de SELinux de “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">Error al escribir</string>\n    <string name=\"file_job_write_error_message_format\">Hubo un error escribiendo “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">Hubo un error obteniendo información sobre “%1$s”.</string>\n    <string name=\"file_job_remount_format\">Volver a montar “%1$s” como lectura-escritura</string>\n    <string name=\"file_job_remount_loading_format\">Volviendo a montar “%1$s” como lectura-escritura…</string>\n    <string name=\"file_job_remount_success_format\">“%1$s” fue vuelto a montar como lectura-escritura</string>\n    <string name=\"file_job_action_all\">Aplicar esta acción a todos los archivos</string>\n\n    <string name=\"file_open_apk_message\">Este archivo es un paquete de Android. ¿Quieres instalarlo o ver su contenido?</string>\n    <string name=\"file_open_as_title\">Abrir como…</string>\n    <string name=\"file_open_as_title_format\">Abrir “%1$s” como</string>\n    <string name=\"file_open_as_type_text\">Texto</string>\n    <string name=\"file_open_as_type_image\">Imagen</string>\n    <string name=\"file_open_as_type_audio\">Audio</string>\n    <string name=\"file_open_as_type_video\">Video</string>\n    <string name=\"file_open_as_type_directory\">Carpeta</string>\n    <string name=\"file_open_as_type_any\">Otro</string>\n    <string name=\"file_install_apk_from_background_title_format\">“%1$s” está listo para ser instalado</string>\n    <string name=\"file_install_apk_from_background_text\">Toca para instalar</string>\n    <string name=\"file_open_from_background_title_format\">“%1$s” está listo para ser abierto</string>\n    <string name=\"file_open_from_background_text\">Toca para abrir</string>\n    <string name=\"file_name_error_empty\">El nombre de archivo no puede estar vacío</string>\n    <string name=\"file_name_error_invalid\">Nombre de archivo no válido</string>\n    <string name=\"file_name_error_already_exists\">Ya existe un archivo con este nombre</string>\n    <string name=\"file_replace_message_format\">¿Reemplazar “%1$s”?</string>\n    <string name=\"file_delete_message_file_format\">¿Eliminar “%1$s”?</string>\n    <string name=\"file_delete_message_directory_format\">¿Eliminar carpeta “%1$s” y su contenido?</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"one\">¿Añadir %1$,d archivo?</item>\n        <item quantity=\"many\">¿Eliminar %1$,d archivos?</item>\n        <item quantity=\"other\">¿Eliminar %1$,d archivos?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"one\">¿Eliminar %1$,d carpeta junto con su contenido?</item>\n        <item quantity=\"many\">¿Eliminar %1$,d carpetas junto con su contenido?</item>\n        <item quantity=\"other\">¿Eliminar %1$,d carpetas junto con su contenido?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"one\">¿Eliminar %1$,d elemento?</item>\n        <item quantity=\"many\">¿Eliminar %1$,d elementos?</item>\n        <item quantity=\"other\">¿Eliminar %1$,d elementos?</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">Crear archivo</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_create_archive_password\">Contraseña (opcional)</string>\n    <string name=\"file_add_bookmark_success\">Carpeta fijada</string>\n    <string name=\"file_create_file_title\">Nuevo archivo</string>\n    <string name=\"file_create_directory_title\">Nueva carpeta</string>\n\n    <string name=\"file_list_title\">Archivos</string>\n    <plurals name=\"file_list_title_open_file\">\n        <item quantity=\"one\">Seleccionar un archivo</item>\n        <item quantity=\"many\">Seleccionar archivos</item>\n        <item quantity=\"other\">Seleccionar archivos</item>\n    </plurals>\n    <string name=\"file_list_title_create_file\">Guardar archivo</string>\n    <plurals name=\"file_list_title_open_directory\">\n        <item quantity=\"one\">Seleccionar una carpeta</item>\n        <item quantity=\"many\">Seleccionar carpetas</item>\n        <item quantity=\"other\">Seleccionar carpetas</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"one\">%1$,d carpeta</item>\n        <item quantity=\"many\">%1$,d carpetas</item>\n        <item quantity=\"other\">%1$,d carpetas</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"one\">%1$,d archivo</item>\n        <item quantity=\"many\">%1$,d archivos</item>\n        <item quantity=\"other\">%1$,d archivos</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">,\\u0020</string>\n    <string name=\"file_list_path_error_empty\">La ruta no puede estar vacía</string>\n    <string name=\"file_list_path_error_invalid\">Ruta inválida</string>\n    <string name=\"file_list_action_view_sort\">Ver y ordenar</string>\n    <string name=\"file_list_action_view_list\">Lista</string>\n    <string name=\"file_list_action_view_grid\">Cuadrícula</string>\n    <string name=\"file_list_action_sort_by_name\">Nombre</string>\n    <string name=\"file_list_action_sort_by_type\">Tipo</string>\n    <string name=\"file_list_action_sort_by_size\">Tamaño</string>\n    <string name=\"file_list_action_sort_by_last_modified\">Última modificación</string>\n    <string name=\"file_list_action_sort_order_ascending\">Ascendente</string>\n    <string name=\"file_list_action_sort_directories_first\">Carpetas primero</string>\n    <string name=\"file_list_action_view_sort_path_specific\">Sólo para esta carpeta</string>\n    <string name=\"file_list_action_new_task\">Nueva ventana</string>\n    <string name=\"file_list_action_navigate_up\">Subir</string>\n    <string name=\"file_list_action_navigate_to\">Ir a</string>\n    <string name=\"file_list_action_show_hidden_files\">Mostrar archivos ocultos</string>\n    <string name=\"file_list_action_copy_path\">Copiar ruta</string>\n    <string name=\"file_list_action_open_in_terminal\">Abrir en terminal</string>\n    <string name=\"file_list_action_add_bookmark\">Fijar carpeta</string>\n    <string name=\"file_list_action_create_shortcut\">Crear acceso directo</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">Abrir en una nueva ventana</string>\n    <string name=\"file_list_select_title_format\">%1$,d</string>\n    <string name=\"file_list_create_file_name_hint\">Nombre del archivo</string>\n    <string name=\"file_list_open_current_directory_format\">Seleccionar “%1$s”</string>\n    <string name=\"file_list_paste_move_title_format\">Moviendo %1$,d</string>\n    <string name=\"file_list_paste_copy_title_format\">Copiando %1$,d</string>\n    <string name=\"file_list_paste_extract_title_format\">Extrayendo %1$,d</string>\n    <string name=\"file_list_paste_action_extract_here\">Extraer aquí</string>\n    <string name=\"file_list_empty\">Sin archivos</string>\n    <string name=\"file_list_action_create_file\">Archivo</string>\n    <string name=\"file_list_action_create_directory\">Carpeta</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">Abrir con</string>\n    <string name=\"file_item_action_extract\">Extraer</string>\n    <string name=\"file_item_action_archive\">Comprimir</string>\n    <string name=\"file_item_action_properties\">Propiedades</string>\n\n    <string name=\"file_properties_title_format\">Propiedades de “%1$s”</string>\n    <string name=\"file_properties_basic\">Básico</string>\n    <string name=\"file_properties_basic_name\">Nombre</string>\n    <string name=\"file_properties_basic_type\">Tipo</string>\n    <string name=\"file_properties_basic_type_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">Enlace a %1$s (%2$s)</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">Objetivo de Enlace</string>\n    <string name=\"file_properties_basic_size\">Tamaño</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_contents\">Contenido</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"one\">%1$,d artículo, con un tamaño %2$s</item>\n        <item quantity=\"many\">%1$,d artículos, en total%2$s</item>\n        <item quantity=\"other\">%1$,d elementos, en total %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">Última modificación</string>\n    <string name=\"file_properties_basic_parent_directory\">Carpeta Superior</string>\n    <string name=\"file_properties_basic_archive_file\">Archivo</string>\n    <string name=\"file_properties_basic_archive_entry\">Entrada de archivo</string>\n    <string name=\"file_properties_basic_free_space\">Espacio libre</string>\n    <string name=\"file_properties_permission\">Permisos</string>\n    <string name=\"file_properties_permission_owner\">Propietario</string>\n    <string name=\"file_properties_permission_group\">Grupo</string>\n    <string name=\"file_properties_permission_principal_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_permission_set_owner_title\">Cambiar propietario</string>\n    <string name=\"file_properties_permission_set_group_title\">Cambiar grupo</string>\n    <string name=\"file_properties_permission_set_principal_filter_hint\">Ingrese un nombre o ID</string>\n    <string name=\"file_properties_permission_set_principal_system\">Sistema</string>\n    <string name=\"file_properties_permission_mode\">Modo</string>\n    <string name=\"file_properties_permission_mode_format\">%1$s (%2$04o)</string>\n    <string name=\"file_properties_permission_set_mode_title\">Cambiar modo</string>\n    <string name=\"file_properties_permission_set_mode_others\">Otros</string>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_file\">\n        <item>Lectura</item>\n        <item>Escritura</item>\n        <item>Ejecución</item>\n    </string-array>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_directory\">\n        <item>Lectura</item>\n        <item>Escritura</item>\n        <item>Búsqueda</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_special\">Especial</string>\n    <string-array name=\"file_properties_permission_set_mode_special_mode_bits\">\n        <item>Establecer UID</item>\n        <item>Establecer GID</item>\n        <item>Sticky bit</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_uppercase_x\">No añadir “Ejecución” para los archivos incluidos</string>\n    <string name=\"file_properties_permission_selinux_context\">Contexto de SELinux</string>\n    <string name=\"file_properties_permission_set_selinux_context_title\">Cambiar el contexto de SELinux</string>\n    <string name=\"file_properties_permission_set_selinux_context_restore\">Restaurar</string>\n    <string name=\"file_properties_permission_recursive\">Aplicar a archivos incluidos</string>\n    <string name=\"file_properties_image\">Imagen</string>\n    <string name=\"file_properties_media_dimensions\">Dimensiones</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">Creación</string>\n    <string name=\"file_properties_media_coordinates\">Coordenadas</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f, %2$.3f</string>\n    <string name=\"file_properties_media_address\">Ubicación</string>\n    <string name=\"file_properties_image_gps_altitude\">Altitud</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f m</string>\n    <string name=\"file_properties_image_equipment\">Cámara</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">Apertura</string>\n    <string name=\"file_properties_image_shutter_speed\">Velocidad de obturación</string>\n    <string name=\"file_properties_image_focal_length\">Longitud focal</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f mm</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">Equivalencia ISO</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">Software</string>\n    <string name=\"file_properties_image_description\">Descripción</string>\n    <string name=\"file_properties_image_artist\">Creador</string>\n    <string name=\"file_properties_image_copyright\">Copyright</string>\n    <string name=\"file_properties_audio\">Audio</string>\n    <string name=\"file_properties_media_title\">Título</string>\n    <string name=\"file_properties_audio_artist\">Artista</string>\n    <string name=\"file_properties_audio_album\">Album</string>\n    <string name=\"file_properties_audio_album_artist\">Artista del album</string>\n    <string name=\"file_properties_audio_composer\">Compositor</string>\n    <string name=\"file_properties_audio_disc_number\">Disco</string>\n    <string name=\"file_properties_audio_track_number\">Pista</string>\n    <string name=\"file_properties_audio_year\">Año</string>\n    <string name=\"file_properties_audio_genre\">Género</string>\n    <string name=\"file_properties_media_duration\">Duración</string>\n    <string name=\"file_properties_media_bit_rate\">Tasa de bits</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d kbps</string>\n    <string name=\"file_properties_audio_sample_rate\">Tasa de muestreo</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Hz</string>\n    <string name=\"file_properties_video\">Video</string>\n    <string name=\"file_properties_apk\">APK</string>\n    <string name=\"file_properties_apk_label\">Nombre</string>\n    <string name=\"file_properties_apk_package_name\">Nombre del paquete</string>\n    <string name=\"file_properties_apk_version\">Versión</string>\n    <string name=\"file_properties_apk_version_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_apk_min_sdk_version\">Versión mínima del sistema</string>\n    <string name=\"file_properties_apk_target_sdk_version\">Versión destino del sistema</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s (%2$s, %3$d)</string>\n    <string name=\"file_properties_apk_requested_permissions\">Permisos</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">No se requieren permisos</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"one\">No se requieren permisos</item>\n        <item quantity=\"many\">%1$,d permisos requeridos</item>\n        <item quantity=\"other\">%1$,d permisos requeridos</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">Firmas</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">Sin firmas válidas</string>\n    <string name=\"file_properties_apk_past_signature_digests\">Firmas antiguas</string>\n    <string name=\"file_properties_checksum\">Sumas de comprobación</string>\n    <string name=\"file_properties_checksum_compare\">Comparar</string>\n    <string name=\"file_properties_checksum_compare_placeholder\">Cualquier suma de comprobación</string>\n    <string name=\"file_properties_checksum_compare_match_format\">Coincide con la suma de comprobación %1$s</string>\n    <string name=\"file_properties_checksum_compare_prefix_match_format\">Coincide con el prefijo de la suma de comprobación %1$s</string>\n    <string name=\"file_properties_checksum_compare_no_match\">No se ha encontrado ninguna coincidencia</string>\n    <string name=\"storage_file_system_root_title\">Raíz</string>\n    <string name=\"storage_list_title\">Almacenamiento</string>\n    <string name=\"storage_list_empty\">No hay almacenamiento</string>\n    <string name=\"storage_add_storage_title\">Añadir almacenamiento</string>\n    <string name=\"storage_add_storage_document_tree\">Almacenamiento externo</string>\n    <string name=\"storage_add_storage_ftp_server\">Servidor FTP</string>\n    <string name=\"storage_add_storage_sftp_server\">Servidor SFTP</string>\n    <string name=\"storage_add_storage_smb_server\">Servidor SMB</string>\n    <string name=\"storage_add_storage_webdav_server\">Servidor WebDAV</string>\n    <string name=\"storage_edit_device_storage_title\">Editar dispositivo de almacenamiento</string>\n    <string name=\"storage_edit_device_storage_name\">Nombre</string>\n    <string name=\"storage_edit_device_storage_path\">Ruta</string>\n    <string name=\"storage_add_external_storage_shortcut_title\">Añadir acceso directo a almacenamiento externo</string>\n    <string name=\"storage_edit_external_storage_shortcut_title\">Editar acceso directo de almacenamiento externo</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id\">Volumen de almacenamiento</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id_error_empty\">Ingrese un volumen de almacenamiento</string>\n    <string name=\"storage_edit_external_storage_shortcut_path\">Ruta</string>\n    <string name=\"storage_add_document_tree_title\">Agregar almacenamiento externo</string>\n    <string name=\"storage_edit_document_tree_title\">Editar almacenamiento externo</string>\n    <string name=\"storage_edit_document_tree_name\">Nombre</string>\n    <string name=\"storage_edit_document_tree_uri\">URI</string>\n    <string name=\"storage_edit_document_tree_path\">Ruta</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">Editar servidor FTP</string>\n    <string name=\"storage_edit_ftp_server_title_add\">Añadir servidor FTP</string>\n    <string name=\"storage_edit_ftp_server_host\">Nombre de host</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">Ingrese un nombre de host</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">Nombre de host no válido</string>\n    <string name=\"storage_edit_ftp_server_port\">Puerto</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">Puerto no válido</string>\n    <string name=\"storage_edit_ftp_server_path\">Ruta</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">Puede dejarse vacío</string>\n    <string name=\"storage_edit_ftp_server_name\">Nombre</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">Usar nombre de host</string>\n    <string name=\"storage_edit_ftp_server_protocol\">Protocolo</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">Autenticación</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>Contraseña</item>\n        <item>Anónimo</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">Nombre de usuario</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">Ingrese un nombre de usuario</string>\n    <string name=\"storage_edit_ftp_server_password\">Contraseña</string>\n    <string name=\"storage_edit_ftp_server_mode\">Modo</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>Activo</item>\n        <item>Pasivo</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">Codificación</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">Conectar y añadir</string>\n    <string name=\"storage_edit_ftp_server_add\">Añadir</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">Editar servidor SFTP</string>\n    <string name=\"storage_edit_sftp_server_title_add\">Añadir servidor SFTP</string>\n    <string name=\"storage_edit_sftp_server_host\">Nombre del host</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">Ingrese un nombre de host</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">Nombre de host no válido</string>\n    <string name=\"storage_edit_sftp_server_port\">Puerto</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">Puerto no válido</string>\n    <string name=\"storage_edit_sftp_server_path\">Ruta</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">Puede dejarse vacío</string>\n    <string name=\"storage_edit_sftp_server_name\">Nombre</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">Usar nombre de host</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">Autenticación</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>Contraseña</item>\n        <item>Clave pública</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">Nombre de usuario</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">Ingrese un nombre de usuario</string>\n    <string name=\"storage_edit_sftp_server_password\">Contraseña</string>\n    <string name=\"storage_edit_sftp_server_private_key\">Clave privada</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">Abrir archivo</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">Ingrese una clave privada</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">Clave privada no válida</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">Contraseña de la clave privada</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">Puede dejarse vacío</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">Contraseña de la clave privada inválida</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">Conectar y añadir</string>\n    <string name=\"storage_edit_sftp_server_add\">Añadir</string>\n    <string name=\"storage_add_lan_smb_server_loading\">Buscando servidores SMB</string>\n    <string name=\"storage_add_lan_smb_server_add\">Añadir manualmente</string>\n    <string name=\"storage_edit_smb_server_title_edit\">Editar servidor SMB</string>\n    <string name=\"storage_edit_smb_server_title_add\">Añadir servidor SMB</string>\n    <string name=\"storage_edit_smb_server_host\">Nombre de host</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">Ingrese un nombre de host</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">Nombre de host no válido</string>\n    <string name=\"storage_edit_smb_server_port\">Puerto</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">Puerto no válido</string>\n    <string name=\"storage_edit_smb_server_path\">Ruta</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">Puede dejarse vacío</string>\n    <string name=\"storage_edit_smb_server_name\">Nombre</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">Usar nombre de host</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">Autenticación</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>Contraseña</item>\n        <item>Invitado</item>\n        <item>Anónimo</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">Nombre de usuario</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">Ingrese un nombre de usuario</string>\n    <string name=\"storage_edit_smb_server_password\">Contraseña</string>\n    <string name=\"storage_edit_smb_server_domain\">Dominio</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">Conectar y añadir</string>\n    <string name=\"storage_edit_smb_server_add\">Añadir</string>\n    <string name=\"storage_edit_webdav_server_title_edit\">Editar servidor WebDAV</string>\n    <string name=\"storage_edit_webdav_server_title_add\">Añadir servidor WebDAV</string>\n    <string name=\"storage_edit_webdav_server_host\">Nombre de host</string>\n    <string name=\"storage_edit_webdav_server_host_error_empty\">Ingrese un nombre de host</string>\n    <string name=\"storage_edit_webdav_server_host_error_invalid\">Nombre de host no válido</string>\n    <string name=\"storage_edit_webdav_server_port\">Puerto</string>\n    <string name=\"storage_edit_webdav_server_port_error_invalid\">Puerto no válido</string>\n    <string name=\"storage_edit_webdav_server_path\">Ruta</string>\n    <string name=\"storage_edit_webdav_server_path_placeholder\">Puede dejarse vacío</string>\n    <string name=\"storage_edit_webdav_server_name\">Nombre</string>\n    <string name=\"storage_edit_webdav_server_name_placeholder\">Usar nombre de host</string>\n    <string name=\"storage_edit_webdav_server_protocol\">Protocolo</string>\n    <string name=\"storage_edit_webdav_server_authentication_type\">Autenticación</string>\n    <string-array name=\"storage_edit_webdav_server_authentication_type_entries\">\n        <item>Contraseña</item>\n        <item>Token de acceso</item>\n        <item>Ninguna</item>\n    </string-array>\n    <string name=\"storage_edit_webdav_server_username\">Nombre de usuario</string>\n    <string name=\"storage_edit_webdav_server_username_error_empty\">Ingrese un nombre de usuario</string>\n    <string name=\"storage_edit_webdav_server_password\">Contraseña</string>\n    <string name=\"storage_edit_webdav_server_access_token\">Token de acceso</string>\n    <string name=\"storage_edit_webdav_server_access_token_error_empty\">Ingrese un token de acceso</string>\n    <string name=\"storage_edit_webdav_server_connect_and_add\">Conectar y añadir</string>\n    <string name=\"storage_edit_webdav_server_add\">Añadir</string>\n\n    <string name=\"navigation_storage_subtitle_format\">%1$s libre de %2$s</string>\n    <string name=\"navigation_add_storage\">Agregar almacenamiento…</string>\n    <string name=\"navigation_standard_directory_alarms\">Alarmas</string>\n    <string name=\"navigation_standard_directory_dcim\">DCIM</string>\n    <string name=\"navigation_standard_directory_documents\">Documentos</string>\n    <string name=\"navigation_standard_directory_downloads\">Descargas</string>\n    <string name=\"navigation_standard_directory_movies\">Películas</string>\n    <string name=\"navigation_standard_directory_music\">Música</string>\n    <string name=\"navigation_standard_directory_notifications\">Notificaciones</string>\n    <string name=\"navigation_standard_directory_pictures\">Fotos</string>\n    <string name=\"navigation_standard_directory_podcasts\">Podcasts</string>\n    <string name=\"navigation_standard_directory_ringtones\">Ringtones</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">Carpeta fijada</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">Nombre</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">Ruta</string>\n    <string name=\"archive_viewer_title\">Visor de archivos</string>\n    <string name=\"image_viewer_title\">Visor de imagen</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"save_as_title\">Guardar como</string>\n    <string name=\"save_as_error\">Error al guardar el archivo</string>\n    <string name=\"save_as_success_format\">“%1$s” se ha guardado en “%2$s”</string>\n    <string name=\"text_editor_title\">Editor de texto</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">Guardado</string>\n    <string name=\"text_editor_reload_message\">¿Está seguro de recargar? Los cambios que no se hayan guardado en el documento se perderán.</string>\n    <string name=\"text_editor_encoding\">Codificación</string>\n    <string name=\"text_editor_close_message\">¿Está seguro de que quiere descartar los cambios sin guardar en el documento?</string>\n\n    <string name=\"ftp_server_title\">Servidor FTP</string>\n    <string name=\"ftp_server_state_title\">Estado</string>\n    <string name=\"ftp_server_state_summary_starting\">Iniciando…</string>\n    <string name=\"ftp_server_state_summary_running\">Activo</string>\n    <string name=\"ftp_server_state_summary_stopping\">Deteniendo…</string>\n    <string name=\"ftp_server_state_summary_stopped\">Sin iniciar</string>\n    <string name=\"ftp_server_url_title\">URL</string>\n    <string name=\"ftp_server_url_summary_no_local_inet_address\">Dirección IP local desconocida</string>\n    <string name=\"ftp_server_url_menu_copy_url\">Copiar URL</string>\n    <string name=\"ftp_server_url_menu_copy_password\">Copiar contraseña</string>\n    <string name=\"ftp_server_add_tile_title\">Añadir a Ajustes Rápidos</string>\n    <string name=\"ftp_server_add_tile_result_added\">“Servidor FTP” ha sido añadido a Ajustes Rápidos</string>\n    <string name=\"ftp_server_add_tile_result_already_added\">“Servidor FTP” ya está en los Ajustes Rápidos</string>\n    <string name=\"ftp_server_add_tile_result_error\">Error al añadir “Servidor FTP” a los Ajustes Rápidos</string>\n    <string name=\"ftp_server_configuration_title\">Configuración</string>\n    <string name=\"ftp_server_anonymous_login_title\">Ingreso anónimo</string>\n    <string name=\"ftp_server_username_title\">Nombre de usuario</string>\n    <string name=\"ftp_server_password_title\">Contraseña</string>\n    <string name=\"ftp_server_port_title\">Puerto</string>\n    <string name=\"ftp_server_home_directory_title\">Carpeta raíz</string>\n    <string name=\"ftp_server_writable_title\">Permitir escritura</string>\n    <string name=\"settings_title\">Ajustes</string>\n    <string name=\"settings_interface_title\">Interfaz</string>\n    <string name=\"settings_locale_title\">Idioma</string>\n    <string name=\"settings_theme_color_title\">Color de tema</string>\n    <string name=\"settings_theme_color_summary\">Color que aparece más frecuentemente en la aplicación</string>\n    <string name=\"settings_material_design_3_title\">Diseño Material 3</string>\n    <string name=\"settings_night_mode_title\">Tema oscuro</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>Seguir al sistema</item>\n        <item>Desactivado</item>\n        <item>Activado</item>\n        <item>En base al horario</item>\n        <item>En base al ahorro de batería</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">Usar negro puro</string>\n    <string name=\"settings_file_list_animation_title\">Animación en la lista de archivos</string>\n    <string name=\"settings_file_name_ellipsize_title\">En nombres de archivo extensos</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>Mostrar la última parte</item>\n        <item>Mostrar el principio y el final</item>\n        <item>Mostrar la primera parte</item>\n        <item>Animar</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">Comportamiento</string>\n    <string name=\"settings_default_directory_title\">Carpeta predeterminada</string>\n    <string name=\"settings_standard_directories_title\">Carpetas estándar</string>\n    <string name=\"settings_standard_directories_summary_empty\">Ninguna carpeta estándar habilitada</string>\n    <string name=\"settings_bookmark_directory_screenshots\">Capturas de pantalla</string>\n    <string name=\"settings_bookmark_directories_title\">Carpetas fijadas</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">Sin carpetas fijadas</string>\n    <string name=\"settings_root_strategy_title\">Modo de acceso administrativo</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"one\">Tienes %1$,d operaciones de archivo ejecutándose y cambiar el modo de acceso administrativo puede causar errores inesperados. ¿Estás seguro que quieres cambiarlo ahora?</item>\n        <item quantity=\"many\">Tienes %1$,d operaciones de archivo ejecutándose y cambiar el modo de acceso administrativo puede causar errores inesperados. ¿Estás seguro que quieres cambiarlo ahora?</item>\n        <item quantity=\"other\">Tiene %1$,d operaciones de archivo ejecutándose y cambiar el modo de acceso administrativo puede causar errores inesperados. ¿Está seguro de que quiere cambiarlo ahora?</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>Sólo acceso normal</item>\n        <item>Automático</item>\n        <item>Sólo acceso administrativo</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">Codificación del nombre de archivo</string>\n    <string name=\"settings_open_apk_default_action_title\">Al abrir un paquete de Android</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>Instalar</item>\n        <item>Ver contenido</item>\n        <item>Preguntar qué hacer</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_title\">Mostrar miniaturas de documentos PDF</string>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">Podría hacer que la aplicación se vuelva inestable en versiones antiguas de Android</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">Leer archivos remotos para obtener una miniatura</string>\n\n    <string name=\"about_title\">Información</string>\n    <string name=\"about_version_title\">Versión</string>\n    <string name=\"about_github_title\">Ver en GitHub</string>\n    <string name=\"about_licenses_title\">Licencias</string>\n    <string name=\"about_privacy_policy_title\">Política de privacidad</string>\n    <string name=\"about_author_title\">Autor</string>\n    <string name=\"about_author_name_title\">Hai Zhang</string>\n    <string name=\"about_author_github_title\">Seguir en GitHub</string>\n    <string name=\"about_author_twitter_title\">Seguir en Twitter</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-eu/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">Fitxategia</string>\n    <string name=\"file_type_name_apk\">Android paketea</string>\n    <string name=\"file_type_name_archive\">%1$s artxiboa</string>\n    <string name=\"file_type_name_audio\">%1$s audioa</string>\n    <string name=\"file_type_name_calendar\">%1$s egutegia</string>\n    <string name=\"file_type_name_certificate\">%1$s ziurtagiria</string>\n    <string name=\"file_type_name_code\">%1$s dokumentua</string>\n    <string name=\"file_type_name_contact\">Bisita-txartel elektronikoa</string>\n    <string name=\"file_type_name_directory\">Karpeta</string>\n    <string name=\"file_type_name_document\">%1$s dokumentua</string>\n    <string name=\"file_type_name_ebook\">%1$s e-liburua</string>\n    <string name=\"file_type_name_email\">%1$s email mezua</string>\n    <string name=\"file_type_name_font\">%1$s tipografia</string>\n    <string name=\"file_type_name_generic\">%1$s fitxategia</string>\n    <string name=\"file_type_name_image\">%1$s irudia</string>\n    <string name=\"file_type_name_pdf\">PDF dokumentua</string>\n    <string name=\"file_type_name_presentation\">%1$s aurkezpena</string>\n    <string name=\"file_type_name_spreadsheet\">%1$s kalkulu-orria</string>\n    <string name=\"file_type_name_text\">%1$s dokumentua</string>\n    <string name=\"file_type_name_text_plain\">Testu soileko dokumentua</string>\n    <string name=\"file_type_name_video\">%1$s bideoa</string>\n    <string name=\"file_type_name_word\">Word dokumentua</string>\n    <string name=\"file_type_name_powerpoint\">PowerPoint aurkezpena</string>\n    <string name=\"file_type_name_excel\">Excel kalkulu-orria</string>\n    <string name=\"file_type_name_posix_character_device\">Karaktere gailua</string>\n    <string name=\"file_type_name_posix_block_device\">Bloke gailua</string>\n    <string name=\"file_type_name_posix_fifo\">Hodia</string>\n    <string name=\"file_type_name_posix_symbolic_link\">Esteka</string>\n    <string name=\"file_type_name_posix_socket\">Socketa</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">Esteka (hautsita)</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-eu/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <string name=\"app_name\">Material Files</string>\n\n    <string name=\"close\">Itxi</string>\n    <string name=\"copy\">Kopiatu</string>\n    <string name=\"cut\">Moztu</string>\n    <string name=\"default_\">Lehenetsia</string>\n    <string name=\"delete\">Ezabatu</string>\n    <string name=\"discard\">Baztertu</string>\n    <string name=\"empty\">Hustu</string>\n    <string name=\"empty_placeholder\">(Hutsik)</string>\n    <string name=\"error\">Errorea</string>\n    <string name=\"install\">Instalatu</string>\n    <string name=\"keep_editing\">Jarraitu editatzen</string>\n    <string name=\"loading\">Kargatzen…</string>\n    <string name=\"maybe_later\">Agian geroago</string>\n    <string name=\"merge\">Bateratu</string>\n    <string name=\"none\">Bat ere ez</string>\n    <string name=\"paste\">Itsatsi</string>\n    <string name=\"refresh\">Freskatu</string>\n    <string name=\"reload\">Birkargatu</string>\n    <string name=\"remove\">Kendu</string>\n    <string name=\"rename\">Aldatu izena</string>\n    <string name=\"replace\">Ordeztu</string>\n    <string name=\"reset\">Leheneratu</string>\n    <string name=\"retry\">Saiatu berriro</string>\n    <string name=\"save\">Gorde</string>\n    <string name=\"search\">Bilatu</string>\n    <string name=\"select\">Hautatu</string>\n    <string name=\"select_all\">Hautatu guztiak</string>\n    <string name=\"share\">Partekatu</string>\n    <string name=\"skip\">Saltatu</string>\n    <string name=\"stop\">Gelditu</string>\n    <string name=\"unknown\">Ezezaguna</string>\n    <string name=\"view\">Ikusi</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">Ez da aplikaziorik aurkitu ekintza hau burutzeko</string>\n    <string name=\"open_settings\">Ireki ezarpenak</string>\n    <string name=\"shortcut_created\">Laster-marka sortuta</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"one\">byte %1$,d</item>\n        <item quantity=\"other\">%1$,d byte</item>\n    </plurals>\n\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">Aplikazioak fitxategiak atzitzeko baimena behar du. Sakatu \\\"BAIMENDU\\\" hurrengo elkarrizketa-koadroan.</string>\n    <string name=\"storage_permission_permanently_denied_message\">Aplikazioak fitxategiak atzitzeko baimena behar du. Eman \\\"Memoria\\\" (sic)  baimena sistemaren ezarpenetan.</string>\n    <string name=\"notification_channel_background_activity_start_name\">Ekintzak bigarren planoan</string>\n    <string name=\"notification_channel_background_activity_start_description\">Egin gauzak aplikazioa bigarren planoan dagoenean</string>\n    <string name=\"notification_channel_file_job_name\">Fitxategi-eragiketak</string>\n    <string name=\"notification_channel_file_job_description\">Erakutsi eta kontrolatu fitxategi-eragiketak</string>\n    <string name=\"notification_channel_ftp_server_name\">FTP zerbitzaria</string>\n    <string name=\"notification_channel_ftp_server_description\">Erakutsi eta kontrolatu FTP zerbitzaria</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"one\">Fitxategi %1$,d konprimitzeko prestatzen (%2$s)</item>\n        <item quantity=\"other\">%1$,d fitxategi konprimitzeko prestatzen (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">\\\"%1$s\\\" konprimitzen hona: \\\"%2$s\\\"</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"one\">Fitxategi %1$,d konprimitzen hona: \\\"%2$s\\\"</item>\n        <item quantity=\"other\">%1$,d fitxategi konprimitzen hona: \\\"%2$s\\\"</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"one\">Fitxategi %1$,d kopiatzeko prestatzen (%2$s)</item>\n        <item quantity=\"other\">%1$,d fitxategi kopiatzeko prestatzen (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">\\\"%1$s\\\" hona kopiatzen: \\\"%2$s\\\"</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"one\">Fitxategi %1$,d hona kopiatzen: \\\"%2$s\\\"</item>\n        <item quantity=\"other\">%1$,d fitxategi hona kopiatzen: \\\"%2$s\\\"</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"one\">Fitxategi %1$ erauzteko prestatzen (%2$s)</item>\n        <item quantity=\"other\">%1$ fitxategi erauzteko prestatzen (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">%1$s\\\" hona erauzten: \\\"%2$s</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"one\">Fitxategi %1$,d hona erauzten: \\\"%2$s\\\"</item>\n        <item quantity=\"other\">%1$,d fitxategi hona erauzten: \\\"%2$s\\\"</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"one\">Fitxategi %1$,d mugitzeko prestatzen (%2$s)</item>\n        <item quantity=\"other\">%1$,d fitxategi mugitzeko prestatzen (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">%1$s\\\" hona mugitzen: \\\"%2$s</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"one\">Fitxategi %1$,d hona mugitzen: \\\"%2$s\\\"</item>\n        <item quantity=\"other\">%1$,d fitxategi hona mugitzen: \\\"%2$s\\\"</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"one\">Fitxategi %1$,d ezabatzeko prestatzen</item>\n        <item quantity=\"other\">%1$,d fitxategi ezabatzeko prestatzen</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\"> “%1$s” ezabatzen</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"one\">Fitxategi %1$,d ezabatzen</item>\n        <item quantity=\"other\">%1$,d fitxategi ezabatzen</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"one\">Fitxategi %1$,den jabetza aldatzeko prestatzen</item>\n        <item quantity=\"other\">%1$,d fitxategien jabetza aldatzeko prestatzen</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">\\\"%1$s\\\"-ren jabetza aldatzen</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"one\">Fitxategi %1$,den jabetza aldatzen</item>\n        <item quantity=\"other\">%1$,d fitxategien jabetza aldatzen</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"one\">Fitxategi %1$,den taldea aldatzeko prestatzen</item>\n        <item quantity=\"other\">%1$,d fitxategien taldea aldatzeko prestatzen</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">\\\"%1$s\\\"-ren taldea aldatzen</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"one\">Fitxategi %1$,den taldea aldatzen</item>\n        <item quantity=\"other\">%1$,d fitxategien taldea aldatzen</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"one\">Fitxategi %1$,den modua aldatzeko prestatzen</item>\n        <item quantity=\"other\">%1$,d fitxategien modua aldatzeko prestatzen</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">\\\"%1$s\\\"-ren modua aldatzen</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"one\">Fitxategi %1$,den modua aldatzen</item>\n        <item quantity=\"other\">%1$,d fitxategien modua aldatzen</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Fitxategi %1$,den SELinux testuingurua aldatzeko prestatzen</item>\n        <item quantity=\"other\">%1$,d fitxategien SELinux testuingurua aldatzeko prestatzen</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">\\\"%1$s\\\"-ren SELinux testuingurua aldatzen</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Fitxategi %1$,den SELinux testuingurua aldatzen</item>\n        <item quantity=\"other\">%1$,d fitxategien SELinux testuingurua aldatzen</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Fitxategi %1$,den SELinux testuingurua berrezartzeko prestatzen</item>\n        <item quantity=\"other\">%1$,d fitxategien SELinux testuingurua berrezartzeko prestatzen</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">\\\"%1$s\\\"-ren SELinux testuingurua berrezartzen</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Fitxategi %1$,den SELinux testuingurua aldatzen</item>\n        <item quantity=\"other\">%1$,d fitxategien SELinux testuingurua aldatzen</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">“%1$s” idazten</string>\n    <string name=\"file_job_create_error_title\">Errorea sortzean</string>\n    <string name=\"file_job_create_error_message_format\">Errorea gertatu da “%1$s” sortzean.\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">Ezin da karpeta bat bere gainean kopiatu</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">Ezin da karpeta bat bere gainean erauzi</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">Ezin da karpeta bat bere gainean mugitu</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">Helmuga karpeta jatorrizko karpetaren barruan dago.</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">Ezin da fitxategi bat bere gainean kopiatu</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">Ezin da fitxategi bat bere gainean erauzi</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">Ezin da fitxategi bat bere gainean mugitu</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">Helmugak jatorrizko fitxategia gainidatziko luke.</string>\n    <string name=\"file_job_replace_title_format\">Ordeztu “%1$s” fitxategia?</string>\n    <string name=\"file_job_replace_message_format\">Badago izen bereko fitxategi bat hemen \\\"%1$s\\\".\\nOrdezten bada edukiak gainidatziko dira</string>\n    <string name=\"file_job_replace_target_name\">Jatorrizko fitxategia</string>\n    <string name=\"file_job_replace_source_name\">Ordeztu honekin</string>\n    <string name=\"file_job_merge_title_format\">Bateratu \\\"%1$s\\\" karpeta?</string>\n    <string name=\"file_job_merge_copy_message_format\">Bateratzeak baieztapena eskatuko du karpetako fitxategiak kopiatzen ari direnekin ordeztu aurretik, talka badago.</string>\n    <string name=\"file_job_merge_extract_message_format\">Bateratzeak baieztapena eskatuko du karpetako fitxategiak erauzten ari direnekin ordeztu aurretik, talka badago.</string>\n    <string name=\"file_job_merge_move_message_format\">Bateratzeak baieztapena eskatuko du karpetako fitxategiak mugitzen ari direnekin ordeztu aurretik, talka badago.</string>\n    <string name=\"file_job_merge_target_name\">Jatorrizko karpeta</string>\n    <string name=\"file_job_merge_source_name\">Bateratu honekin</string>\n    <string name=\"file_job_conflict_show_name\">Aukeratu izen berria helmugarentzat</string>\n    <string name=\"file_job_conflict_name_hint\">Izen berria</string>\n    <string name=\"file_job_archive_error_title_format\">Errorea \\\"%1$s\\\" konprimitzean</string>\n    <string name=\"file_job_archive_error_message_format\">Errorea gertatu da “%1$s” konprimitzean.\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">Errorea \\\"%1$s\\\" kopiatzean</string>\n    <string name=\"file_job_copy_error_message_format\">Errorea gertatu da fitxategia “%1$s”-ra kopiatzean.\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">Errorea \\\"%1$s\\\" erauztean</string>\n    <string name=\"file_job_extract_error_message_format\">Errorea gertatu da fitxategia “%1$s”-ra erauztean.\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">Errorea \\\"%1$s\\\" mugitzean</string>\n    <string name=\"file_job_move_error_message_format\">Errorea gertatu da fitxategia “%1$s”-ra mugitzean.\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">Errorea ezabatzean</string>\n    <string name=\"file_job_delete_error_message_format\">Errorea gertatu da “%1$s” ezabatzean.\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">Errorea \\\"%1$s\\\"-ren jabetza aldatzean</string>\n    <string name=\"file_job_set_owner_error_message_format\">Errorea gertatu da “%1$s”-ren jabetza aldatzean.\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">Errorea \\\"%1$s\\\"-ren taldea aldatzean</string>\n    <string name=\"file_job_set_group_error_message_format\">Errorea gertatu da “%1$s”-ren taldea aldatzean.\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">Errorea \\\"%1$s\\\"-ren modua aldatzean</string>\n    <string name=\"file_job_set_mode_error_message_format\">Errorea gertatu da “%1$s”-ren modua aldatzean.\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">Errorea \\\"%1$s\\\"-ren SELinux testuingurua aldatzean</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">Errorea gertatu da “%1$s”-ren SELinux testuingurua aldatzean.\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">Errorea SELinux testuingurua berrezartzean</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">Errorea gertatu da “%1$s”-ren SELinux testuingurua berrezartzean.\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">Errorea idaztean</string>\n    <string name=\"file_job_write_error_message_format\">Errorea gertatu da “%1$s” idaztean.\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">Errorea gertatu da “%1$s”-ren informazioa jasotzean.</string>\n    <string name=\"file_job_remount_format\">Birmuntatu \\\"%1$s\\\" irakurri-idatzi moduan </string>\n    <string name=\"file_job_remount_loading_format\">\\\"%1$s\\\" irakurri-idatzi moduan birmuntatzen…</string>\n    <string name=\"file_job_remount_success_format\">\\\"%1$s\\\" irakurri-idatzi moduan birmuntatu da</string>\n    <string name=\"file_job_action_all\">Aplikatu ekintza hau fitxategi guztiei</string>\n\n    <string name=\"file_open_apk_message\">Hau Android pakete bat da. Instalatu nahi duzu, ala edukiak ikusi?</string>\n    <string name=\"file_open_as_title\">Ireki honela…</string>\n    <string name=\"file_open_as_title_format\">Ireki “%1$s” honela</string>\n    <string name=\"file_open_as_type_text\">Testua</string>\n    <string name=\"file_open_as_type_image\">Irudia</string>\n    <string name=\"file_open_as_type_audio\">Audioa</string>\n    <string name=\"file_open_as_type_video\">Bideoa</string>\n    <string name=\"file_open_as_type_directory\">Karpeta</string>\n    <string name=\"file_open_as_type_any\">Besteak</string>\n    <string name=\"file_install_apk_from_background_title_format\">\\\"%1$s\\\" instalatzeko prest dago</string>\n    <string name=\"file_install_apk_from_background_text\">Sakatu instalatzeko</string>\n    <string name=\"file_open_from_background_title_format\">\\\"%1$s\\\" irekitzeko prest dago</string>\n    <string name=\"file_open_from_background_text\">Sakatu irekitzeko</string>\n    <string name=\"file_name_error_empty\">Fitxategiaren izena ezin da hutsik egon</string>\n    <string name=\"file_name_error_invalid\">Fitxategi-izen baliogabea</string>\n    <string name=\"file_name_error_already_exists\">Badago izen bereko fitxategi bat aurretik</string>\n    <string name=\"file_delete_message_file_format\">Ezabatu “%1$s”?</string>\n    <string name=\"file_delete_message_directory_format\">Ezabatu \\\"%1$s\\\" karpeta eta bere edukiak?</string>\n    <string name=\"file_create_archive_title\">Sortu artxiboa</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_add_bookmark_success\">Laster-marka gehitu da</string>\n    <string name=\"file_create_file_title\">Fitxategi berria</string>\n    <string name=\"file_create_directory_title\">Karpeta berria</string>\n\n    <string name=\"file_list_title\">Fitxategiak</string>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"one\">Karpeta %1$,d</item>\n        <item quantity=\"other\">%1$,d karpeta</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"one\">Fitxategi %1$,d</item>\n        <item quantity=\"other\">%1$,d fitxategi</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">,\\u0020</string>\n    <string name=\"file_list_action_sort_by_name\">Izena</string>\n    <string name=\"file_list_action_sort_by_type\">Mota</string>\n    <string name=\"file_list_action_sort_by_size\">Tamaina</string>\n    <string name=\"file_list_action_sort_by_last_modified\">Azken aldaketa</string>\n    <string name=\"file_list_action_sort_order_ascending\">Gorantza</string>\n    <string name=\"file_list_action_sort_directories_first\">Karpetak aurretik</string>\n    <string name=\"file_list_action_new_task\">Leiho berria</string>\n    <string name=\"file_list_action_navigate_up\">Joan gora</string>\n    <string name=\"file_list_action_show_hidden_files\">Erakutsi ezkutatutako fitxategiak</string>\n    <string name=\"file_list_action_copy_path\">Kopiatu bidea</string>\n    <string name=\"file_list_action_open_in_terminal\">Ireki terminalean</string>\n    <string name=\"file_list_action_add_bookmark\">Gehitu laster-marka</string>\n    <string name=\"file_list_action_create_shortcut\">Sortu laster-marka</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">Ireki leiho berrian</string>\n    <string name=\"file_list_paste_move_title_format\">%1$,d lekuz aldatzen</string>\n    <string name=\"file_list_paste_copy_title_format\">%1$,d kopiatzen</string>\n    <string name=\"file_list_paste_extract_title_format\">%1$,d erauzten</string>\n    <string name=\"file_list_paste_action_extract_here\">Erauzi hemen</string>\n    <string name=\"file_list_empty\">Fitxategirik ez</string>\n    <string name=\"file_list_action_create_file\">Fitxategia</string>\n    <string name=\"file_list_action_create_directory\">Karpeta</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">Ireki honekin</string>\n    <string name=\"file_item_action_extract\">Erauzi</string>\n    <string name=\"file_item_action_archive\">Konprimitu</string>\n    <string name=\"file_item_action_properties\">Propietateak</string>\n\n    <string name=\"file_properties_title_format\">“%1$s” propietateak</string>\n    <string name=\"file_properties_basic\">Oinarrizkoa</string>\n    <string name=\"file_properties_basic_name\">Izena</string>\n    <string name=\"file_properties_basic_type\">Mota</string>\n    <string name=\"file_properties_basic_type_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">Esteka hona %1$s (%2$s)</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">Estekaren helmuga</string>\n    <string name=\"file_properties_basic_size\">Tamaina</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_contents\">Edukiak</string>\n    <string name=\"file_properties_basic_last_modification_time\">Azken aldaketa</string>\n    <string name=\"file_properties_basic_parent_directory\">Karpeta gurasoa</string>\n    <string name=\"file_properties_basic_archive_file\">Artxibo fitxategia</string>\n    <string name=\"file_properties_basic_archive_entry\">Artxibo sarrera</string>\n    <string name=\"file_properties_basic_free_space\">Espazio librea</string>\n    <string name=\"storage_file_system_root_title\">Erroa</string>\n    <string name=\"navigation_storage_subtitle_format\">%1$s libre %2$s guztira</string>\n    <string name=\"navigation_add_storage\">Gehitu biltegia…</string>\n    <string name=\"navigation_standard_directory_alarms\">Alarmak</string>\n    <string name=\"navigation_standard_directory_dcim\">DCIM</string>\n    <string name=\"navigation_standard_directory_documents\">Dokumentuak</string>\n    <string name=\"navigation_standard_directory_downloads\">Deskargak</string>\n    <string name=\"navigation_standard_directory_movies\">Filmak</string>\n    <string name=\"navigation_standard_directory_music\">Musika</string>\n    <string name=\"navigation_standard_directory_notifications\">Jakinarazpenak</string>\n    <string name=\"navigation_standard_directory_pictures\">Argazkiak</string>\n    <string name=\"navigation_standard_directory_podcasts\">Podcastak</string>\n    <string name=\"navigation_standard_directory_ringtones\">Dei-doinuak</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">Karpetaren laster-marka</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">Izena</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">Bidea</string>\n    <string name=\"image_viewer_title\">Irudi ikuslea</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"text_editor_title\">Testu editorea</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">Gordeta</string>\n    <string name=\"text_editor_reload_message\">Ziur birkargatu nahi duzula? Dokumentu honi egindako gorde gabeko aldaketak galduko dira.</string>\n    <string name=\"text_editor_close_message\">Ziur dokumentu honi egindako gorde gabeko aldaketak baztertu nahi dituzula?</string>\n\n    <string name=\"ftp_server_title\">FTP zerbitzaria</string>\n    <string name=\"ftp_server_state_title\">Egoera</string>\n    <string name=\"ftp_server_state_summary_starting\">Hasten…</string>\n    <string name=\"ftp_server_state_summary_running\">Abian</string>\n    <string name=\"ftp_server_state_summary_stopping\">Gelditzen…</string>\n    <string name=\"ftp_server_state_summary_stopped\">Hasi gabe</string>\n    <string name=\"ftp_server_url_title\">URLa</string>\n    <string name=\"ftp_server_url_menu_copy_url\">Kopiatu URLa</string>\n    <string name=\"ftp_server_url_menu_copy_password\">Kopiatu pasahitza</string>\n    <string name=\"ftp_server_configuration_title\">Konfigurazioa</string>\n    <string name=\"ftp_server_anonymous_login_title\">Saio anonimoa</string>\n    <string name=\"ftp_server_username_title\">Erabiltzaile-izena</string>\n    <string name=\"ftp_server_password_title\">Pasahitza</string>\n    <string name=\"ftp_server_port_title\">Ataka</string>\n    <string name=\"ftp_server_home_directory_title\">Erro karpeta</string>\n    <string name=\"ftp_server_writable_title\">Baimendu idaztea</string>\n    <string name=\"settings_title\">Ezarpenak</string>\n    <string name=\"settings_interface_title\">Interfazea</string>\n    <string name=\"settings_night_mode_title\">Gauerako modua</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>Jarraitu sistemari</item>\n        <item>Ez</item>\n        <item>Bai</item>\n        <item>Denboraren arabera</item>\n        <item>Bateria aurrezlearen arabera</item>\n    </string-array>\n    <string name=\"settings_file_list_animation_title\">Fitxategien zerrendaren animazioa</string>\n    <string name=\"settings_behavior_title\">Portaera</string>\n    <string name=\"settings_default_directory_title\">Lehenetsitako karpeta</string>\n    <string name=\"settings_standard_directories_title\">Karpeta estandarrak</string>\n    <string name=\"settings_standard_directories_summary_empty\">Ez da karpeta estandarrik gaitu</string>\n    <string name=\"settings_bookmark_directory_screenshots\">Pantaila-argazkiak</string>\n    <string name=\"settings_bookmark_directories_title\">Karpeten laster-markak</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">Karpeten laster-markarik ez</string>\n    <string name=\"settings_root_strategy_title\">Root sarbidea modua</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"one\">Eragiketa %1$,d duzu abian eta root sarbide modua orain aldatzeak ustekabeko errorea sortu dezake. Ziur orain aldatu nahi duzula?</item>\n        <item quantity=\"other\">%1$,d eragiketa dituzu abian eta root sarbide modua orain aldatzeak ustekabeko errorea sortu dezake. Ziur orain aldatu nahi duzula?</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>Sarbide arrunta soilik</item>\n        <item>Automatic</item>\n        <item>Root sarbidea soilik</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">Artxibo fitxategi-izenaren kodeketa</string>\n    <string name=\"settings_open_apk_default_action_title\">Ireki Android paketea</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>Instalatu</item>\n        <item>Ikusi edukiak</item>\n        <item>Galdetu zer egin</item>\n    </string-array>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">Irakurri iruditxoen urruneko fitxategiak</string>\n\n    <string name=\"about_title\">Honi buruz</string>\n    <string name=\"about_version_title\">Bertsioa</string>\n    <string name=\"about_github_title\">Ikusi GitHub-en</string>\n    <string name=\"about_licenses_title\">Lizentziak</string>\n    <string name=\"about_privacy_policy_title\">Pribatutasun gidalerroak</string>\n    <string name=\"about_author_title\">Egilea</string>\n    <string name=\"about_author_name_title\">Hai Zhang</string>\n    <string name=\"about_author_github_title\">Jarraitu GitHub-en</string>\n    <string name=\"about_author_twitter_title\">Jarraitu Twitter-en</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-fa/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">پرونده</string>\n    <string name=\"file_type_name_apk\">بستهٔ اندروید</string>\n    <string name=\"file_type_name_archive\">%1$s بایگانی</string>\n    <string name=\"file_type_name_audio\">%1$s صدا</string>\n    <string name=\"file_type_name_calendar\">%1$s تقویم</string>\n    <string name=\"file_type_name_certificate\">%1$s گواهی</string>\n    <string name=\"file_type_name_code\">%1$s سند</string>\n    <string name=\"file_type_name_contact\">کارت ویزیت الکترونیکی</string>\n    <string name=\"file_type_name_directory\">شاخه</string>\n    <string name=\"file_type_name_document\">%1$s سند</string>\n    <string name=\"file_type_name_ebook\">%1$s کتاب الکترونیک</string>\n    <string name=\"file_type_name_email\">%1$s پیام ایمیل</string>\n    <string name=\"file_type_name_font\">%1$s قلم</string>\n    <string name=\"file_type_name_generic\">%1$s پرونده</string>\n    <string name=\"file_type_name_image\">%1$s تصویر</string>\n    <string name=\"file_type_name_pdf\">سند پی‌دی‌اف</string>\n    <string name=\"file_type_name_presentation\">%1$s ارائه</string>\n    <string name=\"file_type_name_spreadsheet\">%1$s صفحه گسترده</string>\n    <string name=\"file_type_name_text\">%1$s سند</string>\n    <string name=\"file_type_name_text_plain\">سند متن خام</string>\n    <string name=\"file_type_name_video\">%1$s ویدیو</string>\n    <string name=\"file_type_name_word\">سند ورد</string>\n    <string name=\"file_type_name_powerpoint\">ارائه پاورپوینت</string>\n    <string name=\"file_type_name_excel\">صفحه گسترده اکسل</string>\n    <string name=\"file_type_name_posix_character_device\">افزارهٔ نویسه</string>\n    <string name=\"file_type_name_posix_block_device\">افزارهٔ بلوک</string>\n    <string name=\"file_type_name_posix_fifo\">لوله</string>\n    <string name=\"file_type_name_posix_symbolic_link\">پیوند</string>\n    <string name=\"file_type_name_posix_socket\">سوکت</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">پیوند (خراب)</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-fa/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:ignore=\"ImpliedQuantity\">\n\n    <string name=\"app_name\">پرونده‌های متریال</string>\n\n    <string name=\"close\">بستن</string>\n    <string name=\"copy\">رونوشت</string>\n    <string name=\"cut\">برش</string>\n    <string name=\"default_\">پیش‌گزیده</string>\n    <string name=\"delete\">حذف</string>\n    <string name=\"discard\">دورانداختن</string>\n    <string name=\"edit\">ویرایش</string>\n    <string name=\"empty\">خالی</string>\n    <string name=\"empty_placeholder\">(خالی)</string>\n    <string name=\"error\">خطا</string>\n    <string name=\"hide\">پنهان</string>\n    <string name=\"install\">نصب</string>\n    <string name=\"keep_editing\">نگه‌داشتن ویرایش</string>\n    <string name=\"loading\">در حال بار کردن…</string>\n    <string name=\"maybe_later\">شاید بعداً</string>\n    <string name=\"merge\">ادغام</string>\n    <string name=\"none\">هیچ</string>\n    <string name=\"open_navigation_drawer\">گشودن کشوی ناوبری</string>\n    <string name=\"paste\">جای‌گذاری</string>\n    <string name=\"refresh\">نوسازی</string>\n    <string name=\"reload\">بار کردن دوباره</string>\n    <string name=\"remove\">حذف</string>\n    <string name=\"rename\">تغییر نام</string>\n    <string name=\"replace\">جایگزینی</string>\n    <string name=\"reset\">بازنشانی</string>\n    <string name=\"retry\">تلاش دوباره</string>\n    <string name=\"save\">ذخیره</string>\n    <string name=\"search\">جست‌وجو</string>\n    <string name=\"select\">گزینش</string>\n    <string name=\"select_all\">گزینش همه</string>\n    <string name=\"share\">هم‌رسانی</string>\n    <string name=\"show\">نمایش</string>\n    <string name=\"skip\">پرش</string>\n    <string name=\"stop\">توقّف</string>\n    <string name=\"unknown\">ناشناس</string>\n    <string name=\"view\">دیدن</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">برنامه‌ای برای اداره کردن این کنش یافت نشد</string>\n    <string name=\"open_settings\">گشودن تنظیمات</string>\n    <string name=\"shortcut_created\">میان‌بر ایجاد شد</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"one\">%1$,d بایت</item>\n        <item quantity=\"other\">%1$,d بایت</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">برنامه برای مدیریت همهٔ پرونده‌ها نیاز به دسترسی دارد. لطفاً اجازهٔ دسترسی را در تنظیمات سامانهٔ پیش‌رو، بدهید.</string>\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">برنامه اجازهٔ دسترسی به پرونده‌ها را نیاز دارد. لطفاً در کادر گفت‌وگوی سامانه که به زودی می‌آید، روی «پذیرش» کلیک کنید.</string>\n    <string name=\"storage_permission_permanently_denied_message\">برنامه اجازهٔ دسترسی به پرونده‌ها را نیاز دارد. لطفاً اجازهٔ «ذخیره‌سازی» را در تنظیمات سامانه بدهید.</string>\n    <string name=\"notification_channel_background_activity_start_name\">کنش‌های در پس‌زمیه</string>\n    <string name=\"notification_channel_background_activity_start_description\">هنگامی که برنامه در پس‌زمینه است، کنش‌ها را انجام دهید</string>\n    <string name=\"notification_channel_file_job_name\">عملیات‌های پرونده</string>\n    <string name=\"notification_channel_file_job_description\">نمایش و کنترل عملیات‌های پرونده</string>\n    <string name=\"notification_channel_ftp_server_name\">کارساز اف‌تی‌پی</string>\n    <string name=\"notification_channel_ftp_server_description\">نمایش و مهار کارساز اف‌تی‌پی</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"one\">در حال آماده‌سازی برای فشرده کردن %1$,d پرونده (⁨%2$s⁩)</item>\n        <item quantity=\"other\">در حال آماده‌سازی برای فشرده کردن %1$,d پرونده (⁨%2$s⁩)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">در حال فشرده کردن «⁨%1$s⁩» به «⁨%2$s⁩»</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"one\">در حال فشرده کردن %1$,d پرونده به «⁨%2$s⁩»</item>\n        <item quantity=\"other\">در حال فشرده کردن %1$,d پرونده به «⁨%2$s⁩»</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"one\">در حال آماده‌سازی برای رونوشت %1$,d پرونده (⁨%2$s⁩)</item>\n        <item quantity=\"other\">در حال آماده‌سازی برای رونوشت %1$,d پرونده (⁨%2$s⁩)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">در حال رونوشت «⁨%1$s⁩» به «⁨%2$s⁩»</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"one\">در حال رونوشت %1$,d پرونده به «⁨%2$s⁩»</item>\n        <item quantity=\"other\">در حال رونوشت %1$,d پرونده به «⁨%2$s⁩»</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"one\">در حال آماده‌سازی برای استخراج %1$,d پرونده (⁨%2$s⁩)</item>\n        <item quantity=\"other\">در حال آماده‌سازی برای استخراج %1$,d پرونده (⁨%2$s⁩)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">در حال استخراج «⁨%1$s⁩» به «⁨%2$s⁩»</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"one\">در حال استخراج %1$,d پرونده به «⁨%2$s⁩»</item>\n        <item quantity=\"other\">در حال استخراج %1$,d پرونده به «⁨%2$s⁩»</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"one\">در حال آماده‌سازی برای جابه‌جایی %1$,d پرونده (⁨%2$s⁩)</item>\n        <item quantity=\"other\">در حال آماده‌سازی برای جابه‌جایی %1$,d پرونده (⁨%2$s⁩)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">در حال جابه‌جایی «⁨%1$s⁩» به «⁨%2$s⁩»</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"one\">در حال جابه‌جایی %1$,d پرونده به «⁨%2$s⁩»</item>\n        <item quantity=\"other\">در حال جابه‌جایی %1$,d پرونده به «⁨%2$s⁩»</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">⁦%1$,d / %2$,d⁩</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"one\">در حال آماده‌سازی برای حذف %1$,d پرونده</item>\n        <item quantity=\"other\">در حال آماده‌سازی برای حذف %1$,d پرونده</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">در حال حذف «⁨%1$s⁩»</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"one\">در حال حذف %1$,d پرونده</item>\n        <item quantity=\"other\">در حال حذف %1$,d پرونده</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"one\">در حال آماده‌سازی برای تغییر مالک %1$,d پرونده</item>\n        <item quantity=\"other\">در حال آماده‌سازی برای تغییر مالک %1$,d پرونده</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">در حال تغییر مالک «⁨%1$s⁩»</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"one\">در حال تغییر مالک %1$,d پرونده</item>\n        <item quantity=\"other\">در حال تغییر مالک %1$,d پرونده</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"one\">در حال آماده‌سازی برای تغییر گروه %1$,d پرونده</item>\n        <item quantity=\"other\">در حال آماده‌سازی برای تغییر گروه %1$,d پرونده</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">در حال تغییر گروه «⁨%1$s⁩»</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"one\">در حال تغییر گروه %1$,d پرونده</item>\n        <item quantity=\"other\">در حال تغییر گروه %1$,d پرونده</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"one\">در حال آماده سازی برای تغییر حالت %1$,d پرونده</item>\n        <item quantity=\"other\">در حال آماده سازی برای تغییر حالت %1$,d پرونده</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">در حال تغییر حالت «⁨%1$s⁩»</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"one\">در حال تغییر حالت %1$,d پرونده</item>\n        <item quantity=\"other\">در حال تغییر حالت %1$,d پرونده</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">در حال آماده‌سازی برای تغییر مفاد اس‌ای‌لینوکس برای %1$,d پرونده</item>\n        <item quantity=\"other\">در حال آماده‌سازی برای تغییر مفاد اس‌ای‌لینوکس برای %1$,d پرونده</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">در حال تغییر مفاد اس‌ای‌لینوکس «⁨%1$s⁩»</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">در حال تغییر مفاد اس‌ای‌لینوکس برای %1$,d پرونده</item>\n        <item quantity=\"other\">در حال تغییر مفاد اس‌ای‌لینوکس برای %1$,d پرونده</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">در حال آماده‌سازی برای بازگردانی مفاد اس‌ای‌لینوکس برای %1$,d پرونده</item>\n        <item quantity=\"other\">در حال آماده‌سازی برای بازگردانی مفاد اس‌ای‌لینوکس برای %1$,d پرونده</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">در حال بازگردانی مفاد اس‌ای‌لینوکس «⁨%1$s⁩»</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">در حال بازگردانی مفاد اس‌ای‌لینوکس برای %1$,d پرونده</item>\n        <item quantity=\"other\">در حال بازگردانی مفاد اس‌ای‌لینوکس برای %1$,d پرونده</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">⁦%1$,d / %2$,d⁩</string>\n    <string name=\"file_job_write_notification_title_format\">در حال نوشتن «⁨%1$s⁩»</string>\n    <string name=\"file_job_create_error_title\">خطا هنگام ایجاد کردن</string>\n    <string name=\"file_job_create_error_message_format\">هنگام ایجاد کردن «⁨%1$s⁩» خطایی رخ داد.\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">نمی‌توان یک شاخه را به درون خودش رونوشت کرد</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">نمی‌توان یک شاخه را به درون خودش استخراج کرد</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">نمی‌توان یک شاخه را به درون خودش جابه‌جا کرد</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">شاخهٔ مقصد داخل شاخهٔ مبدأ است.</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">نمی‌توان یک پرونده را روی خودش رونوشت کرد</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">نمی‌توان یک پرونده را روی خودش استخراج کرد</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">نمی‌توان یک پرونده را روی خودش جابه‌جا کرد</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">پروندهٔ مبدأ توسط مقصد بازنویسی می‌شود.</string>\n    <string name=\"file_job_replace_title_format\">پروندهٔ «⁨%1$s⁩» جای‌گزین شود؟</string>\n    <string name=\"file_job_replace_message_format\">هم‌اکنون پرونده‌ای دیگر با همین نام در «⁨%1$s⁩» موجود است.\\nجای‌گزین کردن آن محتویاتش را بازنویسی می‌کند.</string>\n    <string name=\"file_job_replace_target_name\">پروندهٔ اصلی</string>\n    <string name=\"file_job_replace_source_name\">جای‌گزینی با</string>\n    <string name=\"file_job_merge_title_format\">شاخهٔ «⁨%1$s⁩» ادغام شود؟</string>\n    <string name=\"file_job_merge_copy_message_format\">هنگام ادغام کردن پیش از جای‌گزینی پرونده‌های موجود در شاخه که با پرونده‌های در حال رونوشت شدن تضاد دارند، درخواست تأیید می‌کند.</string>\n    <string name=\"file_job_merge_extract_message_format\">هنگام ادغام کردن پیش از جای‌گزینی پرونده‌های موجود در شاخه که با پرونده‌های در حال استخراج تضاد دارند، درخواست تأیید می‌کند.</string>\n    <string name=\"file_job_merge_move_message_format\">هنگام ادغام کردن پیش از جای‌گزینی پرونده‌های موجود در شاخه که با پرونده‌های در حال جابه‌جایی تضاد دارند، درخواست تأیید می‌کند.</string>\n    <string name=\"file_job_merge_target_name\">شاخهٔ اصلی</string>\n    <string name=\"file_job_merge_source_name\">ادغام با</string>\n    <string name=\"file_job_conflict_show_name\">نامی جدید برای مقصد برگزینید</string>\n    <string name=\"file_job_conflict_name_hint\">نام جدید</string>\n    <string name=\"file_job_archive_error_title_format\">خطا هنگام فشرده کردن «⁨%1$s⁩»</string>\n    <string name=\"file_job_archive_error_message_format\">هنگام فشرده کردن پرونده به «⁨%1$s⁩» خطایی رخ داد.\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">خطا هنگام رونوشت «⁨%1$s⁩»</string>\n    <string name=\"file_job_copy_error_message_format\">هنگام رونوشت پرونده به «⁨%1$s⁩» خطایی رخ داد.\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">خطا هنگام استخراج «⁨%1$s⁩»</string>\n    <string name=\"file_job_extract_error_message_format\">هنگام استخراج پرونده به «⁨%1$s⁩» خطایی رخ داد.\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">خطا هنگام جابه‌جایی «⁨%1$s⁩»</string>\n    <string name=\"file_job_move_error_message_format\">هنگام جابه‌جایی پرونده به «⁨%1$s⁩» خطایی رخ داد.\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">خطا هنگام حذف کردن</string>\n    <string name=\"file_job_delete_error_message_format\">هنگام حذف کردن «⁨%1$s⁩» خطایی رخ داد.\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">خطا هنگام تغییر نام «⁨%1$s⁩»</string>\n    <string name=\"file_job_rename_error_message_format\">هنگام تغییر نام پرونده به «⁨%1$s⁩» خطایی رخ داد.\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">خطا هنگام تغییر مالک «⁨%1$s⁩»</string>\n    <string name=\"file_job_set_owner_error_message_format\">هنگام تغییر مالک به «⁨%1$s⁩» خطایی رخ داد.\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">خطا هنگام تغییر گروه «⁨%1$s⁩»</string>\n    <string name=\"file_job_set_group_error_message_format\">هنگام تغییر گروه به «⁨%1$s⁩» خطایی رخ داد.\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">خطا هنگام تغییر حالت «⁨%1$s⁩»</string>\n    <string name=\"file_job_set_mode_error_message_format\">هنگام تغییر حالت «⁨%1$s⁩» خطایی رخ داد.\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">خطا هنگام تغییر مفاد اس‌ای‌لینوکس «⁨%1$s⁩»</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">هنگام تغییر مفاد اس‌ای‌لینوکس به «⁨%1$s⁩» خطایی رخ داد.\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">خطا هنگام بازگردانی مفاد اس‌ای‌لینوکس</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">هنگام بازگردانی مفاد اس‌ای‌لینوکس برای «⁨%1$s⁩» خطایی رخ داد.\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">خطا هنگام نوشتن</string>\n    <string name=\"file_job_write_error_message_format\">هنگام نوشتن «⁨%1$s⁩» خطایی رخ داد.\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">هنگام دریافت اطّلاعات دربارهٔ «⁨%1$s⁩» خطایی رخ داد.</string>\n    <string name=\"file_job_remount_format\">سوار کردن دوبارهٔ «⁨%1$s⁩» به عنوان خواندن-نوشتن</string>\n    <string name=\"file_job_remount_loading_format\">در حال سوار کردن دوبارهٔ «⁨%1$s⁩» به عنوان خواندن-نوشتن…</string>\n    <string name=\"file_job_remount_success_format\">«⁨%1$s⁩» به عنوان خواندن-نوشتن دوباره سوار شد</string>\n    <string name=\"file_job_action_all\">اعمال این کنش به همهٔ پرونده‌ها</string>\n\n    <string name=\"file_open_apk_message\">این پرونده یک بستهٔ اندروید است. آیا می‌خواهید آن را نصب کنید، یا محتویاتش را ببینید؟</string>\n    <string name=\"file_open_as_title\">گشودن به عنوان…</string>\n    <string name=\"file_open_as_title_format\">گشودن «⁨%1$s⁩» به عنوان</string>\n    <string name=\"file_open_as_type_text\">متن</string>\n    <string name=\"file_open_as_type_image\">تصویر</string>\n    <string name=\"file_open_as_type_audio\">صدا</string>\n    <string name=\"file_open_as_type_video\">ویدیو</string>\n    <string name=\"file_open_as_type_directory\">شاخه</string>\n    <string name=\"file_open_as_type_any\">دیگر</string>\n    <string name=\"file_install_apk_from_background_title_format\">«⁨%1$s⁩» آمادهٔ نصب است</string>\n    <string name=\"file_install_apk_from_background_text\">برای نصب ضربه بزنید</string>\n    <string name=\"file_open_from_background_title_format\">«⁨%1$s⁩» آمادهٔ گشودن است</string>\n    <string name=\"file_open_from_background_text\">برای گشودن ضربه بزنید</string>\n    <string name=\"file_name_error_empty\">نام پرونده نمی‌تواند خالی باشد</string>\n    <string name=\"file_name_error_invalid\">نام پرونده نامعتبر است</string>\n    <string name=\"file_name_error_already_exists\">پرونده‌ای با این نام هم‌اکنون موجود است</string>\n    <string name=\"file_delete_message_file_format\">مورد «⁨%1$s⁩» حذف شود؟</string>\n    <string name=\"file_delete_message_directory_format\">شاخهٔ «⁨%1$s⁩» و محتویاتش حذف شود؟</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"one\">تعداد %1$,d پرونده حذف شود؟</item>\n        <item quantity=\"other\">تعداد %1$,d پرونده حذف شوند؟</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"one\">تعداد %1$,d شاخه و محتویاتشان حذف شود؟</item>\n        <item quantity=\"other\">تعداد %1$,d شاخه و محتویاتشان حذف شوند؟</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"one\">تعداد %1$,d مورد حذف شود؟</item>\n        <item quantity=\"other\">تعداد %1$,d مورد حذف شوند؟</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">ساختن بایگانی</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_add_bookmark_success\">نشانک افزوده شد</string>\n    <string name=\"file_create_file_title\">پروندهٔ جدید</string>\n    <string name=\"file_create_directory_title\">شاخهٔ جدید</string>\n\n    <string name=\"file_list_title\">پرونده‌ها</string>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"one\">%1$,d شاخه</item>\n        <item quantity=\"other\">%1$,d شاخه</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"one\">%1$,d پرونده</item>\n        <item quantity=\"other\">%1$,d پرونده</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">،\\u0020</string>\n    <string name=\"file_list_path_error_empty\">مسیر نمی‌تواند خالی باشد</string>\n    <string name=\"file_list_path_error_invalid\">مسیر نامعتبر است</string>\n    <string name=\"file_list_action_sort_by_name\">نام</string>\n    <string name=\"file_list_action_sort_by_type\">گونه</string>\n    <string name=\"file_list_action_sort_by_size\">اندازه</string>\n    <string name=\"file_list_action_sort_by_last_modified\">آخرین تغییر</string>\n    <string name=\"file_list_action_sort_order_ascending\">بالا رونده</string>\n    <string name=\"file_list_action_sort_directories_first\">ابتدا شاخه‌ها</string>\n    <string name=\"file_list_action_new_task\">پنجرهٔ جدید</string>\n    <string name=\"file_list_action_navigate_up\">برو بالا</string>\n    <string name=\"file_list_action_navigate_to\">رفتن به</string>\n    <string name=\"file_list_action_show_hidden_files\">نمایش پرونده‌های پنهان</string>\n    <string name=\"file_list_action_copy_path\">رونوشت مسیر</string>\n    <string name=\"file_list_action_open_in_terminal\">گشودن در پایانه</string>\n    <string name=\"file_list_action_add_bookmark\">افزودن نشانک</string>\n    <string name=\"file_list_action_create_shortcut\">ایجاد میان‌بر</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">گشودن در پنجرهٔ جدید</string>\n    <string name=\"file_list_paste_move_title_format\">در حال جابه‌جایی %1$,d</string>\n    <string name=\"file_list_paste_copy_title_format\">در حال رونوشت %1$,d</string>\n    <string name=\"file_list_paste_extract_title_format\">در حال استخراج %1$,d</string>\n    <string name=\"file_list_paste_action_extract_here\">اینجا استخراج شود</string>\n    <string name=\"file_list_empty\">بدون پرونده</string>\n    <string name=\"file_list_action_create_file\">پرونده</string>\n    <string name=\"file_list_action_create_directory\">شاخه</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">گشودن با</string>\n    <string name=\"file_item_action_extract\">استخراج</string>\n    <string name=\"file_item_action_archive\">فشرده‌سازی</string>\n    <string name=\"file_item_action_properties\">ویژگی‌ها</string>\n\n    <string name=\"file_properties_title_format\">ویژگی‌های «⁨%1$s⁩»</string>\n    <string name=\"file_properties_basic\">پایه‌ای</string>\n    <string name=\"file_properties_basic_name\">نام</string>\n    <string name=\"file_properties_basic_type\">گونه</string>\n    <string name=\"file_properties_basic_type_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">پیوند به %1$s (%2$s)</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">هدف پیوند</string>\n    <string name=\"file_properties_basic_size\">اندازه</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_contents\">محتویات</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"one\">%1$,d مورد، با اندازهٔ %2$s</item>\n        <item quantity=\"other\">%1$,d مورد، مجموعاً %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">آخرین تغییر</string>\n    <string name=\"file_properties_basic_parent_directory\">شاخهٔ والد</string>\n    <string name=\"file_properties_basic_archive_file\">پروندهٔ بایگانی</string>\n    <string name=\"file_properties_basic_archive_entry\">ورودی بایگانی</string>\n    <string name=\"file_properties_basic_free_space\">فضای خالی</string>\n    <string name=\"file_properties_image\">تصویر</string>\n    <string name=\"file_properties_media_dimensions\">ابعاد</string>\n    <string name=\"file_properties_media_dimensions_format\">⁦%1$d \\u00d7 %2$d⁩</string>\n    <string name=\"file_properties_media_date_time\">گرفته‌شده در</string>\n    <string name=\"file_properties_media_coordinates\">مختصات</string>\n    <string name=\"file_properties_media_coordinates_format\">⁦%1$.3f، %2$.3f⁩</string>\n    <string name=\"file_properties_media_address\">مکان</string>\n    <string name=\"file_properties_image_gps_altitude\">ارتفاع</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f متر</string>\n    <string name=\"file_properties_image_equipment\">دوربین</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">دهانه</string>\n    <string name=\"file_properties_image_shutter_speed\">زمان نوردهی</string>\n    <string name=\"file_properties_image_focal_length\">فاصلهٔ کانونی</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f میلی‌متر</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">معادل ایزو</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">%1$d ایزو</string>\n    <string name=\"file_properties_image_software\">نرم‌افزار</string>\n    <string name=\"file_properties_image_description\">شرح</string>\n    <string name=\"file_properties_image_artist\">سازنده</string>\n    <string name=\"file_properties_image_copyright\">حق نشر</string>\n    <string name=\"file_properties_audio\">صدا</string>\n    <string name=\"file_properties_media_title\">عنوان</string>\n    <string name=\"file_properties_audio_artist\">هنرمند</string>\n    <string name=\"file_properties_audio_album\">آلبوم</string>\n    <string name=\"file_properties_audio_album_artist\">هنرمند آلبوم</string>\n    <string name=\"file_properties_audio_composer\">آهنگ‌ساز</string>\n    <string name=\"file_properties_audio_disc_number\">دیسک</string>\n    <string name=\"file_properties_audio_track_number\">قطعه</string>\n    <string name=\"file_properties_audio_year\">سال</string>\n    <string name=\"file_properties_audio_genre\">دسته</string>\n    <string name=\"file_properties_media_duration\">مدّت</string>\n    <string name=\"file_properties_media_bit_rate\">نرخ بیت</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d کیلوبیت بر ثانیه</string>\n    <string name=\"file_properties_audio_sample_rate\">نرخ نمونه</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d هرتز</string>\n    <string name=\"file_properties_video\">ویدیو</string>\n    <string name=\"file_properties_apk\">بستهٔ اندروید</string>\n    <string name=\"file_properties_apk_label\">نام</string>\n    <string name=\"file_properties_apk_package_name\">نام بسته</string>\n    <string name=\"file_properties_apk_version\">نگارش</string>\n    <string name=\"file_properties_apk_version_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_apk_min_sdk_version\">حداقل نگارش سامانه</string>\n    <string name=\"file_properties_apk_target_sdk_version\">نگارش سامانهٔ هدف</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s (%2$s، %3$d)</string>\n    <string name=\"file_properties_apk_requested_permissions\">مجوزها</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">هیچ مجوزی درخواست نشد</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"one\">%1$,d مجوز درخواست شد</item>\n        <item quantity=\"other\">%1$,d مجوز درخواست شد</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">امضاها</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">بدون امضای معتبر</string>\n    <string name=\"file_properties_apk_past_signature_digests\">امضاهای قدیمی</string>\n    <string name=\"storage_file_system_root_title\">ریشه</string>\n    <string name=\"storage_list_title\">ذخیره‌ساز</string>\n    <string name=\"storage_list_empty\">بدون ذخیره‌ساز</string>\n    <string name=\"storage_add_storage_title\">افزودن ذخیره‌ساز</string>\n    <string name=\"storage_add_storage_document_tree\">ذخیره‌ساز خارجی</string>\n    <string name=\"storage_add_storage_ftp_server\">کارساز اف‌تی‌پی</string>\n    <string name=\"storage_add_storage_sftp_server\">کارساز اس‌اف‌تی‌پی</string>\n    <string name=\"storage_add_storage_smb_server\">کارساز اس‌ام‌بی</string>\n    <string name=\"storage_edit_device_storage_title\">ویرایش ذخیره‌ساز افزاره</string>\n    <string name=\"storage_edit_device_storage_name\">نام</string>\n    <string name=\"storage_edit_device_storage_path\">مسیر</string>\n    <string name=\"storage_add_document_tree_title\">افزودن ذخیره‌ساز خارجی</string>\n    <string name=\"storage_edit_document_tree_title\">ویرایش ذخیره‌ساز خارجی</string>\n    <string name=\"storage_edit_document_tree_name\">نام</string>\n    <string name=\"storage_edit_document_tree_uri\">نشانی</string>\n    <string name=\"storage_edit_document_tree_path\">مسیر</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">ویرایش کارساز اف‌تی‌پی</string>\n    <string name=\"storage_edit_ftp_server_title_add\">افزودن کارساز اف‌تی‌پی</string>\n    <string name=\"storage_edit_ftp_server_host\">نام میزبان</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">نام میزبان را وارد کنید</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">نام میزبان نامعتبر است</string>\n    <string name=\"storage_edit_ftp_server_port\">درگاه</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">درگاه نامعتبر است</string>\n    <string name=\"storage_edit_ftp_server_path\">مسیر</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">می‌تواند خالی بماند</string>\n    <string name=\"storage_edit_ftp_server_name\">نام</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">استفاده از نام میزبان</string>\n    <string name=\"storage_edit_ftp_server_protocol\">شیوه‌نامه</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">احراز هویت</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>گذرواژه</item>\n        <item>ناشناس</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">نام کاربری</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">نام کاربری را وارد کنید</string>\n    <string name=\"storage_edit_ftp_server_password\">گذرواژه</string>\n    <string name=\"storage_edit_ftp_server_mode\">حالت</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>فعّال</item>\n        <item>منفعل</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">رمزگذاری</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">اتّصال و افزودن</string>\n    <string name=\"storage_edit_ftp_server_add\">افزودن</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">ویرایش کارساز اس‌اف‌تی‌پی</string>\n    <string name=\"storage_edit_sftp_server_title_add\">افزودن کارساز اس‌اف‌تی‌پی</string>\n    <string name=\"storage_edit_sftp_server_host\">نام میزبان</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">یک نام میزبان وارد کنید</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">نام میزبان نامعتبر است</string>\n    <string name=\"storage_edit_sftp_server_port\">درگاه</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">درگاه نامعتبر</string>\n    <string name=\"storage_edit_sftp_server_path\">مسیر</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">می‌تواند خالی بماند</string>\n    <string name=\"storage_edit_sftp_server_name\">نام</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">استفاده از نام میزبان</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">احراز هویت</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>گذرواژه</item>\n        <item>کلید عمومی</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">نام کاربری</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">یک نام کاربری وارد کنید</string>\n    <string name=\"storage_edit_sftp_server_password\">گذرواژه</string>\n    <string name=\"storage_edit_sftp_server_private_key\">کلید خصوصی</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">گشودن پرونده</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">یک کلید خصوصی وارد کنید</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">کلید خصوصی نامعتبر</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">گذرواژهٔ کلید خصوصی</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">می‌تواند خالی بماند</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">گذرواژهٔ کلید خصوصی نامعتبر است</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">اتّصال و افزودن</string>\n    <string name=\"storage_edit_sftp_server_add\">افزودن</string>\n    <string name=\"storage_add_lan_smb_server_loading\">در حال جست‌وجو برای کارسازهای اس‌ام‌بی…</string>\n    <string name=\"storage_add_lan_smb_server_add\">افزودن دستی</string>\n    <string name=\"storage_edit_smb_server_title_edit\">ویرایش کارساز اس‌ام‌بی</string>\n    <string name=\"storage_edit_smb_server_title_add\">افزودن کارساز اس‌ام‌بی</string>\n    <string name=\"storage_edit_smb_server_host\">نام میزبان</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">یک نام میزبان وارد کنید</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">نام میزبان نامعتبر است</string>\n    <string name=\"storage_edit_smb_server_port\">درگاه</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">درگاه نامعتیر</string>\n    <string name=\"storage_edit_smb_server_path\">مسیر</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">می‌تواند خالی بماند</string>\n    <string name=\"storage_edit_smb_server_name\">نام</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">استفاده از نام میزبان</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">احراز هویت</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>گذرواژه</item>\n        <item>مهمان</item>\n        <item>ناشناس</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">نام کاربری</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">یک نام کاربری وارد کنید</string>\n    <string name=\"storage_edit_smb_server_password\">گذرواژه</string>\n    <string name=\"storage_edit_smb_server_domain\">دامنه</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">اتّصال و افزودن</string>\n    <string name=\"storage_edit_smb_server_add\">افزودن</string>\n    <string name=\"navigation_storage_subtitle_format\">%1$s آزاد از %2$s</string>\n    <string name=\"navigation_add_storage\">افزودن ذخیره‌ساز…</string>\n    <string name=\"navigation_standard_directory_alarms\">زنگ‌ها</string>\n    <string name=\"navigation_standard_directory_dcim\">DCIM</string>\n    <string name=\"navigation_standard_directory_documents\">اسناد</string>\n    <string name=\"navigation_standard_directory_downloads\">بارگیری‌ها</string>\n    <string name=\"navigation_standard_directory_movies\">ویدیوها</string>\n    <string name=\"navigation_standard_directory_music\">آهنگ‌ها</string>\n    <string name=\"navigation_standard_directory_notifications\">آگاهی‌ها</string>\n    <string name=\"navigation_standard_directory_pictures\">تصویرها</string>\n    <string name=\"navigation_standard_directory_podcasts\">پادکست‌ها</string>\n    <string name=\"navigation_standard_directory_ringtones\">آهنگ‌های زنگ</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">شاخهٔ نشانک</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">نام</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">مسیر</string>\n    <string name=\"archive_viewer_title\">نمایشگر بایگانی</string>\n    <string name=\"image_viewer_title\">نمایشگر تصویر</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"text_editor_title\">ویرایشگر متن</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">ذخیره شد</string>\n    <string name=\"text_editor_reload_message\">آیا مطمئن هستید که می‌خواهید دوباره بارگزاری کنید؟ تغییرات ذخیره نشده در این سند از بین خواهند رفت.</string>\n    <string name=\"text_editor_encoding\">رمزگذاری</string>\n    <string name=\"text_editor_close_message\">آیا مطمئن هستید که می‌خواهید تغییرات ذخیره نشده در این سند را دور بریزید؟</string>\n\n    <string name=\"ftp_server_title\">کارساز اف‌تی‌پی</string>\n    <string name=\"ftp_server_state_title\">وضعیت</string>\n    <string name=\"ftp_server_state_summary_starting\">در حال شروع…</string>\n    <string name=\"ftp_server_state_summary_running\">در حال اجرا</string>\n    <string name=\"ftp_server_state_summary_stopping\">در حال توقّف…</string>\n    <string name=\"ftp_server_state_summary_stopped\">شروع نشد</string>\n    <string name=\"ftp_server_url_title\">نشانی</string>\n    <string name=\"ftp_server_url_menu_copy_url\">رونوشت نشانی</string>\n    <string name=\"ftp_server_url_menu_copy_password\">رونوشت گذرواژه</string>\n    <string name=\"ftp_server_configuration_title\">پیکربندی</string>\n    <string name=\"ftp_server_anonymous_login_title\">ورود ناشناس</string>\n    <string name=\"ftp_server_username_title\">نام کاربری</string>\n    <string name=\"ftp_server_password_title\">گذرواژه</string>\n    <string name=\"ftp_server_port_title\">درگاه</string>\n    <string name=\"ftp_server_home_directory_title\">شاخهٔ ریشه</string>\n    <string name=\"ftp_server_writable_title\">اجازهٔ نوشتن</string>\n    <string name=\"settings_title\">تنظیمات</string>\n    <string name=\"settings_interface_title\">رابط</string>\n    <string name=\"settings_theme_color_title\">رنگ زمینه</string>\n    <string name=\"settings_theme_color_summary\">رنگی که بیشتر در برنامه ظاهر می‌شود</string>\n    <string name=\"settings_material_design_3_title\">طراحی متریال ۳</string>\n    <string name=\"settings_night_mode_title\">حالت شب</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>پی‌روی از سامانه</item>\n        <item>خاموش</item>\n        <item>روشن</item>\n        <item>بر پایهٔ زمان</item>\n        <item>بر پایهٔ محافظ باتری</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">حالت شب تاریک</string>\n    <string name=\"settings_file_list_animation_title\">پویانمایی فهرست پرونده‌ها</string>\n    <string name=\"settings_file_name_ellipsize_title\">نمایش نام پروندهٔ طولانی</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>پیچاندن ابتدا</item>\n        <item>پیچاندن وسط</item>\n        <item>پیچاندن انتها</item>\n        <item>متحرک</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">رفتار</string>\n    <string name=\"settings_default_directory_title\">شاخهٔ پیش‌گزیده</string>\n    <string name=\"settings_standard_directories_title\">شاخه‌های استاندارد</string>\n    <string name=\"settings_standard_directories_summary_empty\">هیچ شاخهٔ استانداردی به کار انداخته نشده است</string>\n    <string name=\"settings_bookmark_directory_screenshots\">نماگرفت‌ها</string>\n    <string name=\"settings_bookmark_directories_title\">شاخه‌های نشان شده</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">بدون شاخهٔ نشان شده</string>\n    <string name=\"settings_root_strategy_title\">حالت دسترسی ریشه</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"one\">شما %1$,d عملیات پروندهٔ در حال اجرا دارید و تغییر حالت دسترسی ریشه اکنون ممکن است منجر به خطای غیرمنتظره شود. آیا مطمئنید که اکنون می‌خواهید آن را تغییر دهید؟</item>\n        <item quantity=\"other\">شما %1$,d عملیات پروندهٔ در حال اجرا دارید و تغییر حالت دسترسی ریشه اکنون ممکن است منجر به خطای غیرمنتظره شود. آیا مطمئنید که اکنون می‌خواهید آن را تغییر دهید؟</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>فقط دسترسی معمولی</item>\n        <item>خودکار</item>\n        <item>فقط دسترسی ریشه</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">رمزگذاری نام پروندهٔ بایگانی</string>\n    <string name=\"settings_open_apk_default_action_title\">گشودن بستهٔ اندروید</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>نصب</item>\n        <item>دیدن محتویات</item>\n        <item>بپرس چه باید کرد</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">ممکن است برنامه را در نگارش‌های قدیمی‌تر اندروید ناپایدار کند</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">پرونده‌های دوردست را برای بندانگشتی بخوان</string>\n\n    <string name=\"about_title\">درباره</string>\n    <string name=\"about_version_title\">نگارش</string>\n    <string name=\"about_github_title\">دیدن در گیت‌هاب</string>\n    <string name=\"about_licenses_title\">پروانه‌ها</string>\n    <string name=\"about_privacy_policy_title\">سیاست حفظ حریم خصوصی</string>\n    <string name=\"about_author_title\">نویسنده</string>\n    <string name=\"about_author_name_title\">Hai Zhang</string>\n    <string name=\"about_author_github_title\">پی‌گیری در گیت‌هاب</string>\n    <string name=\"about_author_twitter_title\">پی‌گیری در توییتر</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-fi/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">Tiedosto</string>\n    <string name=\"file_type_name_apk\">Android-asennuspakkaus</string>\n    <string name=\"file_type_name_archive\">%1$s arkisto</string>\n    <string name=\"file_type_name_audio\">%1$s ääni</string>\n    <string name=\"file_type_name_calendar\">%1$s kalenteri</string>\n    <string name=\"file_type_name_certificate\">%1$s sertifikaatti</string>\n    <string name=\"file_type_name_code\">%1$s dokumentti</string>\n    <string name=\"file_type_name_directory\">Kansio</string>\n    <string name=\"file_type_name_document\">%1$s dokumentti</string>\n    <string name=\"file_type_name_ebook\">%1$s e-kirja</string>\n    <string name=\"file_type_name_email\">%1$s sähköpostiviesti</string>\n    <string name=\"file_type_name_font\">%1$s kirjasin</string>\n    <string name=\"file_type_name_generic\">%1$s tiedosto</string>\n    <string name=\"file_type_name_image\">%1$s kuva</string>\n    <string name=\"file_type_name_pdf\">PDF-dokumentti</string>\n    <string name=\"file_type_name_presentation\">%1$s esitys</string>\n    <string name=\"file_type_name_spreadsheet\">%1$s laskentataulukko</string>\n    <string name=\"file_type_name_text\">%1$s dokumentti</string>\n    <string name=\"file_type_name_text_plain\">Pelkkä teksti-dokumentti</string>\n    <string name=\"file_type_name_video\">%1$s video</string>\n    <string name=\"file_type_name_word\">Word-dokumentti</string>\n    <string name=\"file_type_name_powerpoint\">PowerPoint-esitys</string>\n    <string name=\"file_type_name_excel\">Excel-laskentataulukko</string></resources>\n"
  },
  {
    "path": "app/src/main/res/values-fi/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <string name=\"app_name\">Material Files</string>\n\n    <string name=\"close\">Sulje</string>\n    <string name=\"copy\">Kopioi</string>\n    <string name=\"cut\">Leikkaa</string>\n    <string name=\"default_\">Oletus</string>\n    <string name=\"delete\">Poista</string>\n    <string name=\"discard\">Hylkää</string>\n    <string name=\"edit\">Muokkaa</string>\n    <string name=\"empty\">Tyhjennä</string>\n    <string name=\"empty_placeholder\">(tyhjä)</string>\n    <string name=\"error\">Virhe</string>\n    <string name=\"hide\">Piilota</string>\n    <string name=\"install\">Asenna</string>\n    <string name=\"keep_editing\">Jatka muokkaamista</string>\n    <string name=\"loading\">Ladataan…</string>\n    <string name=\"maybe_later\">Ehkä myöhemmin</string>\n    <string name=\"merge\">Yhdistä</string>\n    <string name=\"none\">Ei mitään</string>\n    <string name=\"open_navigation_drawer\">Avaa navigointipalkki</string>\n    <string name=\"paste\">Liitä</string>\n    <string name=\"refresh\">Päivitä</string>\n    <string name=\"reload\">Lataa uudelleen</string>\n    <string name=\"remove\">Poista</string>\n    <string name=\"rename\">Nimeä uudelleen</string>\n    <string name=\"replace\">Korvaa</string>\n    <string name=\"reset\">Nollaa</string>\n    <string name=\"retry\">Yritä uudelleen</string>\n    <string name=\"save\">Tallenna</string>\n    <string name=\"search\">Etsi</string>\n    <string name=\"select\">Valitse</string>\n    <string name=\"select_all\">Valitse kaikki</string>\n    <string name=\"share\">Jaa</string>\n    <string name=\"show\">Näytä</string>\n    <string name=\"skip\">Ohita</string>\n    <string name=\"stop\">Pysäytä</string>\n    <string name=\"system_default\">Järjestelmän oletus</string>\n    <string name=\"unknown\">Tuntematon</string>\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">Toiminnalle ei löytynyt sovellusta</string>\n    <string name=\"open_settings\">Avaa asetukset</string>\n    <string name=\"shortcut_created\">Pikakuvake luotu</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"one\">%1$,d tavu</item>\n        <item quantity=\"other\">%1$,d tavua</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">Sovellus tarvitsee kaikkien tiedostojen käyttöoikeuden. Anna käyttöoikeus seuraavaksi avautuvissa järjestelmän asetuksissa.</string>\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">Sovellus tarvitsee tiedostojen käyttöoikeuden. Klikkaa “HYVÄKSY” seuraavassa järjestelmän ikkunassa.</string>\n    <string name=\"storage_permission_permanently_denied_message\">Sovellus tarvitsee tiedostojen käyttöoikeuden. Anna käyttöoikeus tallennustilan käyttöön järjestelmän asetuksista.</string>\n    <string name=\"notification_channel_background_activity_start_name\">Toiminnot taustalla</string>\n    <string name=\"notification_channel_background_activity_start_description\">Suorita toimintoja sovelluksen ollessa taustalla</string>\n    <string name=\"notification_channel_file_job_name\">Tiedostotoiminnot</string>\n    <string name=\"notification_channel_file_job_description\">Näytä ja hallinnoi tiedostotoimintoja</string>\n    <string name=\"notification_channel_ftp_server_name\">FTP-palvelin</string>\n    <string name=\"notification_channel_ftp_server_description\">Näytä ja hallinnoi FTP-palvelinta</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"one\">Valmistaudutaan pakkaamaan %1$,d tiedosto (%2$s)</item>\n        <item quantity=\"other\">Valmistaudutaan pakkaamaan %1$,d tiedostoa (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">Pakataan “%1$s” kohteeseen “%2$s”</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"one\">Pakataan %1$,d tiedosto kohteeseen “%2$s”</item>\n        <item quantity=\"other\">Pakataan %1$,d tiedostoa kohteeseen “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"one\">Valmistaudutaan kopioimaan %1$,d tiedosto (%2$s)</item>\n        <item quantity=\"other\">Valmistaudutaan kopioimaan %1$,d tiedostoa (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">Kopioidaan “%1$s” kohteeseen “%2$s”</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"one\">Kopioidaan tiedosto %1$,d kohteeseen “%2$s”</item>\n        <item quantity=\"other\">Kopioidaan %1$,d tiedostoa kohteeseen “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"one\">Valmistaudutaan purkamaan %1$,d tiedosto (%2$s)</item>\n        <item quantity=\"other\">Valmistaudutaan purkamaan %1$,d tiedostoa (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">Puretaan “%1$s” kohteeseen “%2$s”</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"one\">Puretaan %1$,d tiedosto kohteeseen “%2$s”</item>\n        <item quantity=\"other\">Puretaan %1$,d tiedostoa kohteeseen “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"one\">Valmistaudutaan siirtämään %1$,d tiedosto (%2$s)</item>\n        <item quantity=\"other\">Valmistaudutaan siirtämään %1$,d tiedostoa (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">Siirretään “%1$s” kohteeseen “%2$s”</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"one\">Siirretään %1$,d tiedosto kohteeseen “%2$s”</item>\n        <item quantity=\"other\">Siirretään %1$,d tiedostoa kohteeseen “%2$s”</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"one\">Valmistaudutaan poistamaan %1$,d tiedosto</item>\n        <item quantity=\"other\">Valmistaudutaan poistamaan %1$,d tiedostoa</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">Poistetaan “%1$s”</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"one\">Poistetaan %1$,d tiedosto</item>\n        <item quantity=\"other\">Poistetaan %1$,d tiedostoa</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"one\">Valmistaudutaan vaihtamaan %1$,d tiedoston omistajaa</item>\n        <item quantity=\"other\">Valmistaudutaan vaihtamaan %1$,d tiedoston omistajaa</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">Vaihdetaan kohteen “%1$s” omistajaa</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"one\">Vaihdetaan %1$,d tiedoston omistajaa</item>\n        <item quantity=\"other\">Vaihdetaan %1$,d tiedoston omistajaa</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"one\">Valmistaudutaan vaihtamaan %1$,d tiedoston ryhmää</item>\n        <item quantity=\"other\">Valmistaudutaan vaihtamaan %1$,d tiedoston ryhmää</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">Vaihdetaan kohteen “%1$s” ryhmää</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"one\">Vaihdetaan %1$,d tiedoston ryhmää</item>\n        <item quantity=\"other\">Vaihdetaan %1$,d tiedoston ryhmää</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"one\">Valmistaudutaan vaihtamaan %1$,d tiedoston tilaa</item>\n        <item quantity=\"other\">Valmistaudutaan vaihtamaan %1$,d tiedoston tilaa</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">Vaihdetaan kohteen “%1$s” tilaa</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"one\">Vaihdetaan %1$,d tiedoston tilaa</item>\n        <item quantity=\"other\">Vaihdetaan %1$,d tiedoston tilaa</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Valmistaudutaan muuttamaan SELinux-konteksti %1$,d:lle tiedostolle</item>\n        <item quantity=\"other\">Valmistaudutaan muuttamaan SELinux-konteksti %1$,d:lle tiedostolle</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">Muutetaan SELinux-konteksti kohteelle “%1$s”</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Muutetaan SELinux-konteksti %1$,d:lle tiedostolle</item>\n        <item quantity=\"other\">Muutetaan SELinux-konteksti %1$,d:lle tiedostolle</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Valmistaudutaan palauttamaan SELinux-konteksti %1$,d:lle tiedostolle</item>\n        <item quantity=\"other\">Valmistaudutaan palauttamaan SELinux-konteksti %1$,d:lle tiedostolle</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">Palautetaan SELinux-konteksti kohteelle “%1$s”</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Palautetaan SELinux-konteksti %1$,d:lle tiedostolle</item>\n        <item quantity=\"other\">Palautetaan SELinux-konteksti %1$,d:lle tiedostolle</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">Tallennetaan “%1$s”</string>\n    <string name=\"file_job_create_error_title\">Virhe tiedoston luomisessa</string>\n    <string name=\"file_job_create_error_message_format\">Tapahtui virhe luotaessa “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">Kansiota ei voida kopioida itsensä sisälle</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">Kansiota ei voida purkaa itsensä sisälle</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">Kansiota ei voida siirtää itsensä sisälle</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">Kohdehakemisto on lähdehakemiston sisällä</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">Tiedostoa ei voida kopioida itsensä päälle</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">Tiedostoa ei voida purkaa itsensä päälle</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">Tiedostoa ei voida siirtää itsensä päälle</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">Kohdetiedosto korvaisi lähdetiedoston</string>\n    <string name=\"file_job_replace_title_format\">Korvataanko tiedosto “%1$s”?</string>\n    <string name=\"file_job_replace_message_format\">Samanniminen tiedosto on jo olemassa hakemistossa “%1$s”.\\nKorvaaminen hävittää sen sisällön.</string>\n    <string name=\"file_job_replace_target_name\">Alkuperäinen tiedosto</string>\n    <string name=\"file_job_replace_source_name\">Korvataan tiedostolla</string>\n    <string name=\"file_job_merge_title_format\">Yhdistetäänkö kansio “%1$s”?</string>\n    <string name=\"file_job_merge_target_name\">Alkuperäinen kansio</string>\n    <string name=\"file_job_merge_source_name\">Yhdistä kansioon</string>\n    <string name=\"file_job_conflict_show_name\">Valitse uusi nimi kohteelle</string>\n    <string name=\"file_job_conflict_name_hint\">Uusi nimi</string>\n    <string name=\"file_job_archive_error_title_format\">Virhe pakattaessa “%1$s”</string>\n    <string name=\"file_job_archive_error_message_format\">Tapahtui virhe tiedostoa kohteeseen “%1$s” pakatessa.\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">Virhe kopioitaessa “%1$s”</string>\n    <string name=\"file_job_copy_error_message_format\">Tapahtui virhe kopioitaessa tiedostoa kohteeseen “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">Virhe purkaessa “%1$s”</string>\n    <string name=\"file_job_extract_error_message_format\">Tapahtui virhe purkaessa tiedostoa kohteeseen “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">Virhe siirtäessä “%1$s”</string>\n    <string name=\"file_job_move_error_message_format\">Tapahtui virhe siirtäessä tiedostoa kohteeseen “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">Virhe poistossa</string>\n    <string name=\"file_job_delete_error_message_format\">Tapahtui virhe poistaessa “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">Virhe uudelleennimetessä “%1$s”</string>\n    <string name=\"file_job_rename_error_message_format\">Tapahtui virhe nimetessä tiedostoa nimelle “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">Virhe kohteen “%1$s” omistajaa vaihtaessa</string>\n    <string name=\"file_job_set_owner_error_message_format\">Tapahtui virhe vaihtaessa omistajaa “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">Virhe ryhmän vaihtamisessa kohteelle “%1$s”</string>\n    <string name=\"file_job_set_group_error_message_format\">Tapahtui virhe vaihtaessa ryhmäksi “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">Virhe SE-Linux-kontekstin palauttamisessa</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">Tapahtui virhe palauttaessa SELinux-kontekstia kohteelle “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">Virhe kirjoittamisessa</string>\n    <string name=\"file_job_write_error_message_format\">Tapahtui virhe kirjoittaessa “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">Tapahtui virhe hakiessa tietoa kohteesta “%1$s”.</string>\n    <string name=\"file_job_action_all\">Suorita toiminto kaikille tiedostoille</string>\n\n    <string name=\"file_open_apk_message\">Tämä tiedosto on Android-asennustiedosto. Haluatko asentaa sen vai nähdä sen sisällön?</string>\n    <string name=\"file_open_as_title\">Avaa…</string>\n    <string name=\"file_open_as_title_format\">Avaa “%1$s”…</string>\n    <string name=\"file_open_as_type_text\">Teksti</string>\n    <string name=\"file_open_as_type_image\">Kuva</string>\n    <string name=\"file_open_as_type_audio\">Audio</string>\n    <string name=\"file_open_as_type_video\">Video</string>\n    <string name=\"file_open_as_type_directory\">Kansio</string>\n    <string name=\"file_open_as_type_any\">Muu</string>\n    <string name=\"file_install_apk_from_background_title_format\">“%1$s” on valmiina asennettavaksi</string>\n    <string name=\"file_install_apk_from_background_text\">Napauta asentaaksesi</string>\n    <string name=\"file_open_from_background_title_format\">“%1$s” on valmiina avattavaksi</string>\n    <string name=\"file_open_from_background_text\">Napauta avataksesi</string>\n    <string name=\"file_name_error_empty\">Tiedostonimi ei voi olla tyhjä</string>\n    <string name=\"file_name_error_invalid\">Virheellinen tiedostonimi</string>\n    <string name=\"file_name_error_already_exists\">Samanniminen tiedosto on jo olemassa</string>\n    <string name=\"file_delete_message_file_format\">Poistetaanko “%1$s”?</string>\n    <string name=\"file_delete_message_directory_format\">Poistetaanko kansio “%1$s” sisältöineen?</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"one\">Poistetaanko %1$,d tiedosto?</item>\n        <item quantity=\"other\">Poistetaanko %1$,d tiedostoa?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"one\">Poistetaanko %1$,d kansio sisältöineen?</item>\n        <item quantity=\"other\">Poistetaanko %1$,d kansiota sisältöineen?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"one\">Poistetaanko %1$,d kohde?</item>\n        <item quantity=\"other\">Poistetaanko %1$,d kohdetta?</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">Luo arkisto</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_add_bookmark_success\">Kirjanmerkki lisätty</string>\n    <string name=\"file_create_file_title\">Uusi tiedosto</string>\n    <string name=\"file_create_directory_title\">Uusi kansio</string>\n\n    <string name=\"file_list_title\">Tiedostot</string>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"one\">%1$,d kansio</item>\n        <item quantity=\"other\">%1$,d kansiota</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"one\">%1$,d tiedosto</item>\n        <item quantity=\"other\">%1$,d tiedostoa</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">,\\u0020</string>\n    <string name=\"file_list_path_error_empty\">Polku ei voi olla tyhjä</string>\n    <string name=\"file_list_path_error_invalid\">Virheellinen polku</string>\n    <string name=\"file_list_action_view_sort\">Näkymä ja lajittelu</string>\n    <string name=\"file_list_action_view_list\">Lista</string>\n    <string name=\"file_list_action_view_grid\">Ruudukko</string>\n    <string name=\"file_list_action_sort_by_name\">Nimi</string>\n    <string name=\"file_list_action_sort_by_type\">Tyyppi</string>\n    <string name=\"file_list_action_sort_by_size\">Koko</string>\n    <string name=\"file_list_action_sort_by_last_modified\">Viimeksi muokattu</string>\n    <string name=\"file_list_action_sort_order_ascending\">Nouseva</string>\n    <string name=\"file_list_action_sort_directories_first\">Kansiot ensin</string>\n    <string name=\"file_list_action_view_sort_path_specific\">Vain tälle kansiolle</string>\n    <string name=\"file_list_action_new_task\">Uusi ikkuna</string>\n    <string name=\"file_list_action_navigate_up\">Mene ylös</string>\n    <string name=\"file_list_action_navigate_to\">Mene…</string>\n    <string name=\"file_list_action_show_hidden_files\">Näytä piilotetut tiedostot</string>\n    <string name=\"file_list_action_copy_path\">Kopioi polku</string>\n    <string name=\"file_list_action_open_in_terminal\">Avaa päätteessä</string>\n    <string name=\"file_list_action_add_bookmark\">Lisää kirjanmerkki</string>\n    <string name=\"file_list_action_create_shortcut\">Luo pikakuvake</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">Avaa uudessa ikkunassa</string>\n    <string name=\"file_list_select_title_format\">%1$,d</string>\n    <string name=\"file_list_paste_move_title_format\">Siirretään %1$,d</string>\n    <string name=\"file_list_paste_copy_title_format\">Kopioidaan %1$,d</string>\n    <string name=\"file_list_paste_extract_title_format\">Puretaan %1$,d</string>\n    <string name=\"file_list_paste_action_extract_here\">Pura tähän</string>\n    <string name=\"file_list_empty\">Ei tiedostoja</string>\n    <string name=\"file_list_action_create_file\">Tiedosto</string>\n    <string name=\"file_list_action_create_directory\">Kansio</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">Avaa…</string>\n    <string name=\"file_item_action_extract\">Pura</string>\n    <string name=\"file_item_action_archive\">Pakkaa</string>\n    <string name=\"file_item_action_properties\">Ominaisuudet</string>\n\n    <string name=\"file_properties_title_format\">“%1$s” ominaisuudet</string>\n    <string name=\"file_properties_basic\">Perustiedot</string>\n    <string name=\"file_properties_basic_name\">Nimi</string>\n    <string name=\"file_properties_basic_type\">Tyyppi</string>\n    <string name=\"file_properties_basic_type_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">Linkki kohteeseen %1$s (%2$s)</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">Linkin kohde</string>\n    <string name=\"file_properties_basic_size\">Koko</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_contents\">Sisältö</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"one\">%1$,d kohde kooltaan %2$s</item>\n        <item quantity=\"other\">%1$,d kohdetta, yhteensä %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">Viimeksi muokattu</string>\n    <string name=\"file_properties_basic_parent_directory\">Ylähakemisto</string>\n    <string name=\"file_properties_basic_free_space\">Vapaata tilaa</string>\n    <string name=\"file_properties_image\">Kuva</string>\n    <string name=\"file_properties_media_dimensions\">Mitat</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">Ottopäivä</string>\n    <string name=\"file_properties_media_coordinates\">Koordinaatit</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f, %2$.3f</string>\n    <string name=\"file_properties_media_address\">Sijainti</string>\n    <string name=\"file_properties_image_gps_altitude\">Korkeus</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f m</string>\n    <string name=\"file_properties_image_equipment\">Kamera</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">Aukkokoko</string>\n    <string name=\"file_properties_image_shutter_speed\">Suljinnopeus</string>\n    <string name=\"file_properties_image_focal_length\">Polttoväli</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f mm</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">Ohjelmisto</string>\n    <string name=\"file_properties_image_description\">Kuvaus</string>\n    <string name=\"file_properties_image_artist\">Luoja</string>\n    <string name=\"file_properties_image_copyright\">Copyright</string>\n    <string name=\"file_properties_audio\">Audio</string>\n    <string name=\"file_properties_media_title\">Nimi</string>\n    <string name=\"file_properties_audio_artist\">Artisti</string>\n    <string name=\"file_properties_audio_album\">Albumi</string>\n    <string name=\"file_properties_audio_album_artist\">Albumin artisti</string>\n    <string name=\"file_properties_audio_composer\">Säveltäjä</string>\n    <string name=\"file_properties_audio_disc_number\">Levy</string>\n    <string name=\"file_properties_audio_track_number\">Kappale</string>\n    <string name=\"file_properties_audio_year\">Vuosi</string>\n    <string name=\"file_properties_audio_genre\">Genre</string>\n    <string name=\"file_properties_media_duration\">Kesto</string>\n    <string name=\"file_properties_media_bit_rate\">Bitrate</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d kbps</string>\n    <string name=\"file_properties_audio_sample_rate\">Näytteenottotaajuus</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Hz</string>\n    <string name=\"file_properties_video\">Video</string>\n    <string name=\"file_properties_apk\">APK</string>\n    <string name=\"file_properties_apk_label\">Nimi</string>\n    <string name=\"file_properties_apk_package_name\">Pakkauksen nimi</string>\n    <string name=\"file_properties_apk_version\">Versio</string>\n    <string name=\"file_properties_apk_version_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_apk_min_sdk_version\">Järestelmän vähimmäisversio</string>\n    <string name=\"file_properties_apk_target_sdk_version\">Järjestelmän kohdeversio</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s (%2$s, %3$d)</string>\n    <string name=\"file_properties_apk_requested_permissions\">Käyttöoikeudet</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">Ei pyydettäviä käyttöoikeuksia</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"one\">%1$,d pyydettävä käyttöoikeus</item>\n        <item quantity=\"other\">%1$,d pyydettävää käyttöoikeutta</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">Allerkirjoitukset</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">Ei kelvollisia allekirjoituksia</string>\n    <string name=\"file_properties_apk_past_signature_digests\">Vanhat allekirjoitukset</string>\n    <string name=\"storage_file_system_root_title\">Root</string>\n    <string name=\"storage_list_title\">Tallennustila</string>\n    <string name=\"storage_list_empty\">Ei tallennustilaa</string>\n    <string name=\"storage_add_storage_title\">Lisää tallennustila</string>\n    <string name=\"storage_add_storage_document_tree\">Ulkoinen tallennustila</string>\n    <string name=\"storage_add_storage_ftp_server\">FTP-palvelin</string>\n    <string name=\"storage_add_storage_sftp_server\">SFTP-palvelin</string>\n    <string name=\"storage_add_storage_smb_server\">SMB-palvelin</string>\n    <string name=\"storage_edit_device_storage_title\">Muokkaa laitteen tallennustilaa</string>\n    <string name=\"storage_edit_device_storage_name\">Nimi</string>\n    <string name=\"storage_edit_device_storage_path\">polku</string>\n    <string name=\"storage_add_document_tree_title\">Lisää ulkoinen tallennustila</string>\n    <string name=\"storage_edit_document_tree_title\">Muokkaa ulkoista tallennustilaa</string>\n    <string name=\"storage_edit_document_tree_name\">Nimi</string>\n    <string name=\"storage_edit_document_tree_uri\">URI</string>\n    <string name=\"storage_edit_document_tree_path\">Polku</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">Muokkaa FTP-palvelinta</string>\n    <string name=\"storage_edit_ftp_server_title_add\">Lisää FTP-palvelin</string>\n    <string name=\"storage_edit_ftp_server_host\">Isäntänimi</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">Syötä isäntänimi</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">Virheellinen isäntänimi</string>\n    <string name=\"storage_edit_ftp_server_port\">Portti</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">Virheellinen portti</string>\n    <string name=\"storage_edit_ftp_server_path\">Polku</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">Voidaan jättää tyhjäksi</string>\n    <string name=\"storage_edit_ftp_server_name\">Nimi</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">Käytä isäntänimeä</string>\n    <string name=\"storage_edit_ftp_server_protocol\">Protokolla</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">Tunnistautuminen</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>Salasana</item>\n        <item>Anonyymi</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">Käyttäjänimi</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">Syötä käyttäjänimi</string>\n    <string name=\"storage_edit_ftp_server_password\">Salasana</string>\n    <string name=\"storage_edit_ftp_server_mode\">Tila</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>Aktiivinen</item>\n        <item>Passiivinen</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">Koodaus</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">Yhdistä ja lisää</string>\n    <string name=\"storage_edit_ftp_server_add\">Lisää</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">Muokkaa SFTP-palvelinta</string>\n    <string name=\"storage_edit_sftp_server_title_add\">Lisää SFTP-palvelin</string>\n    <string name=\"storage_edit_sftp_server_host\">Isäntänimi</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">Syötä isäntänimi</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">Virheellinen isäntänimi</string>\n    <string name=\"storage_edit_sftp_server_port\">Portti</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">Virheellinen portti</string>\n    <string name=\"storage_edit_sftp_server_path\">Polku</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">Voidaan jättää tyhjäksi</string>\n    <string name=\"storage_edit_sftp_server_name\">Nimi</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">Käytä isäntänimeä</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">Tunnistautuminen</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>Salasana</item>\n        <item>Julkinen avain</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">Käyttäjänimi</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">Syötä käyttäjänimi</string>\n    <string name=\"storage_edit_sftp_server_password\">Salasana</string>\n    <string name=\"storage_edit_sftp_server_private_key\">Yksityinen avain</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">Avaa tiedosto</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">Syötä yksityinen avain</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">Virheellinen yksityinen avain</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">Ykstityisen avaimen salasana</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">Voidaan jättää tyhjäksi</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">Virheellinen yksityisen avaimen salasana</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">Yhdistä ja lisää</string>\n    <string name=\"storage_edit_sftp_server_add\">Lisää</string>\n    <string name=\"storage_add_lan_smb_server_loading\">Etsitään SMB-palvelimia…</string>\n    <string name=\"storage_add_lan_smb_server_add\">Lisää manuaalisesti</string>\n    <string name=\"storage_edit_smb_server_title_edit\">Muokkaa SMB-palvelinta</string>\n    <string name=\"storage_edit_smb_server_title_add\">Lisää SMB-palvelin</string>\n    <string name=\"storage_edit_smb_server_host\">Isäntänimi</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">Syötä isäntänimi</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">Virheellinen isäntänimi</string>\n    <string name=\"storage_edit_smb_server_port\">Portti</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">Virheellinen portti</string>\n    <string name=\"storage_edit_smb_server_path\">Polku</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">Voidaan jättää tyhjäksi</string>\n    <string name=\"storage_edit_smb_server_name\">Nimi</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">Käytä isäntänimeä</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">Tunnistautuminen</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>Salasana</item>\n        <item>Vieras</item>\n        <item>Anonyymi</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">Käyttäjänimi</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">Syötä käyttäjänimi</string>\n    <string name=\"storage_edit_smb_server_password\">Salasana</string>\n    <string name=\"storage_edit_smb_server_domain\">Domain</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">Yhdistä ja lisää</string>\n    <string name=\"storage_edit_smb_server_add\">Lisää</string>\n    <string name=\"navigation_storage_subtitle_format\">%1$s/%2$s vapaana</string>\n    <string name=\"navigation_add_storage\">Lisää tallennustila…</string>\n    <string name=\"navigation_standard_directory_alarms\">Hälytykset</string>\n    <string name=\"navigation_standard_directory_dcim\">DCIM</string>\n    <string name=\"navigation_standard_directory_documents\">Dokumentit</string>\n    <string name=\"navigation_standard_directory_downloads\">Lataukset</string>\n    <string name=\"navigation_standard_directory_movies\">Elokuvat</string>\n    <string name=\"navigation_standard_directory_music\">Musiikki</string>\n    <string name=\"navigation_standard_directory_notifications\">Ilmoitukset</string>\n    <string name=\"navigation_standard_directory_pictures\">Kuvat</string>\n    <string name=\"navigation_standard_directory_podcasts\">Podcastit</string>\n    <string name=\"navigation_standard_directory_ringtones\">Soittoäänet</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">Nimi</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">Polku</string>\n    <string name=\"image_viewer_title\">Kuvan katselija</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"text_editor_title\">Tekstieditori</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">Tallennettu</string>\n    <string name=\"text_editor_reload_message\">Haluatko varmasti ladata tiedoston uudelleen? Tallentamattomat muutokset häviävät.</string>\n    <string name=\"text_editor_encoding\">Merkistökoodaus</string>\n    <string name=\"text_editor_close_message\">Haluatko varmasti hylätä tiedoston tallentamattomat muutokset?</string>\n\n    <string name=\"ftp_server_title\">FTP-palvelin</string>\n    <string name=\"ftp_server_state_title\">Tila</string>\n    <string name=\"ftp_server_state_summary_starting\">Käynnistetään…</string>\n    <string name=\"ftp_server_state_summary_running\">Käynnissä</string>\n    <string name=\"ftp_server_state_summary_stopping\">Pysäytetään…</string>\n    <string name=\"ftp_server_state_summary_stopped\">Ei käynnistettynä</string>\n    <string name=\"ftp_server_url_title\">URL</string>\n    <string name=\"ftp_server_url_summary_no_local_inet_address\">Paikallinen IP-osoite on tuntematon</string>\n    <string name=\"ftp_server_url_menu_copy_url\">Kopioi URL</string>\n    <string name=\"ftp_server_url_menu_copy_password\">Kopioi salasana</string>\n    <string name=\"ftp_server_add_tile_title\">Lisää pika-asetuksiin</string>\n    <string name=\"ftp_server_configuration_title\">Asetukset</string>\n    <string name=\"ftp_server_anonymous_login_title\">Anonyymi kirjautuminen</string>\n    <string name=\"ftp_server_username_title\">Käyttäjänimi</string>\n    <string name=\"ftp_server_password_title\">Salasana</string>\n    <string name=\"ftp_server_port_title\">Portti</string>\n    <string name=\"ftp_server_home_directory_title\">Juurihakemisto</string>\n    <string name=\"ftp_server_writable_title\">Salli kirjoittaminen</string>\n    <string name=\"settings_title\">Asetukset</string>\n    <string name=\"settings_interface_title\">Käyttöliittymä</string>\n    <string name=\"settings_locale_title\">Kieli</string>\n    <string name=\"settings_theme_color_title\">Teemaväri</string>\n    <string name=\"settings_theme_color_summary\">Eniten näytetty väri sovelluksessa</string>\n    <string name=\"settings_material_design_3_title\">Material Design 3</string>\n    <string name=\"settings_night_mode_title\">Yötila</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>Seuraa järjestelmää</item>\n        <item>Pois päältä</item>\n        <item>Päältä</item>\n        <item>Aikaan perustuen</item>\n        <item>Virransäästöön perustue</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">Musta yöteema</string>\n    <string name=\"settings_file_list_animation_title\">Tiedostolistan anmiaatioita</string>\n    <string name=\"settings_file_name_ellipsize_title\">Näytä pitkä tiedostonimi</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>Lyhennä alusta</item>\n        <item>Lyhennä keskeltä</item>\n        <item>Lyhennä lopusta</item>\n        <item>Marquee</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">Toiminta</string>\n    <string name=\"settings_default_directory_title\">Oletuskansio</string>\n    <string name=\"settings_standard_directories_title\">Standardihakemistot</string>\n    <string name=\"settings_standard_directories_summary_empty\">Standardihakemistoja ei ole otettu käyttöön</string>\n    <string name=\"settings_bookmark_directory_screenshots\">Kuvakaappaukset</string>\n    <string name=\"settings_bookmark_directories_title\">Kirjanmerkkikansiot</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">Ei kirjanmerkkikansioita</string>\n    <string name=\"settings_root_strategy_title\">Root-käyttöoikeus</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"one\">%1$,d tiedostotoiminto on käynnissä ja root-käyttöoikeuden muuttaminen saattaa johtaa odottamattomaan virheeseen. Haluatko varmasti muuttaa asetusta nyt?</item>\n        <item quantity=\"other\">%1$,d tiedostotoimintoa on käynnissä ja root-käyttöoikeuden muuttaminen saattaa johtaa odottamattomaan virheeseen. Haluatko varmasti muuttaa asetusta nyt?</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>Vain normaalikäyttö</item>\n        <item>Automaattinen</item>\n        <item>Vain root-käyttö</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">Pakkausten tiedostonimen koodaus</string>\n    <string name=\"settings_open_apk_default_action_title\">Avaa Android-pakkaus</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>Asenna</item>\n        <item>Näytä sisältö</item>\n        <item>Kysy, mikä toiminto suoritetaan</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">Saattaa aiheuttaa sovelluksen epävakauden vanhemmilla Android-versioilla</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">Hae esikatselukuvat etätiedostoista</string>\n\n    <string name=\"about_title\">Tietoa</string>\n    <string name=\"about_version_title\">Versio</string>\n    <string name=\"about_github_title\">Avaa GitHubissa</string>\n    <string name=\"about_licenses_title\">Lisenssit</string>\n    <string name=\"about_privacy_policy_title\">Tietosuojaseloste</string>\n    <string name=\"about_author_title\">Tekijä</string>\n    <string name=\"about_author_name_title\">Hai Zhang</string>\n    <string name=\"about_author_github_title\">Seuraa GitHubissa</string>\n    <string name=\"about_author_twitter_title\">Seuraa Twitterissä</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-fr/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">Fichier</string>\n    <string name=\"file_type_name_apk\">Paquet Android</string>\n    <string name=\"file_type_name_archive\">%1$s archive</string>\n    <string name=\"file_type_name_audio\">%1$s audio</string>\n    <string name=\"file_type_name_calendar\">%1$s agenda</string>\n    <string name=\"file_type_name_certificate\">%1$s certificat</string>\n    <string name=\"file_type_name_code\">%1$s document</string>\n    <string name=\"file_type_name_contact\">Carte de visite électronique</string>\n    <string name=\"file_type_name_directory\">Dossier</string>\n    <string name=\"file_type_name_document\">%1$s document</string>\n    <string name=\"file_type_name_ebook\">%1$s livre numérique</string>\n    <string name=\"file_type_name_email\">%1$s courriel</string>\n    <string name=\"file_type_name_font\">%1$s police</string>\n    <string name=\"file_type_name_generic\">%1$s fichier</string>\n    <string name=\"file_type_name_image\">%1$s image</string>\n    <string name=\"file_type_name_pdf\">Document PDF</string>\n    <string name=\"file_type_name_presentation\">%1$s présentation</string>\n    <string name=\"file_type_name_spreadsheet\">%1$s feuille de calcul</string>\n    <string name=\"file_type_name_text\">%1$s document</string>\n    <string name=\"file_type_name_text_plain\">Document texte</string>\n    <string name=\"file_type_name_video\">%1$s vidéo</string>\n    <string name=\"file_type_name_word\">Document Word</string>\n    <string name=\"file_type_name_powerpoint\">Présentation PowerPoint</string>\n    <string name=\"file_type_name_excel\">Feuille de calcul Excel</string>\n    <string name=\"file_type_name_posix_character_device\">Dispositif de caractère</string>\n    <string name=\"file_type_name_posix_block_device\">Périphérique bloc</string>\n    <string name=\"file_type_name_posix_fifo\">Pipe</string>\n    <string name=\"file_type_name_posix_symbolic_link\">Lien</string>\n    <string name=\"file_type_name_posix_socket\">Interface de connexion</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">Lien (mort)</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-fr/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:ignore=\"ImpliedQuantity,MissingQuantity\">\n\n    <string name=\"app_name\">Material Files</string>\n\n    <string name=\"close\">Fermer</string>\n    <string name=\"copy\">Copier</string>\n    <string name=\"cut\">Couper</string>\n    <string name=\"default_\">Par défaut</string>\n    <string name=\"delete\">Supprimer</string>\n    <string name=\"discard\">Jeter</string>\n    <string name=\"edit\">Modifier</string>\n    <string name=\"empty\">Vide</string>\n    <string name=\"empty_placeholder\">(vide)</string>\n    <string name=\"error\">Erreur</string>\n    <string name=\"hide\">Cacher</string>\n    <string name=\"install\">Installer</string>\n    <string name=\"keep_editing\">Continuer à modifier</string>\n    <string name=\"loading\">Chargement…</string>\n    <string name=\"maybe_later\">Peut-être plus tard</string>\n    <string name=\"merge\">Fusionner</string>\n    <string name=\"none\">Aucun</string>\n    <string name=\"open_navigation_drawer\">Ouvrir le tiroir de navigation</string>\n    <string name=\"paste\">Coller</string>\n    <string name=\"refresh\">Rafraîchir</string>\n    <string name=\"reload\">Recharger</string>\n    <string name=\"remove\">Supprimer</string>\n    <string name=\"rename\">Renommer</string>\n    <string name=\"replace\">Remplacer</string>\n    <string name=\"reset\">Remettre à zéro</string>\n    <string name=\"retry\">Ré-essayer</string>\n    <string name=\"save\">Sauvegarder</string>\n    <string name=\"search\">Recherche</string>\n    <string name=\"select\">Sélectionner</string>\n    <string name=\"select_all\">Sélectionner tout</string>\n    <string name=\"share\">Partager</string>\n    <string name=\"show\">Montrer</string>\n    <string name=\"skip\">Passer</string>\n    <string name=\"stop\">Arrêter</string>\n    <string name=\"system_default\">Système par défaut</string>\n    <string name=\"unknown\">Inconnu</string>\n    <string name=\"view\">Voir</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">Aucune application n’a été trouvée pour gérer cette action</string>\n    <string name=\"open_settings\">Ouvrir les réglages</string>\n    <string name=\"shortcut_created\">Raccourci créé</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"one\">%1$,d octet</item>\n        <item quantity=\"many\">%1$,d octets</item>\n        <item quantity=\"other\">%1$,d octets</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">L\\'application a besoin d\\'un accès pour gérer tous les fichiers. Veuillez autoriser cet accès dans les paramètres du système.</string>\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">L’application a besoin de la permission pour accéder aux fichiers. Veuillez cliquer sur “AUTORISER” dans la boîte de dialogue suivante.</string>\n    <string name=\"storage_permission_permanently_denied_message\">L’application a besoin de la permission d’accéder aux fichiers. Veuillez accorder la permission de stockage dans les paramètres du système.</string>\n    <!-- The allow button is in sentence case now. -->\n    <string name=\"notification_permission_rationale_message\">L’application a besoin la permission pour envoyer des notifications pour les opérations sur les fichiers. Veuillez cliquer sur “AUTORISER” dans la boîte de dialogue suivante.</string>\n    <string name=\"notification_permission_permanently_denied_message\">L’application a besoin de permission pour envoyer des notifications sur les opérations sur les fichiers. Veuillez accorder la permission \\\"Notification\\\" dans les paramètres systèmes</string>\n\n    <string name=\"notification_channel_background_activity_start_name\">Actions en arrière-plan</string>\n    <string name=\"notification_channel_background_activity_start_description\">Maintenir les actions lorsque l’application est en arrière-plan</string>\n    <string name=\"notification_channel_file_job_name\">Opérations sur les fichiers</string>\n    <string name=\"notification_channel_file_job_description\">Afficher et contrôler les opérations sur les fichiers</string>\n    <string name=\"notification_channel_ftp_server_name\">Serveur FTP</string>\n    <string name=\"notification_channel_ftp_server_description\">Afficher et contrôler le serveur FTP</string>\n\n    <string name=\"file_action_archive_password_title\">Mot de passe requis</string>\n    <string name=\"file_action_archive_password_message_format\">\\\"%1$s\\\" est protégé par un mot de passe</string>\n    <string name=\"file_action_archive_password_error_empty\">Le mot de passe ne peut pas être vide</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"one\">Préparation de la compression de %1$,d fichier (%2$s)</item>\n        <item quantity=\"many\">Préparation de la compression de %1$,d fichiers (%2$s)</item>\n        <item quantity=\"other\">Préparation de la compression de %1$,d fichiers (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">Compresser “%1$s” dans “%2$s”</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"one\">Compresser %1$,d fichier dans “%2$s”</item>\n        <item quantity=\"many\">Compresser %1$,d fichiers dans “%2$s”</item>\n        <item quantity=\"other\">Compresser %1$,d fichiers dans “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"one\">Préparation de la copie de %1$,d fichier (%2$s)</item>\n        <item quantity=\"many\">Préparation de la copie de %1$,d fichiers (%2$s)</item>\n        <item quantity=\"other\">Préparation de la copie de %1$,d fichiers (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">Copier “%1$s” dans “%2$s”</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"one\">Copier %1$,d fichier dans “%2$s”</item>\n        <item quantity=\"many\">Copier %1$,d fichiers dans “%2$s”</item>\n        <item quantity=\"other\">Copier %1$,d fichiers dans “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"one\">Préparation de l\\'extraction de %1$,d fichier (%2$s)</item>\n        <item quantity=\"many\">Préparation de l\\'extraction de %1$,d fichiers (%2$s)</item>\n        <item quantity=\"other\">Préparation de l\\'extraction de %1$,d fichiers (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">Extraire “%1$s” dans “%2$s”</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"one\">Extraire %1$,d fichier dans “%2$s”</item>\n        <item quantity=\"many\">Extraire %1$,d fichiers dans “%2$s”</item>\n        <item quantity=\"other\">Extraire %1$,d fichiers dans “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"one\">Préparation du déplacement de %1$,d fichier (%2$s)</item>\n        <item quantity=\"many\">Préparation du déplacement de %1$,d fichiers (%2$s)</item>\n        <item quantity=\"other\">Préparation du déplacement de %1$,d fichiers (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">Déplacer “%1$s” dans “%2$s”</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"one\">Déplacer %1$,d fichier dans “%2$s”</item>\n        <item quantity=\"many\">Déplacer %1$,d fichiers dans “%2$s”</item>\n        <item quantity=\"other\">Déplacer %1$,d fichiers dans “%2$s”</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"one\">Préparation de la suppression de %1$,d fichier</item>\n        <item quantity=\"many\">Préparation de la suppression de %1$,d fichiers</item>\n        <item quantity=\"other\">Préparation de la suppression de %1$,d fichiers</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">Supprimer “%1$s”</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"one\">Supprimer %1$,d fichier</item>\n        <item quantity=\"many\">Supprimer %1$,d fichiers</item>\n        <item quantity=\"other\">Supprimer %1$,d fichiers</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"one\">Préparation du changement de propriétaire pour %1$,d fichier</item>\n        <item quantity=\"many\">Préparation du changement de propriétaire pour %1$,d fichiers</item>\n        <item quantity=\"other\">Préparation du changement de propriétaire pour %1$,d fichiers</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">Changer le propriétaire de “%1$s”</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"one\">Changer le propriétaire de %1$,d fichier</item>\n        <item quantity=\"many\">Changer le propriétaire de %1$,d fichiers</item>\n        <item quantity=\"other\">Changer le propriétaire de %1$,d fichiers</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"one\">Préparation du changement de groupe pour %1$,d fichier</item>\n        <item quantity=\"many\">Préparation du changement de groupe pour %1$,d fichiers</item>\n        <item quantity=\"other\">Préparation du changement de groupe pour %1$,d fichiers</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">Changer le groupe de “%1$s”</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"one\">Changer le groupe de %1$,d fichier</item>\n        <item quantity=\"many\">Changer le groupe de %1$,d fichiers</item>\n        <item quantity=\"other\">Changer le groupe de %1$,d fichiers</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"one\">Préparation du changement de mode pour %1$,d fichier</item>\n        <item quantity=\"many\">Préparation du changement de mode pour %1$,d fichiers</item>\n        <item quantity=\"other\">Préparation du changement de mode pour %1$,d fichiers</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">Changer le mode de “%1$s”</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"one\">Changer le mode de %1$,d fichier</item>\n        <item quantity=\"many\">Changer le mode de %1$,d fichiers</item>\n        <item quantity=\"other\">Changer le mode de %1$,d fichiers</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Préparation du changement de contexte SElinux pour %1$,d fichier</item>\n        <item quantity=\"many\">Préparation du changement de contexte SElinux pour %1$,d fichiers</item>\n        <item quantity=\"other\">Préparation du changement de contexte SElinux pour %1$,d fichiers</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">Changement de contexte SELinux pour “%1$s”</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Changement du contexte SELinux pour %1$,d fichier</item>\n        <item quantity=\"many\">Changement du contexte SELinux pour %1$,d fichiers</item>\n        <item quantity=\"other\">Changement du contexte SELinux pour %1$,d fichiers</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Préparation de la restauration du contexte SELinux pour %1$,d fichier</item>\n        <item quantity=\"many\">Préparation de la restauration du contexte SELinux pour %1$,d fichiers</item>\n        <item quantity=\"other\">Préparation de la restauration du contexte SELinux pour %1$,d fichiers</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">Restauration du context SELinux pour \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Restauration du contexte SELinux pour %1$,d fichier</item>\n        <item quantity=\"many\">Restauration du contexte SELinux pour %1$,d fichiers</item>\n        <item quantity=\"other\">Restauration du contexte SELinux pour %1$,d fichiers</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">Ecrire “%1$s”</string>\n    <string name=\"file_job_create_error_title\">Erreur lors de la création</string>\n    <string name=\"file_job_create_error_message_format\">Il y a eu une erreur lors de la création “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">Impossible de copier un dossier dans lui-même</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">Impossible d’extraire un dossier dans lui-même</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">Impossible de déplacer un dossier dans lui-même</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">Le dossier de destination est à l\\'intérieur du dossier source</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">Impossible de copier un fichier sur lui-même</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">Impossible d’extraire un fichier sur lui-même</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">Impossible de déplacer un fichier sur lui-même</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">Le fichier source serait écrasé par la destination.</string>\n    <string name=\"file_job_replace_title_format\">Remplacer le fichier “%1$s” ?</string>\n    <string name=\"file_job_replace_message_format\">Un autre fichier portant le même nom existe déjà dans \\\"%1$s\\\".\\nLe remplacer réecrira son contenu.</string>\n    <string name=\"file_job_replace_target_name\">Fichier original</string>\n    <string name=\"file_job_replace_source_name\">Remplacer par</string>\n    <string name=\"file_job_merge_title_format\">Fusionner le dossier “%1$s” ?</string>\n    <string name=\"file_job_merge_copy_message_format\">La fusion demandera une confirmation avant de remplacer les fichiers du dossier qui entrent en conflit avec les fichiers copiés.</string>\n    <string name=\"file_job_merge_extract_message_format\">La fusion demandera une confirmation avant de remplacer tous les fichiers du dossier qui entrent en conflit avec les fichiers extraits.</string>\n    <string name=\"file_job_merge_move_message_format\">La fusion demandera une confirmation avant de remplacer tous les fichiers du dossier qui entrent en conflit avec les fichiers en cours de déplacement.</string>\n    <string name=\"file_job_merge_target_name\">Dossier original</string>\n    <string name=\"file_job_merge_source_name\">Fusionner avec</string>\n    <string name=\"file_job_conflict_show_name\">Sélectionner un nouveau nom pour la destination</string>\n    <string name=\"file_job_conflict_name_hint\">Nouveau nom</string>\n    <string name=\"file_job_archive_error_title_format\">Erreur lors de la compression “%1$s”</string>\n    <string name=\"file_job_archive_error_message_format\">Une erreur s\\'est produite lors de la compression du fichier \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">Erreur lors de copie “%1$s”</string>\n    <string name=\"file_job_copy_error_message_format\">Une erreur s\\'est produite lors de la copie du fichier dans \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">Erreur lors de l\\'extraction “%1$s”</string>\n    <string name=\"file_job_extract_error_message_format\">Une erreur s\\'est produite lors de l\\'extraction du fichier dans \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">Erreur lors du déplacement “%1$s”</string>\n    <string name=\"file_job_move_error_message_format\">Une erreur s\\'est produite lors du déplacement du fichier vers \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">Erreur lors de la suppression</string>\n    <string name=\"file_job_delete_error_message_format\">Une erreur s\\'est produite lors de la suppression de \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">Erreur lors du renommage “%1$s”</string>\n    <string name=\"file_job_rename_error_message_format\">Une erreur s\\'est produite lors du changement de nom du fichier en “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">Erreur lors du changement de propriétaire de “%1$s”</string>\n    <string name=\"file_job_set_owner_error_message_format\">Une erreur s\\'est produite lors du changement de propriétaire par \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">Erreur lors du changement de groupe de “%1$s”</string>\n    <string name=\"file_job_set_group_error_message_format\">Une erreur s\\'est produite lors du changement de groupe \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">Erreur lors du changement du mode de “%1$s”</string>\n    <string name=\"file_job_set_mode_error_message_format\">Une erreur s\\'est produite lors du changement du mode en \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">Erreur lors du changement de contexte SELinux pour \\\"%1$s\\\"</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">Une erreur s\\'est produite lors du changement de contexte SELinux en \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">Erreur lors de la restauration du contexte SELinux</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">Une erreur s\\'est produite lors de la restauration du contexte SELinux pour \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">Erreur lors de l\\'écriture</string>\n    <string name=\"file_job_write_error_message_format\">Une erreur s\\'est produite lors de l\\'écriture de \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">Une erreur s\\'est produite lors de l\\'obtention d\\'informations sur \\\"%1$s\\\".</string>\n    <string name=\"file_job_remount_format\">Remontez \\\"%1$s\\\" en lecture-écriture</string>\n    <string name=\"file_job_remount_loading_format\">Remontage de \\\"%1$s\\\" en lecture-écriture…</string>\n    <string name=\"file_job_remount_success_format\">\\\"%1$s\\\" a été remonté en lecture-écriture</string>\n    <string name=\"file_job_action_all\">Appliquer cette action à tous les fichiers</string>\n\n    <string name=\"file_open_apk_message\">Ce fichier est un package Android. Voulez-vous l\\'installer ou afficher son contenu?</string>\n    <string name=\"file_open_as_title\">Ouvrir avec…</string>\n    <string name=\"file_open_as_title_format\">Ouvrir “%1$s” comme</string>\n    <string name=\"file_open_as_type_text\">Texte</string>\n    <string name=\"file_open_as_type_image\">Image</string>\n    <string name=\"file_open_as_type_audio\">Audio</string>\n    <string name=\"file_open_as_type_video\">Vidéo</string>\n    <string name=\"file_open_as_type_directory\">Dossier</string>\n    <string name=\"file_open_as_type_any\">Autre</string>\n    <string name=\"file_install_apk_from_background_title_format\">“%1$s” est prêt à être installé</string>\n    <string name=\"file_install_apk_from_background_text\">Appuyer pour installer</string>\n    <string name=\"file_open_from_background_title_format\">“%1$s” est prêt à être ouvert</string>\n    <string name=\"file_open_from_background_text\">Appuyer pour ouvrir</string>\n    <string name=\"file_name_error_empty\">Le nom du fichier ne peut pas être vide</string>\n    <string name=\"file_name_error_invalid\">Nom de fichier invalide</string>\n    <string name=\"file_name_error_already_exists\">Un fichier avec ce nom existe déjà</string>\n    <string name=\"file_replace_message_format\">Remplacer \\\"%1$s\\\" ?</string>\n    <string name=\"file_delete_message_file_format\">Supprimer “%1$s” ?</string>\n    <string name=\"file_delete_message_directory_format\">Supprimer le dossier “%1$s” et son contenu ?</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"one\">Supprimer %1$,d fichier?</item>\n        <item quantity=\"many\">Supprimer %1$,d fichiers?</item>\n        <item quantity=\"other\">Supprimer %1$,d fichiers?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"one\">Supprimer %1$,d dossier et son contenu ?</item>\n        <item quantity=\"many\">Supprimer %1$,d dossiers et leur contenu ?</item>\n        <item quantity=\"other\">Supprimer %1$,d dossiers et leur contenu ?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"one\">Supprimer %1$,d élément?</item>\n        <item quantity=\"many\">Supprimer %1$,d éléments?</item>\n        <item quantity=\"other\">Supprimer %1$,d éléments?</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">Créer une archive</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_create_archive_password\">Mot de passe (optionnel)</string>\n    <string name=\"file_add_bookmark_success\">Marque-page ajouté</string>\n    <string name=\"file_create_file_title\">Nouveau fichier</string>\n    <string name=\"file_create_directory_title\">Nouveau dossier</string>\n\n    <string name=\"file_list_title\">Fichiers</string>\n    <plurals name=\"file_list_title_open_file\">\n        <item quantity=\"one\">Selection un fichier</item>\n        <item quantity=\"many\">Sélectionner les fichiers</item>\n        <item quantity=\"other\">Sélectionner les fichiers</item>\n    </plurals>\n    <string name=\"file_list_title_create_file\">Sauvegarder le fichier</string>\n    <plurals name=\"file_list_title_open_directory\">\n        <item quantity=\"one\">Sélectionner un dossier</item>\n        <item quantity=\"many\">Sélectionner les dossiers</item>\n        <item quantity=\"other\">Sélectionner les dossiers</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"one\">%1$,d dossier</item>\n        <item quantity=\"many\">%1$,d dossiers</item>\n        <item quantity=\"other\">%1$,d dossiers</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"one\">%1$,d fichier</item>\n        <item quantity=\"many\">%1$,d fichiers</item>\n        <item quantity=\"other\">%1$,d fichiers</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">,\\u0020</string>\n    <string name=\"file_list_path_error_empty\">Le chemin ne peut pas être vide</string>\n    <string name=\"file_list_path_error_invalid\">Chemin invalide</string>\n    <string name=\"file_list_action_view_sort\">Afficher et trier</string>\n    <string name=\"file_list_action_view_list\">Liste</string>\n    <string name=\"file_list_action_view_grid\">Grille</string>\n    <string name=\"file_list_action_sort_by_name\">Nom</string>\n    <string name=\"file_list_action_sort_by_type\">Type</string>\n    <string name=\"file_list_action_sort_by_size\">Taille</string>\n    <string name=\"file_list_action_sort_by_last_modified\">Dernière modification</string>\n    <string name=\"file_list_action_sort_order_ascending\">Ascendant</string>\n    <string name=\"file_list_action_sort_directories_first\">Dossiers en premier</string>\n    <string name=\"file_list_action_view_sort_path_specific\">Seulement pour ce dossier</string>\n    <string name=\"file_list_action_new_task\">Nouvelle fenêtre</string>\n    <string name=\"file_list_action_navigate_up\">Aller en haut</string>\n    <string name=\"file_list_action_navigate_to\">Aller à</string>\n    <string name=\"file_list_action_show_hidden_files\">Voir les fichiers cachés</string>\n    <string name=\"file_list_action_copy_path\">Copier le chemin</string>\n    <string name=\"file_list_action_open_in_terminal\">Ouvrir dans le terminal</string>\n    <string name=\"file_list_action_add_bookmark\">Ajouter un marque-page</string>\n    <string name=\"file_list_action_create_shortcut\">Créer un raccourci</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">Ouvrir dans une nouvelle fenêtre</string>\n    <string name=\"file_list_select_title_format\">%1$,d</string>\n    <string name=\"file_list_create_file_name_hint\">Nom de fichier</string>\n    <string name=\"file_list_open_current_directory_format\">Sélectionner \\\"%1$s\\\"</string>\n    <string name=\"file_list_paste_move_title_format\">Déplacer %1$,d</string>\n    <string name=\"file_list_paste_copy_title_format\">Copier %1$,d</string>\n    <string name=\"file_list_paste_extract_title_format\">Extraire %1$,d</string>\n    <string name=\"file_list_paste_action_extract_here\">Extraire ici</string>\n    <string name=\"file_list_empty\">Pas de fichiers</string>\n    <string name=\"file_list_action_create_file\">Fichier</string>\n    <string name=\"file_list_action_create_directory\">Dossier</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">Ouvrir avec</string>\n    <string name=\"file_item_action_extract\">Extraire</string>\n    <string name=\"file_item_action_archive\">Compresser</string>\n    <string name=\"file_item_action_properties\">Propriétés</string>\n\n    <string name=\"file_properties_title_format\">“%1$s” propriétés</string>\n    <string name=\"file_properties_basic\">Basique</string>\n    <string name=\"file_properties_basic_name\">Nom</string>\n    <string name=\"file_properties_basic_type\">Type</string>\n    <string name=\"file_properties_basic_type_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">Lié à %1$s (%2$s) </string>\n    <string name=\"file_properties_basic_symbolic_link_target\">Lien Cible</string>\n    <string name=\"file_properties_basic_size\">Taille</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_contents\">Contenus</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"one\">%1$,d élément, pesant %2$s</item>\n        <item quantity=\"many\">%1$,d éléments, totalisant %2$s</item>\n        <item quantity=\"other\">%1$,d éléments, totalisant %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">Dernière Modification</string>\n    <string name=\"file_properties_basic_parent_directory\">Dossier parent</string>\n    <string name=\"file_properties_basic_archive_file\">Fichier d\\'archive</string>\n    <string name=\"file_properties_basic_archive_entry\">Entrée d\\'Archive</string>\n    <string name=\"file_properties_basic_free_space\">Espace libre</string>\n    <string name=\"file_properties_permission\">Permissions</string>\n    <string name=\"file_properties_permission_owner\">Propriétaire</string>\n    <string name=\"file_properties_permission_group\">Groupe</string>\n    <string name=\"file_properties_permission_principal_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_permission_set_owner_title\">Changer le propriétaire</string>\n    <string name=\"file_properties_permission_set_group_title\">Changer le groupe</string>\n    <string name=\"file_properties_permission_set_principal_filter_hint\">Entrer un nom ou un ID</string>\n    <string name=\"file_properties_permission_set_principal_system\">Système</string>\n    <string name=\"file_properties_permission_mode\">Mode</string>\n    <string name=\"file_properties_permission_mode_format\">%1$s (%2$04o)</string>\n    <string name=\"file_properties_permission_set_mode_title\">Changer le mode</string>\n    <string name=\"file_properties_permission_set_mode_others\">Autres</string>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_file\">\n        <item>Lire</item>\n        <item>Écrire</item>\n        <item>Exécuter</item>\n    </string-array>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_directory\">\n        <item>Lire</item>\n        <item>Écrire</item>\n        <item>Recherche</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_special\">Spécial</string>\n    <string-array name=\"file_properties_permission_set_mode_special_mode_bits\">\n        <item>Définir l\\'UID</item>\n        <item>Définir le GID</item>\n        <item>Sticky bit</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_uppercase_x\">N\\'ajoutez pas \\\"Exécuter\\\" pour les fichiers joints</string>\n    <string name=\"file_properties_permission_selinux_context\">Contexte SELinux</string>\n    <string name=\"file_properties_permission_set_selinux_context_title\">Changer le contexte SELinux</string>\n    <string name=\"file_properties_permission_set_selinux_context_restore\">Restaurer</string>\n    <string name=\"file_properties_permission_recursive\">Appliquer aux fichiers joints</string>\n    <string name=\"file_properties_image\">Image</string>\n    <string name=\"file_properties_media_dimensions\">Dimensions</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">Prise le</string>\n    <string name=\"file_properties_media_coordinates\">Coordonnées</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f, %2$.3f</string>\n    <string name=\"file_properties_media_address\">Localisation</string>\n    <string name=\"file_properties_image_gps_altitude\">Altitude</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f m</string>\n    <string name=\"file_properties_image_equipment\">Appareil photo</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">Ouverture</string>\n    <string name=\"file_properties_image_shutter_speed\">Temps de pose</string>\n    <string name=\"file_properties_image_focal_length\">Focale</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f mm</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">Équivalent ISO</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">Logiciel</string>\n    <string name=\"file_properties_image_description\">Description</string>\n    <string name=\"file_properties_image_artist\">Auteur</string>\n    <string name=\"file_properties_image_copyright\">Copyright</string>\n    <string name=\"file_properties_audio\">Audio</string>\n    <string name=\"file_properties_media_title\">Titre</string>\n    <string name=\"file_properties_audio_artist\">Artiste</string>\n    <string name=\"file_properties_audio_album\">Album</string>\n    <string name=\"file_properties_audio_album_artist\">Artiste de l\\'album</string>\n    <string name=\"file_properties_audio_composer\">Compositeur</string>\n    <string name=\"file_properties_audio_disc_number\">Disque</string>\n    <string name=\"file_properties_audio_track_number\">Piste</string>\n    <string name=\"file_properties_audio_year\">Année</string>\n    <string name=\"file_properties_audio_genre\">Genre</string>\n    <string name=\"file_properties_media_duration\">Durée</string>\n    <string name=\"file_properties_media_bit_rate\">Débit binaire</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d kbit/s</string>\n    <string name=\"file_properties_audio_sample_rate\">Fréquence d\\'échantillonnage</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Hz</string>\n    <string name=\"file_properties_video\">Vidéo</string>\n    <string name=\"file_properties_apk\">APK</string>\n    <string name=\"file_properties_apk_label\">Nom</string>\n    <string name=\"file_properties_apk_package_name\">Nom du paquet</string>\n    <string name=\"file_properties_apk_version\">Version</string>\n    <string name=\"file_properties_apk_version_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_apk_min_sdk_version\">Version système minimale</string>\n    <string name=\"file_properties_apk_target_sdk_version\">Version système cible</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s (%2$s, %3$d)</string>\n    <string name=\"file_properties_apk_requested_permissions\">Permissions</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">Aucune permission demandée</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"one\">%1$,d permission demandée</item>\n        <item quantity=\"many\">%1$,d permissions demandées</item>\n        <item quantity=\"other\">%1$,d permissions demandées</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">Signatures</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">Signatures non valides</string>\n    <string name=\"file_properties_apk_past_signature_digests\">Anciennes signatures</string>\n    <string name=\"file_properties_checksum\">Somme de contrôle</string>\n    <string name=\"file_properties_checksum_compare\">Comparer</string>\n    <string name=\"file_properties_checksum_compare_placeholder\">Toute somme de contrôle</string>\n    <string name=\"file_properties_checksum_compare_match_format\">Correspond à la somme de contrôle %1$s</string>\n    <string name=\"file_properties_checksum_compare_prefix_match_format\">Correspond au prefix de la somme de contrôle %1$s</string>\n    <string name=\"file_properties_checksum_compare_no_match\">Aucune somme de contrôle correspondante trouvée</string>\n    <string name=\"storage_file_system_root_title\">Root</string>\n    <string name=\"storage_list_title\">Support de stockage</string>\n    <string name=\"storage_list_empty\">Pas de support de stockage</string>\n    <string name=\"storage_add_storage_title\">Ajouter un support de stockage</string>\n    <string name=\"storage_add_storage_document_tree\">Support de stockage externe</string>\n    <string name=\"storage_add_storage_ftp_server\">Serveur FTP</string>\n    <string name=\"storage_add_storage_sftp_server\">Serveur SFTP</string>\n    <string name=\"storage_add_storage_smb_server\">Serveur SMB</string>\n    <string name=\"storage_add_storage_webdav_server\">Serveur WebDAV</string>\n    <string name=\"storage_edit_device_storage_title\">Modifier le stockage de l\\'appareil</string>\n    <string name=\"storage_edit_device_storage_name\">Nom</string>\n    <string name=\"storage_edit_device_storage_path\">Chemin</string>\n    <string name=\"storage_add_external_storage_shortcut_title\">Ajouter un raccourci de stockage externe</string>\n    <string name=\"storage_edit_external_storage_shortcut_title\">Éditer un raccourci de stockage externe</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id\">Volume de stockage</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id_error_empty\">Entrer un volume de stockage</string>\n    <string name=\"storage_edit_external_storage_shortcut_path\">Chemin</string>\n    <string name=\"storage_add_document_tree_title\">Ajouter un support de stockage externe</string>\n    <string name=\"storage_edit_document_tree_title\">Modifier le support de stockage externe</string>\n    <string name=\"storage_edit_document_tree_name\">Nom</string>\n    <string name=\"storage_edit_document_tree_uri\">URI</string>\n    <string name=\"storage_edit_document_tree_path\">Chemin</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">Modifier le serveur FTP</string>\n    <string name=\"storage_edit_ftp_server_title_add\">Ajouter un serveur FTP</string>\n    <string name=\"storage_edit_ftp_server_host\">Nom d\\'hôte</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">Entrez un nom d\\'hôte</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">Nom d\\'hôte invalide</string>\n    <string name=\"storage_edit_ftp_server_port\">Port</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">Port invalide</string>\n    <string name=\"storage_edit_ftp_server_path\">Chemin d\\'accès</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">Peut être laissé vide</string>\n    <string name=\"storage_edit_ftp_server_name\">Nom</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">Utiliser le nom d\\'hôte</string>\n    <string name=\"storage_edit_ftp_server_protocol\">Protocole</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">Authentification</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>Mot de passe</item>\n        <item>Anonyme</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">Nom d\\'utilisateur</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">Entrez un nom d\\'utilisateur</string>\n    <string name=\"storage_edit_ftp_server_password\">Mot de passe</string>\n    <string name=\"storage_edit_ftp_server_mode\">Mode</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>Actif</item>\n        <item>Passif</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">Encodage</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">Connecter et ajouter</string>\n    <string name=\"storage_edit_ftp_server_add\">Ajouter</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">Modifier le serveur SFTP</string>\n    <string name=\"storage_edit_sftp_server_title_add\">Ajouter un serveur SFTP</string>\n    <string name=\"storage_edit_sftp_server_host\">Nom d\\'hôte</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">Entrez un nom d\\'hôte</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">Nom d\\'hôte invalide</string>\n    <string name=\"storage_edit_sftp_server_port\">Port</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">Port non valide</string>\n    <string name=\"storage_edit_sftp_server_path\">Chemin</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">Peut être laissé vide</string>\n    <string name=\"storage_edit_sftp_server_name\">Nom</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">Utiliser le nom d\\'hôte</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">Authentification</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>Mot de passe</item>\n        <item>Clé publique</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">Nom d\\'utilisateur</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">Entrer un nom d\\'utilisateur</string>\n    <string name=\"storage_edit_sftp_server_password\">Mot de passe</string>\n    <string name=\"storage_edit_sftp_server_private_key\">Clé privée</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">Ouvrir le fichier</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">Entrer une clé privée</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">Clé privée non valide</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">Mot de passe de clé privée</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">Peut être laissé vide</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">Mot de passe de clé privée invalide</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">Connecter et ajouter</string>\n    <string name=\"storage_edit_sftp_server_add\">Ajouter</string>\n    <string name=\"storage_add_lan_smb_server_loading\">Recherche de serveurs SMB…</string>\n    <string name=\"storage_add_lan_smb_server_add\">Ajouter manuellement</string>\n    <string name=\"storage_edit_smb_server_title_edit\">Modifier le serveur SMB</string>\n    <string name=\"storage_edit_smb_server_title_add\">Ajouter un serveur SMB</string>\n    <string name=\"storage_edit_smb_server_host\">Nom d\\'hôte</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">Entrez un nom d\\'hôte</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">Nom d\\'hôte invalide</string>\n    <string name=\"storage_edit_smb_server_port\">Port</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">Port invalide</string>\n    <string name=\"storage_edit_smb_server_path\">Chemin</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">Peut être laissé vide</string>\n    <string name=\"storage_edit_smb_server_name\">Nom</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">Utiliser le nom d\\'hôte</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">Identification</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>Mot de passe</item>\n        <item>Invité</item>\n        <item>Anonyme</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">Nom d\\'utilisateur</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">Entrer un nom d\\'utilisateur</string>\n    <string name=\"storage_edit_smb_server_password\">Mot de passe</string>\n    <string name=\"storage_edit_smb_server_domain\">Domaine</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">Connecter et ajouter</string>\n    <string name=\"storage_edit_smb_server_add\">Ajouter</string>\n    <string name=\"storage_edit_webdav_server_title_edit\">Modifier le serveur WebDAV</string>\n    <string name=\"storage_edit_webdav_server_title_add\">Ajouter un serveur WebDAV</string>\n    <string name=\"storage_edit_webdav_server_host\">Nom d\\'hôte</string>\n    <string name=\"storage_edit_webdav_server_host_error_empty\">Entrez un nom d\\'hôte</string>\n    <string name=\"storage_edit_webdav_server_host_error_invalid\">Nom d\\'hôte invalide</string>\n    <string name=\"storage_edit_webdav_server_port\">Port</string>\n    <string name=\"storage_edit_webdav_server_port_error_invalid\">Port invalide</string>\n    <string name=\"storage_edit_webdav_server_path\">Chemin</string>\n    <string name=\"storage_edit_webdav_server_path_placeholder\">Peut être laissé vide</string>\n    <string name=\"storage_edit_webdav_server_name\">Nom</string>\n    <string name=\"storage_edit_webdav_server_name_placeholder\">Utiliser le nom d\\'hôte</string>\n    <string name=\"storage_edit_webdav_server_protocol\">Protocole</string>\n    <string name=\"storage_edit_webdav_server_authentication_type\">Authentification</string>\n    <string-array name=\"storage_edit_webdav_server_authentication_type_entries\">\n        <item>Mot de passe</item>\n        <item>Jeton d\\'accès</item>\n        <item>Aucun</item>\n    </string-array>\n    <string name=\"storage_edit_webdav_server_username\">Nom d\\'utilisateur</string>\n    <string name=\"storage_edit_webdav_server_username_error_empty\">Entrez un nom d\\'utilisateur</string>\n    <string name=\"storage_edit_webdav_server_password\">Mot de passe</string>\n    <string name=\"storage_edit_webdav_server_access_token\">Jeton d\\'accès</string>\n    <string name=\"storage_edit_webdav_server_access_token_error_empty\">Entrez un jeton d\\'accès</string>\n    <string name=\"storage_edit_webdav_server_connect_and_add\">Connecter et ajouter</string>\n    <string name=\"storage_edit_webdav_server_add\">Ajouter</string>\n\n    <string name=\"navigation_storage_subtitle_format\">%1$s de libre sur %2$s</string>\n    <string name=\"navigation_add_storage\">Ajouter un support de stockage…</string>\n    <string name=\"navigation_standard_directory_alarms\">Alarmes</string>\n    <string name=\"navigation_standard_directory_dcim\">DCIM</string>\n    <string name=\"navigation_standard_directory_documents\">Documents</string>\n    <string name=\"navigation_standard_directory_downloads\">Téléchargements</string>\n    <string name=\"navigation_standard_directory_movies\">Vidéos</string>\n    <string name=\"navigation_standard_directory_music\">Musique</string>\n    <string name=\"navigation_standard_directory_notifications\">Notifications</string>\n    <string name=\"navigation_standard_directory_pictures\">Images</string>\n    <string name=\"navigation_standard_directory_podcasts\">Podcasts</string>\n    <string name=\"navigation_standard_directory_ringtones\">Sonneries</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">Dossier de marque-pages</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">Nom</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">Chemin</string>\n    <string name=\"archive_viewer_title\">Visionneuse d\\'archives</string>\n    <string name=\"image_viewer_title\">Visionneuse d\\'images</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"save_as_title\">Enregistrer sous</string>\n    <string name=\"save_as_error\">Erreur lors de l\\'enregistrement du fichier</string>\n    <string name=\"save_as_success_format\">\\\"%1$s\\\" a été enregister dans \\\"%2$s\\\"</string>\n    <string name=\"text_editor_title\">Éditeur de texte</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">Sauvegarder</string>\n    <string name=\"text_editor_reload_message\">Êtes-vous sûr de vouloir recharger ? Les modifications non enregistrées apportées à ce document seront perdues.</string>\n    <string name=\"text_editor_encoding\">Encodage</string>\n    <string name=\"text_editor_close_message\">Êtes-vous sûr de vouloir rejeter les modifications non enregistrées apportées à ce document ?</string>\n\n    <string name=\"ftp_server_title\">Serveur FTP</string>\n    <string name=\"ftp_server_state_title\">Status</string>\n    <string name=\"ftp_server_state_summary_starting\">Démarrage…</string>\n    <string name=\"ftp_server_state_summary_running\">Fonctionne</string>\n    <string name=\"ftp_server_state_summary_stopping\">Arrêt…</string>\n    <string name=\"ftp_server_state_summary_stopped\">Pas démarré</string>\n    <string name=\"ftp_server_url_title\">URL</string>\n    <string name=\"ftp_server_url_summary_no_local_inet_address\">L\\'adresse IP locale est inconnue</string>\n    <string name=\"ftp_server_url_menu_copy_url\">Copier l\\'URL</string>\n    <string name=\"ftp_server_url_menu_copy_password\">Copier le mot de passe</string>\n    <string name=\"ftp_server_add_tile_title\">Ajouter aux paramètre rapides</string>\n    <string name=\"ftp_server_add_tile_result_added\">\\\"Serveur FTP\\\" a été ajouté aux paramètres rapides</string>\n    <string name=\"ftp_server_add_tile_result_already_added\">\\\"Serveur FTP\\\" est déjà ajouté aux paramètres rapides</string>\n    <string name=\"ftp_server_add_tile_result_error\">Erreur lors de l\\'ajout de \\\"Serveur FTP\\\" aux paramètres rapides</string>\n    <string name=\"ftp_server_configuration_title\">Configuration</string>\n    <string name=\"ftp_server_anonymous_login_title\">Connexion anonyme</string>\n    <string name=\"ftp_server_username_title\">Nom d\\'utilisateur</string>\n    <string name=\"ftp_server_password_title\">Mot de passe</string>\n    <string name=\"ftp_server_port_title\">Port</string>\n    <string name=\"ftp_server_home_directory_title\">Dossier root</string>\n    <string name=\"ftp_server_writable_title\">Autoriser l\\'écriture</string>\n    <string name=\"settings_title\">Réglages</string>\n    <string name=\"settings_interface_title\">Interface</string>\n    <string name=\"settings_locale_title\">Langage </string>\n    <string name=\"settings_theme_color_title\">Couleur du thème</string>\n    <string name=\"settings_theme_color_summary\">Couleur qui apparaît le plus souvent dans l’application</string>\n    <string name=\"settings_material_design_3_title\">Material Design 3</string>\n    <string name=\"settings_night_mode_title\">Mode nuit</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>Suivre le système</item>\n        <item>Désactivé</item>\n        <item>Activé</item>\n        <item>Basé sur l\\'heure</item>\n        <item>Basé sur l’économiseur de batterie</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">Mode nuit noire</string>\n    <string name=\"settings_file_list_animation_title\">Animation de liste de fichiers</string>\n    <string name=\"settings_file_name_ellipsize_title\">Affichage du nom de fichier</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>Tronquer le début</item>\n        <item>Tronquer le milieu</item>\n        <item>Tronquer la fin</item>\n        <item>Entier</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">Comportement</string>\n    <string name=\"settings_default_directory_title\">Dossier par défaut</string>\n    <string name=\"settings_standard_directories_title\">Dossiers standard</string>\n    <string name=\"settings_standard_directories_summary_empty\">Aucuns dossiers standard activés</string>\n    <string name=\"settings_bookmark_directory_screenshots\">Captures d\\'écran</string>\n    <string name=\"settings_bookmark_directories_title\">Dossiers de marque-pages</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">Pas de dossiers de marque-pages</string>\n    <string name=\"settings_root_strategy_title\">Mode d\\'accès root</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"one\">Vous avez %1$,d opération de fichiers en cours d’exécution et changer maintenant le mode d’accès root peut entraîner une erreur inattendue. Êtes-vous sûr de vouloir changer cela maintenant ?</item>\n        <item quantity=\"many\">Vous avez %1$,d opérations de fichiers en cours d’exécution et changer maintenant le mode d’accès root peut entraîner une erreur inattendue. Êtes-vous sûr de vouloir changer cela maintenant ?</item>\n        <item quantity=\"other\">Vous avez %1$,d opérations de fichiers en cours d’exécution et changer maintenant le mode d’accès root peut entraîner une erreur inattendue. Êtes-vous sûr de vouloir changer cela maintenant ?</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>Accès normal uniquement</item>\n        <item>Automatique</item>\n        <item>Accès root uniquement</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">Encodage du nom de l\\'archive</string>\n    <string name=\"settings_open_apk_default_action_title\">Ouvrir le paquet Android</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>Installer</item>\n        <item>Voir le contenu</item>\n        <item>Demander quoi faire</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_title\">Afficher la miniature des fichiers PDF</string>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">Pourrait rendre l’application instable sur les anciennes versions d’Android</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">Lire les fichiers distants pour les vignettes</string>\n\n    <string name=\"about_title\">A propos</string>\n    <string name=\"about_version_title\">Version</string>\n    <string name=\"about_github_title\">Voir sur GitHub</string>\n    <string name=\"about_licenses_title\">Licences</string>\n    <string name=\"about_privacy_policy_title\">Politique de confidentialité</string>\n    <string name=\"about_author_title\">Auteur</string>\n    <string name=\"about_author_name_title\">Hai Zhang</string>\n    <string name=\"about_author_github_title\">Suivre sur GitHub</string>\n    <string name=\"about_author_twitter_title\">Suivre sur Twitter</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-hu/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">Fájl</string>\n    <string name=\"file_type_name_apk\">Android csomag</string>\n    <string name=\"file_type_name_archive\">%1$s-arhívum</string>\n    <string name=\"file_type_name_audio\">%1$s-hang</string>\n    <string name=\"file_type_name_calendar\">%1$s-naptár</string>\n    <string name=\"file_type_name_certificate\">%1$s-tanúsítvány</string>\n    <string name=\"file_type_name_code\">%1$s-dokumentum</string>\n    <string name=\"file_type_name_contact\">Elektronikus névjegykártya</string>\n    <string name=\"file_type_name_directory\">Mappa</string>\n    <string name=\"file_type_name_document\">%1$s-dokumentum</string>\n    <string name=\"file_type_name_ebook\">%1$s e-könyv</string>\n    <string name=\"file_type_name_email\">%1$s e-mail üzenet</string>\n    <string name=\"file_type_name_font\">%1$s-betűkészlet</string>\n    <string name=\"file_type_name_generic\">%1$s-fájl</string>\n    <string name=\"file_type_name_image\">%1$s-kép</string>\n    <string name=\"file_type_name_pdf\">PDF dokumentum</string>\n    <string name=\"file_type_name_presentation\">%1$s-prezentáció</string>\n    <string name=\"file_type_name_spreadsheet\">%1$s-táblázat</string>\n    <string name=\"file_type_name_text\">%1$s-dokumentum</string>\n    <string name=\"file_type_name_text_plain\">Egyszerű szöveges dokumentum</string>\n    <string name=\"file_type_name_video\">%1$s-videó</string>\n    <string name=\"file_type_name_word\">Word dokumentum</string>\n    <string name=\"file_type_name_powerpoint\">PowerPoint prezentáció</string>\n    <string name=\"file_type_name_excel\">Excel táblázat</string>\n    <string name=\"file_type_name_posix_character_device\">Karakteres eszköz</string>\n    <string name=\"file_type_name_posix_block_device\">Blokkeszköz</string>\n    <string name=\"file_type_name_posix_fifo\">Csővezeték</string>\n    <string name=\"file_type_name_posix_symbolic_link\">Link</string>\n    <string name=\"file_type_name_posix_socket\">Foglalat</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">Link (törött)</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-hu/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <string name=\"app_name\">Material Files</string>\n\n    <string name=\"close\">Bezárás</string>\n    <string name=\"copy\">Másolás</string>\n    <string name=\"cut\">Kivágás</string>\n    <string name=\"default_\">Alapértelmezett</string>\n    <string name=\"delete\">Törlés</string>\n    <string name=\"discard\">Elvetés</string>\n    <string name=\"edit\">Szerkesztés</string>\n    <string name=\"empty\">Ürítés</string>\n    <string name=\"empty_placeholder\">(Üres)</string>\n    <string name=\"error\">Hiba</string>\n    <string name=\"hide\">Elrejtés</string>\n    <string name=\"install\">Telepítés</string>\n    <string name=\"keep_editing\">Szerkesztés folytatása</string>\n    <string name=\"loading\">Betöltés…</string>\n    <string name=\"maybe_later\">Talán később</string>\n    <string name=\"merge\">Egyesítés</string>\n    <string name=\"none\">Nincs</string>\n    <string name=\"open_navigation_drawer\">Navigációs sáv megnyitása</string>\n    <string name=\"paste\">Beillesztés</string>\n    <string name=\"refresh\">Frissítés</string>\n    <string name=\"reload\">Újratöltés</string>\n    <string name=\"remove\">Eltávolítás</string>\n    <string name=\"rename\">Átnevezés</string>\n    <string name=\"replace\">Csere</string>\n    <string name=\"reset\">Visszaállítás</string>\n    <string name=\"retry\">Újra</string>\n    <string name=\"save\">Mentés</string>\n    <string name=\"search\">Keresés</string>\n    <string name=\"select\">Kiválasztás</string>\n    <string name=\"select_all\">Összes kiválasztása</string>\n    <string name=\"share\">Megosztás</string>\n    <string name=\"show\">Megjelenítés</string>\n    <string name=\"skip\">Kihagyás</string>\n    <string name=\"stop\">Leállítás</string>\n    <string name=\"system_default\">Rendszer alapértelmezése</string>\n    <string name=\"unknown\">Ismeretlen</string>\n    <string name=\"view\">Nézet</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">Nincs olyan alkalmazás, amely kezelné ezt a műveletet</string>\n    <string name=\"open_settings\">Beállítások megnyitása</string>\n    <string name=\"shortcut_created\">Parancsikon létrehozva</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"one\">%1$,d bájt</item>\n        <item quantity=\"other\">%1$,d bájt</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">Az alkalmazásnak hozzáférésre van szüksége az összes fájl kezeléséhez. Engedélyezze a hozzáférést a következő rendszer beállításban.</string>\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">Az alkalmazásnak engedélyre van szüksége, hogy elérje a fájlokat. Kattintson az „Engedélyezésre” a következő rendszerüzeneten.</string>\n    <string name=\"storage_permission_permanently_denied_message\">Az alkalmazásnak engedélyre van szüksége, hogy elérje a fájlokat. Adja meg a „Tároló” engedélyt a rendszerbeállításokban.</string>\n    <string name=\"notification_channel_background_activity_start_name\">Műveletek a háttérben</string>\n    <string name=\"notification_channel_background_activity_start_description\">Műveletek elvégzése, míg az alkalmazás a háttérben van</string>\n    <string name=\"notification_channel_file_job_name\">Fájlműveletek</string>\n    <string name=\"notification_channel_file_job_description\">Fájlműveletek megjelenítése és vezérlése</string>\n    <string name=\"notification_channel_ftp_server_name\">FTP-kiszolgáló</string>\n    <string name=\"notification_channel_ftp_server_description\">FTP-kiszolgáló megjelenítése és vezérlése</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"one\">Felkészülés %1$,d fájl tömörítésére (%2$s)</item>\n        <item quantity=\"other\">Felkészülés %1$,d fájl tömörítésére (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">A(z) „%1$s” tömörítése ebbe: „%2$s”</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"one\">%1$,d fájl tömörítése ebbe: „%2$s”</item>\n        <item quantity=\"other\">%1$,d fájl tömörítése ebbe: „%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"one\">Felkészülés %1$,d fájl másolására (%2$s)</item>\n        <item quantity=\"other\">Felkészülés %1$,d fájl másolására (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">A(z) „%1$s” másolása ide: „%2$s”</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"one\">%1$,d fájl másolása ide: „%2$s”</item>\n        <item quantity=\"other\">%1$,d fájl másolása ide: „%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"one\">Felkészülés %1$,d fájl kibontására (%2$s)</item>\n        <item quantity=\"other\">Felkészülés %1$,d fájl kibontására (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">A(z) „%1$s” kibontása ide: „%2$s”</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"one\">%1$,d fájl kibontása ide: „%2$s”</item>\n        <item quantity=\"other\">%1$,d fájl kibontása ide: „%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"one\">Felkészülés %1$,d fájl áthelyezésére (%2$s)</item>\n        <item quantity=\"other\">Felkészülés %1$,d fájl áthelyezésére (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">A(z) „%1$s” áthelyezése ide: „%2$s”</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"one\">%1$,d fájl áthelyezése ide: „%2$s”</item>\n        <item quantity=\"other\">%1$,d fájl áthelyezése ide: „%2$s”</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"one\">Felkészülés %1$,d fájl törlésére</item>\n        <item quantity=\"other\">Felkészülés %1$,d fájl törlésére</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">A(z) „%1$s” törlése</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"one\">%1$,d fájl törlése</item>\n        <item quantity=\"other\">%1$,d fájl törlése</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"one\">Felkészülés %1$,d fájl tulajdonosának módosítására</item>\n        <item quantity=\"other\">Felkészülés %1$,d fájl tulajdonosának módosítására</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">A(z) „%1$s” tulajdonosának módosítása</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"one\">%1$,d fájl tulajdonosának módosítása</item>\n        <item quantity=\"other\">%1$,d fájl tulajdonosának módosítása</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"one\">%1$,d fájl csoportjának módosítása</item>\n        <item quantity=\"other\">%1$,d fájl csoportjának módosítása</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">A(z) „%1$s” csoportjának módosítása</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"one\">%1$,d fájl csoportjának módosítása</item>\n        <item quantity=\"other\">%1$,d fájl csoportjának módosítása</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"one\">Felkészülés %1$,d fájl módjának módosítására</item>\n        <item quantity=\"other\">Felkészülés %1$,d fájl módjának módosítására</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">A(z) „%1$s” módjának módosítása</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"one\">%1$,d fájl módjának módosítása</item>\n        <item quantity=\"other\">%1$,d fájl módjának módosítása</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Felkészülés %1$,d fájl SELinux környezetének módosítására</item>\n        <item quantity=\"other\">Felkészülés %1$,d fájl SELinux környezetének módosítására</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">A(z) „%1$s” SELinux környezetének módosítása</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">%1$,d fájl SELinux környezetének módosítása</item>\n        <item quantity=\"other\">%1$,d fájl SELinux környezetének módosítása</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Felkészülés %1$,d fájl SELinux környezetének helyreállítására</item>\n        <item quantity=\"other\">Felkészülés %1$,d fájl SELinux környezetének helyreállítására</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">A(z) „%1$s” SELinux környezetének helyreállítása</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">%1$,d fájl SELinux környezetének helyreállítása</item>\n        <item quantity=\"other\">%1$,d fájl SELinux környezetének helyreállítása</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">A(z) „%1$s” írása</string>\n    <string name=\"file_job_create_error_title\">Hiba a létrehozáskor</string>\n    <string name=\"file_job_create_error_message_format\">Hiba történt a(z) „%1$s” létrehozásakor.\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">A mappa nem másolható saját magába</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">A mappa nem bontható ki saját magába</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">A mappa nem helyezhető át saját magába</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">A célmappa a forrásmappán belül van.</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">A fájl nem másolható saját magára</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">A fájl nem bontható ki saját magára</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">A fájl nem helyezhető át saját magára</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">A forrásfájlt felülírná a cél</string>\n    <string name=\"file_job_replace_title_format\">Lecseréli a(z) „%1$s” fájlt?</string>\n    <string name=\"file_job_replace_message_format\">Már létezik egy azonos nevű fájl itt: „%1$s”.\\nA csere felülírja a tartalmát.</string>\n    <string name=\"file_job_replace_target_name\">Eredeti fájl</string>\n    <string name=\"file_job_replace_source_name\">Csere ezzel:</string>\n    <string name=\"file_job_merge_title_format\">Egyesíti a(z) „%1$s” mappát?</string>\n    <string name=\"file_job_merge_copy_message_format\">Az egyesítés a másolt fájlok a mappában lévő fájlokkal történő ütközése esetén megerősítést fog kérni a cseréhez.</string>\n    <string name=\"file_job_merge_extract_message_format\">Az egyesítés a kibontott fájlok a mappában lévő fájlokkal történő ütközése esetén megerősítést fog kérni a cseréhez.</string>\n    <string name=\"file_job_merge_move_message_format\">Az egyesítés az áthelyezett fájlok a mappában lévő fájlokkal történő ütközése esetén megerősítést fog kérni a cseréhez.</string>\n    <string name=\"file_job_merge_target_name\">Eredeti mappa</string>\n    <string name=\"file_job_merge_source_name\">Egyesítés ezzel:</string>\n    <string name=\"file_job_conflict_show_name\">Válasszon új nevet a célhoz</string>\n    <string name=\"file_job_conflict_name_hint\">Új név</string>\n    <string name=\"file_job_archive_error_title_format\">Hiba a(z) „%1$s” tömörítésekor</string>\n    <string name=\"file_job_archive_error_message_format\">Hiba történt a(z) „%1$s” létrehozásakor.\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">Hiba a(z) „%1$s” másolásakor</string>\n    <string name=\"file_job_copy_error_message_format\">Hiba történt a fájl ide másolásakor: „%1$s”.\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">Hiba a(z) „%1$s” kibontásakor</string>\n    <string name=\"file_job_extract_error_message_format\">Hiba történt a fájl ide kibontásakor: „%1$s”.\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">Hiba a(z) „%1$s” áthelyezésekor</string>\n    <string name=\"file_job_move_error_message_format\">Hiba történt a fájl ide áthelyezésekor: „%1$s”.\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">Hiba a törléskor</string>\n    <string name=\"file_job_delete_error_message_format\">Hiba történt a(z) „%1$s” törlésekor.\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">Hiba a(z) „%1$s” átnevezésekor</string>\n    <string name=\"file_job_rename_error_message_format\">Hiba történt a fájl erre átnevezésekor: „%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">Hiba a(z) „%1$s” tulajdonosának módosításakor</string>\n    <string name=\"file_job_set_owner_error_message_format\">Hiba történt a fájl tulajdonosának erre módosításakor: „%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">Hiba a(z) „%1$s” csoportjának módosításakor</string>\n    <string name=\"file_job_set_group_error_message_format\">Hiba történt a fájl csoportjának erre módosításakor: „%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">Hiba a(z) „%1$s” módjának módosításakor</string>\n    <string name=\"file_job_set_mode_error_message_format\">Hiba történt a fájl módjának erre módosításakor: „%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">Hiba a(z) „%1$s” SELinux környezetének módosításakor</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">Hiba történt a fájl SELinux környezetének erre módosításakor: „%1$s”.\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">Hiba az SELinux környezet helyreállításakor</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">Hiba történt a(z) „%1$s” SELinux környezetének helyreállításakor.\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">Hiba az íráskor</string>\n    <string name=\"file_job_write_error_message_format\">Hiba történt a(z) „%1$s” írásakor.\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">Hiba történt a(z) „%1$s” információinak lekérésekor.</string>\n    <string name=\"file_job_remount_format\">A(z) „%1$s” újracsatolása írhatóként</string>\n    <string name=\"file_job_remount_loading_format\">A(z) „%1$s” újracsatolása írhatóként…</string>\n    <string name=\"file_job_remount_success_format\">A(z) „%1$s” újra lett csatolva írhatóként</string>\n    <string name=\"file_job_action_all\">E művelet alkalmazása az összes fájlra</string>\n\n    <string name=\"file_open_apk_message\">Ez a fájl egy androidos csomag. Telepíteni szeretné, vagy megtekinti a tartalmát?</string>\n    <string name=\"file_open_as_title\">Megnyitás másként…</string>\n    <string name=\"file_open_as_title_format\">A(z) „%1$s” megnyitása másként</string>\n    <string name=\"file_open_as_type_text\">Szöveg</string>\n    <string name=\"file_open_as_type_image\">Ké</string>\n    <string name=\"file_open_as_type_audio\">Hang</string>\n    <string name=\"file_open_as_type_video\">Videó</string>\n    <string name=\"file_open_as_type_directory\">Mappa</string>\n    <string name=\"file_open_as_type_any\">Egyéb</string>\n    <string name=\"file_install_apk_from_background_title_format\">A(z) „%1$s” telepítésre kész</string>\n    <string name=\"file_install_apk_from_background_text\">Koppintson a telepítéshez</string>\n    <string name=\"file_open_from_background_title_format\">A(z) „%1$s” megnyitásra kész</string>\n    <string name=\"file_open_from_background_text\">Koppintson a megnyitáshoz</string>\n    <string name=\"file_name_error_empty\">A fájlnév nem lehet üres</string>\n    <string name=\"file_name_error_invalid\">Érvénytelen fájlnév</string>\n    <string name=\"file_name_error_already_exists\">Már létezik egy fájl ezzel a névvel</string>\n    <string name=\"file_delete_message_file_format\">Törli a(z) „%1$s” fájlt?</string>\n    <string name=\"file_delete_message_directory_format\">Törli a(z) „%1$s” mappát és a tartalmát?</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"one\">Törli a(z) %1$,d fájlt?</item>\n        <item quantity=\"other\">Töröl %1$,d fájlokat?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"one\">Töröl %1$,d mappát és azok tartalmát?</item>\n        <item quantity=\"other\">Töröl %1$,d mappát és azok tartalmát?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"one\">Töröl %1$,d elemet?</item>\n        <item quantity=\"other\">Töröl %1$,d elemet?</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">Archívum létrehozása</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_add_bookmark_success\">Könyvjelző hozzáadva</string>\n    <string name=\"file_create_file_title\">Új fájl</string>\n    <string name=\"file_create_directory_title\">Új mappa</string>\n\n    <string name=\"file_list_title\">Fájlok</string>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"one\">%1$,d mappa</item>\n        <item quantity=\"other\">%1$,d mappa</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"one\">%1$,d fájl</item>\n        <item quantity=\"other\">%1$,d fájl</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">,\\u0020</string>\n    <string name=\"file_list_path_error_empty\">Az útvonal nem lehet üres</string>\n    <string name=\"file_list_path_error_invalid\">Érvénytelen útvonal</string>\n    <string name=\"file_list_action_view_sort\">Megtekintés és rendezés</string>\n    <string name=\"file_list_action_view_list\">Lista</string>\n    <string name=\"file_list_action_view_grid\">Rács</string>\n    <string name=\"file_list_action_sort_by_name\">Név</string>\n    <string name=\"file_list_action_sort_by_type\">Típus</string>\n    <string name=\"file_list_action_sort_by_size\">Méret</string>\n    <string name=\"file_list_action_sort_by_last_modified\">Legutóbb módosítva</string>\n    <string name=\"file_list_action_sort_order_ascending\">Növekvő</string>\n    <string name=\"file_list_action_sort_directories_first\">Mappák elöl</string>\n    <string name=\"file_list_action_view_sort_path_specific\">Csak ennél a mappánál</string>\n    <string name=\"file_list_action_new_task\">Új ablak</string>\n    <string name=\"file_list_action_navigate_up\">Ugrás fel</string>\n    <string name=\"file_list_action_navigate_to\">Ugrás ide:</string>\n    <string name=\"file_list_action_show_hidden_files\">Rejtett fájlok megjelenítése</string>\n    <string name=\"file_list_action_copy_path\">Útvonal másolása</string>\n    <string name=\"file_list_action_open_in_terminal\">Megnyitás terminálban</string>\n    <string name=\"file_list_action_add_bookmark\">Könyvjelző hozzáadása</string>\n    <string name=\"file_list_action_create_shortcut\">Parancsikon létrehozása</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">Megnyitás új ablakban</string>\n    <string name=\"file_list_select_title_format\">%1$,d</string>\n    <string name=\"file_list_paste_move_title_format\">%1$,d áthelyezése</string>\n    <string name=\"file_list_paste_copy_title_format\">%1$,d másolása</string>\n    <string name=\"file_list_paste_extract_title_format\">%1$,d fájl kibontása</string>\n    <string name=\"file_list_paste_action_extract_here\">Kibontás ide</string>\n    <string name=\"file_list_empty\">Nincsenek fájlok</string>\n    <string name=\"file_list_action_create_file\">Fájl</string>\n    <string name=\"file_list_action_create_directory\">Mappa</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">Megnyitás ezzel:</string>\n    <string name=\"file_item_action_extract\">Kibontás</string>\n    <string name=\"file_item_action_archive\">Tömörítés</string>\n    <string name=\"file_item_action_properties\">Tulajdonságok</string>\n\n    <string name=\"file_properties_title_format\">„%1$s” tulajdonságai</string>\n    <string name=\"file_properties_basic\">Alapvető</string>\n    <string name=\"file_properties_basic_name\">Név</string>\n    <string name=\"file_properties_basic_type\">Típus</string>\n    <string name=\"file_properties_basic_type_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">Link erre: %1$s (%2$s)</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">Link célja</string>\n    <string name=\"file_properties_basic_size\">Méret</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_contents\">Tartalom</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"one\">%1$,d elem, mérete %2$s</item>\n        <item quantity=\"other\">%1$,d elem, összesen %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">Legutóbb módosítva</string>\n    <string name=\"file_properties_basic_parent_directory\"> Szülőmappa</string>\n    <string name=\"file_properties_basic_archive_file\">Fájl archiválása</string>\n    <string name=\"file_properties_basic_archive_entry\">Bejegyzés archiválása</string>\n    <string name=\"file_properties_basic_free_space\">Szabad hely</string>\n    <string name=\"file_properties_image\">Kép</string>\n    <string name=\"file_properties_media_dimensions\">Méretek</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">Készítés ideje</string>\n    <string name=\"file_properties_media_coordinates\">Koordináták</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f, %2$.3f</string>\n    <string name=\"file_properties_media_address\">Hely</string>\n    <string name=\"file_properties_image_gps_altitude\">Magasság</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f m</string>\n    <string name=\"file_properties_image_equipment\">Kamera</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">Apertúra</string>\n    <string name=\"file_properties_image_shutter_speed\">Zársebesség</string>\n    <string name=\"file_properties_image_focal_length\">Gyújtótávolság</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f mm</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">ISO megfelelő</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">Szoftver</string>\n    <string name=\"file_properties_image_description\">Leírás</string>\n    <string name=\"file_properties_image_artist\">Készítő</string>\n    <string name=\"file_properties_image_copyright\">Jogi információ</string>\n    <string name=\"file_properties_audio\">Hang</string>\n    <string name=\"file_properties_media_title\">Cí</string>\n    <string name=\"file_properties_audio_artist\">Előadó</string>\n    <string name=\"file_properties_audio_album\">Album</string>\n    <string name=\"file_properties_audio_album_artist\">Album előadója</string>\n    <string name=\"file_properties_audio_composer\">Zeneszerző</string>\n    <string name=\"file_properties_audio_disc_number\">Lemez</string>\n    <string name=\"file_properties_audio_track_number\">Szám</string>\n    <string name=\"file_properties_audio_year\">Év</string>\n    <string name=\"file_properties_audio_genre\">Műfaj</string>\n    <string name=\"file_properties_media_duration\">Hossz</string>\n    <string name=\"file_properties_media_bit_rate\">Bitsebesség</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d kbps</string>\n    <string name=\"file_properties_audio_sample_rate\">Mintavételi arány</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Hz</string>\n    <string name=\"file_properties_video\">Videó</string>\n    <string name=\"file_properties_apk\">APK</string>\n    <string name=\"file_properties_apk_label\">Név</string>\n    <string name=\"file_properties_apk_package_name\">Csomag neve</string>\n    <string name=\"file_properties_apk_version\">Verzió</string>\n    <string name=\"file_properties_apk_version_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_apk_min_sdk_version\">Minimális rendszerverzi</string>\n    <string name=\"file_properties_apk_target_sdk_version\">Célrendszer verziója</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s (%2$s, %3$d)</string>\n    <string name=\"file_properties_apk_requested_permissions\">Engedélyek</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">Nem kér engedélyeket</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"one\">%1$,d engedélyt kér</item>\n        <item quantity=\"other\">%1$,d engedélyt kér</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">Aláírások</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">Nincsenek érvényes aláírások</string>\n    <string name=\"file_properties_apk_past_signature_digests\">Régi aláírások</string>\n    <string name=\"storage_file_system_root_title\">Gyökér</string>\n    <string name=\"storage_list_title\">Tároló</string>\n    <string name=\"storage_list_empty\">Nincs tároló</string>\n    <string name=\"storage_add_storage_title\">Tároló hozzáadása</string>\n    <string name=\"storage_add_storage_document_tree\">Külső tároló</string>\n    <string name=\"storage_add_storage_ftp_server\">FTP-kiszolgáló</string>\n    <string name=\"storage_add_storage_sftp_server\">SFTP-kiszolgáló</string>\n    <string name=\"storage_add_storage_smb_server\">SMB-kiszolgáló</string>\n    <string name=\"storage_edit_device_storage_title\">Tárolóeszköz szerkesztése</string>\n    <string name=\"storage_edit_device_storage_name\">Név</string>\n    <string name=\"storage_edit_device_storage_path\">Útvonal</string>\n    <string name=\"storage_add_document_tree_title\">Külső tároló hozzáadása</string>\n    <string name=\"storage_edit_document_tree_title\">Külső tároló szerkesztése</string>\n    <string name=\"storage_edit_document_tree_name\">Név</string>\n    <string name=\"storage_edit_document_tree_uri\">URI</string>\n    <string name=\"storage_edit_document_tree_path\">Útvonal</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">FTP-kiszolgáló szerkesztése</string>\n    <string name=\"storage_edit_ftp_server_title_add\">FTP-kiszolgáló hozzáadása</string>\n    <string name=\"storage_edit_ftp_server_host\">Gépnév</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">Gépnév megadása</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">Érvénytelen gépnév</string>\n    <string name=\"storage_edit_ftp_server_port\">Port</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">Érvénytelen port</string>\n    <string name=\"storage_edit_ftp_server_path\">Útvonal</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">Üresen hagyható</string>\n    <string name=\"storage_edit_ftp_server_name\">Név</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">Gépnév használata</string>\n    <string name=\"storage_edit_ftp_server_protocol\">Protokoll</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">Hitelesítés</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>Jelszó</item>\n        <item>Névtelen</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">Felhasználónév</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">Felhasználónév megadása</string>\n    <string name=\"storage_edit_ftp_server_password\">Jelszó</string>\n    <string name=\"storage_edit_ftp_server_mode\">Átviteli mód</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>Aktív</item>\n        <item>Passzív</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">Kódolás</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">Hozzáadás és csatlakozás</string>\n    <string name=\"storage_edit_ftp_server_add\">Hozzáadás</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">SFTP-kiszolgáló szerkesztése</string>\n    <string name=\"storage_edit_sftp_server_title_add\">SFTP-kiszolgáló hozzáadása</string>\n    <string name=\"storage_edit_sftp_server_host\">Gépnév</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">Adjon meg egy gépnevet</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">Érvénytelen gépnév</string>\n    <string name=\"storage_edit_sftp_server_port\">Port</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">Érvénytelen port</string>\n    <string name=\"storage_edit_sftp_server_path\">Útvonal</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">Üresen hagyható</string>\n    <string name=\"storage_edit_sftp_server_name\">Név</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">Gépnév használata</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">Hitelesítés</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>Jelszó</item>\n        <item>Nyilvános kulcs</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">Felhasználónév</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">Adjon meg egy felhasználónevet</string>\n    <string name=\"storage_edit_sftp_server_password\">Jelszó</string>\n    <string name=\"storage_edit_sftp_server_private_key\">Privát kulcs</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">Fájl megnyitása</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">Adjon meg egy privát kulcsot</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">Érvénytelen privát kulcs</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">Privát kulcs jelszava</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">Üresen hagyható</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">Privát kulcs jelszava érvénytelen</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">Kapcsolódás és hozzáadás</string>\n    <string name=\"storage_edit_sftp_server_add\">Hozzáadása</string>\n    <string name=\"storage_add_lan_smb_server_loading\">SMB-kiszolgálók keresése…</string>\n    <string name=\"storage_add_lan_smb_server_add\">Hozzáadás kézzel</string>\n    <string name=\"storage_edit_smb_server_title_edit\">SMB-kiszolgáló szerkesztése</string>\n    <string name=\"storage_edit_smb_server_title_add\">SMB-kiszolgáló hozzáadása</string>\n    <string name=\"storage_edit_smb_server_host\">Gépnév</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">Adjon meg egy gépnevet</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">Érvénytelen gépnév</string>\n    <string name=\"storage_edit_smb_server_port\">Port</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">Érvénytelen port</string>\n    <string name=\"storage_edit_smb_server_path\">Útvonal</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">Üresen hagyható</string>\n    <string name=\"storage_edit_smb_server_name\">Név</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">Gépnév használata</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">Hitelesítés</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>Jelszó</item>\n        <item>Vendég</item>\n        <item>Névtelen</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">Felhasználónév</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">Adjon meg egy felhasználónevet</string>\n    <string name=\"storage_edit_smb_server_password\">Jelszót</string>\n    <string name=\"storage_edit_smb_server_domain\">Domain</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">Kapcsolódás és hozzáadás</string>\n    <string name=\"storage_edit_smb_server_add\">Hozzáadás</string>\n    <string name=\"navigation_storage_subtitle_format\">%1$s szabad ennyiből: %2$s</string>\n    <string name=\"navigation_add_storage\">Tároló hozzáadása…</string>\n    <string name=\"navigation_standard_directory_alarms\">Riasztások</string>\n    <string name=\"navigation_standard_directory_dcim\">DCIM</string>\n    <string name=\"navigation_standard_directory_documents\">Dokumentumok</string>\n    <string name=\"navigation_standard_directory_downloads\">Letöltések</string>\n    <string name=\"navigation_standard_directory_movies\">Filmek</string>\n    <string name=\"navigation_standard_directory_music\">Zenék</string>\n    <string name=\"navigation_standard_directory_notifications\">Értesítések</string>\n    <string name=\"navigation_standard_directory_pictures\">Képek</string>\n    <string name=\"navigation_standard_directory_podcasts\">Podcastok</string>\n    <string name=\"navigation_standard_directory_ringtones\">Csengőhangok</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">Mappa könyvjelzőzése</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">Név</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">Útvonal</string>\n    <string name=\"archive_viewer_title\">Archívum megjelenítő</string>\n    <string name=\"image_viewer_title\">Képmegjelenítő</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"text_editor_title\">Szövegszerkesztő</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">Mentve</string>\n    <string name=\"text_editor_reload_message\">Biztos, hogy újratölti? A dokumentum összes mentetlen módosítása elvész.</string>\n    <string name=\"text_editor_encoding\">Kódolás</string>\n    <string name=\"text_editor_close_message\">Biztos, hogy elveti a dokumentum mentetlen módosításait?</string>\n\n    <string name=\"ftp_server_title\">FTP-kiszolgáló</string>\n    <string name=\"ftp_server_state_title\">Állapot</string>\n    <string name=\"ftp_server_state_summary_starting\">Indítás…</string>\n    <string name=\"ftp_server_state_summary_running\">Fut</string>\n    <string name=\"ftp_server_state_summary_stopping\">Leállítás…</string>\n    <string name=\"ftp_server_state_summary_stopped\">Nem fut</string>\n    <string name=\"ftp_server_url_title\">Webcím</string>\n    <string name=\"ftp_server_url_summary_no_local_inet_address\">A helyi IP-cím ismeretlen</string>\n    <string name=\"ftp_server_url_menu_copy_url\">Webcím másolása</string>\n    <string name=\"ftp_server_url_menu_copy_password\">Jelszó másolása</string>\n    <string name=\"ftp_server_add_tile_title\">Hozzáadás a gyors beállításokhoz</string>\n    <string name=\"ftp_server_configuration_title\">Beállítás</string>\n    <string name=\"ftp_server_anonymous_login_title\">Névtelen bejelentkezés</string>\n    <string name=\"ftp_server_username_title\">Felhasználónév</string>\n    <string name=\"ftp_server_password_title\">Jelszó</string>\n    <string name=\"ftp_server_port_title\">Port</string>\n    <string name=\"ftp_server_home_directory_title\">Gyökérmappa</string>\n    <string name=\"ftp_server_writable_title\">Írás engedélyezése</string>\n    <string name=\"settings_title\">Beállítások</string>\n    <string name=\"settings_interface_title\">Felület</string>\n    <string name=\"settings_locale_title\">Nyelv</string>\n    <string name=\"settings_theme_color_title\">Téma színe</string>\n    <string name=\"settings_theme_color_summary\">A szín, amely a leggyakrabban megjelenik az alkalmazásban</string>\n    <string name=\"settings_material_design_3_title\">Material Design 3</string>\n    <string name=\"settings_night_mode_title\">Éjszakai mód</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>Rendszertéma követése</item>\n        <item>Ki</item>\n        <item>Be</item>\n        <item>Idő alapján</item>\n        <item>Akkumulátortöltöttség alapján</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">Fekete éjszakai mód</string>\n    <string name=\"settings_file_list_animation_title\">Fájllista animáció</string>\n    <string name=\"settings_file_name_ellipsize_title\">Hosszú fájlnév megjelenítése</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>Kipontozás az elején</item>\n        <item>Kipontozás a közepén</item>\n        <item>Kipontozás a végén</item>\n        <item>Futó szöveg</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">Viselkedés</string>\n    <string name=\"settings_default_directory_title\">Alapértelmezett mappa</string>\n    <string name=\"settings_standard_directories_title\">Szokásos mappák</string>\n    <string name=\"settings_standard_directories_summary_empty\">A szokásos mappák nem engedélyezettek</string>\n    <string name=\"settings_bookmark_directory_screenshots\">Képernyőképek</string>\n    <string name=\"settings_bookmark_directories_title\">Mappák könyvjelzőzése</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">Nincsenek könyvjelzőzött mappák</string>\n    <string name=\"settings_root_strategy_title\">Root hozzáférési mód</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"one\">%1$,d fájlművelet fut, és a root hozzáférési mód átváltása váratlan hibát okozhat. Biztos, hogy most módosítja?</item>\n        <item quantity=\"other\">%1$,d fájlművelet fut, és a root hozzáférési mód átváltása váratlan hibát okozhat. Biztos, hogy most módosítja?</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>Csak normál hozzáférés</item>\n        <item>Automatikus</item>\n        <item>Csak root hozzéférés</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">Archívum fájlnévkódolása</string>\n    <string name=\"settings_open_apk_default_action_title\">Androidos csomag megnyitása</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>Telepítés</item>\n        <item>Tartalom megtekintése</item>\n        <item>Kérdezze meg, hogy mit tegyen</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">Az Android régebbi verzióinál instabillá teheti az alkalmazást</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">Távoli fájlok olvasása a bélyegképekhez</string>\n\n    <string name=\"about_title\">Névjegy</string>\n    <string name=\"about_version_title\">Verzió</string>\n    <string name=\"about_github_title\">Megtekintés a GitHubon</string>\n    <string name=\"about_licenses_title\">Licencek</string>\n    <string name=\"about_privacy_policy_title\">Adatvédelmi nyilatkozat</string>\n    <string name=\"about_author_title\">Szerző</string>\n    <string name=\"about_author_name_title\">Hai Zhang</string>\n    <string name=\"about_author_github_title\">Követés a GitHubon</string>\n    <string name=\"about_author_twitter_title\">Követés Twitteren</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-in/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">Berkas</string>\n    <string name=\"file_type_name_apk\">Paket Android</string>\n    <string name=\"file_type_name_archive\">%1$s arsip</string>\n    <string name=\"file_type_name_audio\">%1$s audio</string>\n    <string name=\"file_type_name_calendar\">%1$s kalender</string>\n    <string name=\"file_type_name_certificate\">%1$s sertifikat</string>\n    <string name=\"file_type_name_code\">%1$s dokumen</string>\n    <string name=\"file_type_name_contact\">Kartu bisnis elektronik</string>\n    <string name=\"file_type_name_directory\">Folder</string>\n    <string name=\"file_type_name_document\">%1$s dokumen</string>\n    <string name=\"file_type_name_ebook\">%1$s e-book</string>\n    <string name=\"file_type_name_email\">%1$s pesan email</string>\n    <string name=\"file_type_name_font\">%1$s font</string>\n    <string name=\"file_type_name_generic\">%1$s berkas</string>\n    <string name=\"file_type_name_image\">%1$s gambar</string>\n    <string name=\"file_type_name_pdf\">Dokumen PDF</string>\n    <string name=\"file_type_name_presentation\">Presentasi %1$s</string>\n    <string name=\"file_type_name_spreadsheet\">Spreadsheet %1$s</string>\n    <string name=\"file_type_name_text\">Dokumen %1$s</string>\n    <string name=\"file_type_name_text_plain\">Dokumen teks biasa</string>\n    <string name=\"file_type_name_video\">Video %1$s</string>\n    <string name=\"file_type_name_word\">Dokumen Word</string>\n    <string name=\"file_type_name_powerpoint\">Presentasi PowerPoint</string>\n    <string name=\"file_type_name_excel\">Spreadsheet Excel</string>\n    <string name=\"file_type_name_posix_character_device\">karakter perangkat</string>\n    <string name=\"file_type_name_posix_block_device\">blok perangkat</string>\n    <string name=\"file_type_name_posix_fifo\">pipa</string>\n    <string name=\"file_type_name_posix_symbolic_link\">Tautan</string>\n    <string name=\"file_type_name_posix_socket\">Soket</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">Tautan (rusak)</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-in/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <string name=\"app_name\">Material Files</string>\n\n    <string name=\"close\">Tutup</string>\n    <string name=\"copy\">Salin</string>\n    <string name=\"cut\">Potong</string>\n    <string name=\"default_\">Bawaan</string>\n    <string name=\"delete\">Hapus</string>\n    <string name=\"discard\">Batal</string>\n    <string name=\"edit\">Edit</string>\n    <string name=\"empty\">Kosong</string>\n    <string name=\"empty_placeholder\">(Kosong)</string>\n    <string name=\"error\">kesalahan</string>\n    <string name=\"hide\">Sembunyikan</string>\n    <string name=\"install\">Pasang</string>\n    <string name=\"keep_editing\">Tetap mengedit</string>\n    <string name=\"loading\">Memuat…</string>\n    <string name=\"maybe_later\">Mungkin nanti</string>\n    <string name=\"merge\">Gabung</string>\n    <string name=\"none\">Tidak ada</string>\n    <string name=\"open_navigation_drawer\">Buka drawer navigasi</string>\n    <string name=\"paste\">Tempel</string>\n    <string name=\"refresh\">Segarkan</string>\n    <string name=\"reload\">Muat ulang</string>\n    <string name=\"remove\">Hapus</string>\n    <string name=\"rename\">Ubah nama</string>\n    <string name=\"replace\">Ganti</string>\n    <string name=\"reset\">Setel ulang</string>\n    <string name=\"retry\">Ulangi</string>\n    <string name=\"save\">Simpan</string>\n    <string name=\"search\">Cari</string>\n    <string name=\"select\">Pilih</string>\n    <string name=\"select_all\">Pilih semua</string>\n    <string name=\"share\">Bagikan</string>\n    <string name=\"show\">Tampilkan</string>\n    <string name=\"skip\">Lewati</string>\n    <string name=\"stop\">Hentikan</string>\n    <string name=\"system_default\">Bawaan sistem</string>\n    <string name=\"unknown\">Tidak diketahui</string>\n    <string name=\"view\">Lihat</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">Tidak ada aplikasi yang bisa memproses tindakan ini</string>\n    <string name=\"open_settings\">Buka pengaturan</string>\n    <string name=\"shortcut_created\">Pintasan dibuat</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"other\">%1$,d byte</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">Aplikasi membutuhkan izin untuk mengelola semua berkas. Silakan izinkan akses di dalam pengaturan sistem berikut.</string>\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">Apl membutuhkan izin untuk mengakses berkas. Silakan klik \\\"IZINKAN\\\" pada dialog berikutnya</string>\n    <string name=\"storage_permission_permanently_denied_message\">Apl membutuhkan izin untuk mengakses berkas. Aktifkan izin \\\"Penyimpanan\\\" di dalam pengaturan sistem.</string>\n    <!-- The allow button is in sentence case now. -->\n    <string name=\"notification_permission_rationale_message\">Aplikasi membutuhkan izin untuk mengirimkan notifikasi tentang pemrosesan berkas. Silakan klik \\\"Izinkan\\\" pada jendela sistem berikut ini.</string>\n    <string name=\"notification_permission_permanently_denied_message\">Aplikasi membutuhkan izin untuk mengirimkan notifikasi tentang pemrosesan berkas. Izinkan akses \\\"Notifikasi\\\" pada pengaturan sistem.</string>\n\n    <string name=\"notification_channel_background_activity_start_name\">Tindakan saat di latar belakang</string>\n    <string name=\"notification_channel_background_activity_start_description\">Ambil tindakan saat apl berada di latar belakang</string>\n    <string name=\"notification_channel_file_job_name\">Tindakan berkas</string>\n    <string name=\"notification_channel_file_job_description\">Tampilkan dan kontrol tindakan berkas</string>\n    <string name=\"notification_channel_ftp_server_name\">Server FTP</string>\n    <string name=\"notification_channel_ftp_server_description\">Tampilkan dan kontrol server FTP</string>\n\n    <string name=\"file_action_archive_password_title\">Membutuhkan sandi</string>\n    <string name=\"file_action_archive_password_message_format\">“%1$s” dilindungi sandi.</string>\n    <string name=\"file_action_archive_password_error_empty\">Sandi tidak boleh kosong</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"other\">Mempersiapkan untuk mengompres %1$,d berkas (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">Mengompres “%1$s” ke “%2$s”</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"other\">Mengompres %1$,d berkas ke “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"other\">Mempersiapkan untuk menyalin %1$,d berkas (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">Menyalin “%1$s” ke “%2$s”</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"other\">Menyalin %1$,d berkas ke “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"other\">Mempersiapkan untuk mengekstrak %1$,d berkas (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">Mengekstrak “%1$s” ke “%2$s”</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"other\">Mengekstrak %1$,d berkas ke “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"other\">Mempersiapkan untuk memindahkan %1$,d berkas (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">Memindahkan “%1$s” ke “%2$s”</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"other\">Memindahkan %1$,d berkas ke “%2$s”</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"other\">Mempersiapkan untuk menghapus %1$,d berkas</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">Menghapus “%1$s”</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"other\">Menghapus %1$,d berkas</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"other\">Mempersiapkan untuk mengubah kepemilikan untuk %1$,d file</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">Mengganti kepemilikan untuk \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"other\">Mengganti kepemilikan untuk %1$,d file</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"other\">Mempersiapkan untuk mengganti grup untuk %1$,d file</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">Mengganti grup untuk \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"other\">Mengganti grup untuk %1$,d file</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"other\">Mempersiapkan untuk mengganti mode untuk %1$,d file</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">Mengganti mode untuk \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"other\">Mengganti mode untuk %1$,d file</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"other\">Mempersiapkan untuk mengganti konteks SELinux untuk %1$,d file</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">Mengganti konteks SELinux untul \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"other\">Mengganti konteks SELinux untuk %1$,d file</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"other\">Mempersiapkan untuk memulihkan konteks SELinux untuk %1$,d file</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">Memulihkan konteks SELinux untuk \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"other\">Memulihkan konteks SELinux untuk %1$,d file</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">Menulis \\\"%1$s\\\"</string>\n    <string name=\"file_job_create_error_title\">Terjadi kesalahan saat membuat</string>\n    <string name=\"file_job_create_error_message_format\">Telah terjadi kesalahan saat membuat “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">Tidak bisa menyalin berkas ke dalam dirinya sendiri</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">Tidak bisa mengekstrak berkas ke dalam dirinya sendiri</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">Tidak bisa memindahkan folder ke dalam dirinya sendiri</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">Folder tujuan berada di dalam folder sumber</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">Tidak bisa menyalin berkas ke dirinya sendiri</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">Tidak bisa mengekstrak berkas ke dirinya sendiri</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">Tidak bisa memindahkan berkas ke dirinya sendiri</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">File sumber tujuan akan ditimpa</string>\n    <string name=\"file_job_replace_title_format\">Ganti berkas “%1$s”?</string>\n    <string name=\"file_job_replace_message_format\">Sudah ada berkas dengan nama yang sama di dalam “%1$s”.\\nMenggantinya akan menimpa seluruh isinya.</string>\n    <string name=\"file_job_replace_target_name\">Berkas asli</string>\n    <string name=\"file_job_replace_source_name\">Ganti dengan</string>\n    <string name=\"file_job_merge_title_format\">Gabung folder “%1$s”?</string>\n    <string name=\"file_job_merge_copy_message_format\">Menggabungkan akan meminta konfirmasi sebelum mengganti berkas di dalam folder yang sama dengan berkas yang akan disalin.</string>\n    <string name=\"file_job_merge_extract_message_format\">Menggabungkan akan meminta konfirmasi sebelum mengganti berkas di dalam folder yang sama dengan berkas yang akan diekstrak.</string>\n    <string name=\"file_job_merge_move_message_format\">Menggabungkan akan meminta konfirmasi sebelum mengganti berkas di dalam folder yang sama dengan berkas yang akan dipindahkan.</string>\n    <string name=\"file_job_merge_target_name\">Folder asli</string>\n    <string name=\"file_job_merge_source_name\">Gabung dengan</string>\n    <string name=\"file_job_conflict_show_name\">Pilih nama baru</string>\n    <string name=\"file_job_conflict_name_hint\">Nama baru</string>\n    <string name=\"file_job_archive_error_title_format\">Terjadi kesalahan saat mengompres “%1$s”</string>\n    <string name=\"file_job_archive_error_message_format\">Telah terjadi kesalahan saat mengompres berkas ke dalam “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">Terjadi kesalahan saat menyalin “%1$s”</string>\n    <string name=\"file_job_copy_error_message_format\">Telah terjadi kesalahan saat menyalin berkas ke dalam “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">Terjadi kesalahan saat mengekstrak “%1$s”</string>\n    <string name=\"file_job_extract_error_message_format\">Telah terjadi kesalahan saat mengekstrak berkas ke dalam “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">Terjadi kesalahan saat memindahkan “%1$s”</string>\n    <string name=\"file_job_move_error_message_format\">Telah terjadi kesalahan saat memindahkan berkas ke dalam “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">Terjadi kesalahan saat menghapus</string>\n    <string name=\"file_job_delete_error_message_format\">Terjadi kesalahan saat menghapus “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">Terjadi kesalahan saat mengubah nama “%1$s”</string>\n    <string name=\"file_job_rename_error_message_format\">Telah terjadi kesalahan saat mengubah nama berkas ke “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">Terjadi kesalahan saat mengubah pemilik untuk “%1$s”</string>\n    <string name=\"file_job_set_owner_error_message_format\">Telah terjadi kesalahan saat mengubah pemilik ke “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">Terjadi kesalahan saat mengubah grup untuk “%1$s”</string>\n    <string name=\"file_job_set_group_error_message_format\">Telah terjadi kesalahan saat mengubah grup ke “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">Terjadi kesalahan saat mengubah mode untuk “%1$s”</string>\n    <string name=\"file_job_set_mode_error_message_format\">Telah terjadi kesalahan saat mengubah mode ke “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">Terjadi kesalahan saat mengubah konteks SELinux untuk “%1$s”</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">Telah terjadi kesalahan saat mengubah konteks SELinux ke “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">Terjadi kesalahan saat memulihkan konteks SELinux</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">Telah terjadi kesalahan saat mengembalikan koteks SELinux untuk “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">Terjadi kesalahan saat menyimpan</string>\n    <string name=\"file_job_write_error_message_format\">Telah terjadi kesalahan saat menyimpan “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">Telah terjadi kesalahan saat mengambil informasi tentang “%1$s”.</string>\n    <string name=\"file_job_remount_format\">Mount ulang “%1$s” sebagai read-write</string>\n    <string name=\"file_job_remount_loading_format\">Mount ulang “%1$s” sebagai read-write…</string>\n    <string name=\"file_job_remount_success_format\">“%1$s” telah dimount sebagai read-write</string>\n    <string name=\"file_job_action_all\">Terapkan untuk semua berkas</string>\n\n    <string name=\"file_open_apk_message\">Berkas ini adalah paket Android. Apakah Anda ingin memasangnya, atau melihat isinya?</string>\n    <string name=\"file_open_as_title\">Buka sebagai…</string>\n    <string name=\"file_open_as_title_format\">Buka “%1$s” sebagai</string>\n    <string name=\"file_open_as_type_text\">Teks</string>\n    <string name=\"file_open_as_type_image\">Gambar</string>\n    <string name=\"file_open_as_type_audio\">Audio</string>\n    <string name=\"file_open_as_type_video\">Video</string>\n    <string name=\"file_open_as_type_directory\">Folder</string>\n    <string name=\"file_open_as_type_any\">Lainnya</string>\n    <string name=\"file_install_apk_from_background_title_format\">“%1$s” siap untuk dipasang</string>\n    <string name=\"file_install_apk_from_background_text\">Ketuk untuk memasang</string>\n    <string name=\"file_open_from_background_title_format\">“%1$s” siap untuk dibuka</string>\n    <string name=\"file_open_from_background_text\">Ketuk untuk membuka</string>\n    <string name=\"file_name_error_empty\">Nama berkas tidak boleh kosong</string>\n    <string name=\"file_name_error_invalid\">Nama berkas tidak valid</string>\n    <string name=\"file_name_error_already_exists\">Berkas dengan nama ini sudah ada</string>\n    <string name=\"file_delete_message_file_format\">Hapus “%1$s”?</string>\n    <string name=\"file_delete_message_directory_format\">Hapus folder “%1$s” dan isinya?</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"other\">Hapus %1$,d berkas?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"other\">Hapus %1$,d folder dan semua isinya?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"other\">Hapus %1$,d item?</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">Buat arsip</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_create_archive_password\">Sandi (opsional)</string>\n    <string name=\"file_add_bookmark_success\">Bookmark ditambahkan</string>\n    <string name=\"file_create_file_title\">Berkas baru</string>\n    <string name=\"file_create_directory_title\">Folder baru</string>\n\n    <string name=\"file_list_title\">File</string>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"other\">%1$,d folder</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"other\">%1$,d berkas</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">,\\u0020</string>\n    <string name=\"file_list_path_error_empty\">Jalur tidak boleh kosong</string>\n    <string name=\"file_list_path_error_invalid\">Jalur tidak valid</string>\n    <string name=\"file_list_action_view_sort\">Lihat dan urutkan</string>\n    <string name=\"file_list_action_view_list\">Daftar</string>\n    <string name=\"file_list_action_view_grid\">Grid</string>\n    <string name=\"file_list_action_sort_by_name\">Nama</string>\n    <string name=\"file_list_action_sort_by_type\">Tipe</string>\n    <string name=\"file_list_action_sort_by_size\">Ukuran</string>\n    <string name=\"file_list_action_sort_by_last_modified\">Terakhir diubah</string>\n    <string name=\"file_list_action_sort_order_ascending\">Naik</string>\n    <string name=\"file_list_action_sort_directories_first\">Folder di atas</string>\n    <string name=\"file_list_action_view_sort_path_specific\">Hanya untuk folder ini</string>\n    <string name=\"file_list_action_new_task\">Jendela baru</string>\n    <string name=\"file_list_action_navigate_up\">Ke atas</string>\n    <string name=\"file_list_action_navigate_to\">Kunjungi</string>\n    <string name=\"file_list_action_show_hidden_files\">Tampilkan berkas tersembunyi</string>\n    <string name=\"file_list_action_copy_path\">Salin jalur</string>\n    <string name=\"file_list_action_open_in_terminal\">Buka di terminal</string>\n    <string name=\"file_list_action_add_bookmark\">Tambahkan bookmark</string>\n    <string name=\"file_list_action_create_shortcut\">Buat pintasan</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">Buka di jendela baru</string>\n    <string name=\"file_list_select_title_format\">%1$,d</string>\n    <string name=\"file_list_paste_move_title_format\">Memindahkan %1$,d</string>\n    <string name=\"file_list_paste_copy_title_format\">Menyalin %1$,d</string>\n    <string name=\"file_list_paste_extract_title_format\">Mengekstrak %1$,d</string>\n    <string name=\"file_list_paste_action_extract_here\">Ekstrak di sini</string>\n    <string name=\"file_list_empty\">Tidak ada berkas</string>\n    <string name=\"file_list_action_create_file\">Berkas</string>\n    <string name=\"file_list_action_create_directory\">Folder</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">Buka dengan</string>\n    <string name=\"file_item_action_extract\">Ekstrak</string>\n    <string name=\"file_item_action_archive\">Kompres</string>\n    <string name=\"file_item_action_properties\">Properti</string>\n\n    <string name=\"file_properties_title_format\">Properti “%1$s”</string>\n    <string name=\"file_properties_basic\">Dasar</string>\n    <string name=\"file_properties_basic_name\">Nama</string>\n    <string name=\"file_properties_basic_type\">Tipe</string>\n    <string name=\"file_properties_basic_type_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">Tautan ke %1$s (%2$s)</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">Target Tautan</string>\n    <string name=\"file_properties_basic_size\">Ukuran</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_contents\">Konten</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"other\">%1$,d item, total %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">Terakhir Diubah</string>\n    <string name=\"file_properties_basic_parent_directory\">Folder Induk</string>\n    <string name=\"file_properties_basic_archive_file\">Berkas Arsip</string>\n    <string name=\"file_properties_basic_archive_entry\">Entri Arsip</string>\n    <string name=\"file_properties_basic_free_space\">Ruang Kosong</string>\n    <string name=\"file_properties_image\">Gambar</string>\n    <string name=\"file_properties_media_dimensions\">Dimensi</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">Diambil pada</string>\n    <string name=\"file_properties_media_coordinates\">Koordinat</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f, %2$.3f</string>\n    <string name=\"file_properties_media_address\">Lokasi</string>\n    <string name=\"file_properties_image_gps_altitude\">Ketinggian</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f m</string>\n    <string name=\"file_properties_image_equipment\">Kamera</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">Tingkap</string>\n    <string name=\"file_properties_image_shutter_speed\">Kecepatan rana</string>\n    <string name=\"file_properties_image_focal_length\">Jarak fokus</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f mm</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">Keselarasan ISO</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">Perangkat lunak</string>\n    <string name=\"file_properties_image_description\">Deskripsi</string>\n    <string name=\"file_properties_image_artist\">Pembuat</string>\n    <string name=\"file_properties_image_copyright\">Hak cipta</string>\n    <string name=\"file_properties_audio\">Audio</string>\n    <string name=\"file_properties_media_title\">Judul</string>\n    <string name=\"file_properties_audio_artist\">Artis</string>\n    <string name=\"file_properties_audio_album\">Album</string>\n    <string name=\"file_properties_audio_album_artist\">Artis album</string>\n    <string name=\"file_properties_audio_composer\">Komposer</string>\n    <string name=\"file_properties_audio_disc_number\">Diska</string>\n    <string name=\"file_properties_audio_track_number\">Trek</string>\n    <string name=\"file_properties_audio_year\">Tahun</string>\n    <string name=\"file_properties_audio_genre\">Genre</string>\n    <string name=\"file_properties_media_duration\">Durasi</string>\n    <string name=\"file_properties_media_bit_rate\">Laju bit</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d kbps</string>\n    <string name=\"file_properties_audio_sample_rate\">Laju sampel</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Hz</string>\n    <string name=\"file_properties_video\">Video</string>\n    <string name=\"file_properties_apk\">APK</string>\n    <string name=\"file_properties_apk_label\">Nama</string>\n    <string name=\"file_properties_apk_package_name\">Nama paket</string>\n    <string name=\"file_properties_apk_version\">Versi</string>\n    <string name=\"file_properties_apk_version_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_apk_min_sdk_version\">Minimum versi sistem</string>\n    <string name=\"file_properties_apk_target_sdk_version\">Target versi sistem</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s (%2$s, %3$d)</string>\n    <string name=\"file_properties_apk_requested_permissions\">Perizinan</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">Tidak ada izin yang diminta</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"other\">%1$,d izin diminta</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">Tanda tangan</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">Tidak ada tanda tangan yang valid</string>\n    <string name=\"file_properties_apk_past_signature_digests\">Tanda tangan lama</string>\n    <string name=\"storage_file_system_root_title\">Root</string>\n    <string name=\"storage_list_title\">Penyimpanan</string>\n    <string name=\"storage_list_empty\">Tidak ada penyimpanan</string>\n    <string name=\"storage_add_storage_title\">Tambah penyimpanan</string>\n    <string name=\"storage_add_storage_document_tree\">Penyimpanan eksternal</string>\n    <string name=\"storage_add_storage_ftp_server\">Server FTP</string>\n    <string name=\"storage_add_storage_sftp_server\">Server SFTP</string>\n    <string name=\"storage_add_storage_smb_server\">Server SMB</string>\n    <string name=\"storage_edit_device_storage_title\">Sunting penyimpanan perangkat</string>\n    <string name=\"storage_edit_device_storage_name\">Nama</string>\n    <string name=\"storage_edit_device_storage_path\">Jalur</string>\n    <string name=\"storage_add_document_tree_title\">Tambah penyimpanan eksternal</string>\n    <string name=\"storage_edit_document_tree_title\">Sunting penyimpanan eksternal</string>\n    <string name=\"storage_edit_document_tree_name\">Nama</string>\n    <string name=\"storage_edit_document_tree_uri\">URI</string>\n    <string name=\"storage_edit_document_tree_path\">Jalur</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">Sunting server FTP</string>\n    <string name=\"storage_edit_ftp_server_title_add\">Tambahkan server FTP</string>\n    <string name=\"storage_edit_ftp_server_host\">Nama hos</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">Masukkan nama hos</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">Nama hos tidak valid</string>\n    <string name=\"storage_edit_ftp_server_port\">Port</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">Port tidak valid</string>\n    <string name=\"storage_edit_ftp_server_path\">Jalur</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">Boleh dikosongkan</string>\n    <string name=\"storage_edit_ftp_server_name\">Nama</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">Gunakan nama hos</string>\n    <string name=\"storage_edit_ftp_server_protocol\">Protokol</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">Otentikasi</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>Sandi</item>\n        <item>Anonim</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">Nama pengguna</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">Masukkan nama pengguna</string>\n    <string name=\"storage_edit_ftp_server_password\">Sandi</string>\n    <string name=\"storage_edit_ftp_server_mode\">Mode</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>Aktif</item>\n        <item>Pasif</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">Enkode</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">Sambung dan tambah</string>\n    <string name=\"storage_edit_ftp_server_add\">Tambah</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">Sunting server SFTP</string>\n    <string name=\"storage_edit_sftp_server_title_add\">Tambahkan server SFTP</string>\n    <string name=\"storage_edit_sftp_server_host\">Nama hos</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">Masukkan nama hos</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">Nama hos tidak valid</string>\n    <string name=\"storage_edit_sftp_server_port\">Port</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">Port tidak valid</string>\n    <string name=\"storage_edit_sftp_server_path\">Jalur</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">Boleh dikosongkan</string>\n    <string name=\"storage_edit_sftp_server_name\">Nama</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">Gunakan nama hos</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">Otentikasi</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>Sandi</item>\n        <item>Kunci publik</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">Nama pengguna</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">Masukkan nama pengguna</string>\n    <string name=\"storage_edit_sftp_server_password\">Sandi</string>\n    <string name=\"storage_edit_sftp_server_private_key\">Kunci privat</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">Buka berkas</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">Masukkan kunci privat</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">Kunci privat tidak valid</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">Sandi kunci privat</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">Boleh dikosongkan</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">Sandi kunci privat tidak valid</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">Sambung dan tambah</string>\n    <string name=\"storage_edit_sftp_server_add\">Tambah</string>\n    <string name=\"storage_add_lan_smb_server_loading\">Mencari server SMB…</string>\n    <string name=\"storage_add_lan_smb_server_add\">Tambah secara manual</string>\n    <string name=\"storage_edit_smb_server_title_edit\">Sunting server SMB</string>\n    <string name=\"storage_edit_smb_server_title_add\">Tambah server SMB</string>\n    <string name=\"storage_edit_smb_server_host\">Nama hos</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">Masukkan nama hos</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">Nama hos tidak valid</string>\n    <string name=\"storage_edit_smb_server_port\">Port</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">Port tidak valid</string>\n    <string name=\"storage_edit_smb_server_path\">Jalur</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">Boleh dikosongkan</string>\n    <string name=\"storage_edit_smb_server_name\">Nama</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">Gunakan nama hos</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">Otentikasi</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>Sandi</item>\n        <item>Tamu</item>\n        <item>Anonim</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">Nama pengguna</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">Masukkan nama pengguna</string>\n    <string name=\"storage_edit_smb_server_password\">Sandi</string>\n    <string name=\"storage_edit_smb_server_domain\">Domain</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">Sambung dan tambah</string>\n    <string name=\"storage_edit_smb_server_add\">Tambah</string>\n    <string name=\"navigation_storage_subtitle_format\">%1$s kosong dari %2$s</string>\n    <string name=\"navigation_add_storage\">Tambah penyimpanan…</string>\n    <string name=\"navigation_standard_directory_alarms\">Alarm</string>\n    <string name=\"navigation_standard_directory_dcim\">DCIM</string>\n    <string name=\"navigation_standard_directory_documents\">Dokumen</string>\n    <string name=\"navigation_standard_directory_downloads\">Unduhan</string>\n    <string name=\"navigation_standard_directory_movies\">Movie</string>\n    <string name=\"navigation_standard_directory_music\">Musik</string>\n    <string name=\"navigation_standard_directory_notifications\">Notifikasi</string>\n    <string name=\"navigation_standard_directory_pictures\">Gambar</string>\n    <string name=\"navigation_standard_directory_podcasts\">Podcast</string>\n    <string name=\"navigation_standard_directory_ringtones\">Nada dering</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">Folder markah</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">Nama</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">Jalur</string>\n    <string name=\"archive_viewer_title\">Penilik arsip</string>\n    <string name=\"image_viewer_title\">Penampil gambar</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"text_editor_title\">Penyunting teks</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">Disimpan</string>\n    <string name=\"text_editor_reload_message\">Apakah Anda yakin ingin memuat ulang? Perubahan yang belum disimpan pada dokumen ini akan hilang.</string>\n    <string name=\"text_editor_encoding\">Enkode</string>\n    <string name=\"text_editor_close_message\">Apakah Anda yakin ingin menghapus perubahan yang belum disimpan pada dokumen ini?</string>\n\n    <string name=\"ftp_server_title\">Server FTP</string>\n    <string name=\"ftp_server_state_title\">Status</string>\n    <string name=\"ftp_server_state_summary_starting\">Memulai…</string>\n    <string name=\"ftp_server_state_summary_running\">Berjalan</string>\n    <string name=\"ftp_server_state_summary_stopping\">Menghentikan…</string>\n    <string name=\"ftp_server_state_summary_stopped\">Tidak berjalan</string>\n    <string name=\"ftp_server_url_title\">URL</string>\n    <string name=\"ftp_server_url_summary_no_local_inet_address\">Alamat IP lokal tidak diketahui</string>\n    <string name=\"ftp_server_url_menu_copy_url\">Salin URL</string>\n    <string name=\"ftp_server_url_menu_copy_password\">Salin sandi</string>\n    <string name=\"ftp_server_add_tile_title\">Tambahkan ke Pengaturan Cepat</string>\n    <string name=\"ftp_server_add_tile_result_added\">“Server FTP” telah ditambahkan ke Pengaturan Cepat</string>\n    <string name=\"ftp_server_add_tile_result_already_added\">“Server FTP” sudah ditambahkan ke Pengaturan Cepat</string>\n    <string name=\"ftp_server_add_tile_result_error\">Terjadi kesalahan saat menambahkan “server FTP” ke Pengaturan Cepat</string>\n    <string name=\"ftp_server_configuration_title\">Konfigurasi</string>\n    <string name=\"ftp_server_anonymous_login_title\">Masuk secara anonim</string>\n    <string name=\"ftp_server_username_title\">Nama pengguna</string>\n    <string name=\"ftp_server_password_title\">Sandi</string>\n    <string name=\"ftp_server_port_title\">Port</string>\n    <string name=\"ftp_server_home_directory_title\">Folder root</string>\n    <string name=\"ftp_server_writable_title\">Izinkan menyimpan</string>\n    <string name=\"settings_title\">Pengaturan</string>\n    <string name=\"settings_interface_title\">Antarmuka</string>\n    <string name=\"settings_locale_title\">Bahasa</string>\n    <string name=\"settings_theme_color_title\">Warna tema</string>\n    <string name=\"settings_theme_color_summary\">Warna yang muncul paling sering di dalam apl</string>\n    <string name=\"settings_material_design_3_title\">Material Design 3</string>\n    <string name=\"settings_night_mode_title\">Mode malam</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>Ikuti sistem</item>\n        <item>Mati</item>\n        <item>Nyala</item>\n        <item>Berdasarkan waktu</item>\n        <item>Berdasarkan penghemat baterai</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">Mode super hitam</string>\n    <string name=\"settings_file_list_animation_title\">Animasi daftar berkas</string>\n    <string name=\"settings_file_name_ellipsize_title\">Tampilkan nama berkas panjang</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>Elipsis di awal</item>\n        <item>Elipsis di tengah</item>\n        <item>Elipsis di akhir</item>\n        <item>Marquee</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">Perilaku</string>\n    <string name=\"settings_default_directory_title\">Folder default</string>\n    <string name=\"settings_standard_directories_title\">Folder standar</string>\n    <string name=\"settings_standard_directories_summary_empty\">Tidak ada folder standar yang diaktifkan</string>\n    <string name=\"settings_bookmark_directory_screenshots\">Screenshot</string>\n    <string name=\"settings_bookmark_directories_title\">Folder bookmark</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">Tidak ada folder bookmark</string>\n    <string name=\"settings_root_strategy_title\">Mode akses root</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"other\">Anda memiliki %1$,d proses berkas yang sedang berjalan dan mengubah mode akses root sekarang bisa mengakibatkan kesalahan yang tidak terduga. Apakah Anda yakin ingin mengubahnya sekarang?</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>Hanya akses normal</item>\n        <item>Otomatis</item>\n        <item>Hanya akses root</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">Enkode nama berkas arsip</string>\n    <string name=\"settings_open_apk_default_action_title\">Buka paket Android</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>Pasang</item>\n        <item>Lihat konten</item>\n        <item>Tanyakan yang ingin dilakukan</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">Mungkin membuat apl tidak stabil pada versi Android lama</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">Baca berkas remote untuk thumbnail</string>\n\n    <string name=\"about_title\">Tentang</string>\n    <string name=\"about_version_title\">Versi</string>\n    <string name=\"about_github_title\">Lihat di GitHub</string>\n    <string name=\"about_licenses_title\">Lisensi</string>\n    <string name=\"about_privacy_policy_title\">Kebijakan privasi</string>\n    <string name=\"about_author_title\">Pengembang</string>\n    <string name=\"about_author_name_title\">Hai Zhang</string>\n    <string name=\"about_author_github_title\">Ikuti di GitHub</string>\n    <string name=\"about_author_twitter_title\">Ikuti di Twitter</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-is/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">Skrá</string>\n    <string name=\"file_type_name_apk\">Android-pakki</string>\n    <string name=\"file_type_name_archive\">%1$s safnskrá</string>\n    <string name=\"file_type_name_audio\">%1$s hljóð</string>\n    <string name=\"file_type_name_calendar\">%1$s dagatal</string>\n    <string name=\"file_type_name_certificate\">%1$s skilríki</string>\n    <string name=\"file_type_name_code\">%1$s skjal</string>\n    <string name=\"file_type_name_contact\">Rafrænt nafnspjald</string>\n    <string name=\"file_type_name_directory\">Mappa</string>\n    <string name=\"file_type_name_document\">%1$s skjal</string>\n    <string name=\"file_type_name_ebook\">%1$s rafbók</string>\n    <string name=\"file_type_name_email\">%1$s tölvupóstskilaboð</string>\n    <string name=\"file_type_name_font\">%1$s letur</string>\n    <string name=\"file_type_name_generic\">%1$s skrá</string>\n    <string name=\"file_type_name_image\">%1$s mynd</string>\n    <string name=\"file_type_name_pdf\">PDF-skjal</string>\n    <string name=\"file_type_name_presentation\">%1$s kynning</string>\n    <string name=\"file_type_name_spreadsheet\">%1$s töflureiknir</string>\n    <string name=\"file_type_name_text\">%1$s skjal</string>\n    <string name=\"file_type_name_text_plain\">Hreintextaskjal</string>\n    <string name=\"file_type_name_video\">%1$s myndskeið</string>\n    <string name=\"file_type_name_word\">Word-skjal</string>\n    <string name=\"file_type_name_powerpoint\">PowerPoint-kynning</string>\n    <string name=\"file_type_name_excel\">Excel-töflureiknir</string>\n    <string name=\"file_type_name_posix_character_device\">Stafatæki</string>\n    <string name=\"file_type_name_posix_block_device\">Blokkatæki</string>\n    <string name=\"file_type_name_posix_fifo\">Pípa</string>\n    <string name=\"file_type_name_posix_symbolic_link\">Tengill</string>\n    <string name=\"file_type_name_posix_socket\">Sökkull</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">Tengill (rofinn)</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-is/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:ignore=\"ImpliedQuantity\">\n\n    <string name=\"app_name\">Material-skrár</string>\n\n    <string name=\"close\">Loka</string>\n    <string name=\"copy\">Afrita</string>\n    <string name=\"cut\">Klippa</string>\n    <string name=\"default_\">Sjálfgefið</string>\n    <string name=\"delete\">Eyða</string>\n    <string name=\"discard\">Henda</string>\n    <string name=\"edit\">Breyta</string>\n    <string name=\"empty\">Tómt</string>\n    <string name=\"empty_placeholder\">(Tómt)</string>\n    <string name=\"error\">Villa</string>\n    <string name=\"hide\">Fela</string>\n    <string name=\"install\">Setja upp</string>\n    <string name=\"keep_editing\">Halda áfram með breytingar</string>\n    <string name=\"loading\">Hleð inn…</string>\n    <string name=\"maybe_later\">Kannski seinna</string>\n    <string name=\"merge\">Sameina</string>\n    <string name=\"none\">Ekkert</string>\n    <string name=\"open_navigation_drawer\">Opna leiðsagnarspjald</string>\n    <string name=\"paste\">Líma</string>\n    <string name=\"refresh\">Endurnýja</string>\n    <string name=\"reload\">Endurlesa</string>\n    <string name=\"remove\">Fjarlægja</string>\n    <string name=\"rename\">Endurnefna</string>\n    <string name=\"replace\">Skipta út</string>\n    <string name=\"reset\">Frumstilla</string>\n    <string name=\"retry\">Reyna aftur</string>\n    <string name=\"save\">Vista</string>\n    <string name=\"search\">Leita</string>\n    <string name=\"select\">Velja</string>\n    <string name=\"select_all\">Velja allt</string>\n    <string name=\"share\">Deila</string>\n    <string name=\"show\">Birta</string>\n    <string name=\"skip\">Sleppa</string>\n    <string name=\"stop\">Stöðva</string>\n    <string name=\"system_default\">Sjálfgefið í kerfinu</string>\n    <string name=\"unknown\">Óþekkt</string>\n    <string name=\"view\">Skoða</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">Ekkert forrit fannst sem getur meðhöndlað þessa aðgerð</string>\n    <string name=\"open_settings\">Opna stillingar</string>\n    <string name=\"shortcut_created\">Útbjó flýtileið</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"one\">%1$,d bæti</item>\n        <item quantity=\"other\">%1$,d bæti</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">Forritið þarf heimild til að sýsla með allar skrár. Veittu heimild fyrir að nota skrár í kerfisstillingunum sem munu birtast.</string>\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">Forritið þarf heimild til að nota skrár. Ýttu á “LEYFA” í kerfisglugganum sem mun birtast.</string>\n    <string name=\"storage_permission_permanently_denied_message\">Forritið þarf heimild til að nota skrár. Veittu heimild fyrir að nota “Gagnageymslu” í kerfisstillingunum.</string>\n    <!-- The allow button is in sentence case now. -->\n    <string name=\"notification_permission_rationale_message\">Forritið þarf heimild til að senda tilkynningar um skráaaðgerðir. Ýttu á “LEYFA” í kerfisglugganum sem mun birtast.</string>\n    <string name=\"notification_permission_permanently_denied_message\">Forritið þarf heimild til að senda tilkynningar um skráaaðgerðir. Veittu heimild fyrir að nota “Tilkynningar” í kerfisstillingunum.</string>\n\n    <string name=\"notification_channel_background_activity_start_name\">Aðgerðir meðan er í bakgrunni</string>\n    <string name=\"notification_channel_background_activity_start_description\">Framkvæma aðgerðir á meðan verið er í bakgrunni</string>\n    <string name=\"notification_channel_file_job_name\">Skráaaðgerðir</string>\n    <string name=\"notification_channel_file_job_description\">Birta og stýra skráaaðgerðum</string>\n    <string name=\"notification_channel_ftp_server_name\">FTP-netþjónn</string>\n    <string name=\"notification_channel_ftp_server_description\">Birta og stýra FTP-netþjóninum</string>\n\n    <string name=\"file_action_archive_password_title\">Lykilorðs krafist</string>\n    <string name=\"file_action_archive_password_message_format\">“%1$s” er varið með lykilorði.</string>\n    <string name=\"file_action_archive_password_error_empty\">Lykilorð má ekki vera autt</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"one\">Undirbý að þjappa %1$,d skrá (%2$s)</item>\n        <item quantity=\"other\">Undirbý að þjappa %1$,d skrám (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">Þjappa “%1$s” inn í “%2$s”</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"one\">Þjappa %1$,d skrá inn í “%2$s”</item>\n        <item quantity=\"other\">Þjappa %1$,d skrám inn í “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"one\">Undirbý að afrita %1$,d skrá (%2$s)</item>\n        <item quantity=\"other\">Undirbý að afrita %1$,d skrár (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">Afrita “%1$s” yfir í “%2$s”</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"one\">Afrita %1$,d skrá yfir í “%2$s”</item>\n        <item quantity=\"other\">Afrita %1$,d skrár yfir í “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"one\">Undirbý að afþjappa %1$,d skrár (%2$s)</item>\n        <item quantity=\"other\">Undirbý að afþjappa %1$,d skrár (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">Afþjappa “%1$s” yfir í “%2$s”</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"one\">Afþjappa %1$,d skrá yfir í “%2$s”</item>\n        <item quantity=\"other\">Afþjappa %1$,d skrár yfir í “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"one\">Undirbý að færa %1$,d skrá (%2$s)</item>\n        <item quantity=\"other\">Undirbý að færa %1$,d skrár (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">Færi “%1$s” yfir í “%2$s”</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"one\">Færi %1$,d skrá yfir í “%2$s”</item>\n        <item quantity=\"other\">Færi %1$,d skrár yfir í “%2$s”</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"one\">Undirbý að eyða %1$,d skrá</item>\n        <item quantity=\"other\">Undirbý að eyða %1$,d skrám</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">Eyði “%1$s”</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"one\">Eyði %1$,d skrá</item>\n        <item quantity=\"other\">Eyði %1$,d skrám</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"one\">Undirbý að breyta eiganda á %1$,d skrá</item>\n        <item quantity=\"other\">Undirbý að breyta eiganda á %1$,d skrám</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">Breyti eiganda á “%1$s”</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"one\">Breyti eiganda á %1$,d skrá</item>\n        <item quantity=\"other\">Breyti eiganda á %1$,d skrám</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"one\">Undirbý að breyta hópi á %1$,d skrá</item>\n        <item quantity=\"other\">Undirbý að breyta hópi á %1$,d skrám</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">Breyti hópi á “%1$s”</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"one\">Breyti hópi á %1$,d skrá</item>\n        <item quantity=\"other\">Breyti hópi á %1$,d skrám</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"one\">Undirbý að breyta ham á %1$,d skrá</item>\n        <item quantity=\"other\">Undirbý að breyta ham á %1$,d skrám</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">Breyti ham fyrir “%1$s”</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"one\">Breyti ham fyrir %1$,d skrá</item>\n        <item quantity=\"other\">Breyti ham fyrir %1$,d skrár</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Undirbý að breyta SELinux-samhengi fyrir %1$,d skrá</item>\n        <item quantity=\"other\">Undirbý að breyta SELinux-samhengi fyrir %1$,d skrár</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">Breyti SELinux-samhengi fyrir “%1$s”</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Breyti SELinux-samhengi fyrir %1$,d skrá</item>\n        <item quantity=\"other\">Breyti SELinux-samhengi fyrir %1$,d skrár</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Undirbý að endurheimta SELinux-samhengi fyrir %1$,d skrá</item>\n        <item quantity=\"other\">Undirbý að endurheimta SELinux-samhengi fyrir %1$,d skrár</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">Endurheimti SELinux-samhengi fyrir “%1$s”</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Endurheimti SELinux-samhengi fyrir %1$,d skrá</item>\n        <item quantity=\"other\">Endurheimti SELinux-samhengi fyrir %1$,d skrár</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">Skrifa “%1$s”</string>\n    <string name=\"file_job_create_error_title\">Villa kom upp við að útbúa</string>\n    <string name=\"file_job_create_error_message_format\">Villa kom upp við að útbúa “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">Get ekki afritað möppu inn í sjálfa sig</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">Get ekki afþjappað möppu inn í sjálfa sig</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">Get ekki fært möppu inn í sjálfa sig</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">Úttaksmappan er inni í upprunamöppunni.</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">Get ekki afritað skrá yfir sjálfa sig</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">Get ekki afþjappað skrá yfir sjálfa sig</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">Get ekki fært skrá yfir sjálfa sig</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">Upprunaskráin myndi verða yfirskrifuð af úttaksskránni.</string>\n    <string name=\"file_job_replace_title_format\">Skipta út skránni “%1$s”?</string>\n    <string name=\"file_job_replace_message_format\">Önnur skrá með sama heiti er þegar til staðar í “%1$s”.\\nEf henni er skipt út verður skrifað yfir allt efni hennar.</string>\n    <string name=\"file_job_replace_target_name\">Upprunaleg skrá</string>\n    <string name=\"file_job_replace_source_name\">Skipta út með</string>\n    <string name=\"file_job_merge_title_format\">Sameina möppuna “%1$s”?</string>\n    <string name=\"file_job_merge_copy_message_format\">Sameining mun biðja um staðfestingu áður en skrám er skipt út í möppunni sem rekast á við skrárnar sem verið er að afrita.</string>\n    <string name=\"file_job_merge_extract_message_format\">Sameining mun biðja um staðfestingu áður en skrám er skipt út í möppunni sem rekast á við skrárnar sem verið er að afþjappa.</string>\n    <string name=\"file_job_merge_move_message_format\">Sameining mun biðja um staðfestingu áður en skrám er skipt út í möppunni sem rekast á við skrárnar sem verið er að flytja.</string>\n    <string name=\"file_job_merge_target_name\">Upprunaleg mappa</string>\n    <string name=\"file_job_merge_source_name\">Sameina við</string>\n    <string name=\"file_job_conflict_show_name\">Veldu nýtt nafn á úttakið</string>\n    <string name=\"file_job_conflict_name_hint\">Nýtt nafn</string>\n    <string name=\"file_job_archive_error_title_format\">Villa kom upp við að þjappa “%1$s”</string>\n    <string name=\"file_job_archive_error_message_format\">Villa kom upp við að þjappa skrána inn í “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">Villa kom upp við að afrita “%1$s”</string>\n    <string name=\"file_job_copy_error_message_format\">Villa kom upp við að afrita skrána inn í “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">Villa kom upp við að afþjappa “%1$s”</string>\n    <string name=\"file_job_extract_error_message_format\">Villa kom upp við að afþjappa skrána inn í “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">Villa kom upp við að færa “%1$s”</string>\n    <string name=\"file_job_move_error_message_format\">Villa kom upp við að færa skrána inn í “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">Villa kom upp við að eyða</string>\n    <string name=\"file_job_delete_error_message_format\">Villa kom upp við að eyða “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">Villa kom upp við að endurnefna “%1$s”</string>\n    <string name=\"file_job_rename_error_message_format\">Villa kom upp við að endurnefna skrána sem “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">Villa kom upp við að breyta eiganda á “%1$s”</string>\n    <string name=\"file_job_set_owner_error_message_format\">Villa kom upp við að breyta eigandanum í “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">Villa kom upp við að breyta hópi á “%1$s”</string>\n    <string name=\"file_job_set_group_error_message_format\">Villa kom upp við að breyta hópnum í “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">Villa kom upp við að breyta ham á “%1$s”</string>\n    <string name=\"file_job_set_mode_error_message_format\">Villa kom upp við að breyta hamnum í “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">Villa kom upp við að breyta SELinux-samhengi fyrir “%1$s”</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">Villa kom upp við að breyta SELinux-samhenginu í “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">Villa kom upp við að endurheimta SELinux-samhengi</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">Villa kom upp við að endurheimta SELinux-samhengi fyrir “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">Villa kom upp við að skrifa</string>\n    <string name=\"file_job_write_error_message_format\">Villa kom upp við að skrifa “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">Villa kom upp við að nálgast upplýsingar um “%1$s”.</string>\n    <string name=\"file_job_remount_format\">Endurtengja “%1$s” sem les-skrifanlegt</string>\n    <string name=\"file_job_remount_loading_format\">Endurtengi “%1$s” sem les-skrifanlegt</string>\n    <string name=\"file_job_remount_success_format\">“%1$s” hefur verið endurtengt sem les-skrifanlegt</string>\n    <string name=\"file_job_action_all\">Beita þessari aðgerð á allar skrár</string>\n\n    <string name=\"file_open_apk_message\">Þessi skrá er Android-pakki. Viltu setja hana upp eða skoða innihald hennar?</string>\n    <string name=\"file_open_as_title\">Opna sem…</string>\n    <string name=\"file_open_as_title_format\">Opna “%1$s” sem</string>\n    <string name=\"file_open_as_type_text\">Texta</string>\n    <string name=\"file_open_as_type_image\">Mynd</string>\n    <string name=\"file_open_as_type_audio\">Hljóð</string>\n    <string name=\"file_open_as_type_video\">Myndskeið</string>\n    <string name=\"file_open_as_type_directory\">Möppu</string>\n    <string name=\"file_open_as_type_any\">Annað</string>\n    <string name=\"file_install_apk_from_background_title_format\">“%1$s” er tilbúið til uppsetningar</string>\n    <string name=\"file_install_apk_from_background_text\">Ýttu til að setja upp</string>\n    <string name=\"file_open_from_background_title_format\">“%1$s” er tilbúið til opnunar</string>\n    <string name=\"file_open_from_background_text\">Ýttu til að opna</string>\n    <string name=\"file_name_error_empty\">Skráarheiti má ekki vera autt</string>\n    <string name=\"file_name_error_invalid\">Ógilt skráarheiti</string>\n    <string name=\"file_name_error_already_exists\">Skrá með þessu heiti er þegar til staðar</string>\n    <string name=\"file_replace_message_format\">Skipta út “%1$s”?</string>\n    <string name=\"file_delete_message_file_format\">Eyða “%1$s”?</string>\n    <string name=\"file_delete_message_directory_format\">Eyða möppunni “%1$s” og öllu innihaldi hennar?</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"one\">Eyða %1$,d skrá?</item>\n        <item quantity=\"other\">Eyða %1$,d skrám?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"one\">Eyða %1$,d möppu og innihaldi hennar?</item>\n        <item quantity=\"other\">Eyða %1$,d möppum og innihaldi þeirra?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"one\">Eyða %1$,d atriði?</item>\n        <item quantity=\"other\">Eyða %1$,d atriðum?</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">Útbúa safnskrá</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_create_archive_password\">Lykilorð (valkvætt)</string>\n    <string name=\"file_add_bookmark_success\">Bætt við bókamerki</string>\n    <string name=\"file_create_file_title\">Ný skrá</string>\n    <string name=\"file_create_directory_title\">Ný mappa</string>\n\n    <string name=\"file_list_title\">Skrár</string>\n    <plurals name=\"file_list_title_open_file\">\n        <item quantity=\"one\">Veldu skrá</item>\n        <item quantity=\"other\">Veldu skrár</item>\n    </plurals>\n    <string name=\"file_list_title_create_file\">Vista skrá</string>\n    <plurals name=\"file_list_title_open_directory\">\n        <item quantity=\"one\">Veldu möppu</item>\n        <item quantity=\"other\">Veldu möppur</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"one\">%1$,d mappa</item>\n        <item quantity=\"other\">%1$,d möppur</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"one\">%1$,d skrá</item>\n        <item quantity=\"other\">%1$,d skrár</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">,\\u0020</string>\n    <string name=\"file_list_path_error_empty\">Slóð má ekki vera auð</string>\n    <string name=\"file_list_path_error_invalid\">Ógild slóð</string>\n    <string name=\"file_list_action_view_sort\">Skoðun og röðun</string>\n    <string name=\"file_list_action_view_list\">Listi</string>\n    <string name=\"file_list_action_view_grid\">Reitir</string>\n    <string name=\"file_list_action_sort_by_name\">Nafn</string>\n    <string name=\"file_list_action_sort_by_type\">Tegund</string>\n    <string name=\"file_list_action_sort_by_size\">Stærð</string>\n    <string name=\"file_list_action_sort_by_last_modified\">Síðast breytt</string>\n    <string name=\"file_list_action_sort_order_ascending\">Hækkandi</string>\n    <string name=\"file_list_action_sort_directories_first\">Möppur fyrst</string>\n    <string name=\"file_list_action_view_sort_path_specific\">Aðeins í þessari möppu</string>\n    <string name=\"file_list_action_new_task\">Nýr gluggi</string>\n    <string name=\"file_list_action_navigate_up\">Fara upp</string>\n    <string name=\"file_list_action_navigate_to\">Fara í</string>\n    <string name=\"file_list_action_show_hidden_files\">Birta faldar skrár</string>\n    <string name=\"file_list_action_copy_path\">Afrita slóð</string>\n    <string name=\"file_list_action_open_in_terminal\">Opna í skjáhermi</string>\n    <string name=\"file_list_action_add_bookmark\">Bæta við bókamerki</string>\n    <string name=\"file_list_action_create_shortcut\">Útbúa flýtileið</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">Opna í nýjum glugga</string>\n    <string name=\"file_list_select_title_format\">%1$,d</string>\n    <string name=\"file_list_create_file_name_hint\">Skráarheiti</string>\n    <string name=\"file_list_open_current_directory_format\">Velja “%1$s”</string>\n    <string name=\"file_list_paste_move_title_format\">Færi %1$,d</string>\n    <string name=\"file_list_paste_copy_title_format\">Afrita %1$,d</string>\n    <string name=\"file_list_paste_extract_title_format\">Afþjappa %1$,d</string>\n    <string name=\"file_list_paste_action_extract_here\">Afþjappa hér</string>\n    <string name=\"file_list_empty\">Engar skrár</string>\n    <string name=\"file_list_action_create_file\">Skrá</string>\n    <string name=\"file_list_action_create_directory\">Mappa</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">Opna með</string>\n    <string name=\"file_item_action_extract\">Afþjappa</string>\n    <string name=\"file_item_action_archive\">Þjappa</string>\n    <string name=\"file_item_action_properties\">Eiginleikar</string>\n\n    <string name=\"file_properties_title_format\">Eiginleikar “%1$s”</string>\n    <string name=\"file_properties_basic\">Einfalt</string>\n    <string name=\"file_properties_basic_name\">Nafn</string>\n    <string name=\"file_properties_basic_type\">Tegund</string>\n    <string name=\"file_properties_basic_type_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">Tengill í %1$s (%2$s)</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">Markmið tengils</string>\n    <string name=\"file_properties_basic_size\">Stærð</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_contents\">Innihald</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"one\">%1$,d atriði, samtals %2$s</item>\n        <item quantity=\"other\">%1$,d atriði, samtals %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">Síðast breytt</string>\n    <string name=\"file_properties_basic_parent_directory\">Yfirmappa</string>\n    <string name=\"file_properties_basic_archive_file\">Setja skrá í geymslu</string>\n    <string name=\"file_properties_basic_archive_entry\">Setja færslu í geymslu</string>\n    <string name=\"file_properties_basic_free_space\">Laust pláss</string>\n    <string name=\"file_properties_permission\">Heimildir</string>\n    <string name=\"file_properties_permission_owner\">Eigandi</string>\n    <string name=\"file_properties_permission_group\">Hópur</string>\n    <string name=\"file_properties_permission_principal_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_permission_set_owner_title\">Skipta um eiganda</string>\n    <string name=\"file_properties_permission_set_group_title\">Skipta um hóp</string>\n    <string name=\"file_properties_permission_set_principal_filter_hint\">Settu inn heiti eða auðkenni</string>\n    <string name=\"file_properties_permission_set_principal_system\">Kerfi</string>\n    <string name=\"file_properties_permission_mode\">Hamur</string>\n    <string name=\"file_properties_permission_mode_format\">%1$s (%2$04o)</string>\n    <string name=\"file_properties_permission_set_mode_title\">Skipta um ham</string>\n    <string name=\"file_properties_permission_set_mode_others\">Aðrir</string>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_file\">\n        <item>Lesa</item>\n        <item>Skrifa</item>\n        <item>Keyra</item>\n    </string-array>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_directory\">\n        <item>Lesa</item>\n        <item>Skrifa</item>\n        <item>Leita</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_special\">Sérstakt</string>\n    <string-array name=\"file_properties_permission_set_mode_special_mode_bits\">\n        <item>Stilla UID</item>\n        <item>Stilla GID</item>\n        <item>Fastur biti</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_uppercase_x\">Ekki bæta við “Keyra” fyrir umluktar skrár</string>\n    <string name=\"file_properties_permission_selinux_context\">SELinux-samhengi</string>\n    <string name=\"file_properties_permission_set_selinux_context_title\">Breyta SELinux-samhengi</string>\n    <string name=\"file_properties_permission_set_selinux_context_restore\">Endurheimta</string>\n    <string name=\"file_properties_permission_recursive\">Beita á umluktar skrár</string>\n    <string name=\"file_properties_image\">Mynd</string>\n    <string name=\"file_properties_media_dimensions\">Stærðir</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">Tekin</string>\n    <string name=\"file_properties_media_coordinates\">Hnit</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f, %2$.3f</string>\n    <string name=\"file_properties_media_address\">Staðsetning</string>\n    <string name=\"file_properties_image_gps_altitude\">Hæð</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f m</string>\n    <string name=\"file_properties_image_equipment\">Myndavél</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">Ljósop</string>\n    <string name=\"file_properties_image_shutter_speed\">Lokhraði</string>\n    <string name=\"file_properties_image_focal_length\">Brennivídd</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f mm</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">ISO-samsvörun</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">Hugbúnaður</string>\n    <string name=\"file_properties_image_description\">Lýsing</string>\n    <string name=\"file_properties_image_artist\">Höfundur</string>\n    <string name=\"file_properties_image_copyright\">Höfundarréttur</string>\n    <string name=\"file_properties_audio\">Hljóð</string>\n    <string name=\"file_properties_media_title\">Titill</string>\n    <string name=\"file_properties_audio_artist\">Flytjandi</string>\n    <string name=\"file_properties_audio_album\">Hljómplata</string>\n    <string name=\"file_properties_audio_album_artist\">Flytjandi hljómplötu</string>\n    <string name=\"file_properties_audio_composer\">Tónskáld</string>\n    <string name=\"file_properties_audio_disc_number\">Diskur</string>\n    <string name=\"file_properties_audio_track_number\">Hljóðspor</string>\n    <string name=\"file_properties_audio_year\">Ár</string>\n    <string name=\"file_properties_audio_genre\">Stíll</string>\n    <string name=\"file_properties_media_duration\">Lengd</string>\n    <string name=\"file_properties_media_bit_rate\">Bitahraði</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d kbps</string>\n    <string name=\"file_properties_audio_sample_rate\">Safntíðni</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Hz</string>\n    <string name=\"file_properties_video\">Myndskeið</string>\n    <string name=\"file_properties_apk\">APK</string>\n    <string name=\"file_properties_apk_label\">Nafn</string>\n    <string name=\"file_properties_apk_package_name\">Heiti pakka</string>\n    <string name=\"file_properties_apk_version\">Útgáfa</string>\n    <string name=\"file_properties_apk_version_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_apk_min_sdk_version\">Lágmarks kerfisútgáfa</string>\n    <string name=\"file_properties_apk_target_sdk_version\">Markmið kerfisútgáfu</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s (%2$s, %3$d)</string>\n    <string name=\"file_properties_apk_requested_permissions\">Heimildir</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">Ekki beðið um neinar heimildir</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"one\">Beðið um %1$,d heimild</item>\n        <item quantity=\"other\">Beðið um %1$,d heimildir</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">Undirritanir</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">Engar gildar undirritanir</string>\n    <string name=\"file_properties_apk_past_signature_digests\">Gamlar undirritanir</string>\n    <string name=\"file_properties_checksum\">Gátsummur</string>\n    <string name=\"file_properties_checksum_compare\">Bera saman</string>\n    <string name=\"file_properties_checksum_compare_placeholder\">Hvaða gátsumma sem er</string>\n    <string name=\"file_properties_checksum_compare_match_format\">Samsvarar %1$s gátsummu</string>\n    <string name=\"file_properties_checksum_compare_prefix_match_format\">Samsvarar forskeyti á %1$s gátsummu</string>\n    <string name=\"file_properties_checksum_compare_no_match\">Engin samsvarandi gátsumma fannst</string>\n    <string name=\"storage_file_system_root_title\">Rót</string>\n    <string name=\"storage_list_title\">Geymslurými</string>\n    <string name=\"storage_list_empty\">Ekkert geymslurými</string>\n    <string name=\"storage_add_storage_title\">Bæta við geymslurými</string>\n    <string name=\"storage_add_storage_document_tree\">Ytra geymslurými</string>\n    <string name=\"storage_add_storage_ftp_server\">FTP-netþjónn</string>\n    <string name=\"storage_add_storage_sftp_server\">SFTP-netþjónn</string>\n    <string name=\"storage_add_storage_smb_server\">SMB-netþjónn</string>\n    <string name=\"storage_add_storage_webdav_server\">WebDAV-þjónn</string>\n    <string name=\"storage_edit_device_storage_title\">Breyta geymslurými tækis</string>\n    <string name=\"storage_edit_device_storage_name\">Nafn</string>\n    <string name=\"storage_edit_device_storage_path\">Slóð</string>\n    <string name=\"storage_add_external_storage_shortcut_title\">Bæta við flýtileið á ytra geymslurými</string>\n    <string name=\"storage_edit_external_storage_shortcut_title\">Breyta flýtileið á ytra geymslurými</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id\">Geymslurými</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id_error_empty\">Fara í geymslurými</string>\n    <string name=\"storage_edit_external_storage_shortcut_path\">Slóð</string>\n    <string name=\"storage_add_document_tree_title\">Bæta við ytra geymslurými</string>\n    <string name=\"storage_edit_document_tree_title\">Breyta ytra geymslurými</string>\n    <string name=\"storage_edit_document_tree_name\">Nafn</string>\n    <string name=\"storage_edit_document_tree_uri\">URI-slóð</string>\n    <string name=\"storage_edit_document_tree_path\">Slóð</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">Breyta FTP-netþjóni</string>\n    <string name=\"storage_edit_ftp_server_title_add\">Bæta við FTP-netþjóni</string>\n    <string name=\"storage_edit_ftp_server_host\">Vélarheiti</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">Settu inn vélarheiti</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">Ógilt vélarheiti</string>\n    <string name=\"storage_edit_ftp_server_port\">Gátt</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">Ógild gátt</string>\n    <string name=\"storage_edit_ftp_server_path\">Slóð</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">Má skilja eftir autt</string>\n    <string name=\"storage_edit_ftp_server_name\">Nafn</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">Nota vélarheiti</string>\n    <string name=\"storage_edit_ftp_server_protocol\">Samskiptamáti</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">Auðkenning</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>Lykilorð</item>\n        <item>Nafnlaus</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">Notandanafn</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">Settu inn notandanafn</string>\n    <string name=\"storage_edit_ftp_server_password\">Lykilorð</string>\n    <string name=\"storage_edit_ftp_server_mode\">Hamur</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>Virkt</item>\n        <item>Passíft</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">Kóðun</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">Tengjast og bæta við</string>\n    <string name=\"storage_edit_ftp_server_add\">Bæta við</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">Breyta SFTP-netþjóni</string>\n    <string name=\"storage_edit_sftp_server_title_add\">Bæta við SFTP-netþjóni</string>\n    <string name=\"storage_edit_sftp_server_host\">Vélarheiti</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">Settu inn vélarheiti</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">Ógilt vélarheiti</string>\n    <string name=\"storage_edit_sftp_server_port\">Gátt</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">Ógild gátt</string>\n    <string name=\"storage_edit_sftp_server_path\">Slóð</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">Má skilja eftir autt</string>\n    <string name=\"storage_edit_sftp_server_name\">Nafn</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">Nota vélarheiti</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">Auðkenning</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>Lykilorð</item>\n        <item>Dreifilykill</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">Notandanafn</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">Settu inn notandanafn</string>\n    <string name=\"storage_edit_sftp_server_password\">Lykilorð</string>\n    <string name=\"storage_edit_sftp_server_private_key\">Einkalykill</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">Opna skrá</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">Settu inn einkalykil</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">Ógildur einkalykill</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">Lykilorð einkalykils</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">Má skilja eftir autt</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">Ógilt lykilorð einkalykils</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">Tengjast og bæta við</string>\n    <string name=\"storage_edit_sftp_server_add\">Bæta við</string>\n    <string name=\"storage_add_lan_smb_server_loading\">Leita að SMB-netþjónum…</string>\n    <string name=\"storage_add_lan_smb_server_add\">Bæta við handvirkt</string>\n    <string name=\"storage_edit_smb_server_title_edit\">Breyta SMB-netþjóni</string>\n    <string name=\"storage_edit_smb_server_title_add\">Bæta við SMB-netþjóni</string>\n    <string name=\"storage_edit_smb_server_host\">Vélarheiti</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">Settu inn vélarheiti</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">Ógilt vélarheiti</string>\n    <string name=\"storage_edit_smb_server_port\">Gátt</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">Ógild gátt</string>\n    <string name=\"storage_edit_smb_server_path\">Slóð</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">Má skilja eftir autt</string>\n    <string name=\"storage_edit_smb_server_name\">Nafn</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">Nota vélarheiti</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">Auðkenning</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>Lykilorð</item>\n        <item>Gestur</item>\n        <item>Nafnlaus</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">Notandanafn</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">Settu inn notandanafn</string>\n    <string name=\"storage_edit_smb_server_password\">Lykilorð</string>\n    <string name=\"storage_edit_smb_server_domain\">Lén</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">Tengjast og bæta við</string>\n    <string name=\"storage_edit_smb_server_add\">Bæta við</string>\n    <string name=\"storage_edit_webdav_server_title_edit\">Breyta WebDAV-þjóni</string>\n    <string name=\"storage_edit_webdav_server_title_add\">Bæta við WebDAV-þjóni</string>\n    <string name=\"storage_edit_webdav_server_host\">Vélarheiti</string>\n    <string name=\"storage_edit_webdav_server_host_error_empty\">Settu inn vélarheiti</string>\n    <string name=\"storage_edit_webdav_server_host_error_invalid\">Ógilt vélarheiti</string>\n    <string name=\"storage_edit_webdav_server_port\">Gátt</string>\n    <string name=\"storage_edit_webdav_server_port_error_invalid\">Ógild gátt</string>\n    <string name=\"storage_edit_webdav_server_path\">Slóð</string>\n    <string name=\"storage_edit_webdav_server_path_placeholder\">Má skilja eftir autt</string>\n    <string name=\"storage_edit_webdav_server_name\">Nafn</string>\n    <string name=\"storage_edit_webdav_server_name_placeholder\">Nota vélarheiti</string>\n    <string name=\"storage_edit_webdav_server_protocol\">Samskiptamáti</string>\n    <string name=\"storage_edit_webdav_server_authentication_type\">Auðkenning</string>\n    <string-array name=\"storage_edit_webdav_server_authentication_type_entries\">\n        <item>Lykilorð</item>\n        <item>Aðgangsteikn</item>\n        <item>Ekkert</item>\n    </string-array>\n    <string name=\"storage_edit_webdav_server_username\">Notandanafn</string>\n    <string name=\"storage_edit_webdav_server_username_error_empty\">Settu inn notandanafn</string>\n    <string name=\"storage_edit_webdav_server_password\">Lykilorð</string>\n    <string name=\"storage_edit_webdav_server_access_token\">Aðgangsteikn</string>\n    <string name=\"storage_edit_webdav_server_access_token_error_empty\">Settu inn aðgangsteikn</string>\n    <string name=\"storage_edit_webdav_server_connect_and_add\">Tengjast og bæta við</string>\n    <string name=\"storage_edit_webdav_server_add\">Bæta við</string>\n\n    <string name=\"navigation_storage_subtitle_format\">%1$s laust af %2$s</string>\n    <string name=\"navigation_add_storage\">Bæta við geymslurými…</string>\n    <string name=\"navigation_standard_directory_alarms\">Áminningar</string>\n    <string name=\"navigation_standard_directory_dcim\">DCIM</string>\n    <string name=\"navigation_standard_directory_documents\">Skjöl</string>\n    <string name=\"navigation_standard_directory_downloads\">Sótt gögn</string>\n    <string name=\"navigation_standard_directory_movies\">Kvikmyndir</string>\n    <string name=\"navigation_standard_directory_music\">Tónlist</string>\n    <string name=\"navigation_standard_directory_notifications\">Tilkynningar</string>\n    <string name=\"navigation_standard_directory_pictures\">Myndir</string>\n    <string name=\"navigation_standard_directory_podcasts\">Hlaðvörp</string>\n    <string name=\"navigation_standard_directory_ringtones\">Hringitónar</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">Bókamerkjamappa</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">Nafn</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">Slóð</string>\n    <string name=\"archive_viewer_title\">Safnskoðari</string>\n    <string name=\"image_viewer_title\">Myndaskoðari</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"save_as_title\">Vista sem</string>\n    <string name=\"save_as_error\">Villa kom upp við að vista skrá</string>\n    <string name=\"save_as_success_format\">“%1$s” hefur verið vistuð í “%2$s”</string>\n    <string name=\"text_editor_title\">Textaritill</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">Vistað</string>\n    <string name=\"text_editor_reload_message\">Ertu viss um að þú viljir endurhlaða? Óvistaðar breytingar á þessu skjali munu tapast.</string>\n    <string name=\"text_editor_encoding\">Kóðun</string>\n    <string name=\"text_editor_close_message\">Ertu viss um að þú viljir henda óvistuðum breytingum á þessu skjali?</string>\n\n    <string name=\"ftp_server_title\">FTP-netþjónn</string>\n    <string name=\"ftp_server_state_title\">Staða</string>\n    <string name=\"ftp_server_state_summary_starting\">Ræsing…</string>\n    <string name=\"ftp_server_state_summary_running\">Keyrandi</string>\n    <string name=\"ftp_server_state_summary_stopping\">Stöðvast…</string>\n    <string name=\"ftp_server_state_summary_stopped\">Ekki ræst</string>\n    <string name=\"ftp_server_url_title\">URL-slóð</string>\n    <string name=\"ftp_server_url_summary_no_local_inet_address\">Staðvært IP-vistfang er ekki þekkt</string>\n    <string name=\"ftp_server_url_menu_copy_url\">Afrita slóð</string>\n    <string name=\"ftp_server_url_menu_copy_password\">Afrita lykilorð</string>\n    <string name=\"ftp_server_add_tile_title\">Bæta í flýtistillingar</string>\n    <string name=\"ftp_server_add_tile_result_added\">“FTP-netþjónn” hefur verið bætt í flýtistillingar</string>\n    <string name=\"ftp_server_add_tile_result_already_added\">“FTP-netþjónn” hefur þegar verið bætt í flýtistillingar</string>\n    <string name=\"ftp_server_add_tile_result_error\">Villa við að bæta “FTP-netþjónn” í flýtistillingar</string>\n    <string name=\"ftp_server_configuration_title\">Uppsetning</string>\n    <string name=\"ftp_server_anonymous_login_title\">Nafnlaus innskráning</string>\n    <string name=\"ftp_server_username_title\">Notandanafn</string>\n    <string name=\"ftp_server_password_title\">Lykilorð</string>\n    <string name=\"ftp_server_port_title\">Gátt</string>\n    <string name=\"ftp_server_home_directory_title\">Rótarmappa</string>\n    <string name=\"ftp_server_writable_title\">Leyfa skrifun</string>\n    <string name=\"settings_title\">Stillingar</string>\n    <string name=\"settings_interface_title\">Viðmót</string>\n    <string name=\"settings_locale_title\">Tungumál</string>\n    <string name=\"settings_theme_color_title\">Litur þema</string>\n    <string name=\"settings_theme_color_summary\">Litur sem birtist oftast í forritinu</string>\n    <string name=\"settings_material_design_3_title\">Material-hönnun 3</string>\n    <string name=\"settings_night_mode_title\">Næturhamur</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>Fylgja kerfi</item>\n        <item>Af</item>\n        <item>Á</item>\n        <item>Byggt á tíma sólarhrings</item>\n        <item>Byggt á rafhlöðusparnaði</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">Svartur næturhamur</string>\n    <string name=\"settings_file_list_animation_title\">Hreyfa skráalista</string>\n    <string name=\"settings_file_name_ellipsize_title\">Birta löng skráaheiti</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>Setja úrfellingarpunkta í byrjun</item>\n        <item>Setja úrfellingarpunkta í miðju</item>\n        <item>Setja úrfellingarpunkta í enda</item>\n        <item>Valumgjörð</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">Hegðun</string>\n    <string name=\"settings_default_directory_title\">Sjálfgefin mappa</string>\n    <string name=\"settings_standard_directories_title\">Staðlaðar möppur</string>\n    <string name=\"settings_standard_directories_summary_empty\">Engar staðlaðar möppur virkjaðar</string>\n    <string name=\"settings_bookmark_directory_screenshots\">Skjámyndir</string>\n    <string name=\"settings_bookmark_directories_title\">Bókamerkjamöppur</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">Engar bókamerkjamöppur</string>\n    <string name=\"settings_root_strategy_title\">Hamur kerfisstjóraaðgangs</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"one\">Þú ert með %1$,d skráaaðgerð í gangi, ef skipt er núna yfir í rótaraðgangsham getur það valdið óvæntum villum. Ertu viss um að þú viljir skipta núna?</item>\n        <item quantity=\"other\">Þú ert með %1$,d skráaaðgerðir í gangi, ef skipt er núna yfir í rótaraðgangsham getur það valdið óvæntum villum. Ertu viss um að þú viljir skipta núna?</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>Aðeins venjulegur aðgangur</item>\n        <item>Sjálfvirkt</item>\n        <item>Aðeins kerfisstjóraaðgangur</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">Kóðun skráaheita í geymslu</string>\n    <string name=\"settings_open_apk_default_action_title\">Opna Android-pakka</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>Setja upp</item>\n        <item>Skoða innihald</item>\n        <item>Spyrja hvað eigi að gera</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_title\">Birta smámyndir fyrir PDF-skjöl</string>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">Gæti gert forritið óstöðugt í eldri útgáfum Android</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">Lesa fjartengdar skrár til að útbúa smámyndir</string>\n\n    <string name=\"about_title\">Um hugbúnaðinn</string>\n    <string name=\"about_version_title\">Útgáfa</string>\n    <string name=\"about_github_title\">Skoða á GitHub</string>\n    <string name=\"about_licenses_title\">Notkunarleyfi</string>\n    <string name=\"about_privacy_policy_title\">Persónuverndarstefna</string>\n    <string name=\"about_author_title\">Höfundur</string>\n    <string name=\"about_author_name_title\">Hai Zhang</string>\n    <string name=\"about_author_github_title\">Fylgjast með á GitHub</string>\n    <string name=\"about_author_twitter_title\">Fylgjast með á Twitter</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-it/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">File</string>\n    <string name=\"file_type_name_apk\">Pacchetto Android</string>\n    <string name=\"file_type_name_archive\">Archivio %1$s</string>\n    <string name=\"file_type_name_audio\">Audio %1$s</string>\n    <string name=\"file_type_name_calendar\">Calendario %1$s</string>\n    <string name=\"file_type_name_certificate\">Certificato %1$s</string>\n    <string name=\"file_type_name_code\">Documento %1$s</string>\n    <string name=\"file_type_name_contact\">Biglietto da visita elettronico</string>\n    <string name=\"file_type_name_directory\">Cartella</string>\n    <string name=\"file_type_name_document\">Documento %1$s</string>\n    <string name=\"file_type_name_ebook\">E-book %1$s</string>\n    <string name=\"file_type_name_email\">Messaggio email %1$s</string>\n    <string name=\"file_type_name_font\">Font %1$s</string>\n    <string name=\"file_type_name_generic\">File %1$s</string>\n    <string name=\"file_type_name_image\">Immagine %1$s</string>\n    <string name=\"file_type_name_pdf\">Documento PDF</string>\n    <string name=\"file_type_name_presentation\">Presentazione %1$s</string>\n    <string name=\"file_type_name_spreadsheet\">Foglio elettronico %1$s</string>\n    <string name=\"file_type_name_text\">Documento %1$s</string>\n    <string name=\"file_type_name_text_plain\">Documento di testo</string>\n    <string name=\"file_type_name_video\">Video %1$s</string>\n    <string name=\"file_type_name_word\">Documento Word</string>\n    <string name=\"file_type_name_powerpoint\">Presentazione PowerPoint</string>\n    <string name=\"file_type_name_excel\">Foglio elettronico Excel</string>\n    <string name=\"file_type_name_posix_character_device\">Dispositivo a caratteri</string>\n    <string name=\"file_type_name_posix_block_device\">Dispositivo a blocchi</string>\n    <string name=\"file_type_name_posix_fifo\">Pipe</string>\n    <string name=\"file_type_name_posix_symbolic_link\">Collegamento</string>\n    <string name=\"file_type_name_posix_socket\">Socket</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">Collegamento (rotto)</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-it/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <string name=\"app_name\">Material Files</string>\n\n    <string name=\"close\">Chiudi</string>\n    <string name=\"copy\">Copia</string>\n    <string name=\"cut\">Taglia</string>\n    <string name=\"default_\">Predefinito</string>\n    <string name=\"delete\">Elimina</string>\n    <string name=\"discard\">Scarta</string>\n    <string name=\"edit\">Modifica</string>\n    <string name=\"empty\">Vuoto</string>\n    <string name=\"empty_placeholder\">(Vuoto)</string>\n    <string name=\"error\">Errore</string>\n    <string name=\"hide\">Nascondi</string>\n    <string name=\"install\">Installa</string>\n    <string name=\"keep_editing\">Continua a modificare</string>\n    <string name=\"loading\">Caricamento…</string>\n    <string name=\"maybe_later\">Forse dopo</string>\n    <string name=\"merge\">Unisci</string>\n    <string name=\"none\">Nessuno</string>\n    <string name=\"open_navigation_drawer\">Apri il navigation drawer</string>\n    <string name=\"paste\">Incolla</string>\n    <string name=\"refresh\">Aggiorna</string>\n    <string name=\"reload\">Ricarica</string>\n    <string name=\"remove\">Rimuovi</string>\n    <string name=\"rename\">Rinomina</string>\n    <string name=\"replace\">Sostituisci</string>\n    <string name=\"reset\">Ripristina</string>\n    <string name=\"retry\">Riprova</string>\n    <string name=\"save\">Salva</string>\n    <string name=\"search\">Cerca</string>\n    <string name=\"select\">Seleziona</string>\n    <string name=\"select_all\">Seleziona tutto</string>\n    <string name=\"share\">Condividi</string>\n    <string name=\"show\">Mostra</string>\n    <string name=\"skip\">Salta</string>\n    <string name=\"stop\">Ferma</string>\n    <string name=\"system_default\">Predefinito di sistema</string>\n    <string name=\"unknown\">Sconosciuto</string>\n    <string name=\"view\">Visualizza</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">Nessuna app trovata per gestire l\\'azione</string>\n    <string name=\"open_settings\">Apri impostazioni</string>\n    <string name=\"shortcut_created\">Scorciatoia creata</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"one\">%1$,d byte</item>\n        <item quantity=\"many\">%1$,d bytes</item>\n        <item quantity=\"other\">%1$,d bytes</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">L\\'app richiede l\\'autorizzazione di gestire i file. Consenti l\\'accesso nella prossima impostazione di sistema.</string>\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">L\\'app richiede l\\'autorizzazione di accedere ai file. Clicca “CONSENTI” nella prossima finestra.</string>\n    <string name=\"storage_permission_permanently_denied_message\">L\\'app richiede l\\'autorizzazione di accedere ai file. Consenti l\\'autorizzazione “Archiviazione” nelle impostazioni di sistema.</string>\n    <!-- The allow button is in sentence case now. -->\n    <string name=\"notification_permission_rationale_message\">L\\'app richiede l\\'autorizzazione di mostrare notifiche sulle operazioni sui file. Clicca “CONSENTI” nella prossima finestra.</string>\n    <string name=\"notification_permission_permanently_denied_message\">L\\'app richiede l\\'autorizzazione di mostrare notifiche sulle operazioni sui file. Concedi l\\'autorizzazione “Notifiche” nelle impostazioni di sistema.</string>\n\n    <string name=\"notification_channel_background_activity_start_name\">Azioni in secondo piano</string>\n    <string name=\"notification_channel_background_activity_start_description\">Esegui azioni mentre l\\'app è in secondo piano</string>\n    <string name=\"notification_channel_file_job_name\">Operazioni sui file</string>\n    <string name=\"notification_channel_file_job_description\">Mostra e controlla le operazioni sui file</string>\n    <string name=\"notification_channel_ftp_server_name\">Server FTP</string>\n    <string name=\"notification_channel_ftp_server_description\">Mostra e controlla il server FTP</string>\n\n    <string name=\"file_action_archive_password_title\">Password richiesta</string>\n    <string name=\"file_action_archive_password_message_format\">“%1$s” è protetto da password.</string>\n    <string name=\"file_action_archive_password_error_empty\">La password non può essere vuota</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"one\">Preparazione per comprimere %1$,d file (%2$s)</item>\n        <item quantity=\"many\">Preparazione per comprimere %1$,d file (%2$s)</item>\n        <item quantity=\"other\">Preparazione per comprimere %1$,d file (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">Compressione di “%1$s” in “%2$s”</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"one\">Compressione di %1$,d file in “%2$s”</item>\n        <item quantity=\"many\">Compressione di %1$,d file in “%2$s”</item>\n        <item quantity=\"other\">Compressione di %1$,d file in “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"one\">Preparazione per copiare %1$,d file (%2$s)</item>\n        <item quantity=\"many\">Preparazione per copiare %1$,d file (%2$s)</item>\n        <item quantity=\"other\">Preparazione per copiare %1$,d file (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">Copia di “%1$s” in “%2$s”</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"one\">Copia di %1$,d file in “%2$s”</item>\n        <item quantity=\"many\">Copia di %1$,d file in “%2$s”</item>\n        <item quantity=\"other\">Copia di %1$,d file in “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"one\">Preparazione per estrarre %1$,d file (%2$s)</item>\n        <item quantity=\"many\">Preparazione per estrarre %1$,d file (%2$s)</item>\n        <item quantity=\"other\">Preparazione per estrarre %1$,d file (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">Estrazione di “%1$s” in “%2$s”</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"one\">Estrazione di %1$,d file in “%2$s”</item>\n        <item quantity=\"many\">Estrazione di %1$,d file in “%2$s”</item>\n        <item quantity=\"other\">Estrazione di %1$,d file in “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"one\">Preparazione per spostare %1$,d file (%2$s)</item>\n        <item quantity=\"many\">Preparazione per spostare %1$,d file (%2$s)</item>\n        <item quantity=\"other\">Preparazione per spostare %1$,d file (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">Spostamento di “%1$s” in “%2$s”</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"one\">Spostamento di %1$,d file in “%2$s”</item>\n        <item quantity=\"many\">Spostamento di %1$,d file in “%2$s”</item>\n        <item quantity=\"other\">Spostamento di %1$,d file in “%2$s”</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"one\">Preparazione per eliminare %1$,d file</item>\n        <item quantity=\"many\">Preparazione per eliminare %1$,d file</item>\n        <item quantity=\"other\">Preparazione per eliminare %1$,d file</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">Eliminazione di “%1$s”</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"one\">Eliminazione di %1$,d file</item>\n        <item quantity=\"many\">Eliminazione di %1$,d file</item>\n        <item quantity=\"other\">Eliminazione di %1$,d file</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"one\">Preparazione per cambiare il proprietario di %1$,d file</item>\n        <item quantity=\"many\">Preparazione per cambiare il proprietario di %1$,d file</item>\n        <item quantity=\"other\">Preparazione per cambiare il proprietario di %1$,d file</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">Modifica del proprietario di “%1$s”</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"one\">Modifica del proprietario di %1$,d file</item>\n        <item quantity=\"many\">Modifica del proprietario di %1$,d file</item>\n        <item quantity=\"other\">Modifica del proprietario di %1$,d file</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"one\">Preparazione per cambiare il gruppo di %1$,d file</item>\n        <item quantity=\"many\">Preparazione per cambiare il gruppo di %1$,d file</item>\n        <item quantity=\"other\">Preparazione per cambiare il gruppo di %1$,d file</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">Modifica del gruppo di “%1$s”</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"one\">Modifica del gruppo di %1$,d file</item>\n        <item quantity=\"many\">Modifica del gruppo di %1$,d file</item>\n        <item quantity=\"other\">Modifica del gruppo di %1$,d file</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"one\">Preparazione per cambiare i permessi di base di %1$,d file</item>\n        <item quantity=\"many\">Preparazione per cambiare i permessi di base di %1$,d file</item>\n        <item quantity=\"other\">Preparazione per cambiare i permessi di base di %1$,d file</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">Modifica dei permessi di base di “%1$s”</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"one\">Modifica dei permessi di base di %1$,d file</item>\n        <item quantity=\"many\">Modifica dei permessi di base di %1$,d file</item>\n        <item quantity=\"other\">Modifica dei permessi di base di %1$,d file</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Preparazione per cambiare il contesto SELinux di %1$,d file</item>\n        <item quantity=\"many\">Preparazione per cambiare il contesto SELinux di %1$,d file</item>\n        <item quantity=\"other\">Preparazione per cambiare il contesto SELinux di %1$,d file</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">Modifica del contesto di SELinux di “%1$s”</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Modifica del contesto di SELinux di %1$,d file</item>\n        <item quantity=\"many\">Modifica del contesto di SELinux di %1$,d file</item>\n        <item quantity=\"other\">Modifica del contesto di SELinux di %1$,d file</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Preparazione per ripristinare il contesto di SELinux di %1$,d file</item>\n        <item quantity=\"many\">Preparazione per ripristinare il contesto di SELinux di %1$,d file</item>\n        <item quantity=\"other\">Preparazione per ripristinare il contesto di SELinux di %1$,d file</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">Ripristino del contesto di SELinux di “%1$s”</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Ripristino del contesto di SELinux di %1$,d file</item>\n        <item quantity=\"many\">Ripristino del contesto di SELinux di %1$,d file</item>\n        <item quantity=\"other\">Ripristino del contesto di SELinux di %1$,d file</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">Scrittura di “%1$s”</string>\n    <string name=\"file_job_create_error_title\">Errore nella creazione</string>\n    <string name=\"file_job_create_error_message_format\">Si è verificato un errore nella creazione di “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">Impossibile copiare una cartella in se stessa</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">Impossibile estrarre una cartella in se stessa</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">Impossibile spostare una cartella in se stessa</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">La cartella di destinazione è dentro la cartella di origine.</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">Impossibile copiare un file su se stesso</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">Impossibile estrarre un file su se stesso</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">Impossibile spostare un file su se stesso</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">Il file di origine verrebbe sovrascritto dalla destinazione.</string>\n    <string name=\"file_job_replace_title_format\">Sostituire il file “%1$s”?</string>\n    <string name=\"file_job_replace_message_format\">Esiste già un altro file con lo stesso nome in “%1$s”.\\nSostituirlo sovrascriverà il suo contenuto.</string>\n    <string name=\"file_job_replace_target_name\">File originale</string>\n    <string name=\"file_job_replace_source_name\">Sostituisci con</string>\n    <string name=\"file_job_merge_title_format\">Unire la cartella “%1$s”?</string>\n    <string name=\"file_job_merge_copy_message_format\">L\\'unione chiederà una conferma prima di sostituire i file nella cartella che vanno in conflitto con i file che vengono copiati.</string>\n    <string name=\"file_job_merge_extract_message_format\">L\\'unione chiederà una conferma prima di sostituire i file nella cartella che vanno in conflitto con i file che vengono estratti.</string>\n    <string name=\"file_job_merge_move_message_format\">L\\'unione chiederà una conferma prima di sostituire i file nella cartella che vanno in conflitto con i file che vengono spostati.</string>\n    <string name=\"file_job_merge_target_name\">Cartella originale</string>\n    <string name=\"file_job_merge_source_name\">Unisci con</string>\n    <string name=\"file_job_conflict_show_name\">Seleziona un nuovo nome per la destinazione</string>\n    <string name=\"file_job_conflict_name_hint\">Nuovo nome</string>\n    <string name=\"file_job_archive_error_title_format\">Errore nella compressione di “%1$s”</string>\n    <string name=\"file_job_archive_error_message_format\">Si è verificato un errore comprimendo il file in “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">Errore nella copia di “%1$s”</string>\n    <string name=\"file_job_copy_error_message_format\">Si è verificato un errore copiando il file in “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">Errore nell\\'estrazione di “%1$s”</string>\n    <string name=\"file_job_extract_error_message_format\">Si è verificato un errore estraendo il file in “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">Errore nello spostamento di “%1$s”</string>\n    <string name=\"file_job_move_error_message_format\">Si è verificato un errore spostando il file in “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">Errore nell\\'eliminazione</string>\n    <string name=\"file_job_delete_error_message_format\">Si è verificato un errore eliminando “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">Errore nella rinominazione di “%1$s”</string>\n    <string name=\"file_job_rename_error_message_format\">Si è verificato un errore rinominando il file in “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">Errore nel cambio di proprietario di “%1$s”</string>\n    <string name=\"file_job_set_owner_error_message_format\">Si è verificato un cambiando il proprietario in “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">Errore nel cambio del gruppo di “%1$s”</string>\n    <string name=\"file_job_set_group_error_message_format\">Si è verificato un errore cambiando il gruppo in “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">Errore nel cambio dei permessi di base di “%1$s”</string>\n    <string name=\"file_job_set_mode_error_message_format\">Si è verificato un errore cambiando i permessi di base in “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">Errore nel cambio del contesto di SELinux di “%1$s”</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">Si è verificato un errore cambiando il contesto di SELinux in “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">Errore nel ripristino del contesto di SELinux</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">Si è verificato un errore ripristinando il contesto di SELinux di “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">Errore di scrittura</string>\n    <string name=\"file_job_write_error_message_format\">Si è verificato un errore scrivendo “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">Si è verificato un errore ottenendo informazioni su “%1$s”.</string>\n    <string name=\"file_job_remount_format\">Rimonta “%1$s” in lettura-scrittura</string>\n    <string name=\"file_job_remount_loading_format\">Rimonto “%1$s” in lettura-scrittura…</string>\n    <string name=\"file_job_remount_success_format\">“%1$s” è stato rimontato in lettura-scrittura</string>\n    <string name=\"file_job_action_all\">Applica questa azione a tutti i file</string>\n\n    <string name=\"file_open_apk_message\">Questo file è un pacchetto di Android. Vuoi installarlo o vederne i contenuti?</string>\n    <string name=\"file_open_as_title\">Apri come…</string>\n    <string name=\"file_open_as_title_format\">Apri “%1$s” come</string>\n    <string name=\"file_open_as_type_text\">Testo</string>\n    <string name=\"file_open_as_type_image\">Immagine</string>\n    <string name=\"file_open_as_type_audio\">Audio</string>\n    <string name=\"file_open_as_type_video\">Video</string>\n    <string name=\"file_open_as_type_directory\">Cartella</string>\n    <string name=\"file_open_as_type_any\">Altro</string>\n    <string name=\"file_install_apk_from_background_title_format\">“%1$s” è pronto per l\\'installazione</string>\n    <string name=\"file_install_apk_from_background_text\">Tocca per installare</string>\n    <string name=\"file_open_from_background_title_format\">“%1$s” è pronto per essere aperto</string>\n    <string name=\"file_open_from_background_text\">Tocca per aprire</string>\n    <string name=\"file_name_error_empty\">Il nome del file non può essere vuoto</string>\n    <string name=\"file_name_error_invalid\">Nome file non valido</string>\n    <string name=\"file_name_error_already_exists\">Un file con questo nome esiste già</string>\n    <string name=\"file_replace_message_format\">Sostituire \\\"%1$s\\\"?</string>\n    <string name=\"file_delete_message_file_format\">Eliminare “%1$s”?</string>\n    <string name=\"file_delete_message_directory_format\">Eliminare la cartella “%1$s” e il suo contenuto?</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"one\">Eliminare %1$,d file?</item>\n        <item quantity=\"many\">Eliminare %1$,d file?</item>\n        <item quantity=\"other\">Eliminare %1$,d file?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"one\">Eliminare %1$,d cartella e i suoi contenuti?</item>\n        <item quantity=\"many\">Eliminare %1$,d cartelle e i loro contenuti?</item>\n        <item quantity=\"other\">Eliminare %1$,d cartelle e i loro contenuti?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"one\">Eliminare %1$,d elemento?</item>\n        <item quantity=\"many\">Eliminare %1$,d elementi?</item>\n        <item quantity=\"other\">Eliminare %1$,d elementi?</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">Crea archivio</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_create_archive_password\">Password (facoltativa)</string>\n    <string name=\"file_add_bookmark_success\">Segnalibro aggiunto</string>\n    <string name=\"file_create_file_title\">Nuovo file</string>\n    <string name=\"file_create_directory_title\">Nuova cartella</string>\n\n    <string name=\"file_list_title\">File</string>\n    <plurals name=\"file_list_title_open_file\">\n        <item quantity=\"one\">Seleziona un file</item>\n        <item quantity=\"many\">Seleziona i file</item>\n        <item quantity=\"other\">Seleziona i file</item>\n    </plurals>\n    <string name=\"file_list_title_create_file\">Salva file</string>\n    <plurals name=\"file_list_title_open_directory\">\n        <item quantity=\"one\">Seleziona una cartella</item>\n        <item quantity=\"many\">Seleziona le cartelle</item>\n        <item quantity=\"other\">Seleziona le cartelle</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"one\">%1$,d cartella</item>\n        <item quantity=\"many\">%1$,d cartelle</item>\n        <item quantity=\"other\">%1$,d cartelle</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"one\">%1$,d file</item>\n        <item quantity=\"many\">%1$,d file</item>\n        <item quantity=\"other\">%1$,d file</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">,\\u0020</string>\n    <string name=\"file_list_path_error_empty\">Il percorso non può essere vuoto</string>\n    <string name=\"file_list_path_error_invalid\">Percorso non valido</string>\n    <string name=\"file_list_action_view_sort\">Visualizzazione e ordinamento</string>\n    <string name=\"file_list_action_view_list\">Lista</string>\n    <string name=\"file_list_action_view_grid\">Griglia</string>\n    <string name=\"file_list_action_sort_by_name\">Nome</string>\n    <string name=\"file_list_action_sort_by_type\">Tipo</string>\n    <string name=\"file_list_action_sort_by_size\">Dimensione</string>\n    <string name=\"file_list_action_sort_by_last_modified\">Ultima modifica</string>\n    <string name=\"file_list_action_sort_order_ascending\">Crescente</string>\n    <string name=\"file_list_action_sort_directories_first\">Prima le cartelle</string>\n    <string name=\"file_list_action_view_sort_path_specific\">Solo per questa cartella</string>\n    <string name=\"file_list_action_new_task\">Nuova finestra</string>\n    <string name=\"file_list_action_navigate_up\">Cartella superiore</string>\n    <string name=\"file_list_action_navigate_to\">Vai in</string>\n    <string name=\"file_list_action_show_hidden_files\">Mostra file nascosti</string>\n    <string name=\"file_list_action_copy_path\">Copia percorso</string>\n    <string name=\"file_list_action_open_in_terminal\">Apri nel terminale</string>\n    <string name=\"file_list_action_add_bookmark\">Aggiungi segnalibro</string>\n    <string name=\"file_list_action_create_shortcut\">Crea segnalibro</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">Apri in una nuova finestra</string>\n    <string name=\"file_list_select_title_format\">%1$,d</string>\n    <string name=\"file_list_create_file_name_hint\">Nome del file</string>\n    <string name=\"file_list_open_current_directory_format\">Seleziona \\\"%1$s\\\"</string>\n    <string name=\"file_list_paste_move_title_format\">Spostamento di %1$,d</string>\n    <string name=\"file_list_paste_copy_title_format\">Copia di %1$,d</string>\n    <string name=\"file_list_paste_extract_title_format\">Estrazione di %1$,d</string>\n    <string name=\"file_list_paste_action_extract_here\">Estrai qui</string>\n    <string name=\"file_list_empty\">Nessun file</string>\n    <string name=\"file_list_action_create_file\">File</string>\n    <string name=\"file_list_action_create_directory\">Cartella</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">Apri con</string>\n    <string name=\"file_item_action_extract\">Estrai</string>\n    <string name=\"file_item_action_archive\">Comprimi</string>\n    <string name=\"file_item_action_properties\">Proprietà</string>\n\n    <string name=\"file_properties_title_format\">Proprietà di “%1$s”</string>\n    <string name=\"file_properties_basic\">Di base</string>\n    <string name=\"file_properties_basic_name\">Nome</string>\n    <string name=\"file_properties_basic_type\">Tipo</string>\n    <string name=\"file_properties_basic_type_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">Collegamento a %1$s (%2$s)</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">Destinazione collegamento</string>\n    <string name=\"file_properties_basic_size\">Dimensione</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_contents\">Contenuto</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"one\">%1$,d elemento, con dimensione %2$s</item>\n        <item quantity=\"many\">%1$,d elementi, in totale %2$s</item>\n        <item quantity=\"other\">%1$,d elementi, in totale %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">Ultima modifica</string>\n    <string name=\"file_properties_basic_parent_directory\">Cartella superiore</string>\n    <string name=\"file_properties_basic_archive_file\">File archivio</string>\n    <string name=\"file_properties_basic_archive_entry\">File dentro archivio</string>\n    <string name=\"file_properties_basic_free_space\">Spazio libero</string>\n    <string name=\"file_properties_permission\">Permessi</string>\n    <string name=\"file_properties_permission_owner\">Proprietario</string>\n    <string name=\"file_properties_permission_group\">Gruppo</string>\n    <string name=\"file_properties_permission_principal_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_permission_set_owner_title\">Modifica proprietario</string>\n    <string name=\"file_properties_permission_set_group_title\">Modifica gruppo</string>\n    <string name=\"file_properties_permission_set_principal_filter_hint\">Inserisci un nome o un ID</string>\n    <string name=\"file_properties_permission_set_principal_system\">Sistema</string>\n    <string name=\"file_properties_permission_mode\">Modalità</string>\n    <string name=\"file_properties_permission_mode_format\">%1$s (%2$04o)</string>\n    <string name=\"file_properties_permission_set_mode_title\">Modifica modalità</string>\n    <string name=\"file_properties_permission_set_mode_others\">Altri</string>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_file\">\n        <item>Lettura</item>\n        <item>Scrittura</item>\n        <item>Esecuzione</item>\n    </string-array>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_directory\">\n        <item>Lettura</item>\n        <item>Scrittura</item>\n        <item>Cerca</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_special\">Speciale</string>\n    <string-array name=\"file_properties_permission_set_mode_special_mode_bits\">\n        <item>Imposta UID</item>\n        <item>Imposta GID</item>\n        <item>Sticky bit</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_uppercase_x\">Non aggiungere \\\"Esegui\\\" per i file allegati</string>\n    <string name=\"file_properties_permission_selinux_context\">Contesto SELinux</string>\n    <string name=\"file_properties_permission_set_selinux_context_title\">Modifica contesto SELinux</string>\n    <string name=\"file_properties_permission_set_selinux_context_restore\">Ripristina</string>\n    <string name=\"file_properties_permission_recursive\">Applica ai file allegati</string>\n    <string name=\"file_properties_image\">Immagine</string>\n    <string name=\"file_properties_media_dimensions\">Dimensioni</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">Scattata il</string>\n    <string name=\"file_properties_media_coordinates\">Coordinate</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f, %2$.3f</string>\n    <string name=\"file_properties_media_address\">Posizione</string>\n    <string name=\"file_properties_image_gps_altitude\">Altitudine</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f m</string>\n    <string name=\"file_properties_image_equipment\">Fotocamera</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">Apertura</string>\n    <string name=\"file_properties_image_shutter_speed\">Tempo di esposizione</string>\n    <string name=\"file_properties_image_focal_length\">Lunghezza focale</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f mm</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">Equivalente ISO</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">Software</string>\n    <string name=\"file_properties_image_description\">Descrizione</string>\n    <string name=\"file_properties_image_artist\">Autore</string>\n    <string name=\"file_properties_image_copyright\">Copyright</string>\n    <string name=\"file_properties_audio\">Audio</string>\n    <string name=\"file_properties_media_title\">Titolo</string>\n    <string name=\"file_properties_audio_artist\">Artista</string>\n    <string name=\"file_properties_audio_album\">Album</string>\n    <string name=\"file_properties_audio_album_artist\">Artista album</string>\n    <string name=\"file_properties_audio_composer\">Compositore</string>\n    <string name=\"file_properties_audio_disc_number\">Disco</string>\n    <string name=\"file_properties_audio_track_number\">Traccia</string>\n    <string name=\"file_properties_audio_year\">Anno</string>\n    <string name=\"file_properties_audio_genre\">Genere</string>\n    <string name=\"file_properties_media_duration\">Durata</string>\n    <string name=\"file_properties_media_bit_rate\">Velocità di trasmissione</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d kbps</string>\n    <string name=\"file_properties_audio_sample_rate\">Frequenza di campionamento</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Hz</string>\n    <string name=\"file_properties_video\">Video</string>\n    <string name=\"file_properties_apk\">APK</string>\n    <string name=\"file_properties_apk_label\">Nome</string>\n    <string name=\"file_properties_apk_package_name\">Nome pacchetto</string>\n    <string name=\"file_properties_apk_version\">Versione</string>\n    <string name=\"file_properties_apk_version_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_apk_min_sdk_version\">Versione di sistema minima</string>\n    <string name=\"file_properties_apk_target_sdk_version\">Versione di sistema di destinazione</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s (%2$s, %3$d)</string>\n    <string name=\"file_properties_apk_requested_permissions\">Autorizzazioni</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">Nessuna autorizzazione richiesta</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"one\">%1$,d autorizzazione richiesta</item>\n        <item quantity=\"many\">%1$,d autorizzazioni richieste</item>\n        <item quantity=\"other\">%1$,d autorizzazioni richieste</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">Firme</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">Nessuna firma valida</string>\n    <string name=\"file_properties_apk_past_signature_digests\">Vecchie firme</string>\n    <string name=\"file_properties_checksum\">Checksums</string>\n    <string name=\"file_properties_checksum_compare\">Confronta</string>\n    <string name=\"file_properties_checksum_compare_placeholder\">Qualsiasi checksum</string>\n    <string name=\"file_properties_checksum_compare_match_format\">Corrisponde a %1$s checksum</string>\n    <string name=\"file_properties_checksum_compare_prefix_match_format\">Corrisponde al prefisso di %1$s checksum</string>\n    <string name=\"file_properties_checksum_compare_no_match\">Nessun checksum corrispondente trovato</string>\n    <string name=\"storage_file_system_root_title\">Root</string>\n    <string name=\"storage_list_title\">Archiviazione</string>\n    <string name=\"storage_list_empty\">Nessuna archiviazione</string>\n    <string name=\"storage_add_storage_title\">Aggiungi archiviazione</string>\n    <string name=\"storage_add_storage_document_tree\">Archiviazione esterna</string>\n    <string name=\"storage_add_storage_ftp_server\">Server FTP</string>\n    <string name=\"storage_add_storage_sftp_server\">Server SFTP</string>\n    <string name=\"storage_add_storage_smb_server\">Server SMB</string>\n    <string name=\"storage_add_storage_webdav_server\">Server WebDAV</string>\n    <string name=\"storage_edit_device_storage_title\">Modifica archiviazione dispositivo</string>\n    <string name=\"storage_edit_device_storage_name\">Nome</string>\n    <string name=\"storage_edit_device_storage_path\">Percorso</string>\n    <string name=\"storage_add_external_storage_shortcut_title\">Aggiungi collegamento all\\'archiviazione esterna</string>\n    <string name=\"storage_edit_external_storage_shortcut_title\">Modifica collegamento all\\'archiviazione esterna</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id\">Volume di archiviazione</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id_error_empty\">Inserisci un volume di archiviazione</string>\n    <string name=\"storage_edit_external_storage_shortcut_path\">Percorso</string>\n    <string name=\"storage_add_document_tree_title\">Aggiungi archiviazione esterna</string>\n    <string name=\"storage_edit_document_tree_title\">Modifica archiviazione esterna</string>\n    <string name=\"storage_edit_document_tree_name\">Nome</string>\n    <string name=\"storage_edit_document_tree_uri\">URI</string>\n    <string name=\"storage_edit_document_tree_path\">Percorso</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">Modifica server FTP</string>\n    <string name=\"storage_edit_ftp_server_title_add\">Aggiungi server FTP</string>\n    <string name=\"storage_edit_ftp_server_host\">Nome host</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">Inserisci un nome host</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">Nome host non valido</string>\n    <string name=\"storage_edit_ftp_server_port\">Porta</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">Porta non valida</string>\n    <string name=\"storage_edit_ftp_server_path\">Percorso</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">Può essere lasciato vuoto</string>\n    <string name=\"storage_edit_ftp_server_name\">Nome</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">Usa nome host</string>\n    <string name=\"storage_edit_ftp_server_protocol\">Protocollo</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">Autenticazione</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>Password</item>\n        <item>Anonimo</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">Nome utente</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">Inserisci un nome utente</string>\n    <string name=\"storage_edit_ftp_server_password\">Password</string>\n    <string name=\"storage_edit_ftp_server_mode\">Permessi di base</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>Attivo</item>\n        <item>Passivo</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">Codifica</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">Connetti e aggiungi</string>\n    <string name=\"storage_edit_ftp_server_add\">Aggiungi</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">Modifica server SFTP</string>\n    <string name=\"storage_edit_sftp_server_title_add\">Aggiungi server SFTP</string>\n    <string name=\"storage_edit_sftp_server_host\">Nome host</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">Inserisci un nome host</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">Nome host non valido</string>\n    <string name=\"storage_edit_sftp_server_port\">Porta</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">Porta non valida</string>\n    <string name=\"storage_edit_sftp_server_path\">Percorso</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">Può essere lasciato vuoto</string>\n    <string name=\"storage_edit_sftp_server_name\">Nome</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">Usa nome host</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">Autenticazione</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>Password</item>\n        <item>Chiave pubblica</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">Nome utente</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">Inserisci un nome utente</string>\n    <string name=\"storage_edit_sftp_server_password\">Password</string>\n    <string name=\"storage_edit_sftp_server_private_key\">Chiave privata</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">Apri file</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">Inserisci una chiave privata</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">Chiave privata non valida</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">Password chiave privata</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">Può essere lasciata vuota</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">Password chiave privata non valida</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">Connetti e aggiungi</string>\n    <string name=\"storage_edit_sftp_server_add\">Aggiungi</string>\n    <string name=\"storage_add_lan_smb_server_loading\">Ricerca di server SMB…</string>\n    <string name=\"storage_add_lan_smb_server_add\">Aggiungi manualmente</string>\n    <string name=\"storage_edit_smb_server_title_edit\">Modifica server SMB</string>\n    <string name=\"storage_edit_smb_server_title_add\">Aggiungi server SMB</string>\n    <string name=\"storage_edit_smb_server_host\">Nome host</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">Inserisci un nome host</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">Nome host non valido</string>\n    <string name=\"storage_edit_smb_server_port\">Porta</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">Porta non valida</string>\n    <string name=\"storage_edit_smb_server_path\">Percorso</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">Può essere lasciato vuoto</string>\n    <string name=\"storage_edit_smb_server_name\">Nome</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">Usa nome host</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">Autenticazione</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>Password</item>\n        <item>Ospite</item>\n        <item>Anonimo</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">Nome utente</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">Inserisci un nome utente</string>\n    <string name=\"storage_edit_smb_server_password\">Password</string>\n    <string name=\"storage_edit_smb_server_domain\">Dominio</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">Connetti e aggiungi</string>\n    <string name=\"storage_edit_smb_server_add\">Aggiungi</string>\n    <string name=\"storage_edit_webdav_server_title_edit\">Modifica server WebDAV</string>\n    <string name=\"storage_edit_webdav_server_title_add\">Aggiungi server WebDAV</string>\n    <string name=\"storage_edit_webdav_server_host\">Nome host</string>\n    <string name=\"storage_edit_webdav_server_host_error_empty\">Inserisci un nome host</string>\n    <string name=\"storage_edit_webdav_server_host_error_invalid\">Modifica un nome host</string>\n    <string name=\"storage_edit_webdav_server_port\">Porta</string>\n    <string name=\"storage_edit_webdav_server_port_error_invalid\">Porta non valida</string>\n    <string name=\"storage_edit_webdav_server_path\">Percorso</string>\n    <string name=\"storage_edit_webdav_server_path_placeholder\">Può essere lasciato vuoto</string>\n    <string name=\"storage_edit_webdav_server_name\">Nome</string>\n    <string name=\"storage_edit_webdav_server_name_placeholder\">Usa nome host</string>\n    <string name=\"storage_edit_webdav_server_protocol\">Protocollo</string>\n    <string name=\"storage_edit_webdav_server_authentication_type\">Autenticazione</string>\n    <string-array name=\"storage_edit_webdav_server_authentication_type_entries\">\n        <item>Password</item>\n        <item>Token di accesso</item>\n        <item>Nessuno</item>\n    </string-array>\n    <string name=\"storage_edit_webdav_server_username\">Nome utente</string>\n    <string name=\"storage_edit_webdav_server_username_error_empty\">Inserisci un nome utente</string>\n    <string name=\"storage_edit_webdav_server_password\">Password</string>\n    <string name=\"storage_edit_webdav_server_access_token\">Token di accesso</string>\n    <string name=\"storage_edit_webdav_server_access_token_error_empty\">Inserisci un token d\\'accesso</string>\n    <string name=\"storage_edit_webdav_server_connect_and_add\">Connetti e aggiungi</string>\n    <string name=\"storage_edit_webdav_server_add\">Aggiungi</string>\n\n    <string name=\"navigation_storage_subtitle_format\">%1$s liberi su %2$s</string>\n    <string name=\"navigation_add_storage\">Aggiungi archiviazione…</string>\n    <string name=\"navigation_standard_directory_alarms\">Avvisi</string>\n    <string name=\"navigation_standard_directory_dcim\">DCIM</string>\n    <string name=\"navigation_standard_directory_documents\">Documenti</string>\n    <string name=\"navigation_standard_directory_downloads\">Scaricati</string>\n    <string name=\"navigation_standard_directory_movies\">Video</string>\n    <string name=\"navigation_standard_directory_music\">Musica</string>\n    <string name=\"navigation_standard_directory_notifications\">Notifiche</string>\n    <string name=\"navigation_standard_directory_pictures\">Immagini</string>\n    <string name=\"navigation_standard_directory_podcasts\">Podcast</string>\n    <string name=\"navigation_standard_directory_ringtones\">Suonerie</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">Cartella segnalibri</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">Nome</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">Percorso</string>\n    <string name=\"archive_viewer_title\">Visualizzatore archivio</string>\n    <string name=\"image_viewer_title\">Visualizzatore di immagini</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"save_as_title\">Salva come</string>\n    <string name=\"save_as_error\">Errore durante il salvataggio del file</string>\n    <string name=\"save_as_success_format\">\\\"%1$s\\\" è stato salvato in \\\"%2$s\\\"</string>\n    <string name=\"text_editor_title\">Editor di testo</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">Salvato</string>\n    <string name=\"text_editor_reload_message\">Sei sicuro di volere ricaricare? Le modifiche a questo documento non salvate verranno perse.</string>\n    <string name=\"text_editor_encoding\">Codifica</string>\n    <string name=\"text_editor_close_message\">Sei sicuro di volere scartare le modifiche a questo documento non salvate?</string>\n\n    <string name=\"ftp_server_title\">Server FTP</string>\n    <string name=\"ftp_server_state_title\">Stato</string>\n    <string name=\"ftp_server_state_summary_starting\">Avvio…</string>\n    <string name=\"ftp_server_state_summary_running\">In esecuzione</string>\n    <string name=\"ftp_server_state_summary_stopping\">Arresto…</string>\n    <string name=\"ftp_server_state_summary_stopped\">Spento</string>\n    <string name=\"ftp_server_url_title\">URL</string>\n    <string name=\"ftp_server_url_summary_no_local_inet_address\">L\\'indirizzo IP locale è sconosciuto</string>\n    <string name=\"ftp_server_url_menu_copy_url\">Copia URL</string>\n    <string name=\"ftp_server_url_menu_copy_password\">Copia password</string>\n    <string name=\"ftp_server_add_tile_title\">Aggiungi alle impostazioni rapide</string>\n    <string name=\"ftp_server_add_tile_result_added\">“Server FTP” è stato aggiunto alle impostazioni rapide</string>\n    <string name=\"ftp_server_add_tile_result_already_added\">\\\"Server FTP\\\" è già stato aggiunto alle impostazioni rapide</string>\n    <string name=\"ftp_server_add_tile_result_error\">Errore durante l\\'aggiunta di \\\"Server FTP\\\" alle impostazioni rapide</string>\n    <string name=\"ftp_server_configuration_title\">Configurazione</string>\n    <string name=\"ftp_server_anonymous_login_title\">Accesso anonimo</string>\n    <string name=\"ftp_server_username_title\">Nome utente</string>\n    <string name=\"ftp_server_password_title\">Password</string>\n    <string name=\"ftp_server_port_title\">Porta</string>\n    <string name=\"ftp_server_home_directory_title\">Cartella root</string>\n    <string name=\"ftp_server_writable_title\">Consenti scrittura</string>\n    <string name=\"settings_title\">Impostazioni</string>\n    <string name=\"settings_interface_title\">Interfaccia</string>\n    <string name=\"settings_locale_title\">Lingua</string>\n    <string name=\"settings_theme_color_title\">Colore tema</string>\n    <string name=\"settings_theme_color_summary\">Colore che appare più spesso nell\\'app</string>\n    <string name=\"settings_material_design_3_title\">Material Design 3</string>\n    <string name=\"settings_night_mode_title\">Modalità notte</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>Segui il sistema</item>\n        <item>Off</item>\n        <item>On</item>\n        <item>A seconda dell\\'ora</item>\n        <item>A seconda del risparmio energetico</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">Modalità notte nera</string>\n    <string name=\"settings_file_list_animation_title\">Animazione elenco file</string>\n    <string name=\"settings_file_name_ellipsize_title\">Mostra nomi file lunghi</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>Puntini all\\'inizio</item>\n        <item>Puntini in mezzo</item>\n        <item>Puntini alla fine</item>\n        <item>Scorrimento</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">Comportamento</string>\n    <string name=\"settings_default_directory_title\">Cartella predefinita</string>\n    <string name=\"settings_standard_directories_title\">Cartelle standard</string>\n    <string name=\"settings_standard_directories_summary_empty\">Nessuna cartella standard attivata</string>\n    <string name=\"settings_bookmark_directory_screenshots\">Screenshot</string>\n    <string name=\"settings_bookmark_directories_title\">Cartelle segnalibri</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">Nessuna cartella per segnalibri</string>\n    <string name=\"settings_root_strategy_title\">Modalità di accesso root</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"one\">È in corso %1$,d operazione su file e cambiare ora la modalità di accesso root potrebbe causare un errore. Sei sicuro di volere cambiarla ora?</item>\n        <item quantity=\"many\">Sono in corso %1$,d operazioni su file e cambiare ora la modalità di accesso root potrebbe causare un errore. Sei sicuro di volere cambiarla ora?</item>\n        <item quantity=\"other\">Sono in corso %1$,d operazioni su file e cambiare ora la modalità di accesso root potrebbe causare un errore. Sei sicuro di volere cambiarla ora?</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>Solo accesso normale</item>\n        <item>Automatico</item>\n        <item>Solo accesso root</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">Codifica nome file archivio</string>\n    <string name=\"settings_open_apk_default_action_title\">Apri pacchetto Android</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>Installa</item>\n        <item>Vedi contenuti</item>\n        <item>Chiedi cosa fare</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_title\">Mostra miniature per i documenti PDF</string>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">Può rendere l\\'app instabile su versioni vecchie di Android</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">Leggi file remoti per miniatura</string>\n\n    <string name=\"about_title\">Al riguardo</string>\n    <string name=\"about_version_title\">Versione</string>\n    <string name=\"about_github_title\">Vedi su GitHub</string>\n    <string name=\"about_licenses_title\">Licenze</string>\n    <string name=\"about_privacy_policy_title\">Informativa sulla privacy</string>\n    <string name=\"about_author_title\">Autore</string>\n    <string name=\"about_author_name_title\">Hai Zhang</string>\n    <string name=\"about_author_github_title\">Segui su GitHub</string>\n    <string name=\"about_author_twitter_title\">Segui su Twitter</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-iw/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">קובץ</string>\n    <string name=\"file_type_name_apk\">חבילה לאנדרואיד</string>\n    <string name=\"file_type_name_archive\">ארכיון %1$s</string>\n    <string name=\"file_type_name_audio\">קובץ שמע %1$s</string>\n    <string name=\"file_type_name_calendar\">לוח שנה %1$s</string>\n    <string name=\"file_type_name_certificate\">תעודת %1$s</string>\n    <string name=\"file_type_name_code\">מסמך %1$s</string>\n    <string name=\"file_type_name_contact\">כרטיס ביקור אלקטרוני</string>\n    <string name=\"file_type_name_directory\">תיקייה</string>\n    <string name=\"file_type_name_document\">מסמך %1$s</string>\n    <string name=\"file_type_name_ebook\">ספר אלקטרוני %1$s</string>\n    <string name=\"file_type_name_email\">הודעת דוא\\\"ל %1$s</string>\n    <string name=\"file_type_name_font\">גופן %1$s</string>\n    <string name=\"file_type_name_generic\">קובץ %1$s</string>\n    <string name=\"file_type_name_image\">תמונה %1$s</string>\n    <string name=\"file_type_name_pdf\">מסמך PDF</string>\n    <string name=\"file_type_name_presentation\">מצגת %1$s</string>\n    <string name=\"file_type_name_spreadsheet\">גיליון אלקטרוני%1$s</string>\n    <string name=\"file_type_name_text\">מסמך %1$s</string>\n    <string name=\"file_type_name_text_plain\">מסמך טקסט רגיל</string>\n    <string name=\"file_type_name_video\">וידאו %1$s</string>\n    <string name=\"file_type_name_word\">מסמך Word</string>\n    <string name=\"file_type_name_powerpoint\">מצגת PowerPoint</string>\n    <string name=\"file_type_name_excel\">גיליון Excel</string>\n    <string name=\"file_type_name_posix_character_device\">התקן תכונה</string>\n    <string name=\"file_type_name_posix_block_device\">התקן בלוק</string>\n    <string name=\"file_type_name_posix_fifo\">צינור</string>\n    <string name=\"file_type_name_posix_symbolic_link\">קישור</string>\n    <string name=\"file_type_name_posix_socket\">שקע</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">קישור (שבור)</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-iw/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <string name=\"app_name\">Material Files</string>\n\n    <string name=\"close\">סגירה</string>\n    <string name=\"copy\">העתקה</string>\n    <string name=\"cut\">גזירה</string>\n    <string name=\"default_\">ברירת מחדל</string>\n    <string name=\"delete\">מחיקה</string>\n    <string name=\"discard\">השלכה</string>\n    <string name=\"edit\">עריכה</string>\n    <string name=\"empty\">ריק</string>\n    <string name=\"empty_placeholder\">(ריק)</string>\n    <string name=\"error\">שגיאה</string>\n    <string name=\"hide\">הסתרה</string>\n    <string name=\"install\">התקנה</string>\n    <string name=\"keep_editing\">המשך עריכה</string>\n    <string name=\"loading\">טעינה…</string>\n    <string name=\"maybe_later\">אולי אחר כך</string>\n    <string name=\"merge\">מיזוג</string>\n    <string name=\"none\">ללא</string>\n    <string name=\"open_navigation_drawer\">פתיחת חלונית ההזזה לניווט</string>\n    <string name=\"paste\">הדבקה</string>\n    <string name=\"refresh\">רענון</string>\n    <string name=\"reload\">טעינה מחדש</string>\n    <string name=\"remove\">הסרה</string>\n    <string name=\"rename\">שינוי שם</string>\n    <string name=\"replace\">החלפה</string>\n    <string name=\"reset\">איפוס</string>\n    <string name=\"retry\">ניסיון חוזר</string>\n    <string name=\"save\">שמירה</string>\n    <string name=\"search\">חיפוש</string>\n    <string name=\"select\">בחירה</string>\n    <string name=\"select_all\">לבחור הכול</string>\n    <string name=\"share\">שיתוף</string>\n    <string name=\"show\">הצגה</string>\n    <string name=\"skip\">דילוג</string>\n    <string name=\"stop\">עצירה</string>\n    <string name=\"system_default\">ברירת המחדל של המערכת</string>\n    <string name=\"unknown\">לא ידוע</string>\n    <string name=\"view\">הצגה</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">לא נמצא יישום מתאים לפעולה זו</string>\n    <string name=\"open_settings\">פתיחת הגדרות</string>\n    <string name=\"shortcut_created\">נוצר קיצור דרך</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"one\">בייט %1$,d</item>\n        <item quantity=\"two\">%1$,d בייטים</item>\n        <item quantity=\"many\">%1$,d בייטים</item>\n        <item quantity=\"other\">%1$,d בתים</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">היישום זקוק לגישה כדי לנהל את כל הקבצים. יש לאפשר את הגישה בהגדרת המערכת הבאה.</string>\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">היישום זקוק להרשאה כדי לגשת לקבצים. יש ללחוץ על \\\"אפשר\\\" בתיבת הדו שיח הבאה של המערכת.</string>\n    <string name=\"storage_permission_permanently_denied_message\">היישום זקוק להרשאה כדי לגשת לקבצים. יש להעניק הרשאת \\\"אחסון\\\" בהגדרות המערכת.</string>\n    <!-- The allow button is in sentence case now. -->\n    <string name=\"notification_permission_rationale_message\">היישומון צריך הרשאה כדי לפרסם התראות על פעולות קבצים. נא ללחוץ על „הרשאה” בחלונית המערכת שכבר תופיע.</string>\n    <string name=\"notification_permission_permanently_denied_message\">היישומון צריך הרשאה כדי לפרסם התראות על פעולות קבצים. נא להעניק את ההרשאה „התראות” בהגדרות המערכת.</string>\n\n    <string name=\"notification_channel_background_activity_start_name\">פעולות ברקע</string>\n    <string name=\"notification_channel_background_activity_start_description\">ביצוע פעולות כשהיישום פועל ברקע</string>\n    <string name=\"notification_channel_file_job_name\">פעולות קובץ</string>\n    <string name=\"notification_channel_file_job_description\">הצגה ושליטה בפעולות הקבצים</string>\n    <string name=\"notification_channel_ftp_server_name\">שרת FTP</string>\n    <string name=\"notification_channel_ftp_server_description\">הצגה ושליטה בשרת FTP</string>\n\n    <string name=\"file_action_archive_password_title\">צריך סיסמה</string>\n    <string name=\"file_action_archive_password_message_format\">„%1$s” מוגן בסיסמה</string>\n    <string name=\"file_action_archive_password_error_empty\">הסיסמה לא יכולה להיות ריקה</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"one\">מתכונן לדחיסת קובץ אחד (%2$s)</item>\n        <item quantity=\"two\">מתכונן לדחיסת %1$,d קבצים (%2$s)</item>\n        <item quantity=\"many\">מתכונן לדחיסת %1$,d קבצים (%2$s)</item>\n        <item quantity=\"other\">מתכונן לדחיסת %1$,d קבצים (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">„%1$s” נדחס לתוך „%2$s”</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"one\">דוחס קובץ אחד לתוך “%2$s”</item>\n        <item quantity=\"two\">דוחס %1$,d קבצים לתוך “%2$s”</item>\n        <item quantity=\"many\">דוחס %1$,d קבצים לתוך “%2$s”</item>\n        <item quantity=\"other\">דוחס %1$,d קבצים לתוך “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"one\">מתכונן להעתקת קובץ אחד (%2$s)</item>\n        <item quantity=\"two\">מתכונן להעתקת %1$,d קבצים (%2$s)</item>\n        <item quantity=\"many\">מתכונן להעתקת %1$,d קבצים (%2$s)</item>\n        <item quantity=\"other\">מתכונן להעתקת %1$,d קבצים (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">„%1$s” מועתק אל „%2$s”</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"one\">מעתיק קובץ אחד אל “%2$s”</item>\n        <item quantity=\"two\">מעתיק %1$,d קבצים אל “%2$s”</item>\n        <item quantity=\"many\">מעתיק %1$,d קבצים אל “%2$s”</item>\n        <item quantity=\"other\">מעתיק %1$,d קבצים אל “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"one\">מתכונן לחילוץ קובץ אחד (%2$s)</item>\n        <item quantity=\"two\">מתכונן לחילוץ %1$,d קבצים (%2$s)</item>\n        <item quantity=\"many\">מתכונן לחילוץ %1$,d קבצים (%2$s)</item>\n        <item quantity=\"other\">מתכונן לחילוץ %1$,d קבצים (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">מחלץ את “%1$s” אל “%2$s”</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"one\">מחלץ קובץ אחד אל “%2$s”</item>\n        <item quantity=\"two\">מחלץ %1$,d קבצים אל “%2$s”</item>\n        <item quantity=\"many\">מחלץ %1$,d קבצים אל “%2$s”</item>\n        <item quantity=\"other\">מחלץ %1$,d קבצים אל “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"one\">מתכונן להעברת קובץ אחד (%2$s)</item>\n        <item quantity=\"two\">מתכונן להעברת %1$,d קבצים (%2$s)</item>\n        <item quantity=\"many\">מתכונן להעברת %1$,d קבצים (%2$s)</item>\n        <item quantity=\"other\">מתכונן להעברת %1$,d קבצים (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">מעביר את “%1$s” אל “%2$s”</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"one\">מעביר קובץ אחד אל “%2$s”</item>\n        <item quantity=\"two\">מעביר %1$,d קבצים אל “%2$s”</item>\n        <item quantity=\"many\">מעביר %1$,d קבצים אל “%2$s”</item>\n        <item quantity=\"other\">מעביר %1$,d קבצים אל “%2$s”</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"one\">מתכונן למחיקת קובץ אחד</item>\n        <item quantity=\"two\">מתכונן למחיקת %1$,d קבצים</item>\n        <item quantity=\"many\">מתכונן למחיקת %1$,d קבצים</item>\n        <item quantity=\"other\">מתכונן למחיקת %1$,d קבצים</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">„%1$s” נמחק</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"one\">קובץ נמחק</item>\n        <item quantity=\"two\">%1$,d קבצים נמחקים</item>\n        <item quantity=\"many\">%1$,d קבצים נמחקים</item>\n        <item quantity=\"other\">%1$,d קבצים נמחקים</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"one\">מתכונן להחליף בעלים לקובץ אחד</item>\n        <item quantity=\"two\">מתכונן להחליף בעלים עבור %1$,d קבצים</item>\n        <item quantity=\"many\">מתכונן להחליף בעלים עבור %1$,d קבצים</item>\n        <item quantity=\"other\">מתכונן להחליף בעלים עבור %1$,d קבצים</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">מחליף בעלים עבור  “%1$s”</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"one\">מחליף בעלים עבור קובץ אחד</item>\n        <item quantity=\"two\">מחליף בעלים עבור %1$,d קבצים</item>\n        <item quantity=\"many\">מחליף בעלים עבור %1$,d קבצים</item>\n        <item quantity=\"other\">מחליף בעלים עבור %1$,d קבצים</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"one\">מתכונן לחלפת קבוצה עבור קובץ אחד</item>\n        <item quantity=\"two\">מתכונן לחלפת קבוצה עבור %1$,d קבצים</item>\n        <item quantity=\"many\">מתכונן לחלפת קבוצה עבור %1$,d קבצים</item>\n        <item quantity=\"other\">מתכונן לחלפת קבוצה עבור %1$,d קבצים</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">מחליף קבוצה עבור “%1$s”</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"one\">מחליף קבוצה עבור קובץ אחד</item>\n        <item quantity=\"two\">מחליף קבוצה עבור %1$,d קבצים</item>\n        <item quantity=\"many\">מחליף קבוצה עבור %1$,d קבצים</item>\n        <item quantity=\"other\">מחליף קבוצה עבור %1$,d קבצים</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"one\">מתכון להחלפת מצב עבור קובץ אחד</item>\n        <item quantity=\"two\">מתכון להחלפת מצב עבור %1$,d קבצים</item>\n        <item quantity=\"many\">מתכון להחלפת מצב עבור %1$,d קבצים</item>\n        <item quantity=\"other\">מתכון להחלפת מצב עבור %1$,d קבצים</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">מחליף מצב עבור “%1$s”</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"one\">מחליף מצב עבור קובץ אחד</item>\n        <item quantity=\"two\">מחליף מצב עבור %1$,d קבצים</item>\n        <item quantity=\"many\">מחליף מצב עבור %1$,d קבצים</item>\n        <item quantity=\"other\">מחליף מצב עבור %1$,d קבצים</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">מתכונן לשנות את ההקשר של SELinux עבור קובץ אחד</item>\n        <item quantity=\"two\">מתכונן לשנות את ההקשר של SELinux עבור %1$,d קבצים</item>\n        <item quantity=\"many\">מתכונן לשנות את ההקשר של SELinux עבור %1$,d קבצים</item>\n        <item quantity=\"other\">מתכונן לשנות את ההקשר של SELinux עבור %1$,d קבצים</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">משנה את ההקשר של SELinux עבור “%1$s”</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">משנה את ההקשר של SELinux עבור קובץ אחד</item>\n        <item quantity=\"two\">משנה את ההקשר של SELinux עבור %1$,d קבצים</item>\n        <item quantity=\"many\">משנה את ההקשר של SELinux עבור %1$,d קבצים</item>\n        <item quantity=\"other\">משנה את ההקשר של SELinux עבור %1$,d קבצים</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">מתכונן לשחזר את ההקשר של SELinux עבור קובץ אחד</item>\n        <item quantity=\"two\">מתכונן לשחזר את ההקשר של SELinux עבור %1$,d קבצים</item>\n        <item quantity=\"many\">מתכונן לשחזר את ההקשר של SELinux עבור %1$,d קבצים</item>\n        <item quantity=\"other\">מתכונן לשחזר את ההקשר של SELinux עבור %1$,d קבצים</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">משחזר את ההקשר של SELinux עבור “%1$s”</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">משחזר את ההקשר של SELinux עבור קובץ אחד</item>\n        <item quantity=\"two\">משחזר את ההקשר של SELinux עבור %1$,d קבצים</item>\n        <item quantity=\"many\">משחזר את ההקשר של SELinux עבור %1$,d קבצים</item>\n        <item quantity=\"other\">משחזר את ההקשר של SELinux עבור %1$,d קבצים</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">כותב “%1$s”</string>\n    <string name=\"file_job_create_error_title\">שגיאה במהלך היצירה</string>\n    <string name=\"file_job_create_error_message_format\">שגיאה בעת יצירת “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">לא ניתן להעתיק תיקייה לעצמה</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">לא ניתן לחלץ תיקייה לתוך עצמה</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">לא ניתן להעביר תיקייה לתוך עצמה</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">תיקיית היעד נמצאת בתוך תיקיית המקור.</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">לא ניתן להעתיק קובץ לעצמו</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">לא ניתן לחלץ קובץ לתוך עצמו</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">לא ניתן להעביר קובץ לתוך עצמו</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">קובץ המקור יוחלף על ידי היעד.</string>\n    <string name=\"file_job_replace_title_format\">להחליף את הקובץ „%1$s”?</string>\n    <string name=\"file_job_replace_message_format\">קובץ אחר באותו שם כבר קיים ב-“%1$s”.\\nהחלפתו תדרוס את התוכן שלו.</string>\n    <string name=\"file_job_replace_target_name\">הקובץ המקורי</string>\n    <string name=\"file_job_replace_source_name\">החלפה עם</string>\n    <string name=\"file_job_merge_title_format\">למזג את תיקיית “%1$s”?</string>\n    <string name=\"file_job_merge_copy_message_format\">מיזוג יבקש אישור לפני החלפת קבצים בתיקייה שמתנגשים עם הקבצים המועתקים.</string>\n    <string name=\"file_job_merge_extract_message_format\">מיזוג יבקש אישור לפני החלפת קבצים בתיקייה שמתנגשים עם הקבצים המחולצים.</string>\n    <string name=\"file_job_merge_move_message_format\">מיזוג יבקש אישור לפני החלפת קבצים בתיקייה שמתנגשים עם הקבצים המועברים.</string>\n    <string name=\"file_job_merge_target_name\">התיקייה המקורית</string>\n    <string name=\"file_job_merge_source_name\">למזג עם</string>\n    <string name=\"file_job_conflict_show_name\">בחירת שם חדש ליעד</string>\n    <string name=\"file_job_conflict_name_hint\">שם חדש</string>\n    <string name=\"file_job_archive_error_title_format\">שגיאה במהלך דחיסת \\\"%1$s\\\"</string>\n    <string name=\"file_job_archive_error_message_format\">שגיאה בעת דחיסת הקובץ אל “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">שגיאה במהלך העתקת \\\"%1$s\\\"</string>\n    <string name=\"file_job_copy_error_message_format\">שגיאה בעת העתקת הקובץ אל “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">שגיאה במהלך חילוץ \\\"%1$s\\\"</string>\n    <string name=\"file_job_extract_error_message_format\">שגיאה בעת חילוץ הקובץ לתוך “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">שגיאה במהלך העברת \\\"%1$s\\\"</string>\n    <string name=\"file_job_move_error_message_format\">שגיאה בעת העברת הקובץ אל “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">שגיאה במהלך המחיקה</string>\n    <string name=\"file_job_delete_error_message_format\">שגיאה בעת מחיקת “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">שגיאה במהלך שינוי השם של \\\"%1$s\\\"</string>\n    <string name=\"file_job_rename_error_message_format\">שגיאה בעת שינוי שם הקובץ לשם “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">שגיאה במהלך החלפת בעלים עבור “%1$s”</string>\n    <string name=\"file_job_set_owner_error_message_format\">שגיאה בעת החלפת בעלים אל “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">שגיאה במהלך החלפת קבוצה עבור “%1$s”</string>\n    <string name=\"file_job_set_group_error_message_format\">שגיאה בעת החלפת קבוצה אל “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">שגיאה במהלך החלפת מצב עבור “%1$s”</string>\n    <string name=\"file_job_set_mode_error_message_format\">שגיאה בעת החלפת מצב אל “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">שגיאה במהלך החלפת הקשר SELinux עבור “%1$s”</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">שגיאה בעת החלפת הקשר SELinux אל “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">שגיאה במהלך שיחזור הקשר SELinux</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">שגיאה בעת שיחזור הקשר SELinux עבור “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">שגיאה במהלך הכתיבה</string>\n    <string name=\"file_job_write_error_message_format\">שגיאה בעת כתיבת “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">אירעה שגיאה בקבלת מידע אודות “%1$s”.</string>\n    <string name=\"file_job_remount_format\">הצבה מחדש של “%1$s” כקריאה-כתיבה</string>\n    <string name=\"file_job_remount_loading_format\">מציב מחדשאת “%1$s” כקריאה-כתיבה…</string>\n    <string name=\"file_job_remount_success_format\">“%1$s” הוצב מחדש כקריאה-כתיבה</string>\n    <string name=\"file_job_action_all\">החלת פעולה זו על כל הקבצים</string>\n\n    <string name=\"file_open_apk_message\">קובץ זה הוא חבילת אנדרואיד. האם ברצונך להתקין אותו, או להציג את תוכנו?</string>\n    <string name=\"file_open_as_title\">פתיחה בתור…</string>\n    <string name=\"file_open_as_title_format\">פתיחת \\\"%1$s\\\" בתור</string>\n    <string name=\"file_open_as_type_text\">טקסט</string>\n    <string name=\"file_open_as_type_image\">תמונה</string>\n    <string name=\"file_open_as_type_audio\">שמע</string>\n    <string name=\"file_open_as_type_video\">וידאו</string>\n    <string name=\"file_open_as_type_directory\">תיקייה</string>\n    <string name=\"file_open_as_type_any\">אחר</string>\n    <string name=\"file_install_apk_from_background_title_format\">“%1$s” מוכן להתקנה</string>\n    <string name=\"file_install_apk_from_background_text\">הקשה להתקנה</string>\n    <string name=\"file_open_from_background_title_format\">“%1$s” מוכן לפתיחה</string>\n    <string name=\"file_open_from_background_text\">הקשה לפתיחה</string>\n    <string name=\"file_name_error_empty\">שם הקובץ אינו יכול להיות ריק</string>\n    <string name=\"file_name_error_invalid\">שם קובץ לא תקין</string>\n    <string name=\"file_name_error_already_exists\">כבר קיים קובץ בשם זה</string>\n    <string name=\"file_replace_message_format\">להחליף את „%1$s”?</string>\n    <string name=\"file_delete_message_file_format\">למחוק את \\\"%1$s\\\"?</string>\n    <string name=\"file_delete_message_directory_format\">למחוק את התיקייה \\\"%1$s\\\" ואת תכולתה?</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"one\">למחוק קובץ אחד?</item>\n        <item quantity=\"two\">למחוק %1$,d קבצים?</item>\n        <item quantity=\"many\">למחוק %1$,d קבצים?</item>\n        <item quantity=\"other\">למחוק %1$,d קבצים?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"one\">למחוק את התיקייה ותכולתה?</item>\n        <item quantity=\"two\">למחוק %1$,d תיקיות ואת תכולתן?</item>\n        <item quantity=\"many\">למחוק %1$,d תיקיות ואת תכולתן?</item>\n        <item quantity=\"other\">למחוק %1$,d תיקיות ואת תכולתן?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"one\">למחוק פריט אחד?</item>\n        <item quantity=\"two\">למחוק %1$,d פריטים?</item>\n        <item quantity=\"many\">למחוק %1$,d פריטים?</item>\n        <item quantity=\"other\">למחוק %1$,d פריטים?</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">יצירת ארכיון</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_create_archive_password\">סיסמה (רשות)</string>\n    <string name=\"file_add_bookmark_success\">נוספה סימנייה</string>\n    <string name=\"file_create_file_title\">קובץ חדש</string>\n    <string name=\"file_create_directory_title\">תיקיה חדשה</string>\n\n    <string name=\"file_list_title\">קבצים</string>\n    <plurals name=\"file_list_title_open_file\">\n        <item quantity=\"one\">בחירת קובץ</item>\n        <item quantity=\"two\">בחירת קבצים</item>\n        <item quantity=\"many\">בחירת קבצים</item>\n        <item quantity=\"other\">בחירת קבצים</item>\n    </plurals>\n    <string name=\"file_list_title_create_file\">שמירת קובץ</string>\n    <plurals name=\"file_list_title_open_directory\">\n        <item quantity=\"one\">בחירת תיקייה</item>\n        <item quantity=\"two\">בחירת תיקיות</item>\n        <item quantity=\"many\">בחירת תיקיות</item>\n        <item quantity=\"other\">בחירת תיקיות</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"one\">תיקיה %1$,d</item>\n        <item quantity=\"two\">%1$,d תיקיות</item>\n        <item quantity=\"many\">%1$,d תיקיות</item>\n        <item quantity=\"other\">%1$,d תיקיות</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"one\">קובץ %1$,d</item>\n        <item quantity=\"two\">%1$,d קבצים</item>\n        <item quantity=\"many\">%1$,d קבצים</item>\n        <item quantity=\"other\">%1$,d קבצים</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">,\\u0020</string>\n    <string name=\"file_list_path_error_empty\">נתיב אינו יכול להיות ריק</string>\n    <string name=\"file_list_path_error_invalid\">נתיב לא חוקי</string>\n    <string name=\"file_list_action_view_sort\">הצגה ומיון</string>\n    <string name=\"file_list_action_view_list\">רשימה</string>\n    <string name=\"file_list_action_view_grid\">טבלה</string>\n    <string name=\"file_list_action_sort_by_name\">שם</string>\n    <string name=\"file_list_action_sort_by_type\">סוג</string>\n    <string name=\"file_list_action_sort_by_size\">גודל</string>\n    <string name=\"file_list_action_sort_by_last_modified\">השתנה לאחרונה</string>\n    <string name=\"file_list_action_sort_order_ascending\">עולה</string>\n    <string name=\"file_list_action_sort_directories_first\">תיקיות תחילה</string>\n    <string name=\"file_list_action_view_sort_path_specific\">רק לתיקייה הזאת</string>\n    <string name=\"file_list_action_new_task\">חלון חדש</string>\n    <string name=\"file_list_action_navigate_up\">עלה</string>\n    <string name=\"file_list_action_navigate_to\">מעבר אל</string>\n    <string name=\"file_list_action_show_hidden_files\">הצגת קבצים מוסתרים</string>\n    <string name=\"file_list_action_copy_path\">העתקת הנתיב</string>\n    <string name=\"file_list_action_open_in_terminal\">פתיחה במסוף</string>\n    <string name=\"file_list_action_add_bookmark\">הוספת סימנייה</string>\n    <string name=\"file_list_action_create_shortcut\">יצירת קיצור דרך</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">פתיחה בחלון חדש</string>\n    <string name=\"file_list_select_title_format\">%1$,d</string>\n    <string name=\"file_list_create_file_name_hint\">שם קובץ</string>\n    <string name=\"file_list_open_current_directory_format\">בחירת „%1$s”</string>\n    <string name=\"file_list_paste_move_title_format\">מעביר את %1$,d</string>\n    <string name=\"file_list_paste_copy_title_format\">מעתיק את %1$,d</string>\n    <string name=\"file_list_paste_extract_title_format\">מחלץ את %1$,d</string>\n    <string name=\"file_list_paste_action_extract_here\">חילוץ לכאן</string>\n    <string name=\"file_list_empty\">אין קבצים</string>\n    <string name=\"file_list_action_create_file\">קובץ</string>\n    <string name=\"file_list_action_create_directory\">תיקייה</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">פתיחה באמצעות</string>\n    <string name=\"file_item_action_extract\">חילוץ</string>\n    <string name=\"file_item_action_archive\">דחיסה</string>\n    <string name=\"file_item_action_properties\">מאפיינים</string>\n\n    <string name=\"file_properties_title_format\">מאפייני \\\"%1$s\\\"</string>\n    <string name=\"file_properties_basic\">בסיסי</string>\n    <string name=\"file_properties_basic_name\">שם</string>\n    <string name=\"file_properties_basic_type\">סוג</string>\n    <string name=\"file_properties_basic_type_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">קישור אל %1$s (%2$s)</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">יעד קישור</string>\n    <string name=\"file_properties_basic_size\">גודל</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_contents\">תוכן</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"one\">פריט אחד, בסך הכל %2$s</item>\n        <item quantity=\"two\">%1$,d פריטים, בסך הכל %2$s</item>\n        <item quantity=\"many\">%1$,d פריטים, בסך הכל %2$s</item>\n        <item quantity=\"other\">%1$,d פריטים, בסך הכל %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">השתנה לאחרונה</string>\n    <string name=\"file_properties_basic_parent_directory\">תיקיית אב</string>\n    <string name=\"file_properties_basic_archive_file\">קובץ ארכיון</string>\n    <string name=\"file_properties_basic_archive_entry\">ארכיון ריק</string>\n    <string name=\"file_properties_basic_free_space\">שטח פנוי</string>\n    <string name=\"file_properties_permission\">הרשאות</string>\n    <string name=\"file_properties_permission_owner\">בעלים</string>\n    <string name=\"file_properties_permission_group\">קבוצה</string>\n    <string name=\"file_properties_permission_principal_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_permission_set_owner_title\">החלפת בעלות</string>\n    <string name=\"file_properties_permission_set_group_title\">החלפת קבוצה</string>\n    <string name=\"file_properties_permission_set_principal_filter_hint\">נא למלא שם או מזהה</string>\n    <string name=\"file_properties_permission_set_principal_system\">מערכת</string>\n    <string name=\"file_properties_permission_mode\">מצב</string>\n    <string name=\"file_properties_permission_mode_format\">%1$s (%2$04o)</string>\n    <string name=\"file_properties_permission_set_mode_title\">החלפת מצב</string>\n    <string name=\"file_properties_permission_set_mode_others\">אחרים</string>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_file\">\n        <item>קריאה</item>\n        <item>כתיבה</item>\n        <item>הרצה</item>\n    </string-array>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_directory\">\n        <item>קריאה</item>\n        <item>כתיבה</item>\n        <item>חיפוש</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_special\">מיוחד</string>\n    <string-array name=\"file_properties_permission_set_mode_special_mode_bits\">\n        <item>הגדרת מזהה משתמש</item>\n        <item>הגדרת מזהה קבוצה</item>\n        <item>סיבית דביקה</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_uppercase_x\">לא להוסיף „הרצה” לקבצים שבפנים</string>\n    <string name=\"file_properties_permission_selinux_context\">הקשר SELinux</string>\n    <string name=\"file_properties_permission_set_selinux_context_title\">החלפת הקשר SELinux</string>\n    <string name=\"file_properties_permission_set_selinux_context_restore\">שחזור</string>\n    <string name=\"file_properties_permission_recursive\">החלה על הקבצים שבפנים</string>\n    <string name=\"file_properties_image\">תמונה</string>\n    <string name=\"file_properties_media_dimensions\">ממדים</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">נלקח על עצמו</string>\n    <string name=\"file_properties_media_coordinates\">קואורדינטות</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f, %2$.3f</string>\n    <string name=\"file_properties_media_address\">מיקום</string>\n    <string name=\"file_properties_image_gps_altitude\">גובה</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f m</string>\n    <string name=\"file_properties_image_equipment\">מצלמה</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">צוהר</string>\n    <string name=\"file_properties_image_shutter_speed\">מהירות תריס</string>\n    <string name=\"file_properties_image_focal_length\">אורך מיקוד</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f mm</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">מקבילה ל-ISO</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">תוכנה</string>\n    <string name=\"file_properties_image_description\">תיאור</string>\n    <string name=\"file_properties_image_artist\">יוצר</string>\n    <string name=\"file_properties_image_copyright\">זכויות יוצרים</string>\n    <string name=\"file_properties_audio\">שמע</string>\n    <string name=\"file_properties_media_title\">כותרת</string>\n    <string name=\"file_properties_audio_artist\">אמן</string>\n    <string name=\"file_properties_audio_album\">אלבום</string>\n    <string name=\"file_properties_audio_album_artist\">אמן האלבום</string>\n    <string name=\"file_properties_audio_composer\">מלחין</string>\n    <string name=\"file_properties_audio_disc_number\">תקליט</string>\n    <string name=\"file_properties_audio_track_number\">רצועה</string>\n    <string name=\"file_properties_audio_year\">שנה</string>\n    <string name=\"file_properties_audio_genre\">סוגה</string>\n    <string name=\"file_properties_media_duration\">משך זמן</string>\n    <string name=\"file_properties_media_bit_rate\">קצב סיביות</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d kbps</string>\n    <string name=\"file_properties_audio_sample_rate\">קצב דגימה</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Hz</string>\n    <string name=\"file_properties_video\">וידאו</string>\n    <string name=\"file_properties_apk\">APK</string>\n    <string name=\"file_properties_apk_label\">שם</string>\n    <string name=\"file_properties_apk_package_name\">שם החבילה</string>\n    <string name=\"file_properties_apk_version\">גירסה</string>\n    <string name=\"file_properties_apk_version_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_apk_min_sdk_version\">גרסת מערכת מינימלית</string>\n    <string name=\"file_properties_apk_target_sdk_version\">גרסת מערכת היעד</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s (%2$s, %3$d)</string>\n    <string name=\"file_properties_apk_requested_permissions\">הרשאות</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">לא התבקשו הרשאות</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"one\">הרשאה אחת התבקשה</item>\n        <item quantity=\"two\">%1$,d הרשאות התבקשו</item>\n        <item quantity=\"many\">%1$,d הרשאות התבקשו</item>\n        <item quantity=\"other\">%1$,d הרשאות התבקשו</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">חתימות</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">אין חתימות תקפות</string>\n    <string name=\"file_properties_apk_past_signature_digests\">חתימות ישנות</string>\n    <string name=\"file_properties_checksum\">סיכומי ביקורת</string>\n    <string name=\"file_properties_checksum_compare\">השוואה</string>\n    <string name=\"file_properties_checksum_compare_placeholder\">כל סיכום ביקורת שהוא</string>\n    <string name=\"file_properties_checksum_compare_match_format\">תואם לסיכום הביקורת %1$s</string>\n    <string name=\"file_properties_checksum_compare_prefix_match_format\">תואם לקידומת של סיכום הביקורת %1$s</string>\n    <string name=\"file_properties_checksum_compare_no_match\">לא נמצא סיכום ביקורת תואם</string>\n    <string name=\"storage_file_system_root_title\">שורש</string>\n    <string name=\"storage_list_title\">אחסון</string>\n    <string name=\"storage_list_empty\">ללא אחסון</string>\n    <string name=\"storage_add_storage_title\">הוספת אחסון</string>\n    <string name=\"storage_add_storage_document_tree\">אחסון חיצוני</string>\n    <string name=\"storage_add_storage_ftp_server\">שרת FTP</string>\n    <string name=\"storage_add_storage_sftp_server\">שרת SFTP</string>\n    <string name=\"storage_add_storage_smb_server\">שרת SMB</string>\n    <string name=\"storage_add_storage_webdav_server\">שרת WebDAV</string>\n    <string name=\"storage_edit_device_storage_title\">עריכת אחסון ההתקן</string>\n    <string name=\"storage_edit_device_storage_name\">שם</string>\n    <string name=\"storage_edit_device_storage_path\">נתיב</string>\n    <string name=\"storage_add_external_storage_shortcut_title\">הוספת קיצור לאחסון חיצוני</string>\n    <string name=\"storage_edit_external_storage_shortcut_title\">עריכת קיצור לאחסון חיצוני</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id\">כרך אחסון</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id_error_empty\">נא למלא כרך אחסון</string>\n    <string name=\"storage_edit_external_storage_shortcut_path\">נתיב</string>\n    <string name=\"storage_add_document_tree_title\">הוספת אחסון חיצוני</string>\n    <string name=\"storage_edit_document_tree_title\">עריכת אחסון חיצוני</string>\n    <string name=\"storage_edit_document_tree_name\">שם</string>\n    <string name=\"storage_edit_document_tree_uri\">כתובת</string>\n    <string name=\"storage_edit_document_tree_path\">נתיב</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">עריכת שרת FTP</string>\n    <string name=\"storage_edit_ftp_server_title_add\">הוספת שרת FTP</string>\n    <string name=\"storage_edit_ftp_server_host\">שם מארח</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">הזנת שם מארח</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">שם מארח לא תקין</string>\n    <string name=\"storage_edit_ftp_server_port\">פתחה</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">פתחה אינה תקפה</string>\n    <string name=\"storage_edit_ftp_server_path\">נתיב</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">ניתן להשאיר ריק</string>\n    <string name=\"storage_edit_ftp_server_name\">שם</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">השתמש בשם מארח</string>\n    <string name=\"storage_edit_ftp_server_protocol\">פרוטוקול</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">אימות</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>סיסמה</item>\n        <item>אנונימי</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">שם משתמש</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">הזנת שם משתמש</string>\n    <string name=\"storage_edit_ftp_server_password\">סיסמה</string>\n    <string name=\"storage_edit_ftp_server_mode\">מצב</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>פעיל</item>\n        <item>פסיבי</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">הצפנה</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">התחברות והוספה</string>\n    <string name=\"storage_edit_ftp_server_add\">הוספה</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">עריכת שרת SFTP</string>\n    <string name=\"storage_edit_sftp_server_title_add\">הוספת שרת SFTP</string>\n    <string name=\"storage_edit_sftp_server_host\">שם מארח</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">הזנת שם מארח</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">שם מארח לא תקין</string>\n    <string name=\"storage_edit_sftp_server_port\">פתחה</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">פתחה אינה תקפה</string>\n    <string name=\"storage_edit_sftp_server_path\">נתיב</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">ניתן להשאיר ריק</string>\n    <string name=\"storage_edit_sftp_server_name\">שם</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">השתמש בשם מארח</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">אימות</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>סיסמה</item>\n        <item>מפתח ציבורי</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">שם משתמש</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">הזנת שם משתמש</string>\n    <string name=\"storage_edit_sftp_server_password\">סיסמה</string>\n    <string name=\"storage_edit_sftp_server_private_key\">מפתח פרטי</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">פתיחת קובץ</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">הזנת מפתח פרטי</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">מפתח פרטי אינו תקף</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">סיסמת מפתח פרטי</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">ניתן להשאיר ריק</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">סיסמת מפתח פרטי לא חוקית</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">התחברות והוספה</string>\n    <string name=\"storage_edit_sftp_server_add\">הוספה</string>\n    <string name=\"storage_add_lan_smb_server_loading\">מחפש שרת SMB…</string>\n    <string name=\"storage_add_lan_smb_server_add\">הוספה ידנית</string>\n    <string name=\"storage_edit_smb_server_title_edit\">עריכת שרת SMB</string>\n    <string name=\"storage_edit_smb_server_title_add\">הוספת שרת SMB</string>\n    <string name=\"storage_edit_smb_server_host\">שם מארח</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">הזנת שם מארח</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">שם מארח לא תקין</string>\n    <string name=\"storage_edit_smb_server_port\">פתחה</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">פתחה אינה תקפה</string>\n    <string name=\"storage_edit_smb_server_path\">נתיב</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">ניתן להשאיר ריק</string>\n    <string name=\"storage_edit_smb_server_name\">שם</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">השתמש בשם מארח</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">אימות</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>סיסמה</item>\n        <item>אורח</item>\n        <item>בעילום שם</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">שם משתמש</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">הזנת שם משתמש</string>\n    <string name=\"storage_edit_smb_server_password\">סיסמה</string>\n    <string name=\"storage_edit_smb_server_domain\">שם תחום</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">התחברות והוספה</string>\n    <string name=\"storage_edit_smb_server_add\">הוספה</string>\n    <string name=\"storage_edit_webdav_server_title_edit\">עריכת שרת WebDAV</string>\n    <string name=\"storage_edit_webdav_server_title_add\">הוספת שרת WebDAV</string>\n    <string name=\"storage_edit_webdav_server_host\">שם מארח</string>\n    <string name=\"storage_edit_webdav_server_host_error_empty\">נא למלא שם מארח</string>\n    <string name=\"storage_edit_webdav_server_host_error_invalid\">שם מארח שגוי</string>\n    <string name=\"storage_edit_webdav_server_port\">פתחה</string>\n    <string name=\"storage_edit_webdav_server_port_error_invalid\">פתחה שגויה</string>\n    <string name=\"storage_edit_webdav_server_path\">נתיב</string>\n    <string name=\"storage_edit_webdav_server_path_placeholder\">אפשר להשאיר ריק</string>\n    <string name=\"storage_edit_webdav_server_name\">שם</string>\n    <string name=\"storage_edit_webdav_server_name_placeholder\">להשתמש בשם המארח</string>\n    <string name=\"storage_edit_webdav_server_protocol\">פרוטוקול</string>\n    <string name=\"storage_edit_webdav_server_authentication_type\">אימות</string>\n    <string-array name=\"storage_edit_webdav_server_authentication_type_entries\">\n        <item>סיסמה</item>\n        <item>אסימון גישה</item>\n        <item>אין</item>\n    </string-array>\n    <string name=\"storage_edit_webdav_server_username\">שם משתמש</string>\n    <string name=\"storage_edit_webdav_server_username_error_empty\">נא למלא שם משתמש</string>\n    <string name=\"storage_edit_webdav_server_password\">סיסמה</string>\n    <string name=\"storage_edit_webdav_server_access_token\">אסימון גישה</string>\n    <string name=\"storage_edit_webdav_server_access_token_error_empty\">נא לספק אסימון גישה</string>\n    <string name=\"storage_edit_webdav_server_connect_and_add\">התחברות והוספה</string>\n    <string name=\"storage_edit_webdav_server_add\">הוספה</string>\n\n    <string name=\"navigation_storage_subtitle_format\">%1$s פנוי מתוך %2$s</string>\n    <string name=\"navigation_add_storage\">הוספת אחסון…</string>\n    <string name=\"navigation_standard_directory_alarms\">אזעקה</string>\n    <string name=\"navigation_standard_directory_dcim\">DCIM</string>\n    <string name=\"navigation_standard_directory_documents\">מסמכים</string>\n    <string name=\"navigation_standard_directory_downloads\">הורדות</string>\n    <string name=\"navigation_standard_directory_movies\">סרטים</string>\n    <string name=\"navigation_standard_directory_music\">מוזיקה</string>\n    <string name=\"navigation_standard_directory_notifications\">התראות</string>\n    <string name=\"navigation_standard_directory_pictures\">תמונות</string>\n    <string name=\"navigation_standard_directory_podcasts\">הסכתים</string>\n    <string name=\"navigation_standard_directory_ringtones\">צלילים</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">תיקיית סימניות</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">שם</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">נתיב</string>\n    <string name=\"archive_viewer_title\">מציג ארכיונים</string>\n    <string name=\"image_viewer_title\">מציג התמונות</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"save_as_title\">שמירה בשם</string>\n    <string name=\"save_as_error\">שגיאה בשמירת הקובץ</string>\n    <string name=\"save_as_success_format\">„%1$s” נשמר אל „%2$s”</string>\n    <string name=\"text_editor_title\">עורך טקסט</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">נשמר</string>\n    <string name=\"text_editor_reload_message\">האם בוודאות ברצונך לטעון מחדש? שינויים שלא נשמרו במסמך זה יאבדו.</string>\n    <string name=\"text_editor_encoding\">הצפנה</string>\n    <string name=\"text_editor_close_message\">האם בוודאות ברצונך למחוק שינויים שלא נשמרו במסמך זה?</string>\n\n    <string name=\"ftp_server_title\">שרת FTP</string>\n    <string name=\"ftp_server_state_title\">סטטוס</string>\n    <string name=\"ftp_server_state_summary_starting\">מתחיל…</string>\n    <string name=\"ftp_server_state_summary_running\">פועל</string>\n    <string name=\"ftp_server_state_summary_stopping\">עוצר…</string>\n    <string name=\"ftp_server_state_summary_stopped\">לא התחיל</string>\n    <string name=\"ftp_server_url_title\">כתובת</string>\n    <string name=\"ftp_server_url_summary_no_local_inet_address\">כתובת ה־IP המקומית הזאת לא מוכרת</string>\n    <string name=\"ftp_server_url_menu_copy_url\">העתקת כתובת</string>\n    <string name=\"ftp_server_url_menu_copy_password\">העתקת סיסמה</string>\n    <string name=\"ftp_server_add_tile_title\">הוספה להגדרות המהירות</string>\n    <string name=\"ftp_server_add_tile_result_added\">\\\"שרת FTP\\\" נוסף להגדרות המהירות</string>\n    <string name=\"ftp_server_add_tile_result_already_added\">\\\"שרת FTP\\\" כבר נוסף להגדרות המהירות</string>\n    <string name=\"ftp_server_add_tile_result_error\">שגיאה בעת הוספת \\\"שרת FTP\\\" להגדרות המהירות</string>\n    <string name=\"ftp_server_configuration_title\">תצורה</string>\n    <string name=\"ftp_server_anonymous_login_title\">כניסה בעילום שם</string>\n    <string name=\"ftp_server_username_title\">שם משתמש</string>\n    <string name=\"ftp_server_password_title\">סיסמה</string>\n    <string name=\"ftp_server_port_title\">פתחה</string>\n    <string name=\"ftp_server_home_directory_title\">תיקיית שורש</string>\n    <string name=\"ftp_server_writable_title\">איפשור כתיבה</string>\n    <string name=\"settings_title\">הגדרות</string>\n    <string name=\"settings_interface_title\">ממשק</string>\n    <string name=\"settings_locale_title\">שפה</string>\n    <string name=\"settings_theme_color_title\">צבע ערכת נושא</string>\n    <string name=\"settings_theme_color_summary\">צבע המופיע בתדירות הגבוהה ביותר ביישום</string>\n    <string name=\"settings_material_design_3_title\">Material Design 3</string>\n    <string name=\"settings_night_mode_title\">מצב לילה</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>כמו במערכת</item>\n        <item>כבוי</item>\n        <item>פועל</item>\n        <item>מבוסס זמן</item>\n        <item>מבוסס על שמירת הסוללה</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">מצב לילה שחור</string>\n    <string name=\"settings_file_list_animation_title\">אנימציית רשימת קבצים</string>\n    <string name=\"settings_file_name_ellipsize_title\">הצגת שם קובץ ארוך</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>התחלה אליפסית</item>\n        <item>אמצע אליפסי</item>\n        <item>סיום אליפסי</item>\n        <item>אוהל גדול</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">התנהגות</string>\n    <string name=\"settings_default_directory_title\">תיקיית ברירת מחדל</string>\n    <string name=\"settings_standard_directories_title\">תיקיות סטנדרטיות</string>\n    <string name=\"settings_standard_directories_summary_empty\">תיקיות סטנדרטיות לא מופעל</string>\n    <string name=\"settings_bookmark_directory_screenshots\">צילומי מסך</string>\n    <string name=\"settings_bookmark_directories_title\">תיקיות סימניות</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">אין תיקיות סימניות</string>\n    <string name=\"settings_root_strategy_title\">מצב גישת שורש</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"one\">יש לך פעולה קובץ שרצה ושינוי מצב גישת שורש כעת עלול לגרום לשגיאה בלתי צפויה. האם בוודאות ברצונך לשנות זאת כעת?</item>\n        <item quantity=\"two\">יש לך %1$,d פעולות קבצים שרצות ושינוי מצב גישת שורש כעת עלול לגרום לשגיאה בלתי צפויה. האם בוודאות ברצונך לשנות זאת כעת?</item>\n        <item quantity=\"many\">יש לך %1$,d פעולות קבצים שרצות ושינוי מצב גישת שורש כעת עלול לגרום לשגיאה בלתי צפויה. האם בוודאות ברצונך לשנות זאת כעת?</item>\n        <item quantity=\"other\">יש לך %1$,d פעולות קבצים שרצות ושינוי מצב גישת שורש כעת עלול לגרום לשגיאה בלתי צפויה. האם בוודאות ברצונך לשנות זאת כעת?</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>גישה רגילה בלבד</item>\n        <item>אוטומטי</item>\n        <item>גישת שורש בלבד</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">הצפנת שם קובץ בארכיון</string>\n    <string name=\"settings_open_apk_default_action_title\">פתיחת חבילת אנדרואיד</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>התקנה</item>\n        <item>הצגת תוכן</item>\n        <item>לשאול מה לעשות</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_title\">הצגת תמונה ממוזערת למסמכי PDF</string>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">עלול להפוך את היישום לבלתי יציב בגרסאות ישנות יותר של אנדרואיד</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">קרא קבצים מרוחקים עבור תמונה ממוזערת</string>\n\n    <string name=\"about_title\">אודות</string>\n    <string name=\"about_version_title\">גירסה</string>\n    <string name=\"about_github_title\">הצגה ב-GitHub</string>\n    <string name=\"about_licenses_title\">רשיונות</string>\n    <string name=\"about_privacy_policy_title\">מדיניות פרטיות</string>\n    <string name=\"about_author_title\">יוצר</string>\n    <string name=\"about_author_name_title\">Hai Zhang</string>\n    <string name=\"about_author_github_title\">לעקוב ב-GitHub</string>\n    <string name=\"about_author_twitter_title\">לעקוב בטוויטר</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ja/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">ファイル</string>\n    <string name=\"file_type_name_apk\">Android パッケージ</string>\n    <string name=\"file_type_name_archive\">%1$s アーカイブ</string>\n    <string name=\"file_type_name_audio\">%1$s 音声</string>\n    <string name=\"file_type_name_calendar\">%1$s カレンダー</string>\n    <string name=\"file_type_name_certificate\">%1$s 証明書</string>\n    <string name=\"file_type_name_code\">%1$s ドキュメント</string>\n    <string name=\"file_type_name_contact\">電子名刺</string>\n    <string name=\"file_type_name_directory\">フォルダ</string>\n    <string name=\"file_type_name_document\">%1$s ドキュメント</string>\n    <string name=\"file_type_name_ebook\">%1$s 電子書籍</string>\n    <string name=\"file_type_name_email\">%1$s メールメッセージ</string>\n    <string name=\"file_type_name_font\">%1$s フォント</string>\n    <string name=\"file_type_name_generic\">%1$s ファイル</string>\n    <string name=\"file_type_name_image\">%1$s 画像</string>\n    <string name=\"file_type_name_pdf\">PDF 文書</string>\n    <string name=\"file_type_name_presentation\">%1$s プレゼンテーション</string>\n    <string name=\"file_type_name_spreadsheet\">%1$s スプレッドシート</string>\n    <string name=\"file_type_name_text\">%1$s ドキュメント</string>\n    <string name=\"file_type_name_text_plain\">テキストファイル</string>\n    <string name=\"file_type_name_video\">%1$s 動画</string>\n    <string name=\"file_type_name_word\">Word 文書</string>\n    <string name=\"file_type_name_powerpoint\">PowerPoint プレゼンテーション</string>\n    <string name=\"file_type_name_excel\">Excel スプレッドシート</string>\n    <string name=\"file_type_name_posix_character_device\">キャラクタデバイス</string>\n    <string name=\"file_type_name_posix_block_device\">ブロックデバイス</string>\n    <string name=\"file_type_name_posix_fifo\">パイプ</string>\n    <string name=\"file_type_name_posix_symbolic_link\">リンク</string>\n    <string name=\"file_type_name_posix_socket\">ソケット</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">リンク (破損)</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ja/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <string name=\"app_name\">Material Files</string>\n\n    <string name=\"close\">閉じる</string>\n    <string name=\"copy\">コピー</string>\n    <string name=\"cut\">切り取り</string>\n    <string name=\"default_\">デフォルト</string>\n    <string name=\"delete\">削除</string>\n    <string name=\"discard\">破棄</string>\n    <string name=\"edit\">編集</string>\n    <string name=\"empty\">空白</string>\n    <string name=\"empty_placeholder\">(空白)</string>\n    <string name=\"error\">エラー</string>\n    <string name=\"hide\">隠す</string>\n    <string name=\"install\">インストール</string>\n    <string name=\"keep_editing\">編集を続ける</string>\n    <string name=\"loading\">読み込み中…</string>\n    <string name=\"maybe_later\">あとで</string>\n    <string name=\"merge\">マージ</string>\n    <string name=\"none\">なし</string>\n    <string name=\"open_navigation_drawer\">ナビゲーションドロワーを開く</string>\n    <string name=\"paste\">貼り付け</string>\n    <string name=\"refresh\">更新</string>\n    <string name=\"reload\">再読み込み</string>\n    <string name=\"remove\">消去</string>\n    <string name=\"rename\">名前を変更</string>\n    <string name=\"replace\">入れ替える</string>\n    <string name=\"reset\">リセット</string>\n    <string name=\"retry\">再試行</string>\n    <string name=\"save\">保存</string>\n    <string name=\"search\">検索</string>\n    <string name=\"select\">選択</string>\n    <string name=\"select_all\">すべて選択</string>\n    <string name=\"share\">共有</string>\n    <string name=\"show\">表示</string>\n    <string name=\"skip\">スキップ</string>\n    <string name=\"stop\">停止</string>\n    <string name=\"system_default\">システムのデフォルト</string>\n    <string name=\"unknown\">不明</string>\n    <string name=\"view\">表示</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">この操作を処理できるアプリが見つかりません</string>\n    <string name=\"open_settings\">設定を開く</string>\n    <string name=\"shortcut_created\">ショートカットを作成しました</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"other\">%1$,d バイト</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">すべてのファイルを管理するには、アプリにアクセス権が必要です。次に表示されるシステム設定でアクセスを許可してください。</string>\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">ファイルにアクセスするには、アプリに権限が必要です。次に表示されるシステムダイアログで「許可」を選択してください。</string>\n    <string name=\"storage_permission_permanently_denied_message\">ファイルにアクセスするには、アプリに権限が必要です。システム設定で「ストレージ」の権限を付与してください。</string>\n    <!-- The allow button is in sentence case now. -->\n    <string name=\"notification_permission_rationale_message\">ファイル操作に関する通知を表示するには、アプリに権限が必要です。次に表示されるシステムダイアログで「許可」を選択してください。</string>\n    <string name=\"notification_permission_permanently_denied_message\">ファイル操作に関する通知を表示するには、アプリに権限が必要です。システム設定で「通知」の権限を付与してください。</string>\n\n    <string name=\"notification_channel_background_activity_start_name\">バックグラウンド処理</string>\n    <string name=\"notification_channel_background_activity_start_description\">バックグラウンドでのアプリの実行です</string>\n    <string name=\"notification_channel_file_job_name\">ファイル操作</string>\n    <string name=\"notification_channel_file_job_description\">ファイル操作の表示・制御です</string>\n    <string name=\"notification_channel_ftp_server_name\">FTP サーバー</string>\n    <string name=\"notification_channel_ftp_server_description\">FTP サーバーの表示・制御です</string>\n\n    <string name=\"file_action_archive_password_title\">パスワードが必要</string>\n    <string name=\"file_action_archive_password_message_format\">「%1$s」はパスワードで保護されています。</string>\n    <string name=\"file_action_archive_password_error_empty\">パスワードは空白にはできません</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"other\">ファイル %1$,d 件の圧縮を準備中 (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">「%1$s」を「%2$s」に圧縮中</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"other\">ファイル %1$,d 件を「%2$s」に圧縮中</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"other\">ファイル %1$,d 件のコピーを準備中 (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">「%1$s」を「%2$s」にコピー中</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"other\">ファイル %1$,d 件を「%2$s」にコピー中</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"other\">ファイル %1$,d 件の展開を準備中 (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">「%1$s」を「%2$s」に展開中</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"other\">ファイル %1$,d 件を「%2$s」に展開中</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"other\">ファイル %1$,d 件の移動を準備中 (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">「%1$s」を「%2$s」へ移動中</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"other\">ファイル %1$,d 件を「%2$s」に移動中</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"other\">ファイル %1$,d 件の削除を準備中</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">「%1$s」を削除中</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"other\">ファイル %1$,d 件を削除中</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"other\">ファイル %1$,d 件の所有者変更を準備中</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">「%1$s」の所有者を変更中</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"other\">ファイル %1$,d 件の所有者を変更中</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"other\">ファイル %1$,d 件のグループ変更を準備中</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">「%1$s」のグループを変更中</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"other\">ファイル %1$,d 件のグループを変更中</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"other\">ファイル %1$,d 件のモード変更を準備中</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">「%1$s」のモードを変更中</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"other\">ファイル %1$,d 件のモードを変更中</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"other\">ファイル %1$,d 件の SELinux コンテキストを変更中</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">「%1$s」の SELinux コンテキストを変更中</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"other\">ファイル %1$,d 件の SELinux コンテキストを変更中</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"other\">ファイル %1$,d 件の SELinux コンテキストの復元を準備中</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">「%1$s」の SELinux コンテキストを復元中</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"other\">ファイル %1$,d 件の SELinux コンテキストを復元中</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">「%1$s」を書き込み中</string>\n    <string name=\"file_job_create_error_title\">作成中にエラーが発生しました</string>\n    <string name=\"file_job_create_error_message_format\">「%1$s」の作成中にエラーがが発生しました。\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">フォルダ自身の中にはコピーできません</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">フォルダ自身の中には展開できません</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">フォルダ自身の中には移動できません</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">コピー先のフォルダがコピー元のフォルダ内にあります。</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">ファイルを同じ場所にはコピーできません</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">ファイルを同じ場所には展開できません</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">ファイルを同じ場所には移動できません</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">元のファイルは移動先で上書きされます。</string>\n    <string name=\"file_job_replace_title_format\">ファイル「%1$s」を置換しますか？</string>\n    <string name=\"file_job_replace_message_format\">同じ名前のファイルが「%1$s」に存在します。\\n置換するとファイルは上書きされます。</string>\n    <string name=\"file_job_replace_target_name\">元のファイル</string>\n    <string name=\"file_job_replace_source_name\">置換</string>\n    <string name=\"file_job_merge_title_format\">フォルダ「%1$s」をマージしますか？</string>\n    <string name=\"file_job_merge_copy_message_format\">マージでは、コピーしたファイルと衝突する場合、フォルダ内を置換する前に確認します。</string>\n    <string name=\"file_job_merge_extract_message_format\">マージでは、展開したファイルと衝突する場合、フォルダ内を置換する前に確認します。</string>\n    <string name=\"file_job_merge_move_message_format\">マージでは、移動するファイルが衝突する場合、フォルダ内を置換する前に確認します。</string>\n    <string name=\"file_job_merge_target_name\">元のフォルダ</string>\n    <string name=\"file_job_merge_source_name\">マージ</string>\n    <string name=\"file_job_conflict_show_name\">移動先の新しい名前を選択してください</string>\n    <string name=\"file_job_conflict_name_hint\">新しい名前</string>\n    <string name=\"file_job_archive_error_title_format\">「%1$s」の圧縮中にエラーが発生しました</string>\n    <string name=\"file_job_archive_error_message_format\">ファイルを「%1$s」に圧縮中にエラーが発生しました。\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">「%1$s」のコピー中にエラーが発生しました</string>\n    <string name=\"file_job_copy_error_message_format\">ファイルを「%1$s」にコピー中にエラーが発生しました。\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">「%1$s」の展開中にエラーが発生しました</string>\n    <string name=\"file_job_extract_error_message_format\">ファイルを「%1$s」に展開中にエラーが発生しました。\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">「%1$s」の移動中にエラーが発生しました</string>\n    <string name=\"file_job_move_error_message_format\">ファイルを「%1$s」に移動中にエラーが発生しました。\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">削除中にエラーが発生しました</string>\n    <string name=\"file_job_delete_error_message_format\">「%1$s」の削除中にエラーが発生しました。\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">「%1$s」の名前を変更中にエラーが発生しました</string>\n    <string name=\"file_job_rename_error_message_format\">ファイル名を「%1$s」に変更中にエラーが発生しました。\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">「%1$s」の所有者を変更中にエラーが発生しました</string>\n    <string name=\"file_job_set_owner_error_message_format\">所有者を「%1$s」に変更中にエラーが発生しました。\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">「%1$s」のグループを変更中にエラーが発生しました。</string>\n    <string name=\"file_job_set_group_error_message_format\">グループを「%1$s」に変更中にエラーが発生しました。\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">「%1$s」のモード変更中にエラーが発生しました</string>\n    <string name=\"file_job_set_mode_error_message_format\">モードを「%1$s」に変更中にエラーが発生しました。\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">「%1$s」の SELinux コンテキストを変更中にエラーが発生しました</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">SELinux コンテキストを「%1$s」に変更中にエラーが発生しました。\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">SELinux コンテキストの復元中にエラーが発生しました</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">「%1$s」の SELinux コンテキストを復元中にエラーが発生しました。\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">書き込み中にエラーが発生しました</string>\n    <string name=\"file_job_write_error_message_format\">「%1$s」の書き込み中にエラーが発生しました。\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">「%1$s」の情報を取得中にエラーが発生しました。</string>\n    <string name=\"file_job_remount_format\">「%1$s」を読み書き可として再マウント</string>\n    <string name=\"file_job_remount_loading_format\">「%1$s」を読み書き可として再マウントしています…</string>\n    <string name=\"file_job_remount_success_format\">「%1$s」が読み書き可として再マウントされました</string>\n    <string name=\"file_job_action_all\">この操作をすべてのファイルに適用</string>\n\n    <string name=\"file_open_apk_message\">このファイルは Android パッケージです。インストールしますか？ それとも中身を表示しますか？</string>\n    <string name=\"file_open_as_title\">…として開く</string>\n    <string name=\"file_open_as_title_format\">「%1$s」を次の形式として開く</string>\n    <string name=\"file_open_as_type_text\">テキスト</string>\n    <string name=\"file_open_as_type_image\">画像</string>\n    <string name=\"file_open_as_type_audio\">音声</string>\n    <string name=\"file_open_as_type_video\">動画</string>\n    <string name=\"file_open_as_type_directory\">フォルダ</string>\n    <string name=\"file_open_as_type_any\">その他</string>\n    <string name=\"file_install_apk_from_background_title_format\">「%1$s」をインストールする準備ができました</string>\n    <string name=\"file_install_apk_from_background_text\">タップでインストール</string>\n    <string name=\"file_open_from_background_title_format\">「%1$s」を開く準備ができました</string>\n    <string name=\"file_open_from_background_text\">タップで開く</string>\n    <string name=\"file_name_error_empty\">ファイル名は空白にできません</string>\n    <string name=\"file_name_error_invalid\">無効なファイル名です</string>\n    <string name=\"file_name_error_already_exists\">この名前のファイルは既に存在します</string>\n    <string name=\"file_replace_message_format\">「%1$s」を置換しますか？</string>\n    <string name=\"file_delete_message_file_format\">「%1$s」を削除しますか？</string>\n    <string name=\"file_delete_message_directory_format\">フォルダ「%1$s」と中のファイルを削除しますか？</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"other\">ファイル %1$,d 件を削除しますか？</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"other\">フォルダ %1$,d 件とコンテンツを削除しますか？</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"other\">アイテム %1$,d 件を削除しますか？</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">アーカイブを作成</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_create_archive_password\">パスワード (オプション)</string>\n    <string name=\"file_add_bookmark_success\">ブックマークが追加されました</string>\n    <string name=\"file_create_file_title\">新しいファイル</string>\n    <string name=\"file_create_directory_title\">新しいフォルダ</string>\n\n    <string name=\"file_list_title\">ファイル</string>\n    <plurals name=\"file_list_title_open_file\">\n        <item quantity=\"other\">ファイルを選択</item>\n    </plurals>\n    <string name=\"file_list_title_create_file\">ファイルを保存</string>\n    <plurals name=\"file_list_title_open_directory\">\n        <item quantity=\"other\">フォルダを選択</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"other\">フォルダ %1$,d 件</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"other\">ファイル %1$,d 件</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">,\\u0020</string>\n    <string name=\"file_list_path_error_empty\">パスは空白にできません</string>\n    <string name=\"file_list_path_error_invalid\">無効なパスです</string>\n    <string name=\"file_list_action_view_sort\">表示と並べ替え</string>\n    <string name=\"file_list_action_view_list\">リスト</string>\n    <string name=\"file_list_action_view_grid\">グリッド</string>\n    <string name=\"file_list_action_sort_by_name\">名前</string>\n    <string name=\"file_list_action_sort_by_type\">種類</string>\n    <string name=\"file_list_action_sort_by_size\">サイズ</string>\n    <string name=\"file_list_action_sort_by_last_modified\">更新日時</string>\n    <string name=\"file_list_action_sort_order_ascending\">昇順</string>\n    <string name=\"file_list_action_sort_directories_first\">フォルダを先頭に表示する</string>\n    <string name=\"file_list_action_view_sort_path_specific\">このフォルダにのみ適用する</string>\n    <string name=\"file_list_action_new_task\">新しいウィンドウ</string>\n    <string name=\"file_list_action_navigate_up\">上のパスへ移動</string>\n    <string name=\"file_list_action_navigate_to\">パスを入力して移動</string>\n    <string name=\"file_list_action_show_hidden_files\">隠しファイルを表示する</string>\n    <string name=\"file_list_action_copy_path\">パスをコピー</string>\n    <string name=\"file_list_action_open_in_terminal\">ターミナルで開く</string>\n    <string name=\"file_list_action_add_bookmark\">ブックマークに追加</string>\n    <string name=\"file_list_action_create_shortcut\">ショートカットを作成</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">新しいウィンドウで開く</string>\n    <string name=\"file_list_select_title_format\">%1$,d</string>\n    <string name=\"file_list_create_file_name_hint\">ファイル名</string>\n    <string name=\"file_list_open_current_directory_format\">「%1$s」を選択</string>\n    <string name=\"file_list_paste_move_title_format\">%1$,d 件を移動中</string>\n    <string name=\"file_list_paste_copy_title_format\">%1$,d 件をコピー中</string>\n    <string name=\"file_list_paste_extract_title_format\">%1$,d 件を展開中</string>\n    <string name=\"file_list_paste_action_extract_here\">ここに展開</string>\n    <string name=\"file_list_empty\">ファイルがありません</string>\n    <string name=\"file_list_action_create_file\">ファイル</string>\n    <string name=\"file_list_action_create_directory\">フォルダ</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">アプリで開く</string>\n    <string name=\"file_item_action_extract\">展開</string>\n    <string name=\"file_item_action_archive\">圧縮</string>\n    <string name=\"file_item_action_properties\">プロパティ</string>\n\n    <string name=\"file_properties_title_format\">「%1$s」のプロパティ</string>\n    <string name=\"file_properties_basic\">基本</string>\n    <string name=\"file_properties_basic_name\">名前</string>\n    <string name=\"file_properties_basic_type\">種類</string>\n    <string name=\"file_properties_basic_type_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">%1$s にリンク (%2$s)</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">対象をリンクする</string>\n    <string name=\"file_properties_basic_size\">サイズ</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_contents\">コンテンツ</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"other\">アイテムの合計 %1$,d 件 %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">更新日時</string>\n    <string name=\"file_properties_basic_parent_directory\">親フォルダ</string>\n    <string name=\"file_properties_basic_archive_file\">圧縮ファイル</string>\n    <string name=\"file_properties_basic_archive_entry\">アーカイブエントリー</string>\n    <string name=\"file_properties_basic_free_space\">空き容量</string>\n    <string name=\"file_properties_permission\">権限</string>\n    <string name=\"file_properties_permission_owner\">所有者</string>\n    <string name=\"file_properties_permission_group\">グループ</string>\n    <string name=\"file_properties_permission_principal_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_permission_set_owner_title\">所有者を変更</string>\n    <string name=\"file_properties_permission_set_group_title\">グループを変更</string>\n    <string name=\"file_properties_permission_set_principal_filter_hint\">名前または ID を入力してください</string>\n    <string name=\"file_properties_permission_set_principal_system\">システム</string>\n    <string name=\"file_properties_permission_mode\">モード</string>\n    <string name=\"file_properties_permission_mode_format\">%1$s (%2$04o)</string>\n    <string name=\"file_properties_permission_set_mode_title\">モードを変更</string>\n    <string name=\"file_properties_permission_set_mode_others\">その他</string>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_file\">\n        <item>読み取り</item>\n        <item>書き込み</item>\n        <item>実行</item>\n    </string-array>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_directory\">\n        <item>読み取り</item>\n        <item>書き込み</item>\n        <item>検索</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_special\">特別</string>\n    <string-array name=\"file_properties_permission_set_mode_special_mode_bits\">\n        <item>UID を設定</item>\n        <item>GID を設定</item>\n        <item>スティッキービット</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_uppercase_x\">内部のファイルに「実行」を付けない</string>\n    <string name=\"file_properties_permission_selinux_context\">SELinux コンテキスト</string>\n    <string name=\"file_properties_permission_set_selinux_context_title\">SELinux コンテキストを変更</string>\n    <string name=\"file_properties_permission_set_selinux_context_restore\">復元</string>\n    <string name=\"file_properties_permission_recursive\">内部のファイルに適用する</string>\n    <string name=\"file_properties_image\">画像</string>\n    <string name=\"file_properties_media_dimensions\">大きさ</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">撮影</string>\n    <string name=\"file_properties_media_coordinates\">座標</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f, %2$.3f</string>\n    <string name=\"file_properties_media_address\">場所</string>\n    <string name=\"file_properties_image_gps_altitude\">高度</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f m</string>\n    <string name=\"file_properties_image_equipment\">カメラ</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">絞り</string>\n    <string name=\"file_properties_image_shutter_speed\">シャッター速度</string>\n    <string name=\"file_properties_image_focal_length\">焦点距離</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f mm</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">ISO 感度</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">ソフトウェア</string>\n    <string name=\"file_properties_image_description\">説明</string>\n    <string name=\"file_properties_image_artist\">作者</string>\n    <string name=\"file_properties_image_copyright\">著作権</string>\n    <string name=\"file_properties_audio\">音声</string>\n    <string name=\"file_properties_media_title\">タイトル</string>\n    <string name=\"file_properties_audio_artist\">アーティスト</string>\n    <string name=\"file_properties_audio_album\">アルバム</string>\n    <string name=\"file_properties_audio_album_artist\">アルバムアーティスト</string>\n    <string name=\"file_properties_audio_composer\">作曲者</string>\n    <string name=\"file_properties_audio_disc_number\">ディスク</string>\n    <string name=\"file_properties_audio_track_number\">トラック</string>\n    <string name=\"file_properties_audio_year\">年</string>\n    <string name=\"file_properties_audio_genre\">ジャンル</string>\n    <string name=\"file_properties_media_duration\">長さ</string>\n    <string name=\"file_properties_media_bit_rate\">ビットレート</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d kbps</string>\n    <string name=\"file_properties_audio_sample_rate\">サンプリング周波数</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Hz</string>\n    <string name=\"file_properties_video\">ビデオ</string>\n    <string name=\"file_properties_apk\">APK</string>\n    <string name=\"file_properties_apk_label\">名前</string>\n    <string name=\"file_properties_apk_package_name\">パッケージ名</string>\n    <string name=\"file_properties_apk_version\">バージョン</string>\n    <string name=\"file_properties_apk_version_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_apk_min_sdk_version\">最低システムバージョン</string>\n    <string name=\"file_properties_apk_target_sdk_version\">対象システムバージョン</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s (%2$s, %3$d)</string>\n    <string name=\"file_properties_apk_requested_permissions\">権限</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">権限を要求しません</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"other\">権限 %1$,d 件を要求</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">署名</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">有効な署名がありません</string>\n    <string name=\"file_properties_apk_past_signature_digests\">古い署名形式です</string>\n    <string name=\"file_properties_checksum\">チェックサム</string>\n    <string name=\"file_properties_checksum_compare\">比較</string>\n    <string name=\"file_properties_checksum_compare_placeholder\">すべてのチェックサム</string>\n    <string name=\"file_properties_checksum_compare_match_format\">%1$s チェックサムが一致</string>\n    <string name=\"file_properties_checksum_compare_prefix_match_format\">%1$s チェックサムが先頭から検索して一致</string>\n    <string name=\"file_properties_checksum_compare_no_match\">一致するチェックサムが見つかりません</string>\n    <string name=\"storage_file_system_root_title\">Root</string>\n    <string name=\"storage_list_title\">ストレージ</string>\n    <string name=\"storage_list_empty\">ストレージがありません</string>\n    <string name=\"storage_add_storage_title\">ストレージを追加</string>\n    <string name=\"storage_add_storage_document_tree\">外部ストレージ</string>\n    <string name=\"storage_add_storage_ftp_server\">FTP サーバー</string>\n    <string name=\"storage_add_storage_sftp_server\">SFTP サーバー</string>\n    <string name=\"storage_add_storage_smb_server\">SMB サーバー</string>\n    <string name=\"storage_add_storage_webdav_server\">WebDAV サーバー</string>\n    <string name=\"storage_edit_device_storage_title\">デバイスのストレージを編集</string>\n    <string name=\"storage_edit_device_storage_name\">名前</string>\n    <string name=\"storage_edit_device_storage_path\">パス</string>\n    <string name=\"storage_add_external_storage_shortcut_title\">外部ストレージのショートカットを追加</string>\n    <string name=\"storage_edit_external_storage_shortcut_title\">外部ストレージのショートカットを編集</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id\">ストレージ領域</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id_error_empty\">ストレージ領域を入力してください</string>\n    <string name=\"storage_edit_external_storage_shortcut_path\">パス</string>\n    <string name=\"storage_add_document_tree_title\">外部ストレージを追加</string>\n    <string name=\"storage_edit_document_tree_title\">外部ストレージを編集</string>\n    <string name=\"storage_edit_document_tree_name\">名前</string>\n    <string name=\"storage_edit_document_tree_uri\">URI</string>\n    <string name=\"storage_edit_document_tree_path\">パス</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">FTP サーバーを編集</string>\n    <string name=\"storage_edit_ftp_server_title_add\">FTP サーバーを追加</string>\n    <string name=\"storage_edit_ftp_server_host\">ホスト名</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">ホスト名を入力してください</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">無効なホスト名です</string>\n    <string name=\"storage_edit_ftp_server_port\">ポート</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">無効なポートです</string>\n    <string name=\"storage_edit_ftp_server_path\">パス</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">空白にもできます</string>\n    <string name=\"storage_edit_ftp_server_name\">名前</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">ホスト名を使用する</string>\n    <string name=\"storage_edit_ftp_server_protocol\">プロトコル</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">認証</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>パスワード</item>\n        <item>匿名</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">ユーザー名</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">ユーザー名を入力してください</string>\n    <string name=\"storage_edit_ftp_server_password\">パスワード</string>\n    <string name=\"storage_edit_ftp_server_mode\">モード</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>アクティブ</item>\n        <item>パッシブ</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">文字コード</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">接続して追加</string>\n    <string name=\"storage_edit_ftp_server_add\">追加</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">SFTP サーバーを編集</string>\n    <string name=\"storage_edit_sftp_server_title_add\">SFTP サーバーを追加</string>\n    <string name=\"storage_edit_sftp_server_host\">ホスト名</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">ホスト名を入力してください</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">無効なホスト名です</string>\n    <string name=\"storage_edit_sftp_server_port\">ポート</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">無効なポートです</string>\n    <string name=\"storage_edit_sftp_server_path\">パス</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">空白にもできます</string>\n    <string name=\"storage_edit_sftp_server_name\">名前</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">ホスト名を使用する</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">認証</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>パスワード</item>\n        <item>公開鍵</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">ユーザー名</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">ユーザー名を入力してください</string>\n    <string name=\"storage_edit_sftp_server_password\">パスワード</string>\n    <string name=\"storage_edit_sftp_server_private_key\">秘密鍵</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">ファイルを開く</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">秘密鍵を入力してください</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">無効な秘密鍵です</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">秘密鍵のパスワード</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">空白にもできます</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">無効な秘密鍵のパスワードです</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">接続して追加</string>\n    <string name=\"storage_edit_sftp_server_add\">追加</string>\n    <string name=\"storage_add_lan_smb_server_loading\">SMB サーバーを検索中…</string>\n    <string name=\"storage_add_lan_smb_server_add\">手動で追加</string>\n    <string name=\"storage_edit_smb_server_title_edit\">SMB サーバーを編集</string>\n    <string name=\"storage_edit_smb_server_title_add\">SMB サーバーを追加</string>\n    <string name=\"storage_edit_smb_server_host\">ホスト名</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">ホスト名を入力してください</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">無効なホスト名です</string>\n    <string name=\"storage_edit_smb_server_port\">ポート</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">無効なポートです</string>\n    <string name=\"storage_edit_smb_server_path\">パス</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">空白にもできます</string>\n    <string name=\"storage_edit_smb_server_name\">名前</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">ホスト名を使用する</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">認証</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>パスワード</item>\n        <item>ゲスト</item>\n        <item>匿名</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">ユーザー名</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">ユーザー名を入力してください</string>\n    <string name=\"storage_edit_smb_server_password\">パスワード</string>\n    <string name=\"storage_edit_smb_server_domain\">ドメイン</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">接続して追加</string>\n    <string name=\"storage_edit_smb_server_add\">追加</string>\n    <string name=\"storage_edit_webdav_server_title_edit\">WebDAV サーバーを編集</string>\n    <string name=\"storage_edit_webdav_server_title_add\">WebDAV サーバーを追加</string>\n    <string name=\"storage_edit_webdav_server_host\">ホスト名</string>\n    <string name=\"storage_edit_webdav_server_host_error_empty\">ホスト名を入力してください</string>\n    <string name=\"storage_edit_webdav_server_host_error_invalid\">無効なホスト名です</string>\n    <string name=\"storage_edit_webdav_server_port\">ポート</string>\n    <string name=\"storage_edit_webdav_server_port_error_invalid\">無効なポートです</string>\n    <string name=\"storage_edit_webdav_server_path\">パス</string>\n    <string name=\"storage_edit_webdav_server_path_placeholder\">空白にもできます</string>\n    <string name=\"storage_edit_webdav_server_name\">名前</string>\n    <string name=\"storage_edit_webdav_server_name_placeholder\">ホスト名を使用する</string>\n    <string name=\"storage_edit_webdav_server_protocol\">プロトコル</string>\n    <string name=\"storage_edit_webdav_server_authentication_type\">認証</string>\n    <string-array name=\"storage_edit_webdav_server_authentication_type_entries\">\n        <item>パスワード</item>\n        <item>アクセストークン</item>\n        <item>なし</item>\n    </string-array>\n    <string name=\"storage_edit_webdav_server_username\">ユーザー名</string>\n    <string name=\"storage_edit_webdav_server_username_error_empty\">ユーザー名を入力してください</string>\n    <string name=\"storage_edit_webdav_server_password\">パスワード</string>\n    <string name=\"storage_edit_webdav_server_access_token\">アクセストークン</string>\n    <string name=\"storage_edit_webdav_server_access_token_error_empty\">アクセストークンを入力してください</string>\n    <string name=\"storage_edit_webdav_server_connect_and_add\">接続して追加</string>\n    <string name=\"storage_edit_webdav_server_add\">追加</string>\n\n    <string name=\"navigation_storage_subtitle_format\">空き容量 %1$s / %2$s</string>\n    <string name=\"navigation_add_storage\">ストレージを追加…</string>\n    <string name=\"navigation_standard_directory_alarms\">アラーム</string>\n    <string name=\"navigation_standard_directory_dcim\">写真</string>\n    <string name=\"navigation_standard_directory_documents\">ドキュメント</string>\n    <string name=\"navigation_standard_directory_downloads\">ダウンロード</string>\n    <string name=\"navigation_standard_directory_movies\">動画</string>\n    <string name=\"navigation_standard_directory_music\">音声</string>\n    <string name=\"navigation_standard_directory_notifications\">通知</string>\n    <string name=\"navigation_standard_directory_pictures\">画像</string>\n    <string name=\"navigation_standard_directory_podcasts\">ポッドキャスト</string>\n    <string name=\"navigation_standard_directory_ringtones\">着信音</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">ブックマークフォルダ</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">名前</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">パス</string>\n    <string name=\"archive_viewer_title\">アーカイブビューアー</string>\n    <string name=\"image_viewer_title\">画像ビューアー</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"save_as_title\">保存</string>\n    <string name=\"save_as_error\">ファイルを保存中にエラーが発生しました</string>\n    <string name=\"save_as_success_format\">「%1$s」は「%2$s」に保存されました</string>\n    <string name=\"text_editor_title\">テキストエディター</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">保存しました</string>\n    <string name=\"text_editor_reload_message\">本当に再読み込みしますか？ このドキュメントの保存していない変更は破棄されます。</string>\n    <string name=\"text_editor_encoding\">エンコーディング</string>\n    <string name=\"text_editor_close_message\">本当にこのドキュメントの保存していない変更は破棄しますか？</string>\n\n    <string name=\"ftp_server_title\">FTP サーバー</string>\n    <string name=\"ftp_server_state_title\">状態</string>\n    <string name=\"ftp_server_state_summary_starting\">開始中…</string>\n    <string name=\"ftp_server_state_summary_running\">実行中</string>\n    <string name=\"ftp_server_state_summary_stopping\">停止中…</string>\n    <string name=\"ftp_server_state_summary_stopped\">待機中</string>\n    <string name=\"ftp_server_url_title\">URL</string>\n    <string name=\"ftp_server_url_summary_no_local_inet_address\">未知のローカル IP です</string>\n    <string name=\"ftp_server_url_menu_copy_url\">URL をコピー</string>\n    <string name=\"ftp_server_url_menu_copy_password\">パスワードをコピー</string>\n    <string name=\"ftp_server_add_tile_title\">クイック設定に追加</string>\n    <string name=\"ftp_server_add_tile_result_added\">「FTP サーバー」はクイック設定に追加されました</string>\n    <string name=\"ftp_server_add_tile_result_already_added\">「FTP サーバー」は既にクイック設定に追加されています</string>\n    <string name=\"ftp_server_add_tile_result_error\">クイック設定に「FTP サーバー」を追加中にエラーが発生しました</string>\n    <string name=\"ftp_server_configuration_title\">設定</string>\n    <string name=\"ftp_server_anonymous_login_title\">匿名ログイン</string>\n    <string name=\"ftp_server_username_title\">ユーザー名</string>\n    <string name=\"ftp_server_password_title\">パスワード</string>\n    <string name=\"ftp_server_port_title\">ポート</string>\n    <string name=\"ftp_server_home_directory_title\">Root フォルダ</string>\n    <string name=\"ftp_server_writable_title\">書き込みを許可</string>\n    <string name=\"settings_title\">設定</string>\n    <string name=\"settings_interface_title\">外観</string>\n    <string name=\"settings_locale_title\">言語</string>\n    <string name=\"settings_theme_color_title\">テーマカラー</string>\n    <string name=\"settings_theme_color_summary\">アプリ内で優先して使用する色です</string>\n    <string name=\"settings_material_design_3_title\">Material Design 3</string>\n    <string name=\"settings_night_mode_title\">夜間モード</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>システム設定に従う</item>\n        <item>オフ</item>\n        <item>オン</item>\n        <item>時刻に従う</item>\n        <item>バッテリーセーバーに従う</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">夜間モードで黒を使用</string>\n    <string name=\"settings_file_list_animation_title\">ファイル一覧のアニメーション</string>\n    <string name=\"settings_file_name_ellipsize_title\">長いファイル名の表示方法</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>先頭を省略</item>\n        <item>中央を省略</item>\n        <item>末尾を省略</item>\n        <item>スクロール</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">動作</string>\n    <string name=\"settings_default_directory_title\">デフォルトのフォルダ</string>\n    <string name=\"settings_standard_directories_title\">標準フォルダ</string>\n    <string name=\"settings_standard_directories_summary_empty\">標準フォルダが有効化されていません</string>\n    <string name=\"settings_bookmark_directory_screenshots\">スクリーンショット</string>\n    <string name=\"settings_bookmark_directories_title\">ブックマークフォルダ</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">ブックマークフォルダがありません</string>\n    <string name=\"settings_root_strategy_title\">Root アクセスモード</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"other\">%1$,d 件のファイルが操作を実行中です。ルートアクセスモードを変更すると予期せぬエラーが発生することがあります。本当に今すぐ変更しますか？</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>通常アクセスのみ</item>\n        <item>自動</item>\n        <item>Root アクセスのみ</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">アーカイブのファイル名のエンコーディング</string>\n    <string name=\"settings_open_apk_default_action_title\">Android パッケージを開く</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>インストール</item>\n        <item>内容を表示</item>\n        <item>何をするか確認する</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_title\">PDF ドキュメントのサムネイルを表示する</string>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">古いバージョンの Android ではアプリが不安定になる可能性があります</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">リモートファイルのサムネイルを読み込む</string>\n\n    <string name=\"about_title\">このアプリについて</string>\n    <string name=\"about_version_title\">バージョン</string>\n    <string name=\"about_github_title\">GitHub で見る</string>\n    <string name=\"about_licenses_title\">ライセンス</string>\n    <string name=\"about_privacy_policy_title\">プライバシーポリシー</string>\n    <string name=\"about_author_title\">開発者</string>\n    <string name=\"about_author_name_title\">張海</string>\n    <string name=\"about_author_github_title\">GitHub でフォロー</string>\n    <string name=\"about_author_twitter_title\">Twitter でフォロー</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ko/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">파일</string>\n    <string name=\"file_type_name_apk\">Android 패키지</string>\n    <string name=\"file_type_name_archive\">%1$s 압축 파일</string>\n    <string name=\"file_type_name_audio\">%1$s 오디오</string>\n    <string name=\"file_type_name_calendar\">%1$s 캘린더</string>\n    <string name=\"file_type_name_certificate\">%1$s 인증서</string>\n    <string name=\"file_type_name_code\">%1$s 문서</string>\n    <string name=\"file_type_name_contact\">전자 명함</string>\n    <string name=\"file_type_name_directory\">폴더</string>\n    <string name=\"file_type_name_document\">%1$s 문서</string>\n    <string name=\"file_type_name_ebook\">%1$s 전자책</string>\n    <string name=\"file_type_name_email\">%1$s 이메일 메시지</string>\n    <string name=\"file_type_name_font\">%1$s 글꼴</string>\n    <string name=\"file_type_name_generic\">%1$s 파일</string>\n    <string name=\"file_type_name_image\">%1$s 이미지</string>\n    <string name=\"file_type_name_pdf\">PDF 문서</string>\n    <string name=\"file_type_name_presentation\">%1$s 프레젠테이션</string>\n    <string name=\"file_type_name_spreadsheet\">%1$s 스프레드시트</string>\n    <string name=\"file_type_name_text\">%1$s 문서</string>\n    <string name=\"file_type_name_text_plain\">텍스트 문서</string>\n    <string name=\"file_type_name_video\">%1$s 영상</string>\n    <string name=\"file_type_name_word\">Word 문서</string>\n    <string name=\"file_type_name_powerpoint\">PowerPoint 프레젠테이션</string>\n    <string name=\"file_type_name_excel\">Excel 스프레드시트</string>\n    <string name=\"file_type_name_posix_character_device\">문자 디바이스</string>\n    <string name=\"file_type_name_posix_block_device\">블록 디바이스</string>\n    <string name=\"file_type_name_posix_fifo\">파이프</string>\n    <string name=\"file_type_name_posix_symbolic_link\">링크</string>\n    <string name=\"file_type_name_posix_socket\">소켓</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">링크 (깨짐)</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ko/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <string name=\"app_name\">Material Files</string>\n\n    <string name=\"close\">닫기</string>\n    <string name=\"copy\">복사</string>\n    <string name=\"cut\">잘라내기</string>\n    <string name=\"default_\">기본</string>\n    <string name=\"delete\">삭제</string>\n    <string name=\"discard\">버리기</string>\n    <string name=\"edit\">편집</string>\n    <string name=\"empty\">빈 항목</string>\n    <string name=\"empty_placeholder\">(빈 항목)</string>\n    <string name=\"error\">오류</string>\n    <string name=\"hide\">숨기기</string>\n    <string name=\"install\">설치</string>\n    <string name=\"keep_editing\">편집 계속하기</string>\n    <string name=\"loading\">불러오는 중…</string>\n    <string name=\"maybe_later\">나중에</string>\n    <string name=\"merge\">합치기</string>\n    <string name=\"none\">없음</string>\n    <string name=\"open_navigation_drawer\">탐색 창 열기</string>\n    <string name=\"paste\">붙여넣기</string>\n    <string name=\"refresh\">새로고침</string>\n    <string name=\"reload\">다시 불러오기</string>\n    <string name=\"remove\">삭제</string>\n    <string name=\"rename\">이름 바꾸기</string>\n    <string name=\"replace\">교체</string>\n    <string name=\"reset\">초기화</string>\n    <string name=\"retry\">다시 시도</string>\n    <string name=\"save\">저장</string>\n    <string name=\"search\">검색</string>\n    <string name=\"select\">선택</string>\n    <string name=\"select_all\">모두 선택</string>\n    <string name=\"share\">공유</string>\n    <string name=\"show\">표시</string>\n    <string name=\"skip\">건너 뛰기</string>\n    <string name=\"stop\">중지</string>\n    <string name=\"system_default\">시스템 기본값</string>\n    <string name=\"unknown\">알 수 없음</string>\n    <string name=\"view\">보기</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">해당 작업을 위한 애플리케이션이 없습니다</string>\n    <string name=\"open_settings\">설정 열기</string>\n    <string name=\"shortcut_created\">바로가기가 생성되었습니다</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"other\">%1$,d 바이트</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">모든 파일 관리 권한이 필요합니다. 곧 나타날 시스템 설정에서 권한을 허용해 주세요.</string>\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">파일에 접근하기 위한 권한이 필요합니다. 곧 나타날 대화 상자에서 \\'허용\\'을 선택해 주세요.</string>\n    <string name=\"storage_permission_permanently_denied_message\">파일에 접근하기 위한 권한이 필요합니다. 시스템 설정에서 \\'저장용량\\' 권한을 허용해 주세요.</string>\n    <!-- The allow button is in sentence case now. -->\n    <string name=\"notification_permission_rationale_message\">파일 작업과 관련된 알림을 띄우기 위한 권한이 필요합니다. 곧 나타날 대화 상자에서 “허용”을 선택해 주세요.</string>\n    <string name=\"notification_permission_permanently_denied_message\">파일 작업과 관련된 알림을 띄우기 위한 권한이 필요합니다. 시스템 설정에서 “알림” 권한을 허용해 주세요.</string>\n\n    <string name=\"notification_channel_background_activity_start_name\">백그라운드에서 작업</string>\n    <string name=\"notification_channel_background_activity_start_description\">앱이 백그라운드에 있는 동안 작업합니다</string>\n    <string name=\"notification_channel_file_job_name\">파일 작업</string>\n    <string name=\"notification_channel_file_job_description\">파일 작업을 표시하고 제어합니다</string>\n    <string name=\"notification_channel_ftp_server_name\">FTP 서버</string>\n    <string name=\"notification_channel_ftp_server_description\">FTP 서버를 표시하고 제어합니다</string>\n\n    <string name=\"file_action_archive_password_title\">비밀번호 필요</string>\n    <string name=\"file_action_archive_password_message_format\">“%1$s” 파일은 비밀번호로 보호되어 있습니다.</string>\n    <string name=\"file_action_archive_password_error_empty\">비밀번호를 입력하세요</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"other\">%1$,d개 파일 압축 준비 중 (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">“%1$s” 항목을 “%2$s” (으)로 압축 중</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"other\">%1$,d개 파일을 “%2$s” 로 압축 중</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"other\">%1$,d개 파일 복사 준비 중 (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">“%1$s” 항목을 “%2$s” (으)로 복사 중</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"other\">%1$,d개 파일을 “%2$s” (으)로 복사 중</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"other\">%1$,d개 파일 압축 해제 준비 중 (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">“%1$s” 항목을 “%2$s” 에 압축 해제 중</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"other\">%1$,d개 파일을 “%2$s” 에 압축 해제 중</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"other\">%1$,d개 파일 이동 준비 중 (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">“%1$s” 항목을 “%2$s” (으)로 이동 중</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"other\">%1$,d개 파일을 “%2$s” (으)로 이동 중</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"other\">%1$,d개 파일 삭제 준비 중</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">“%1$s” 파일 삭제 중</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"other\">%1$,d개 파일 삭제 중</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"other\">%1$,d개 파일 소유자 변경 준비 중</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">“%1$s” 소유자 변경 중</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"other\">%1$,d개 파일 소유자 변경 중</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"other\">%1$,d개 파일 그룹 변경 준비 중</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">“%1$s” 그룹 변경 중</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"other\">%1$,d개 파일 그룹 변경 중</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"other\">%1$,d개 파일 모드 변경 준비 중</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">“%1$s” 모드 변경 중</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"other\">%1$,d개 파일 모드 변경 중</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"other\">%1$,d개 파일 SELinux 컨텍스트 변경 준비 중</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">“%1$s” SELinux 컨텍스트 변경 중</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"other\">%1$,d개 파일 SELinux 컨텍스트 변경 중</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"other\">%1$,d개 파일 SELinux 컨텍스트 복원 준비 중</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">“%1$s” SELinux 컨텍스트 복원 중</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"other\">%1$,d개 파일 SELinux 컨텍스트 복원 중</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">“%1$s” 쓰는 중</string>\n    <string name=\"file_job_create_error_title\">생성 중 오류</string>\n    <string name=\"file_job_create_error_message_format\">“%1$s” 항목을 생성하는 중에 오류가 발생하였습니다.\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">같은 폴더에 복사할 수 없습니다.</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">같은 폴더에 압축 해제할 수 없습니다.</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">같은 폴더로 옮길 수 없습니다.</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">옮길 위치가 현 위치의 아래에 속해 있습니다.</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">같은 파일로 복사할 수 없습니다.</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">같은 파일로 압축 해제할 수 없습니다.</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">같은 파일로 옮길 수 없습니다.</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">원본 파일이 대상 파일을 덮어쓰게 됩니다.</string>\n    <string name=\"file_job_replace_title_format\">“%1$s” 파일을 교체하시겠습니까?</string>\n    <string name=\"file_job_replace_message_format\">이름이 “%1$s”인 파일이 이미 있습니다.\\n파일을 교체하면 대상 파일의 내용을 덮어씁니다.</string>\n    <string name=\"file_job_replace_target_name\">기존 파일</string>\n    <string name=\"file_job_replace_source_name\">교체할 파일</string>\n    <string name=\"file_job_merge_title_format\">“%1$s” 폴더를 합치시겠습니까?</string>\n    <string name=\"file_job_merge_copy_message_format\">합치기를 선택하면 복사할 파일들 중 교체가 필요한 파일이 있을 때 대화 상자를 띄웁니다.</string>\n    <string name=\"file_job_merge_extract_message_format\">합치기를 선택하면 압축 해제할 파일들 중 교체가 필요한 파일이 있을 때 대화 상자를 띄웁니다.</string>\n    <string name=\"file_job_merge_move_message_format\">합치기를 선택하면 이동할 파일들 중 교체가 필요한 파일이 있을 때 대화 상자를 띄웁니다.</string>\n    <string name=\"file_job_merge_target_name\">기존 폴더</string>\n    <string name=\"file_job_merge_source_name\">와 합치기</string>\n    <string name=\"file_job_conflict_show_name\">새 파일 이름 선택</string>\n    <string name=\"file_job_conflict_name_hint\">새 이름</string>\n    <string name=\"file_job_archive_error_title_format\">“%1$s” 압축 중 오류</string>\n    <string name=\"file_job_archive_error_message_format\">“%1$s” 압축 파일을 생성하는 중에 오류가 발생하였습니다.\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">“%1$s” 복사 중 오류</string>\n    <string name=\"file_job_copy_error_message_format\">파일을 “%1$s” (으)로 복사하는 중에 오류가 발생하였습니다.\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">“%1$s” 압축 해제 중 오류</string>\n    <string name=\"file_job_extract_error_message_format\"> “%1$s” 파일의 압축을 해제하는 중에 오류가 발생하였습니다.\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">“%1$s” 이동 중 오류</string>\n    <string name=\"file_job_move_error_message_format\">파일을 “%1$s” (으)로 이동하는 중에 오류가 발생하였습니다.\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">삭제 중 오류</string>\n    <string name=\"file_job_delete_error_message_format\">“%1$s” 파일을 삭제하는 중에 오류가 발생하였습니다.\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">“%1$s” 이름 바꾸기 중 오류</string>\n    <string name=\"file_job_rename_error_message_format\">파일 이름을 “%1$s” (으)로 바꾸는 중에 오류가 발생하였습니다.\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">“%1$s” 소유자 변경 중 오류</string>\n    <string name=\"file_job_set_owner_error_message_format\">소유자를 “%1$s” (으)로 변경하는 중에 오류가 발생하였습니다.\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">“%1$s” 그룹 변경 중 오류</string>\n    <string name=\"file_job_set_group_error_message_format\">그룹을 “%1$s” (으)로 변경하는 중에 오류가 발생하였습니다.\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">“%1$s” 모드 변경 중 오류</string>\n    <string name=\"file_job_set_mode_error_message_format\">모드를 “%1$s” (으)로 변경하는 중에 오류가 발생하였습니다.\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">“%1$s” SELinux 컨텍스트 변경 중 오류</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">SELinux 컨텍스트를 “%1$s” (으)로 변경하는 중에 오류가 발생하였습니다.\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">SELinux 컨텍스트 복원 중 오류</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">“%1$s” 항목의 SELinux 컨텍스트를 복원하는 중에 오류가 발생하였습니다.\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">쓰기 중 오류</string>\n    <string name=\"file_job_write_error_message_format\">“%1$s” 항목을 쓰는 중에 오류가 발생하였습니다.\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">“%1$s” 항목의 정보를 가져오는 중에 오류가 발생하였습니다.</string>\n    <string name=\"file_job_remount_format\">“%1$s” 을(를) 쓰기 허용으로 다시 마운트</string>\n    <string name=\"file_job_remount_loading_format\">쓰기 허용으로 “%1$s” 다시 마운트 중…</string>\n    <string name=\"file_job_remount_success_format\">“%1$s” 을(를) 쓰기 허용으로 다시 마운트했습니다</string>\n    <string name=\"file_job_action_all\">이 선택을 모든 파일에 적용</string>\n\n    <string name=\"file_open_apk_message\">이 파일은 Android 패키지 파일로, 파일 내부를 열어 보거나 기기에 설치할 수 있습니다.</string>\n    <string name=\"file_open_as_title\">연결 프로그램…</string>\n    <string name=\"file_open_as_title_format\">“%1$s” 연결 프로그램</string>\n    <string name=\"file_open_as_type_text\">텍스트</string>\n    <string name=\"file_open_as_type_image\">이미지</string>\n    <string name=\"file_open_as_type_audio\">오디오</string>\n    <string name=\"file_open_as_type_video\">영상</string>\n    <string name=\"file_open_as_type_directory\">폴더</string>\n    <string name=\"file_open_as_type_any\">기타</string>\n    <string name=\"file_install_apk_from_background_title_format\">“%1$s” 설치 준비 완료</string>\n    <string name=\"file_install_apk_from_background_text\">눌러서 설치</string>\n    <string name=\"file_open_from_background_title_format\">“%1$s” 열기 준비 완료</string>\n    <string name=\"file_open_from_background_text\">눌러서 열기</string>\n    <string name=\"file_name_error_empty\">파일 이름을 입력하세요</string>\n    <string name=\"file_name_error_invalid\">올바르지 않은 파일 이름</string>\n    <string name=\"file_name_error_already_exists\">같은 이름을 가진 파일이 이미 존재합니다</string>\n    <string name=\"file_replace_message_format\">“%1$s” 항목을 교체하시겠습니까?</string>\n    <string name=\"file_delete_message_file_format\">“%1$s” 파일을 삭제하시겠습니까?</string>\n    <string name=\"file_delete_message_directory_format\">“%1$s” 폴더를 삭제하시겠습니까?</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"other\">%1$,d개 파일을 삭제하시겠습니까?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"other\">%1$,d개 폴더 및 그 하위 항목을 삭제하시겠습니까?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"other\">%1$,d개 항목을 삭제하시겠습니까?</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">압축 파일 만들기</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_create_archive_password\">비밀번호 (선택)</string>\n    <string name=\"file_add_bookmark_success\">즐겨찾기가 추가되었습니다</string>\n    <string name=\"file_create_file_title\">새 파일</string>\n    <string name=\"file_create_directory_title\">새 폴더</string>\n\n    <string name=\"file_list_title\">파일</string>\n    <plurals name=\"file_list_title_open_file\">\n        <item quantity=\"other\">파일 선택</item>\n    </plurals>\n    <string name=\"file_list_title_create_file\">파일 저장</string>\n    <plurals name=\"file_list_title_open_directory\">\n        <item quantity=\"other\">폴더 저장</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"other\">%1$,d개 폴더</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"other\">%1$,d개 파일</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">,\\u0020</string>\n    <string name=\"file_list_path_error_empty\">경로를 입력하세요</string>\n    <string name=\"file_list_path_error_invalid\">올바르지 않은 경로</string>\n    <string name=\"file_list_action_view_sort\">정렬 및 표시 방식</string>\n    <string name=\"file_list_action_view_list\">목록</string>\n    <string name=\"file_list_action_view_grid\">격자</string>\n    <string name=\"file_list_action_sort_by_name\">이름</string>\n    <string name=\"file_list_action_sort_by_type\">유형</string>\n    <string name=\"file_list_action_sort_by_size\">크기</string>\n    <string name=\"file_list_action_sort_by_last_modified\">수정된 날짜</string>\n    <string name=\"file_list_action_sort_order_ascending\">오름차순</string>\n    <string name=\"file_list_action_sort_directories_first\">폴더를 먼저 표시</string>\n    <string name=\"file_list_action_view_sort_path_specific\">이 폴더에만 적용</string>\n    <string name=\"file_list_action_new_task\">새 창</string>\n    <string name=\"file_list_action_navigate_up\">상위 폴더로</string>\n    <string name=\"file_list_action_navigate_to\">이동</string>\n    <string name=\"file_list_action_show_hidden_files\">숨김 파일 표시</string>\n    <string name=\"file_list_action_copy_path\">경로 복사</string>\n    <string name=\"file_list_action_open_in_terminal\">터미널에서 열기</string>\n    <string name=\"file_list_action_add_bookmark\">즐겨찾기에 추가</string>\n    <string name=\"file_list_action_create_shortcut\">바로가기 만들기</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">새 창에서 열기</string>\n    <string name=\"file_list_select_title_format\">%1$,d</string>\n    <string name=\"file_list_create_file_name_hint\">파일 이름</string>\n    <string name=\"file_list_open_current_directory_format\">“%1$s” 선택</string>\n    <string name=\"file_list_paste_move_title_format\">%1$,d개 항목 이동</string>\n    <string name=\"file_list_paste_copy_title_format\">%1$,d개 항목 복사</string>\n    <string name=\"file_list_paste_extract_title_format\">%1$,d개 항목 압축 해제</string>\n    <string name=\"file_list_paste_action_extract_here\">여기에 압축 풀기</string>\n    <string name=\"file_list_empty\">파일 없음</string>\n    <string name=\"file_list_action_create_file\">파일</string>\n    <string name=\"file_list_action_create_directory\">폴더</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">연결 프로그램</string>\n    <string name=\"file_item_action_extract\">압축 풀기</string>\n    <string name=\"file_item_action_archive\">압축</string>\n    <string name=\"file_item_action_properties\">속성</string>\n\n    <string name=\"file_properties_title_format\">“%1$s” 속성</string>\n    <string name=\"file_properties_basic\">기본</string>\n    <string name=\"file_properties_basic_name\">이름</string>\n    <string name=\"file_properties_basic_type\">유형</string>\n    <string name=\"file_properties_basic_type_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">%1$s (%2$s) 로의 바로가기</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">바로가기 대상</string>\n    <string name=\"file_properties_basic_size\">크기</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_contents\">내부</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"other\">%1$,d개 항목, 총 %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">최근 수정 날짜</string>\n    <string name=\"file_properties_basic_parent_directory\">상위 폴더</string>\n    <string name=\"file_properties_basic_archive_file\">압축 파일</string>\n    <string name=\"file_properties_basic_archive_entry\">압축된 항목</string>\n    <string name=\"file_properties_basic_free_space\">빈 공간</string>\n    <string name=\"file_properties_permission\">권한</string>\n    <string name=\"file_properties_permission_owner\">소유자</string>\n    <string name=\"file_properties_permission_group\">그룹</string>\n    <string name=\"file_properties_permission_principal_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_permission_set_owner_title\">소유자 변경</string>\n    <string name=\"file_properties_permission_set_group_title\">그룹 변경</string>\n    <string name=\"file_properties_permission_set_principal_filter_hint\">이름 또는 ID를 입력하세요</string>\n    <string name=\"file_properties_permission_set_principal_system\">시스템</string>\n    <string name=\"file_properties_permission_mode\">모드</string>\n    <string name=\"file_properties_permission_mode_format\">%1$s (%2$04o)</string>\n    <string name=\"file_properties_permission_set_mode_title\">모드 변경</string>\n    <string name=\"file_properties_permission_set_mode_others\">그 외</string>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_file\">\n        <item>읽기</item>\n        <item>쓰기</item>\n        <item>실행</item>\n    </string-array>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_directory\">\n        <item>읽기</item>\n        <item>쓰기</item>\n        <item>검색</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_special\">특수</string>\n    <string-array name=\"file_properties_permission_set_mode_special_mode_bits\">\n        <item>UID 지정</item>\n        <item>GID 지정</item>\n        <item>스티키 비트</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_uppercase_x\">내부 파일에 “실행” 권한 부여 안 함</string>\n    <string name=\"file_properties_permission_selinux_context\">SELinux 컨텍스트</string>\n    <string name=\"file_properties_permission_set_selinux_context_title\">SELinux 컨텍스트 변경</string>\n    <string name=\"file_properties_permission_set_selinux_context_restore\">복원</string>\n    <string name=\"file_properties_permission_recursive\">내부 파일에도 적용</string>\n    <string name=\"file_properties_image\">이미지</string>\n    <string name=\"file_properties_media_dimensions\">크기</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">촬영된 시각</string>\n    <string name=\"file_properties_media_coordinates\">좌표</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f, %2$.3f</string>\n    <string name=\"file_properties_media_address\">위치</string>\n    <string name=\"file_properties_image_gps_altitude\">고도</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f m</string>\n    <string name=\"file_properties_image_equipment\">카메라</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">조리개 값</string>\n    <string name=\"file_properties_image_shutter_speed\">셔터 속도</string>\n    <string name=\"file_properties_image_focal_length\">초점 거리</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2fmm</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">ISO 감도</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">소프트웨어</string>\n    <string name=\"file_properties_image_description\">설명</string>\n    <string name=\"file_properties_image_artist\">만든이</string>\n    <string name=\"file_properties_image_copyright\">저작권</string>\n    <string name=\"file_properties_audio\">오디오</string>\n    <string name=\"file_properties_media_title\">제목</string>\n    <string name=\"file_properties_audio_artist\">아티스트</string>\n    <string name=\"file_properties_audio_album\">앨범</string>\n    <string name=\"file_properties_audio_album_artist\">앨범 아티스트</string>\n    <string name=\"file_properties_audio_composer\">작곡가</string>\n    <string name=\"file_properties_audio_disc_number\">디스크</string>\n    <string name=\"file_properties_audio_track_number\">트랙</string>\n    <string name=\"file_properties_audio_year\">년도</string>\n    <string name=\"file_properties_audio_genre\">장르</string>\n    <string name=\"file_properties_media_duration\">길이</string>\n    <string name=\"file_properties_media_bit_rate\">비트레이트</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d kbps</string>\n    <string name=\"file_properties_audio_sample_rate\">샘플링 레이트</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Hz</string>\n    <string name=\"file_properties_video\">영상</string>\n    <string name=\"file_properties_apk\">APK</string>\n    <string name=\"file_properties_apk_label\">이름</string>\n    <string name=\"file_properties_apk_package_name\">패키지 이름</string>\n    <string name=\"file_properties_apk_version\">버전</string>\n    <string name=\"file_properties_apk_version_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_apk_min_sdk_version\">최소 시스템 버전</string>\n    <string name=\"file_properties_apk_target_sdk_version\">타겟 시스템 버전</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s (%2$s, %3$d)</string>\n    <string name=\"file_properties_apk_requested_permissions\">권한</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">요청한 권한 없음</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"other\">%1$,d개 권한 요청함</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">서명</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">올바르지 않은 서명</string>\n    <string name=\"file_properties_apk_past_signature_digests\">이전 서명</string>\n    <string name=\"file_properties_checksum\">체크섬</string>\n    <string name=\"file_properties_checksum_compare\">비교</string>\n    <string name=\"file_properties_checksum_compare_placeholder\">아무 체크섬 값</string>\n    <string name=\"file_properties_checksum_compare_match_format\">%1$s의 체크섬과 일치</string>\n    <string name=\"file_properties_checksum_compare_prefix_match_format\">%1$s의 체크섬 앞부분과 일치</string>\n    <string name=\"file_properties_checksum_compare_no_match\">일치하는 체크섬 없음</string>\n    <string name=\"storage_file_system_root_title\">루트</string>\n    <string name=\"storage_list_title\">저장소</string>\n    <string name=\"storage_list_empty\">저장소 없음</string>\n    <string name=\"storage_add_storage_title\">저장소 추가</string>\n    <string name=\"storage_add_storage_document_tree\">외부 저장소</string>\n    <string name=\"storage_add_storage_ftp_server\">FTP 서버</string>\n    <string name=\"storage_add_storage_sftp_server\">SFTP 서버</string>\n    <string name=\"storage_add_storage_smb_server\">SMB 서버</string>\n    <string name=\"storage_add_storage_webdav_server\">WebDAV 서버</string>\n    <string name=\"storage_edit_device_storage_title\">기기 저장소 편집</string>\n    <string name=\"storage_edit_device_storage_name\">이름</string>\n    <string name=\"storage_edit_device_storage_path\">경로</string>\n    <string name=\"storage_add_external_storage_shortcut_title\">외부 저장소 바로가기 추가</string>\n    <string name=\"storage_edit_external_storage_shortcut_title\">외부 저장소 바로가기 편집</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id\">저장소 볼륨</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id_error_empty\">저장소 볼륨을 입력하세요</string>\n    <string name=\"storage_edit_external_storage_shortcut_path\">경로</string>\n    <string name=\"storage_add_document_tree_title\">외부 저장소 추가</string>\n    <string name=\"storage_edit_document_tree_title\">외부 저장소 편집</string>\n    <string name=\"storage_edit_document_tree_name\">이름</string>\n    <string name=\"storage_edit_document_tree_uri\">URI</string>\n    <string name=\"storage_edit_document_tree_path\">경로</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">FTP 서버 편집</string>\n    <string name=\"storage_edit_ftp_server_title_add\">FTP 서버 추가</string>\n    <string name=\"storage_edit_ftp_server_host\">호스트 이름</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">호스트 이름을 입력하세요</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">올바르지 않은 호스트 이름</string>\n    <string name=\"storage_edit_ftp_server_port\">포트</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">올바르지 않은 포트</string>\n    <string name=\"storage_edit_ftp_server_path\">경로</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">비워둘 수 없음</string>\n    <string name=\"storage_edit_ftp_server_name\">이름</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">호스트 이름 사용</string>\n    <string name=\"storage_edit_ftp_server_protocol\">프로토콜</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">인증</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>비밀번호</item>\n        <item>익명</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">사용자 이름</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">사용자 이름을 입력하세요</string>\n    <string name=\"storage_edit_ftp_server_password\">비밀번호</string>\n    <string name=\"storage_edit_ftp_server_mode\">모드</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>액티브</item>\n        <item>패시브</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">인코딩</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">연결 및 추가</string>\n    <string name=\"storage_edit_ftp_server_add\">추가</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">SFTP 서버 편집</string>\n    <string name=\"storage_edit_sftp_server_title_add\">SFTP 서버 추가</string>\n    <string name=\"storage_edit_sftp_server_host\">호스트 이름</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">호스트 이름을 입력하세요</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">올바르지 않은 호스트 이름</string>\n    <string name=\"storage_edit_sftp_server_port\">포트</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">올바르지 않은 포트</string>\n    <string name=\"storage_edit_sftp_server_path\">경로</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">비워둘 수 있음</string>\n    <string name=\"storage_edit_sftp_server_name\">이름</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">호스트 이름 사용</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">인증</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>비밀번호</item>\n        <item>공개 키</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">사용자 이름</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">사용자 이름을 입력하세요</string>\n    <string name=\"storage_edit_sftp_server_password\">비밀번호</string>\n    <string name=\"storage_edit_sftp_server_private_key\">개인 키</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">파일 열기</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">개인 키를 입력하세요</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">올바르지 않은 개인 키</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">개인 키 비밀번호</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">비워둘 수 없음</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">올바르지 않은 개인 키 비밀번호</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">연결 및 추가</string>\n    <string name=\"storage_edit_sftp_server_add\">추가</string>\n    <string name=\"storage_add_lan_smb_server_loading\">SMB 서버 검색 중…</string>\n    <string name=\"storage_add_lan_smb_server_add\">직접 추가</string>\n    <string name=\"storage_edit_smb_server_title_edit\">SMB 서버 편집</string>\n    <string name=\"storage_edit_smb_server_title_add\">SMB 서버 추가</string>\n    <string name=\"storage_edit_smb_server_host\">호스트 이름</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">호스트 이름을 입력하세요</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">올바르지 않은 호스트 이름</string>\n    <string name=\"storage_edit_smb_server_port\">포트</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">올바르지 않은 포트</string>\n    <string name=\"storage_edit_smb_server_path\">경로</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">비워둘 수 있음</string>\n    <string name=\"storage_edit_smb_server_name\">이름</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">호스트 이름 사용</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">인증</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>비밀번호</item>\n        <item>게스트</item>\n        <item>익명</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">사용자 이름</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">사용자 이름을 입력하세요</string>\n    <string name=\"storage_edit_smb_server_password\">비밀번호</string>\n    <string name=\"storage_edit_smb_server_domain\">도메인</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">연결 및 추가</string>\n    <string name=\"storage_edit_smb_server_add\">추가</string>\n    <string name=\"storage_edit_webdav_server_title_edit\">WebDAV 서버 편집</string>\n    <string name=\"storage_edit_webdav_server_title_add\">WebDAV 서버 추가</string>\n    <string name=\"storage_edit_webdav_server_host\">호스트 이름</string>\n    <string name=\"storage_edit_webdav_server_host_error_empty\">호스트 이름을 입력하세요</string>\n    <string name=\"storage_edit_webdav_server_host_error_invalid\">올바르지 않은 호스트 이름</string>\n    <string name=\"storage_edit_webdav_server_port\">포트</string>\n    <string name=\"storage_edit_webdav_server_port_error_invalid\">올바르지 않은 포트</string>\n    <string name=\"storage_edit_webdav_server_path\">경로</string>\n    <string name=\"storage_edit_webdav_server_path_placeholder\">비워둘 수 있음</string>\n    <string name=\"storage_edit_webdav_server_name\">이름</string>\n    <string name=\"storage_edit_webdav_server_name_placeholder\">호스트 이름 사용</string>\n    <string name=\"storage_edit_webdav_server_protocol\">프로토콜</string>\n    <string name=\"storage_edit_webdav_server_authentication_type\">인증</string>\n    <string-array name=\"storage_edit_webdav_server_authentication_type_entries\">\n        <item>비밀번호</item>\n        <item>액세스 토큰</item>\n        <item>없음</item>\n    </string-array>\n    <string name=\"storage_edit_webdav_server_username\">사용자 이름</string>\n    <string name=\"storage_edit_webdav_server_username_error_empty\">사용자 이름을 입력하세요</string>\n    <string name=\"storage_edit_webdav_server_password\">비밀번호</string>\n    <string name=\"storage_edit_webdav_server_access_token\">액세스 토큰</string>\n    <string name=\"storage_edit_webdav_server_access_token_error_empty\">액세스 토큰을 입력하세요</string>\n    <string name=\"storage_edit_webdav_server_connect_and_add\">연결 및 추가</string>\n    <string name=\"storage_edit_webdav_server_add\">추가</string>\n\n    <string name=\"navigation_storage_subtitle_format\">%2$s 중 %1$s 남음</string>\n    <string name=\"navigation_add_storage\">저장소 추가…</string>\n    <string name=\"navigation_standard_directory_alarms\">알람 벨소리</string>\n    <string name=\"navigation_standard_directory_dcim\">DCIM</string>\n    <string name=\"navigation_standard_directory_documents\">문서</string>\n    <string name=\"navigation_standard_directory_downloads\">다운로드</string>\n    <string name=\"navigation_standard_directory_movies\">영화</string>\n    <string name=\"navigation_standard_directory_music\">음악</string>\n    <string name=\"navigation_standard_directory_notifications\">알림 소리</string>\n    <string name=\"navigation_standard_directory_pictures\">사진</string>\n    <string name=\"navigation_standard_directory_podcasts\">팟캐스트</string>\n    <string name=\"navigation_standard_directory_ringtones\">벨소리</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">즐겨찾기 폴더</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">이름</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">경로</string>\n    <string name=\"archive_viewer_title\">압축 파일 뷰어</string>\n    <string name=\"image_viewer_title\">이미지 뷰어</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"save_as_title\">다른 이름으로 저장</string>\n    <string name=\"save_as_error\">파일 저장 중 오류</string>\n    <string name=\"save_as_success_format\">“%1$s” 항목을 “%2$s”에 저장했습니다.</string>\n    <string name=\"text_editor_title\">텍스트 편집기</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">저장됨</string>\n    <string name=\"text_editor_reload_message\">정말 다시 불러오시겠습니까? 이 문서의 저장하지 않은 내용은 사라집니다.</string>\n    <string name=\"text_editor_encoding\">인코딩</string>\n    <string name=\"text_editor_close_message\">정말 이 문서의 저장하지 않은 내용을 버리시겠습니까?</string>\n\n    <string name=\"ftp_server_title\">FTP 서버</string>\n    <string name=\"ftp_server_state_title\">상태</string>\n    <string name=\"ftp_server_state_summary_starting\">시작하는 중…</string>\n    <string name=\"ftp_server_state_summary_running\">실행 중</string>\n    <string name=\"ftp_server_state_summary_stopping\">중지하는 중…</string>\n    <string name=\"ftp_server_state_summary_stopped\">시작되지 않음</string>\n    <string name=\"ftp_server_url_title\">URL</string>\n    <string name=\"ftp_server_url_summary_no_local_inet_address\">로컬 IP 주소 알 수 없음</string>\n    <string name=\"ftp_server_url_menu_copy_url\">URL 복사</string>\n    <string name=\"ftp_server_url_menu_copy_password\">비밀번호 복사</string>\n    <string name=\"ftp_server_add_tile_title\">빠른 설정에 추가</string>\n    <string name=\"ftp_server_add_tile_result_added\">“FTP 서버”를 빠른 설정에 추가했습니다</string>\n    <string name=\"ftp_server_add_tile_result_already_added\">“FTP 서버”는 이미 빠른 설정에 추가되어 있습니다</string>\n    <string name=\"ftp_server_add_tile_result_error\">빠른 설정에 “FTP 서버”를 추가하는 중 오류</string>\n    <string name=\"ftp_server_configuration_title\">설정</string>\n    <string name=\"ftp_server_anonymous_login_title\">익명으로 로그인 허용</string>\n    <string name=\"ftp_server_username_title\">사용자 이름</string>\n    <string name=\"ftp_server_password_title\">비밀번호</string>\n    <string name=\"ftp_server_port_title\">포트</string>\n    <string name=\"ftp_server_home_directory_title\">루트 폴더</string>\n    <string name=\"ftp_server_writable_title\">쓰기 허용</string>\n    <string name=\"settings_title\">설정</string>\n    <string name=\"settings_interface_title\">인터페이스</string>\n    <string name=\"settings_locale_title\">언어</string>\n    <string name=\"settings_theme_color_title\">테마 색상</string>\n    <string name=\"settings_theme_color_summary\">앱에서 가장 많이 보이는 색상</string>\n    <string name=\"settings_material_design_3_title\">머티리얼 디자인 3</string>\n    <string name=\"settings_night_mode_title\">다크 모드</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>시스템 설정에 따름</item>\n        <item>끄기</item>\n        <item>켜기</item>\n        <item>시간대에 따름</item>\n        <item>배터리 절약 모드 사용 시</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">다크 모드에 검정색 사용</string>\n    <string name=\"settings_file_list_animation_title\">파일 목록 애니메이션</string>\n    <string name=\"settings_file_name_ellipsize_title\">긴 파일 이름 표시</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>앞부분 생략</item>\n        <item>중간 부분 생략</item>\n        <item>뒷부분 생략</item>\n        <item>애니메이션으로 표시</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">세부 기능</string>\n    <string name=\"settings_default_directory_title\">기본 폴더</string>\n    <string name=\"settings_standard_directories_title\">주요 폴더</string>\n    <string name=\"settings_standard_directories_summary_empty\">설정된 주요 폴더 없음</string>\n    <string name=\"settings_bookmark_directory_screenshots\">스크린샷</string>\n    <string name=\"settings_bookmark_directories_title\">즐겨찾기 폴더</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">즐겨찾기된 폴더 없음</string>\n    <string name=\"settings_root_strategy_title\">루트 액세스 모드</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"other\">아직 %1$,d개의 파일 작업이 진행 중입니다. 지금 루트 액세스 모드를 변경하면 예기치 못한 오류가 발생할 수 있습니다. 정말 지금 변경하시겠습니까?</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>일반 액세스만</item>\n        <item>자동</item>\n        <item>루트 액세스만</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">압축 파일 이름 인코딩</string>\n    <string name=\"settings_open_apk_default_action_title\">Android 패키지(APK)를 열 때 작업</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>설치</item>\n        <item>내용 보기</item>\n        <item>물어보기</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_title\">PDF 문서 섬네일 표시</string>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">Android 버전이 낮을 경우 앱이 불안정해질 수 있습니다.</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">원격 파일에서 섬네일 읽기</string>\n\n    <string name=\"about_title\">정보</string>\n    <string name=\"about_version_title\">버전</string>\n    <string name=\"about_github_title\">GitHub에서 보기</string>\n    <string name=\"about_licenses_title\">라이선스</string>\n    <string name=\"about_privacy_policy_title\">개인 정보 보호 정책</string>\n    <string name=\"about_author_title\">제작자</string>\n    <string name=\"about_author_name_title\">Hai Zhang</string>\n    <string name=\"about_author_github_title\">GitHub 팔로우하기</string>\n    <string name=\"about_author_twitter_title\">Twitter 팔로우하기</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-lt/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">Failas</string>\n    <string name=\"file_type_name_apk\">Android paketas</string>\n    <string name=\"file_type_name_archive\">%1$s archyvas</string>\n    <string name=\"file_type_name_audio\">%1$s garso įrašas</string>\n    <string name=\"file_type_name_calendar\">%1$s kalendorius</string>\n    <string name=\"file_type_name_certificate\">%1$s sertifikatas</string>\n    <string name=\"file_type_name_code\">%1$s dokumentas</string>\n    <string name=\"file_type_name_contact\">Elektroninė verslo kortelė</string>\n    <string name=\"file_type_name_directory\">Aplankas</string>\n    <string name=\"file_type_name_document\">%1$s dokumentas</string>\n    <string name=\"file_type_name_ebook\">%1$s e. knyga</string>\n    <string name=\"file_type_name_email\">%1$s el. pašto žinutė</string>\n    <string name=\"file_type_name_font\">%1$s šriftas</string>\n    <string name=\"file_type_name_generic\">%1$s failas</string>\n    <string name=\"file_type_name_image\">%1$s vaizdas</string>\n    <string name=\"file_type_name_pdf\">PDF dokumentas</string>\n    <string name=\"file_type_name_presentation\">%1$s pristatymas</string>\n    <string name=\"file_type_name_spreadsheet\">%1$s skaičiuoklė</string>\n    <string name=\"file_type_name_text\">%1$s dokumentas</string>\n    <string name=\"file_type_name_text_plain\">Paprasto teksto dokumentas</string>\n    <string name=\"file_type_name_video\">%1$s vaizdo įrašas</string>\n    <string name=\"file_type_name_word\">Word dokumentas</string>\n    <string name=\"file_type_name_powerpoint\">PowerPoint pristatymas</string>\n    <string name=\"file_type_name_excel\">Excel skaičiuoklė</string>\n    <string name=\"file_type_name_posix_character_device\">Simbolių įrenginys</string>\n    <string name=\"file_type_name_posix_block_device\">Blokavimo įrenginys</string>\n    <string name=\"file_type_name_posix_fifo\">Vamzdis</string>\n    <string name=\"file_type_name_posix_symbolic_link\">Nuoroda</string>\n    <string name=\"file_type_name_posix_socket\">Lizdas</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">Nuoroda (neveikianti)</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-lt/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:ignore=\"ImpliedQuantity\">\n\n    <string name=\"app_name\">Material Failai</string>\n\n    <string name=\"close\">Uždaryti</string>\n    <string name=\"copy\">Kopijuoti</string>\n    <string name=\"cut\">Iškirpti</string>\n    <string name=\"default_\">Numatytasis</string>\n    <string name=\"delete\">Ištrinti</string>\n    <string name=\"discard\">Atmesti</string>\n    <string name=\"edit\">Redaguoti</string>\n    <string name=\"empty\">Tuščia</string>\n    <string name=\"empty_placeholder\">(Tuščia)</string>\n    <string name=\"error\">Klaida</string>\n    <string name=\"hide\">Slėpti</string>\n    <string name=\"install\">Įdiegti</string>\n    <string name=\"keep_editing\">Tęsti redagavimą</string>\n    <string name=\"loading\">Kraunama…</string>\n    <string name=\"maybe_later\">Gal vėliau</string>\n    <string name=\"merge\">Sujungti</string>\n    <string name=\"none\">Nėra</string>\n    <string name=\"open_navigation_drawer\">Atidaryti navigacijos dėklą</string>\n    <string name=\"paste\">Įklijuoti</string>\n    <string name=\"refresh\">Atnaujinti</string>\n    <string name=\"reload\">Įkelti iš naujo</string>\n    <string name=\"remove\">Pašalinti</string>\n    <string name=\"rename\">Pervadinti</string>\n    <string name=\"replace\">Pakeisti</string>\n    <string name=\"reset\">Nustatyti iš naujo</string>\n    <string name=\"retry\">Bandyti dar kartą</string>\n    <string name=\"save\">Išsaugoti</string>\n    <string name=\"search\">Ieškoti</string>\n    <string name=\"select\">Pasirinkti</string>\n    <string name=\"select_all\">Pasirinkti viską</string>\n    <string name=\"share\">Bendrinti</string>\n    <string name=\"show\">Rodyti</string>\n    <string name=\"skip\">Praleisti</string>\n    <string name=\"stop\">Sustabdyti</string>\n    <string name=\"system_default\">Sistemos numatytasis</string>\n    <string name=\"unknown\">Nežinomas</string>\n    <string name=\"view\">Peržiūrėti</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">Nerasta jokių programų, galinčių atlikti šį veiksmą</string>\n    <string name=\"open_settings\">Atidaryti nustatymus</string>\n    <string name=\"shortcut_created\">Sukurtas trumpasis kelias</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"one\">%1$,d baitas</item>\n        <item quantity=\"few\">%1$,d baitai</item>\n        <item quantity=\"many\">%1$,d baitų</item>\n        <item quantity=\"other\">%1$,d baitai</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">Programėlei reikia prieigos, kad būtų galima valdyti visus failus. Leisk prieigą būsimame sistemos nustatyme.</string>\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">Programėlei reikia leidimo pasiekti failus. Būsimame sistemos dialogo lange spustelėk „LEISTI“.</string>\n    <string name=\"storage_permission_permanently_denied_message\">Programėlei reikia leidimo pasiekti failus. Suteik saugyklos leidimą sistemos nustatymuose.</string>\n    <string name=\"notification_channel_background_activity_start_name\">Veiksmai, kol fone</string>\n    <string name=\"notification_channel_background_activity_start_description\">Daryti veiksmus kai programėlė veikia fone</string>\n    <string name=\"notification_channel_file_job_name\">Failo operacijos</string>\n    <string name=\"notification_channel_file_job_description\">Rodyti ir valdyti failų operacijas</string>\n    <string name=\"notification_channel_ftp_server_name\">FTP serveris</string>\n    <string name=\"notification_channel_ftp_server_description\">Rodyti ir valdyti FTP serverį</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"one\">Ruošiamasi suspausti %1$,d failą (%2$s)</item>\n        <item quantity=\"few\">Ruošiamasi suspausti %1$,d failus (%2$s)</item>\n        <item quantity=\"many\">Ruošiamasi suspausti %1$,d failų (%2$s)</item>\n        <item quantity=\"other\">Ruošiami suspausti %1$,d failus (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">Suspaudžiama „%1$s“ į „%2$s“</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"one\">Suspaudžiamas %1$,d failas į „%2$s“</item>\n        <item quantity=\"few\">Suspaudžiami %1$,d failai į „%2$s“</item>\n        <item quantity=\"many\">Suspaudžiami %1$,d failų į „%2$s“</item>\n        <item quantity=\"other\">Suspaudžiami %1$,d failai į „%2$s“</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"one\">Ruošiamasi kopijuoti %1$,d failą (%2$s)</item>\n        <item quantity=\"few\">Ruošiamasi kopijuoti %1$,d failus (%2$s)</item>\n        <item quantity=\"many\">Ruošiamasi kopijuoti %1$,d failų (%2$s)</item>\n        <item quantity=\"other\">Ruošiami kopijuoti %1$,d failus (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">Kopijuojama „%1$s“ į „%2$s“</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"one\">Kopijuojamas %1$,d failas į „%2$s“</item>\n        <item quantity=\"few\">Kopijuojami %1$,d failai į „%2$s“</item>\n        <item quantity=\"many\">Kopijuojami %1$,d failų į „%2$s“</item>\n        <item quantity=\"other\">Kopijuojami %1$,d failai į „%2$s“</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"one\">Ruošiamasi išskleisti %1$,d failą (%2$s)</item>\n        <item quantity=\"few\">Ruošiamasi išskleisti %1$,d failus (%2$s)</item>\n        <item quantity=\"many\">Ruošiamasi išskleisti %1$,d failų (%2$s)</item>\n        <item quantity=\"other\">Ruošiami išskleisti %1$,d failus (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">Išskleidžiama „%1$s“ į „%2$s“</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"one\">Išskleidžiamas %1$,d failas į „%2$s“</item>\n        <item quantity=\"few\">Išskleidžiami %1$,d failai į „%2$s“</item>\n        <item quantity=\"many\">Išskleidžiami %1$,d failų į „%2$s“</item>\n        <item quantity=\"other\">Išskleidžiami %1$,d failai į „%2$s“</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"one\">Ruošiamasi perkelti %1$,d failą (%2$s)</item>\n        <item quantity=\"few\">Ruošiamasi perkelti %1$,d failus (%2$s)</item>\n        <item quantity=\"many\">Ruošiamasi perkelti %1$,d failų (%2$s)</item>\n        <item quantity=\"other\">Ruošiami perkelti %1$,d failus (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">Perkeliama „%1$s“ į „%2$s“</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"one\">Perkeliamas %1$,d failas į „%2$s“</item>\n        <item quantity=\"few\">Perkeliami %1$,d failai į „%2$s“</item>\n        <item quantity=\"many\">Perkeliami %1$,d failų į „%2$s“</item>\n        <item quantity=\"other\">Perkeliami %1$,d failai į „%2$s“</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"one\">Ruošiamasi ištrinti %1$,d failą</item>\n        <item quantity=\"few\">Ruošiamasi ištrinti %1$,d failus</item>\n        <item quantity=\"many\">Ruošiamasi ištrinti %1$,d failų</item>\n        <item quantity=\"other\">Ruošiami ištrinti %1$,d failus</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">Ištrinama „%1$s“</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"one\">Ištrinamas %1$,d failas</item>\n        <item quantity=\"few\">Ištrinami %1$,d failai</item>\n        <item quantity=\"many\">Ištrinami %1$,d failų</item>\n        <item quantity=\"other\">Ištrinami %1$,d failai</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"one\">Ruošiamasi keisti savininką %1$,d failui</item>\n        <item quantity=\"few\">Ruošiamasi keisti savininką %1$,d failams</item>\n        <item quantity=\"many\">Ruošiamasi keisti savininką %1$,d failų</item>\n        <item quantity=\"other\">Ruošiami keisti savininką %1$,d failams</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">Keičiamas „%1$s“ savininkas</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"one\">Keičiamas %1$,d failo savininkas</item>\n        <item quantity=\"few\">Keičiamas %1$,d failų savininkas</item>\n        <item quantity=\"many\">Keičiamas %1$,d failų savininkas</item>\n        <item quantity=\"other\">Keičiamas %1$,d failų savininkas</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"one\">Ruošiamasi keisti %1$,d failo grupę</item>\n        <item quantity=\"few\">Ruošiamasi keisti %1$,d failam grupę</item>\n        <item quantity=\"many\">Ruošiamasi keisti %1$,d failų grupę</item>\n        <item quantity=\"other\">Ruošiami keisti %1$,d failų grupę</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">Keičiama „%1$s“ grupė</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"one\">Keičiama %1$,d failo grupė</item>\n        <item quantity=\"few\">Keičiama %1$,d failų grupė</item>\n        <item quantity=\"many\">Keičiama %1$,d failų grupė</item>\n        <item quantity=\"other\">Keičiama %1$,d failų grupė</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"one\">Ruošiamasi keisti %1$,d failo režimą</item>\n        <item quantity=\"few\">Ruošiamasi keisti %1$,d failų režimą</item>\n        <item quantity=\"many\">Ruošiamasi keisti %1$,d failų režimą</item>\n        <item quantity=\"other\">Ruošiami keisti %1$,d failų režimą</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">Keičiamas „%1$s“ režimas</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"one\">Keičiamas režimas %1$,d failui</item>\n        <item quantity=\"few\">Keičiamas režimas %1$,d failams</item>\n        <item quantity=\"many\">Keičiamas režimas %1$,d failų</item>\n        <item quantity=\"other\">Keičiamas režimas %1$,d failams</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Ruošiami keisti SELinux kontekstą %1$,d failui</item>\n        <item quantity=\"few\">Ruošiami keisti SELinux kontekstą %1$,d failams</item>\n        <item quantity=\"many\">Ruošiami keisti SELinux kontekstą %1$,d failų</item>\n        <item quantity=\"other\">Ruošiami keisti SELinux kontekstą %1$,d failus</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">Keičiamas SELinux kontekstas „%1$s“</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Keičiamas SELinux kontekstas %1$,d failui</item>\n        <item quantity=\"few\">Keičiamas SELinux kontekstas %1$,d failams</item>\n        <item quantity=\"many\">Keičiamas SELinux kontekstas %1$,d failų</item>\n        <item quantity=\"other\">Keičiamas SELinux kontekstas %1$,d failus</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Ruošiamasi atkurti SELinux kontekstą %1$,d failui</item>\n        <item quantity=\"few\">Ruošiamasi atkurti SELinux kontekstą %1$,d failams</item>\n        <item quantity=\"many\">Ruošiamasi atkurti SELinux kontekstą %1$,d failų</item>\n        <item quantity=\"other\">Ruošiamasi atkurti SELinux kontekstą %1$,d failus</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">Atkuriamas SELinux kontekstas „%1$s“</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Atkuriamas SELinux kontekstas %1$,d failui</item>\n        <item quantity=\"few\">SELinux konteksto atkūrimas %1$,d failams</item>\n        <item quantity=\"many\">Atkuriamas SELinux kontekstas %1$,d failų</item>\n        <item quantity=\"other\">SELinux konteksto atkūrimas %1$,d failus</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">Rašoma “%1$s”</string>\n    <string name=\"file_job_create_error_title\">Klaida kuriant</string>\n    <string name=\"file_job_create_error_message_format\">Įvyko klaida sukuriant “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">Negalima nukopijuoti aplanko į jį patį</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">Negalima išskleisti aplanko į jį patį</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">Negalima perkelti aplanko į jį patį</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">Paskirties aplankas yra šaltinio aplanke.</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">Negalima nukopijuoti failo į jį patį</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">Negalima išskleisti failo į jį patį</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">Negalima perkelti failo į jį patį</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">Šaltinio failas būtų perrašytas paskirties vietos.</string>\n    <string name=\"file_job_replace_title_format\">Pakeisti failą \\\"%1$s\\\"?</string>\n    <string name=\"file_job_replace_message_format\">Kitas tokio paties pavadinimo failas jau yra “%1$s”.\\nPakeitus jį, bus perrašytas jo turinys.</string>\n    <string name=\"file_job_replace_target_name\">Originalus failas</string>\n    <string name=\"file_job_replace_source_name\">Pakeisti su</string>\n    <string name=\"file_job_merge_title_format\">Sujungti aplanką \\\"%1$s\\\"?</string>\n    <string name=\"file_job_merge_copy_message_format\">Prieš pakeičiant bet kokius aplanke esančius failus, kurie prieštarauja kopijuojamiems failams, bus paprašyta patvirtinimo.</string>\n    <string name=\"file_job_merge_extract_message_format\">Prieš pakeičiant bet kokius aplanke esančius failus, kurie prieštarauja išskleistiems failams, bus paprašyta patvirtinimo.</string>\n    <string name=\"file_job_merge_move_message_format\">Prieš pakeičiant bet kokius aplanke esančius failus, kurie prieštarauja perkeliamiems failams, bus paprašyta patvirtinimo.</string>\n    <string name=\"file_job_merge_target_name\">Originalus aplankas</string>\n    <string name=\"file_job_merge_source_name\">Sujungti su</string>\n    <string name=\"file_job_conflict_show_name\">Pasirinkite naują paskirties vietos pavadinimą</string>\n    <string name=\"file_job_conflict_name_hint\">Naujas pavadinimas</string>\n    <string name=\"file_job_archive_error_title_format\">Įvyko klaida suspaudžiant “%1$s”</string>\n    <string name=\"file_job_archive_error_message_format\">Įvyko klaida suspaudžiant failą į “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">Klaida kopijuojant “%1$s”</string>\n    <string name=\"file_job_copy_error_message_format\">Įvyko klaida kopijuojant failą į “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">Klaida išskleidžiant “%1$s”</string>\n    <string name=\"file_job_extract_error_message_format\">Įvyko klaida išskleidžiant failą į “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">Įvyko klaida perkeliant “%1$s”</string>\n    <string name=\"file_job_move_error_message_format\">Įvyko klaida perkeliant failą į “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">Įvyko klaida trinant</string>\n    <string name=\"file_job_delete_error_message_format\">Įvyko klaida trinant “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">Įvyko klaida pervadinant “%1$s”</string>\n    <string name=\"file_job_rename_error_message_format\">Įvyko klaida pervadinant failą į “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">Įvyko klaida keičiant “%1$s” savininką</string>\n    <string name=\"file_job_set_owner_error_message_format\">Įvyko klaida keičiant savininką į “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">Įvyko klaida keičiant “%1$s” grupę</string>\n    <string name=\"file_job_set_group_error_message_format\">Įvyko klaida keičiant grupę į “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">Įvyko klaida keičiant “%1$s” režimą</string>\n    <string name=\"file_job_set_mode_error_message_format\">Įvyko klaida keičiant režimą į “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">Įvyko klaida keičiant “%1$s” SELinux kontekstą</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">Įvyko klaida keičiant SELinux kontekstą į “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">Įvyko klaida atkuriant SELinux kontekstą</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">Įvyko klaida atkuriant SELinux kontekstą „%1$s“.\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">Klaida rašant</string>\n    <string name=\"file_job_write_error_message_format\">Įvyko klaida rašant “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">Įvyko klaida gaunant informaciją apie “%1$s”.</string>\n    <string name=\"file_job_remount_format\">Permontuoti „%1$s“ kaip skaitymo ir rašymo</string>\n    <string name=\"file_job_remount_loading_format\">Permontavimas „%1$s“ kaip skaitymo ir rašymo…</string>\n    <string name=\"file_job_remount_success_format\">„%1$s“ buvo permontuotas kaip skaitymo ir rašymo</string>\n    <string name=\"file_job_action_all\">Taikyti šį veiksmą visiems failams</string>\n\n    <string name=\"file_open_apk_message\">Šis failas yra Android paketas. Ar norite jį įdiegti arba peržiūrėti jo turinį?</string>\n    <string name=\"file_open_as_title\">Atidaryti kaip…</string>\n    <string name=\"file_open_as_title_format\">Atidaryti \\\"%1$s\\\" kaip</string>\n    <string name=\"file_open_as_type_text\">Tekstas</string>\n    <string name=\"file_open_as_type_image\">Nuotrauka</string>\n    <string name=\"file_open_as_type_audio\">Garso įrašas</string>\n    <string name=\"file_open_as_type_video\">Vaizdo įrašas</string>\n    <string name=\"file_open_as_type_directory\">Aplankas</string>\n    <string name=\"file_open_as_type_any\">Kiti</string>\n    <string name=\"file_install_apk_from_background_title_format\">“%1$s” yra paruoštas įdiegimui</string>\n    <string name=\"file_install_apk_from_background_text\">Bakstelėkite, kad įdiegti</string>\n    <string name=\"file_open_from_background_title_format\">\\\"%1$s\\\" yra paruoštas atidarymui</string>\n    <string name=\"file_open_from_background_text\">Bakstelėkite, kad atidaryti</string>\n    <string name=\"file_name_error_empty\">Failo pavadinimas negali būti tuščias</string>\n    <string name=\"file_name_error_invalid\">Netinkamas failo pavadinimas</string>\n    <string name=\"file_name_error_already_exists\">Failas šiuo pavadinimu jau egzistuoja</string>\n    <string name=\"file_delete_message_file_format\">Ištrinti \\\"%1$s\\\"?</string>\n    <string name=\"file_delete_message_directory_format\">Ištrinti aplanką \\\"%1$s\\\" ir jo turinį?</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"one\">Ištrinti %1$,d failą?</item>\n        <item quantity=\"few\">Ištrinti %1$,d failus?</item>\n        <item quantity=\"many\">Ištrinti %1$,d failų?</item>\n        <item quantity=\"other\">Ištrinti %1$,d failus?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"one\">Ištrinti %1$,d aplanką ir jo turinį?</item>\n        <item quantity=\"few\">Ištrinti %1$,d aplankus ir jų turinį?</item>\n        <item quantity=\"many\">Ištrinti %1$,d aplankų ir jų turinį?</item>\n        <item quantity=\"other\">Ištrinti %1$,d aplankus ir jų turinį?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"one\">Ištrinti %1$,d įrašą?</item>\n        <item quantity=\"few\">Ištrinti %1$,d įrašus?</item>\n        <item quantity=\"many\">Ištrinti %1$,d įrašų?</item>\n        <item quantity=\"other\">Ištrinti %1$,d įrašus?</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">Sukurti archyvą</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_add_bookmark_success\">Pridėta žymė</string>\n    <string name=\"file_create_file_title\">Naujas failas</string>\n    <string name=\"file_create_directory_title\">Naujas aplankas</string>\n\n    <string name=\"file_list_title\">Failai</string>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"one\">%1$,d aplankas</item>\n        <item quantity=\"few\">%1$,d aplankai</item>\n        <item quantity=\"many\">%1$,d aplankų</item>\n        <item quantity=\"other\">%1$,d aplankai</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"one\">%1$,d failas</item>\n        <item quantity=\"few\">%1$,d failai</item>\n        <item quantity=\"many\">%1$,d failų</item>\n        <item quantity=\"other\">%1$,d failai</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">,\\u0020</string>\n    <string name=\"file_list_path_error_empty\">Kelias negali būti tuščias</string>\n    <string name=\"file_list_path_error_invalid\">Netinkamas kelias</string>\n    <string name=\"file_list_action_view_sort\">Peržiūra ir rūšiavimas</string>\n    <string name=\"file_list_action_view_list\">Sąrašas</string>\n    <string name=\"file_list_action_view_grid\">Tinklelis</string>\n    <string name=\"file_list_action_sort_by_name\">Pavadinimas</string>\n    <string name=\"file_list_action_sort_by_type\">Tipas</string>\n    <string name=\"file_list_action_sort_by_size\">Dydis</string>\n    <string name=\"file_list_action_sort_by_last_modified\">Paskutinį kartą keista</string>\n    <string name=\"file_list_action_sort_order_ascending\">Didėjantis</string>\n    <string name=\"file_list_action_sort_directories_first\">Aplankai pirmiausia</string>\n    <string name=\"file_list_action_view_sort_path_specific\">Tik šiam aplankui</string>\n    <string name=\"file_list_action_new_task\">Naujas langas</string>\n    <string name=\"file_list_action_navigate_up\">Eiti į viršų</string>\n    <string name=\"file_list_action_navigate_to\">Eiti į</string>\n    <string name=\"file_list_action_show_hidden_files\">Rodyti paslėptus failus</string>\n    <string name=\"file_list_action_copy_path\">Kopijuoti kelią</string>\n    <string name=\"file_list_action_open_in_terminal\">Atidaryti terminale</string>\n    <string name=\"file_list_action_add_bookmark\">Pridėti žymę</string>\n    <string name=\"file_list_action_create_shortcut\">Sukurti spartųjį kelią</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">Atidaryti naujame lange</string>\n    <string name=\"file_list_select_title_format\">%1$,d</string>\n    <string name=\"file_list_paste_move_title_format\">Perkeliama %1$,d</string>\n    <string name=\"file_list_paste_copy_title_format\">Kopijuojama %1$,d</string>\n    <string name=\"file_list_paste_extract_title_format\">Išskleidžiama %1$,d</string>\n    <string name=\"file_list_paste_action_extract_here\">Išskleisti čia</string>\n    <string name=\"file_list_empty\">Nėra failų</string>\n    <string name=\"file_list_action_create_file\">Failas</string>\n    <string name=\"file_list_action_create_directory\">Aplankas</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">Atidaryti su</string>\n    <string name=\"file_item_action_extract\">Išskleisti</string>\n    <string name=\"file_item_action_archive\">Suspausti</string>\n    <string name=\"file_item_action_properties\">Savybės</string>\n\n    <string name=\"file_properties_title_format\">“%1$s” savybės</string>\n    <string name=\"file_properties_basic\">Pagrindiniai</string>\n    <string name=\"file_properties_basic_name\">Pavadinimas</string>\n    <string name=\"file_properties_basic_type\">Tipas</string>\n    <string name=\"file_properties_basic_type_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">Nuoroda į %1$s (%2$s)</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">Nuorodos tikslas</string>\n    <string name=\"file_properties_basic_size\">Dydis</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_contents\">Turinys</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"one\">%1$,d įrašas, kurio dydis %2$s</item>\n        <item quantity=\"few\">%1$,d įrašai, viso %2$s</item>\n        <item quantity=\"many\">%1$,d įrašų, viso %2$s</item>\n        <item quantity=\"other\">%1$,d įrašai, viso %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">Paskutinį kartą keista</string>\n    <string name=\"file_properties_basic_parent_directory\">Pirminis aplankas</string>\n    <string name=\"file_properties_basic_archive_file\">Archyvo failas</string>\n    <string name=\"file_properties_basic_archive_entry\">Archyvo įrašas</string>\n    <string name=\"file_properties_basic_free_space\">Laisvos vietos</string>\n    <string name=\"file_properties_image\">Nuotrauka</string>\n    <string name=\"file_properties_media_dimensions\">Matmenys</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">Padaryta</string>\n    <string name=\"file_properties_media_coordinates\">Koordinatės</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f, %2$.3f</string>\n    <string name=\"file_properties_media_address\">Vietovė</string>\n    <string name=\"file_properties_image_gps_altitude\">Aukštis</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f m</string>\n    <string name=\"file_properties_image_equipment\">Kamera</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">Diafragma</string>\n    <string name=\"file_properties_image_shutter_speed\">Sklendės greitis</string>\n    <string name=\"file_properties_image_focal_length\">Fokusavimo nuotolis</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f mm</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">ISO atitikmuo</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">Programinė įranga</string>\n    <string name=\"file_properties_image_description\">Aprašymas</string>\n    <string name=\"file_properties_image_artist\">Kūrėjas</string>\n    <string name=\"file_properties_image_copyright\">Autorinės teisės</string>\n    <string name=\"file_properties_audio\">Vaizdo įrašas</string>\n    <string name=\"file_properties_media_title\">Pavadinimas</string>\n    <string name=\"file_properties_audio_artist\">Atlikėjas</string>\n    <string name=\"file_properties_audio_album\">Albumas</string>\n    <string name=\"file_properties_audio_album_artist\">Albumo atlikėjas</string>\n    <string name=\"file_properties_audio_composer\">Kompozitorius</string>\n    <string name=\"file_properties_audio_disc_number\">Diskas</string>\n    <string name=\"file_properties_audio_track_number\">Takelis</string>\n    <string name=\"file_properties_audio_year\">Metai</string>\n    <string name=\"file_properties_audio_genre\">Žanras</string>\n    <string name=\"file_properties_media_duration\">Trukmė</string>\n    <string name=\"file_properties_media_bit_rate\">Bitų sparta</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d kbps</string>\n    <string name=\"file_properties_audio_sample_rate\">Mėginių ėmimo dažnis</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Hz</string>\n    <string name=\"file_properties_video\">Vaizdo įrašas</string>\n    <string name=\"file_properties_apk\">APK</string>\n    <string name=\"file_properties_apk_label\">Pavadinimas</string>\n    <string name=\"file_properties_apk_package_name\">Paketo pavadinimas</string>\n    <string name=\"file_properties_apk_version\">Versija</string>\n    <string name=\"file_properties_apk_version_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_apk_min_sdk_version\">Minimali sistemos versija</string>\n    <string name=\"file_properties_apk_target_sdk_version\">Tikslinė sistemos versija</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s (%2$s, %3$d)</string>\n    <string name=\"file_properties_apk_requested_permissions\">Leidimai</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">Jokių leidimų neprašoma</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"one\">%1$,d prašomas leidimas</item>\n        <item quantity=\"few\">%1$,d prašomi leidimai</item>\n        <item quantity=\"many\">%1$,d prašomų leidimų</item>\n        <item quantity=\"other\">%1$,d prašomi leidimai</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">Parašai</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">Nėra tinkamų parašų</string>\n    <string name=\"file_properties_apk_past_signature_digests\">Seni parašai</string>\n    <string name=\"storage_file_system_root_title\">Root</string>\n    <string name=\"storage_list_title\">Saugykla</string>\n    <string name=\"storage_list_empty\">Nėra saugyklos</string>\n    <string name=\"storage_add_storage_title\">Pridėti saugyklą</string>\n    <string name=\"storage_add_storage_document_tree\">Išorinė saugykla</string>\n    <string name=\"storage_add_storage_ftp_server\">FTP serveris</string>\n    <string name=\"storage_add_storage_sftp_server\">SFTP serveris</string>\n    <string name=\"storage_add_storage_smb_server\">SMB serveris</string>\n    <string name=\"storage_edit_device_storage_title\">Redaguoti įrenginio saugylką</string>\n    <string name=\"storage_edit_device_storage_name\">Pavadinimas</string>\n    <string name=\"storage_edit_device_storage_path\">Kelias</string>\n    <string name=\"storage_add_document_tree_title\">Pridėti išorinę saugyklą</string>\n    <string name=\"storage_edit_document_tree_title\">Redaguoti išorinę saugyklą</string>\n    <string name=\"storage_edit_document_tree_name\">Pavadinimas</string>\n    <string name=\"storage_edit_document_tree_uri\">URI</string>\n    <string name=\"storage_edit_document_tree_path\">Kelias</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">Redaguoti FTP serverį</string>\n    <string name=\"storage_edit_ftp_server_title_add\">Pridėti FTP serverį</string>\n    <string name=\"storage_edit_ftp_server_host\">Serverio vardas</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">Įveskite serverio vardą</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">Netinkamas serverio vardas</string>\n    <string name=\"storage_edit_ftp_server_port\">Prievadas</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">Netinkamas prievadas</string>\n    <string name=\"storage_edit_ftp_server_path\">Kelias</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">Gali būti paliktas tuščias</string>\n    <string name=\"storage_edit_ftp_server_name\">Pavadinimas</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">Naudoti serverio vardą</string>\n    <string name=\"storage_edit_ftp_server_protocol\">Protokolas</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">Autentifikacija</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>Slaptažodis</item>\n        <item>Anonimas</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">Naudotojo vardas</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">Įvesti naudotojo vardą</string>\n    <string name=\"storage_edit_ftp_server_password\">Slaotažodis</string>\n    <string name=\"storage_edit_ftp_server_mode\">Režimas</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>Aktyvus</item>\n        <item>Pasyvus</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">Kodavimas</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">Prisijungti ir pridėti</string>\n    <string name=\"storage_edit_ftp_server_add\">Pridėti</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">Redaguoti SFTP serverį</string>\n    <string name=\"storage_edit_sftp_server_title_add\">Pridėti SFTP serverį</string>\n    <string name=\"storage_edit_sftp_server_host\">Serverio vardas</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">Įveskite serverio vardą</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">Netinkamas serverio vardas</string>\n    <string name=\"storage_edit_sftp_server_port\">Prievadas</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">Netinkamas prievadas</string>\n    <string name=\"storage_edit_sftp_server_path\">Kelias</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">Gali būti paliktas tuščias</string>\n    <string name=\"storage_edit_sftp_server_name\">Pavadinimas</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">Naudoti serverio vardą</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">Autentifikacija</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>Slaptažodis</item>\n        <item>Viešas raktas</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">Naudotojo vardas</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">Įvesti naudotojo vardą</string>\n    <string name=\"storage_edit_sftp_server_password\">Slaptažodis</string>\n    <string name=\"storage_edit_sftp_server_private_key\">Privatus raktas</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">Atidaryti failą</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">Įvesti privatų raktą</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">Netinkamas privatus raktas</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">Privataus rakto slaptažodis</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">Gali būti paliktas tuščias</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">Netinkamas privataus rakto slaptažodis</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">Prisijungti ir pridėti</string>\n    <string name=\"storage_edit_sftp_server_add\">Pridėti</string>\n    <string name=\"storage_add_lan_smb_server_loading\">Ieškoma SMB serverių…</string>\n    <string name=\"storage_add_lan_smb_server_add\">Pridėti rankiniu būdu</string>\n    <string name=\"storage_edit_smb_server_title_edit\">Redaguoti SMB serverį</string>\n    <string name=\"storage_edit_smb_server_title_add\">Pridėti SMB serverį</string>\n    <string name=\"storage_edit_smb_server_host\">Serverio vardas</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">Įveskite serverio vardą</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">Netinkamas serverio vardas</string>\n    <string name=\"storage_edit_smb_server_port\">Prievadas</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">Netinkamas prievadas</string>\n    <string name=\"storage_edit_smb_server_path\">Kelias</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">Gali būti paliktas tuščias</string>\n    <string name=\"storage_edit_smb_server_name\">Pavadinimas</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">Naudoti serverio vardą</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">Autentifikacija</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>Slaptažodis</item>\n        <item>Svečias</item>\n        <item>Anonimas</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">Naudotojo vardas</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">Įvesti naudotojo vardą</string>\n    <string name=\"storage_edit_smb_server_password\">Slaptažodis</string>\n    <string name=\"storage_edit_smb_server_domain\">Domenas</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">Prisijungti ir pridėti</string>\n    <string name=\"storage_edit_smb_server_add\">Pridėti</string>\n    <string name=\"navigation_storage_subtitle_format\">%1$s laisva iš %2$s</string>\n    <string name=\"navigation_add_storage\">Pridėti saugyklą…</string>\n    <string name=\"navigation_standard_directory_alarms\">Aliarmai</string>\n    <string name=\"navigation_standard_directory_dcim\">DCIM</string>\n    <string name=\"navigation_standard_directory_documents\">Dokumentai</string>\n    <string name=\"navigation_standard_directory_downloads\">Atsisiuntimai</string>\n    <string name=\"navigation_standard_directory_movies\">Filmai</string>\n    <string name=\"navigation_standard_directory_music\">Muzika</string>\n    <string name=\"navigation_standard_directory_notifications\">Pranešimai</string>\n    <string name=\"navigation_standard_directory_pictures\">Paveikslėliai</string>\n    <string name=\"navigation_standard_directory_podcasts\">Transliacijos</string>\n    <string name=\"navigation_standard_directory_ringtones\">Skambučių melodijos</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">Pažymėtas aplankas</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">Pavadinimas</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">Kelias</string>\n    <string name=\"archive_viewer_title\">Archyvų žiūryklė</string>\n    <string name=\"image_viewer_title\">Nuotraukų žiūryklė</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"text_editor_title\">Teksto redaktorius</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">Išsaugota</string>\n    <string name=\"text_editor_reload_message\">Ar tikrai norite įkelti iš naujo? Neišsaugoti šio dokumento pakeitimai bus prarasti.</string>\n    <string name=\"text_editor_encoding\">Kodavimas</string>\n    <string name=\"text_editor_close_message\">Ar tikrai norite atmesti neišsaugotus šio dokumento pakeitimus?</string>\n\n    <string name=\"ftp_server_title\">FTP serveris</string>\n    <string name=\"ftp_server_state_title\">Būsena</string>\n    <string name=\"ftp_server_state_summary_starting\">Startuojamas…</string>\n    <string name=\"ftp_server_state_summary_running\">Veikia</string>\n    <string name=\"ftp_server_state_summary_stopping\">Sustabdomas…</string>\n    <string name=\"ftp_server_state_summary_stopped\">Išjungtas</string>\n    <string name=\"ftp_server_url_title\">URL</string>\n    <string name=\"ftp_server_url_summary_no_local_inet_address\">Vietinis IP adresas nežinomas</string>\n    <string name=\"ftp_server_url_menu_copy_url\">Kopijuoti URL</string>\n    <string name=\"ftp_server_url_menu_copy_password\">Kopijuoti slaptažodį</string>\n    <string name=\"ftp_server_add_tile_title\">Pridėti prie greitųjų nustatymų</string>\n    <string name=\"ftp_server_configuration_title\">Konfigūracija</string>\n    <string name=\"ftp_server_anonymous_login_title\">Anonimiškas prisijungimas</string>\n    <string name=\"ftp_server_username_title\">Naudotojo vardas</string>\n    <string name=\"ftp_server_password_title\">Slaptažodis</string>\n    <string name=\"ftp_server_port_title\">Prievadas</string>\n    <string name=\"ftp_server_home_directory_title\">Root aplankas</string>\n    <string name=\"ftp_server_writable_title\">Leisti rašyti</string>\n    <string name=\"settings_title\">Nustatymai</string>\n    <string name=\"settings_interface_title\">Sąsaja</string>\n    <string name=\"settings_locale_title\">Kalba</string>\n    <string name=\"settings_theme_color_title\">Temos spalva</string>\n    <string name=\"settings_theme_color_summary\">Dažniausiai programėlėje pasitaikanti spalva</string>\n    <string name=\"settings_material_design_3_title\">Material Design 3</string>\n    <string name=\"settings_night_mode_title\">Nakties režimas</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>Sekti sistemą</item>\n        <item>Išj.</item>\n        <item>Įj.</item>\n        <item>Pagal laiką</item>\n        <item>Pagal akumuliatoriaus taupymą</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">Juodos nakties režimas</string>\n    <string name=\"settings_file_list_animation_title\">Failų sąrašo animacija</string>\n    <string name=\"settings_file_name_ellipsize_title\">Rodyti ilgą failo pavadinimą</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>Elipsuok pradžią</item>\n        <item>Elipsuok vidurį</item>\n        <item>Elipsuok pabaigą</item>\n        <item>Stendas</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">Elgsena</string>\n    <string name=\"settings_default_directory_title\">Numatytas aplankas</string>\n    <string name=\"settings_standard_directories_title\">Standartiniai aplankai</string>\n    <string name=\"settings_standard_directories_summary_empty\">Neįjungti jokie standartiniai aplankai</string>\n    <string name=\"settings_bookmark_directory_screenshots\">Ekrano nuotraukos</string>\n    <string name=\"settings_bookmark_directories_title\">Pažymėti aplankai</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">Nėra pažymėtų aplankų</string>\n    <string name=\"settings_root_strategy_title\">Root prieigos režimas</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"one\">Vykdoma %1$,d failų operacija, todėl dabar pakeitus root prieigos režimą gali įvykti netikėta klaida. Ar tikrai norite jį keisti dabar?</item>\n        <item quantity=\"few\">Vykdomos %1$,d failų operacijos, todėl dabar pakeitus root prieigos režimą gali įvykti netikėta klaida. Ar tikrai norite jį keisti dabar?</item>\n        <item quantity=\"many\">Vykdomos %1$,d failų operacijos, todėl dabar pakeitus root prieigos režimą gali įvykti netikėta klaida. Ar tikrai norite jį keisti dabar?</item>\n        <item quantity=\"other\">Vykdomos %1$,d failų operacijos, todėl dabar pakeitus root prieigos režimą gali įvykti netikėta klaida. Ar tikrai norite jį keisti dabar?</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>Tik įprasta prieiga</item>\n        <item>Automatinis</item>\n        <item>Tik root prieiga</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">Archyvo failo pavadinimo kodavimas</string>\n    <string name=\"settings_open_apk_default_action_title\">Atidaryti Android paketą</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>Įdiegti</item>\n        <item>Peržiūrėti turinį</item>\n        <item>Klausti ką daryti</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">Gali padaryti programėlę nestabilią senesnėse Android versijose</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">Rodyti nuotolinių failų miniatiūras</string>\n\n    <string name=\"about_title\">Apie</string>\n    <string name=\"about_version_title\">Versija</string>\n    <string name=\"about_github_title\">Peržiūrėti GitHub svetainėje</string>\n    <string name=\"about_licenses_title\">Licencijos</string>\n    <string name=\"about_privacy_policy_title\">Privatumo Politika</string>\n    <string name=\"about_author_title\">Autorius</string>\n    <string name=\"about_author_name_title\">Hai Zhang</string>\n    <string name=\"about_author_github_title\">Sekti GitHub svetainėje</string>\n    <string name=\"about_author_twitter_title\">Sekti Twitter svetainėje</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-nb/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">Fil</string>\n    <string name=\"file_type_name_apk\">Android-pakke</string>\n    <string name=\"file_type_name_archive\">%1$s-arkiv</string>\n    <string name=\"file_type_name_audio\">%1$s-lyd</string>\n    <string name=\"file_type_name_calendar\">%1$s-kalender</string>\n    <string name=\"file_type_name_certificate\">%1$s-sertifikat</string>\n    <string name=\"file_type_name_code\">%1$s-dokument</string>\n    <string name=\"file_type_name_contact\">Elektronisk visittkort</string>\n    <string name=\"file_type_name_directory\">Mappe</string>\n    <string name=\"file_type_name_document\">%1$s-dokument</string>\n    <string name=\"file_type_name_ebook\">%1$s e-bok</string>\n    <string name=\"file_type_name_email\">%1$s e-postmelding</string>\n    <string name=\"file_type_name_font\">%1$s-skrifttype</string>\n    <string name=\"file_type_name_generic\">%1$s-fil</string>\n    <string name=\"file_type_name_image\">%1$s-bilde</string>\n    <string name=\"file_type_name_pdf\">PDF-dokument</string>\n    <string name=\"file_type_name_presentation\">%1$s-presentasjon</string>\n    <string name=\"file_type_name_spreadsheet\">%1$s-regneark</string>\n    <string name=\"file_type_name_text\">%1$s-dokument</string>\n    <string name=\"file_type_name_text_plain\">Dokument med ren tekst</string>\n    <string name=\"file_type_name_video\">%1$s-video</string>\n    <string name=\"file_type_name_word\">Word-dokument</string>\n    <string name=\"file_type_name_powerpoint\">PowerPoint-presentasjon</string>\n    <string name=\"file_type_name_excel\">Excel-regneark</string>\n    <string name=\"file_type_name_posix_character_device\">Tegnenhet</string>\n    <string name=\"file_type_name_posix_block_device\">Blokkenhet</string>\n    <string name=\"file_type_name_posix_fifo\">Rør</string>\n    <string name=\"file_type_name_posix_symbolic_link\">Lenke</string>\n    <string name=\"file_type_name_posix_socket\">Kontakt</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">Lenke (brutt)</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-nb/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <string name=\"app_name\">Material Filer</string>\n\n    <string name=\"close\">Lukk</string>\n    <string name=\"copy\">Kopiér</string>\n    <string name=\"cut\">Klipp ut</string>\n    <string name=\"default_\">Standard</string>\n    <string name=\"delete\">Slett</string>\n    <string name=\"discard\">Forkast</string>\n    <string name=\"edit\">Rediger</string>\n    <string name=\"empty\">Tom</string>\n    <string name=\"empty_placeholder\">(Tom)</string>\n    <string name=\"error\">Feil</string>\n    <string name=\"hide\">Skjul</string>\n    <string name=\"install\">Installer</string>\n    <string name=\"keep_editing\">Fortsett å redigere</string>\n    <string name=\"loading\">Laster inn …</string>\n    <string name=\"maybe_later\">Kanskje senere</string>\n    <string name=\"merge\">Slå sammen</string>\n    <string name=\"none\">Ingen</string>\n    <string name=\"open_navigation_drawer\">Åpne uttrekksmenyen</string>\n    <string name=\"paste\">Lim inn</string>\n    <string name=\"refresh\">Last inn på nytt</string>\n    <string name=\"reload\">Last inn på nytt</string>\n    <string name=\"remove\">Fjern</string>\n    <string name=\"rename\">Gi nytt navn</string>\n    <string name=\"replace\">Erstatt</string>\n    <string name=\"reset\">Tilbakestill</string>\n    <string name=\"retry\">Prøv på nytt</string>\n    <string name=\"save\">Lagre</string>\n    <string name=\"search\">Søke</string>\n    <string name=\"select\">Velg</string>\n    <string name=\"select_all\">Velg alle</string>\n    <string name=\"share\">Del</string>\n    <string name=\"show\">Vis</string>\n    <string name=\"skip\">Hopp over</string>\n    <string name=\"stop\">Stopp</string>\n    <string name=\"system_default\">Systemstandard</string>\n    <string name=\"unknown\">Ukjent</string>\n    <string name=\"view\">Se innholdet</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">Fant ingen apper som kan utføre denne handlingen</string>\n    <string name=\"open_settings\">Åpne innstillingene</string>\n    <string name=\"shortcut_created\">Snarveien er opprettet</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"one\">%1$,d byte</item>\n        <item quantity=\"other\">%1$,d byte</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">Appen trenger tillatelse til å administrere alle filer. Gi tillatelse i de kommende systeminnstillingene.</string>\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">Appen trenger tilgang til filene. Trykk på «TILLAT» i den kommende systemdialogboksen.</string>\n    <string name=\"storage_permission_permanently_denied_message\">Appen trenger tilgang til filene. Gi «Lagring»-tillatelsen i systeminnstillingene.</string>\n    <!-- The allow button is in sentence case now. -->\n    <string name=\"notification_permission_rationale_message\">Appen trenger tillatelse til å sende varsler om filhandlinger. Trykk på «Tillat» i den kommende systemdialogboksen.</string>\n    <string name=\"notification_permission_permanently_denied_message\">Appen trenger tillatelse til å sende varsler om filhandlinger. Gi «Varsler»-tillatelsen i systeminnstillingene.</string>\n\n    <string name=\"notification_channel_background_activity_start_name\">Handlinger i bakgrunnen</string>\n    <string name=\"notification_channel_background_activity_start_description\">Utfør handlinger mens appen er i bakgrunnen</string>\n    <string name=\"notification_channel_file_job_name\">Filhandlinger</string>\n    <string name=\"notification_channel_file_job_description\">Se og kontrollér filhandlinger</string>\n    <string name=\"notification_channel_ftp_server_name\">FTP-tjener</string>\n    <string name=\"notification_channel_ftp_server_description\">Se og kontrollér FTP-tjeneren</string>\n\n    <string name=\"file_action_archive_password_title\">Passord kreves</string>\n    <string name=\"file_action_archive_password_message_format\">«%1$s» er passordbeskyttet.</string>\n    <string name=\"file_action_archive_password_error_empty\">Passord kan ikke være tomt</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"one\">Forbereder komprimering av %1$,d fil (%2$s)</item>\n        <item quantity=\"other\">Forbereder komprimering av %1$,d filer (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">Komprimerer «%1$s» til «%2$s»</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"one\">Komprimerer %1$,d fil til «%2$s»</item>\n        <item quantity=\"other\">Komprimerer %1$,d filer til «%2$s»</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"one\">Forbereder kopiering av %1$,d fil (%2$s)</item>\n        <item quantity=\"other\">Forbereder kopiering av %1$,d filer (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">Kopierer «%1$s» til «%2$s»</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"one\">Kopierer %1$,d fil til «%2$s»</item>\n        <item quantity=\"other\">Kopierer %1$,d filer til «%2$s»</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"one\">Forbereder utpakkingen av %1$,d fil (%2$s)</item>\n        <item quantity=\"other\">Forbereder utpakking av %1$,d filer (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">Pakker ut «%1$s» til «%2$s»</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"one\">Pakker ut %1$,d fil til «%2$s»</item>\n        <item quantity=\"other\">Pakker ut %1$,d filer til «%2$s»</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"one\">Forbereder flytting av %1$,d fil (%2$s)</item>\n        <item quantity=\"other\">Forbereder flytting av %1$,d filer (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">Flytter «%1$s» til «%2$s»</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"one\">Flytter %1$,d fil til «%2$s»</item>\n        <item quantity=\"other\">Flytter %1$,d filer til «%2$s»</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"one\">Forbereder sletting av %1$,d fil</item>\n        <item quantity=\"other\">Forbereder sletting av %1$,d filer</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">Sletter «%1$s»</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"one\">Sletter %1$,d fil</item>\n        <item quantity=\"other\">Sletter %1$,d filer</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"one\">Forbereder endring av eier for %1$,d fil</item>\n        <item quantity=\"other\">Forbereder endring av eier for %1$,d filer</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">Endrer eier for «%1$s»</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"one\">Endrer eier for %1$,d fil</item>\n        <item quantity=\"other\">Endrer eier for %1$,d filer</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"one\">Forbereder endring av gruppe for %1$,d fil</item>\n        <item quantity=\"other\">Forbereder endring av gruppe for %1$,d filer</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">Endrer gruppe for «%1$s»</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"one\">Endrer gruppe for %1$,d fil</item>\n        <item quantity=\"other\">Enrer gruppe for %1$,d filer</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"one\">Forbereder endring av modus for %1$,d fil</item>\n        <item quantity=\"other\">Forbereder endring av modus for %1$,d filer</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">Endrer modus for «%1$s»</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"one\">Endrer modus for %1$,d fil</item>\n        <item quantity=\"other\">Endrer modus for %1$,d filer</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Forbereder endring av SElinux-kontekst for %1$,d fil</item>\n        <item quantity=\"other\">Forbereder endring av SElinux-kontekst for %1$,d filer</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">Endrer SELinux-kontekst for «%1$s»</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Endrer SELinux-kontekst for %1$,d fil</item>\n        <item quantity=\"other\">Endrer SELinux-kontekst for %1$,d filer</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Forbereder tilbakestilling av SELinux-kontekst for %1$,d fil</item>\n        <item quantity=\"other\">Forbereder gjenoppretting av SELinux-kontekst for %1$,d filer</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">Gjenoppretter SELinux-kontekst for «%1$s»</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Gjenoppretter SELinux-kontekst for %1$,d fil</item>\n        <item quantity=\"other\">Gjenoppretter SELinux-kontekst for %1$,d filer</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">Skriver til «%1$s»</string>\n    <string name=\"file_job_create_error_title\">Feil under oppretting</string>\n    <string name=\"file_job_create_error_message_format\">Det oppsto en feil under opprettingen av «%1$s».\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">Kan ikke kopiere en mappe inn i seg selv</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">Kan ikke pakke ut en mappe inn i seg selv</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">Kan ikke flytte en mappe inn i seg selv</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">Målmappen er inne i kildemappen.</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">Kan ikke kopiere en fil over seg selv</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">Kan ikke pakke ut en fil over seg selv</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">Kan ikke flytte en fil over seg selv</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">Kildefilen ville blitt overskrevet av målfilen.</string>\n    <string name=\"file_job_replace_title_format\">Vil du erstatte «%1$s»-filen?</string>\n    <string name=\"file_job_replace_message_format\">En annen fil med det samme navnet finnes allerede i «%1$s».\\nÅ erstatte den vil overskrive innholdet til filen.</string>\n    <string name=\"file_job_replace_target_name\">Opprinnelig fil</string>\n    <string name=\"file_job_replace_source_name\">Erstatt med</string>\n    <string name=\"file_job_merge_title_format\">Vil du slå sammen mappen «%1$s»?</string>\n    <string name=\"file_job_merge_copy_message_format\">Sammenslåing vil be om bekreftelse før noen av filene i mappen som er i konflikt blir erstattet med filene som blir kopiert.</string>\n    <string name=\"file_job_merge_extract_message_format\">Sammenslåing vil be om bekreftelse før noen av filene i mappen som er i konflikt blir erstattet med filene som blir pakket ut.</string>\n    <string name=\"file_job_merge_move_message_format\">Sammenslåing vil be om bekreftelse før noen av filene i mappen som er i konflikt blir erstattet med filene som blir flyttet.</string>\n    <string name=\"file_job_merge_target_name\">Opprinnelig mappe</string>\n    <string name=\"file_job_merge_source_name\">Slå sammen med</string>\n    <string name=\"file_job_conflict_show_name\">Velg et nytt navn for målet</string>\n    <string name=\"file_job_conflict_name_hint\">Nytt navn</string>\n    <string name=\"file_job_archive_error_title_format\">Feil under komprimering av «%1$s»</string>\n    <string name=\"file_job_archive_error_message_format\">Det oppsto en feil under komprimering av filen inn i «%1$s».\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">Feil under kopiering av «%1$s»</string>\n    <string name=\"file_job_copy_error_message_format\">Det oppsto en feil under kopiering av filen inn i «%1$s».\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">Feil under utpakking av «%1$s»</string>\n    <string name=\"file_job_extract_error_message_format\">Det oppsto en feil under utpakking av filen inn i «%1$s».\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">Feil under flytting av «%1$s»</string>\n    <string name=\"file_job_move_error_message_format\">Det oppsto en feil under flytting av filen inn i «%1$s».\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">Feil under sletting</string>\n    <string name=\"file_job_delete_error_message_format\">Det oppsto en feil under sletting av «%1$s».\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">Feil under endring av navn for «%1$s»</string>\n    <string name=\"file_job_rename_error_message_format\">Det oppsto en feil under endring av filnavnet til «%1$s».\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">Feil under endring av eier for «%1$s»</string>\n    <string name=\"file_job_set_owner_error_message_format\">Det oppsto en feil under endring av eier til «%1$s».\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">Feil under endring av gruppe for «%1$s»</string>\n    <string name=\"file_job_set_group_error_message_format\">Det oppsto en feil under endring av gruppe til «%1$s».\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">Feil under endring av modus for «%1$s»</string>\n    <string name=\"file_job_set_mode_error_message_format\">Det oppsto en feil under endring av modus til «%1$s».\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">Feil under endring av SELinux-kontekst for «%1$s»</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">Det oppsto en feil under endring av SELinux-kontekst til «%1$s».\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">Feil under gjenoppretting av SELinux-kontekst</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">Det oppsto en feil under gjenoppretting av SELinux-kontekst for «%1$s».\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">Feil under skriving</string>\n    <string name=\"file_job_write_error_message_format\">Det oppsto en feil under skriving til «%1$s».\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">Det oppsto en feil under henting av informasjon om «%1$s».</string>\n    <string name=\"file_job_remount_format\">Koble til «%1$s» på nytt med lese- og skrivetilgang</string>\n    <string name=\"file_job_remount_loading_format\">Kobler til «%1$s» på nytt med lese- og skrivetilgang …</string>\n    <string name=\"file_job_remount_success_format\">«%1$s» er koblet til på nytt med lese- og skrivetilgang</string>\n    <string name=\"file_job_action_all\">Bruk denne handlingen på alle filene</string>\n\n    <string name=\"file_open_apk_message\">Denne filen er en Android-pakke. Vil du installere den eller se innholdet i den?</string>\n    <string name=\"file_open_as_title\">Åpne som …</string>\n    <string name=\"file_open_as_title_format\">Åpne «%1$s» som</string>\n    <string name=\"file_open_as_type_text\">Tekst</string>\n    <string name=\"file_open_as_type_image\">Bilde</string>\n    <string name=\"file_open_as_type_audio\">Lyd</string>\n    <string name=\"file_open_as_type_video\">Video</string>\n    <string name=\"file_open_as_type_directory\">Mappe</string>\n    <string name=\"file_open_as_type_any\">Annet</string>\n    <string name=\"file_install_apk_from_background_title_format\">«%1$s» er klar til installering</string>\n    <string name=\"file_install_apk_from_background_text\">Trykk for å installere</string>\n    <string name=\"file_open_from_background_title_format\">«%1$s» er klar til åpning</string>\n    <string name=\"file_open_from_background_text\">Trykk for å åpne</string>\n    <string name=\"file_name_error_empty\">Filnavnet kan ikke være tomt</string>\n    <string name=\"file_name_error_invalid\">Ugyldig filnavn</string>\n    <string name=\"file_name_error_already_exists\">Det finnes allerede en fil med dette navnet</string>\n    <string name=\"file_replace_message_format\">Vil du erstatte «%1$s»?</string>\n    <string name=\"file_delete_message_file_format\">Vil du slette «%1$s»?</string>\n    <string name=\"file_delete_message_directory_format\">Vil du slette «%1$s»-mappen og innholdet i den?</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"one\">Vil du slette %1$,d fil?</item>\n        <item quantity=\"other\">Vil du slette %1$,d filer?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"one\">Vil du slette %1$,d mappe og innholdet i den?</item>\n        <item quantity=\"other\">Vil du slette %1$,d mapper og innholdet i dem?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"one\">Vil du slette %1$,d element?</item>\n        <item quantity=\"other\">Vil du slette %1$,d elementer?</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">Opprett arkiv</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_create_archive_password\">Passord (valgfritt)</string>\n    <string name=\"file_add_bookmark_success\">Bokmerke er lagt til</string>\n    <string name=\"file_create_file_title\">Ny fil</string>\n    <string name=\"file_create_directory_title\">Ny mappe</string>\n\n    <string name=\"file_list_title\">Filer</string>\n    <plurals name=\"file_list_title_open_file\">\n        <item quantity=\"one\">Velg en fil</item>\n        <item quantity=\"other\">Velg filer</item>\n    </plurals>\n    <string name=\"file_list_title_create_file\">Lagre fil</string>\n    <plurals name=\"file_list_title_open_directory\">\n        <item quantity=\"one\">Velg en mappe</item>\n        <item quantity=\"other\">Velg mapper</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"one\">%1$,d mappe</item>\n        <item quantity=\"other\">%1$,d mapper</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"one\">%1$,d mappe</item>\n        <item quantity=\"other\">%1$,d mapper</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">,\\u0020</string>\n    <string name=\"file_list_path_error_empty\">Banen kan ikke være tom</string>\n    <string name=\"file_list_path_error_invalid\">Ugyldig bane</string>\n    <string name=\"file_list_action_view_sort\">Se og sortér</string>\n    <string name=\"file_list_action_view_list\">Liste</string>\n    <string name=\"file_list_action_view_grid\">Rutenett</string>\n    <string name=\"file_list_action_sort_by_name\">Navn</string>\n    <string name=\"file_list_action_sort_by_type\">Type</string>\n    <string name=\"file_list_action_sort_by_size\">Størrelse</string>\n    <string name=\"file_list_action_sort_by_last_modified\">Sist endret</string>\n    <string name=\"file_list_action_sort_order_ascending\">Stigende</string>\n    <string name=\"file_list_action_sort_directories_first\">Mapper først</string>\n    <string name=\"file_list_action_view_sort_path_specific\">Kun for denne mappen</string>\n    <string name=\"file_list_action_new_task\">Nytt vindu</string>\n    <string name=\"file_list_action_navigate_up\">Gå opp</string>\n    <string name=\"file_list_action_navigate_to\">Gå til</string>\n    <string name=\"file_list_action_show_hidden_files\">Vis skjulte filer</string>\n    <string name=\"file_list_action_copy_path\">Kopiér bane</string>\n    <string name=\"file_list_action_open_in_terminal\">Åpne i terminalen</string>\n    <string name=\"file_list_action_add_bookmark\">Legg til bokmerke</string>\n    <string name=\"file_list_action_create_shortcut\">Opprett snarvei</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">Åpne i et nytt vindu</string>\n    <string name=\"file_list_select_title_format\">%1$,d</string>\n    <string name=\"file_list_create_file_name_hint\">Filnavn</string>\n    <string name=\"file_list_open_current_directory_format\">Velg «%1$s»</string>\n    <string name=\"file_list_paste_move_title_format\">Flytter %1$,d</string>\n    <string name=\"file_list_paste_copy_title_format\">Kopierer %1$,d</string>\n    <string name=\"file_list_paste_extract_title_format\">Pakker ut %1$,d</string>\n    <string name=\"file_list_paste_action_extract_here\">Pakk ut her</string>\n    <string name=\"file_list_empty\">Ingen filer</string>\n    <string name=\"file_list_action_create_file\">Fil</string>\n    <string name=\"file_list_action_create_directory\">Mappe</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">Åpne med</string>\n    <string name=\"file_item_action_extract\">Pakk ut</string>\n    <string name=\"file_item_action_archive\">Komprimer</string>\n    <string name=\"file_item_action_properties\">Egenskaper</string>\n\n    <string name=\"file_properties_title_format\">Egenskaper for «%1$s»</string>\n    <string name=\"file_properties_basic\">Generelt</string>\n    <string name=\"file_properties_basic_name\">Navn</string>\n    <string name=\"file_properties_basic_type\">Type</string>\n    <string name=\"file_properties_basic_type_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">Lenke til %1$s (%2$s)</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">Lenkemål</string>\n    <string name=\"file_properties_basic_size\">Størrelse</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_contents\">Innhold</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"one\">%1$,d fil, med størrelse %2$s</item>\n        <item quantity=\"other\">%1$,d filer, totalt %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">Sist endret</string>\n    <string name=\"file_properties_basic_parent_directory\">Overordnet mappe</string>\n    <string name=\"file_properties_basic_archive_file\">Arkivfil</string>\n    <string name=\"file_properties_basic_archive_entry\">Arkivoppføring</string>\n    <string name=\"file_properties_basic_free_space\">Ledig plass</string>\n    <string name=\"file_properties_permission\">Tillatelser</string>\n    <string name=\"file_properties_permission_owner\">Eier</string>\n    <string name=\"file_properties_permission_group\">Gruppe</string>\n    <string name=\"file_properties_permission_principal_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_permission_set_owner_title\">Endre eier</string>\n    <string name=\"file_properties_permission_set_group_title\">Endre gruppe</string>\n    <string name=\"file_properties_permission_set_principal_filter_hint\">Skriv inn et navn eller en ID</string>\n    <string name=\"file_properties_permission_set_principal_system\">System</string>\n    <string name=\"file_properties_permission_mode\">Modus</string>\n    <string name=\"file_properties_permission_mode_format\">%1$s (%2$04o)</string>\n    <string name=\"file_properties_permission_set_mode_title\">Endre modus</string>\n    <string name=\"file_properties_permission_set_mode_others\">Andre</string>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_file\">\n        <item>Lese</item>\n        <item>Skrive</item>\n        <item>Kjøre</item>\n    </string-array>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_directory\">\n        <item>Lese</item>\n        <item>Skrive</item>\n        <item>Søke</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_special\">Spesielt</string>\n    <string-array name=\"file_properties_permission_set_mode_special_mode_bits\">\n        <item>Angi UID</item>\n        <item>Angi GID</item>\n        <item>Låst</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_uppercase_x\">Ikke legg til «Kjør» på underfilene</string>\n    <string name=\"file_properties_permission_selinux_context\">SELinux-kontekst</string>\n    <string name=\"file_properties_permission_set_selinux_context_title\">Endre SELinux-kontekst</string>\n    <string name=\"file_properties_permission_set_selinux_context_restore\">Gjenopprett</string>\n    <string name=\"file_properties_permission_recursive\">Bruk på alle underfilene</string>\n    <string name=\"file_properties_image\">Bilde</string>\n    <string name=\"file_properties_media_dimensions\">Dimensjoner</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">Tatt</string>\n    <string name=\"file_properties_media_coordinates\">Koordinater</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f, %2$.3f</string>\n    <string name=\"file_properties_media_address\">Posisjon</string>\n    <string name=\"file_properties_image_gps_altitude\">Høyde over havet</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f m</string>\n    <string name=\"file_properties_image_equipment\">Kamera</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">Blender</string>\n    <string name=\"file_properties_image_shutter_speed\">Lukkerhastighet</string>\n    <string name=\"file_properties_image_focal_length\">Brennvidde</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f mm</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">Tilsvarende ISO</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">Programvare</string>\n    <string name=\"file_properties_image_description\">Beskrivelse</string>\n    <string name=\"file_properties_image_artist\">Opprettet av</string>\n    <string name=\"file_properties_image_copyright\">Opphavsrett</string>\n    <string name=\"file_properties_audio\">Lyd</string>\n    <string name=\"file_properties_media_title\">Tittel</string>\n    <string name=\"file_properties_audio_artist\">Artist</string>\n    <string name=\"file_properties_audio_album\">Album</string>\n    <string name=\"file_properties_audio_album_artist\">Albumartist</string>\n    <string name=\"file_properties_audio_composer\">Komponist</string>\n    <string name=\"file_properties_audio_disc_number\">Disk</string>\n    <string name=\"file_properties_audio_track_number\">Spor</string>\n    <string name=\"file_properties_audio_year\">År</string>\n    <string name=\"file_properties_audio_genre\">Sjanger</string>\n    <string name=\"file_properties_media_duration\">Varighet</string>\n    <string name=\"file_properties_media_bit_rate\">Bithastighet</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d kbit/s</string>\n    <string name=\"file_properties_audio_sample_rate\">Samplefrekvens</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Hz</string>\n    <string name=\"file_properties_video\">Video</string>\n    <string name=\"file_properties_apk\">APK</string>\n    <string name=\"file_properties_apk_label\">Navn</string>\n    <string name=\"file_properties_apk_package_name\">Pakkenavn</string>\n    <string name=\"file_properties_apk_version\">Versjon</string>\n    <string name=\"file_properties_apk_version_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_apk_min_sdk_version\">Minimum systemversjon</string>\n    <string name=\"file_properties_apk_target_sdk_version\">Målsystemversjon</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s (%2$s, %3$d)</string>\n    <string name=\"file_properties_apk_requested_permissions\">Tillatelser</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">Ingen tillatelser er forespurt</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"one\">%1$,d tillatelse er forespurt</item>\n        <item quantity=\"other\">%1$,d tillatelser er forespurt</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">Signaturer</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">Ingen gyldige signaturer</string>\n    <string name=\"file_properties_apk_past_signature_digests\">Gamle signaturer</string>\n    <string name=\"file_properties_checksum\">Sjekksummer</string>\n    <string name=\"file_properties_checksum_compare\">Sammenlign</string>\n    <string name=\"file_properties_checksum_compare_placeholder\">Hvilken som helst sjekksum</string>\n    <string name=\"file_properties_checksum_compare_match_format\">Samsvarer med %1$s-sjekksumen</string>\n    <string name=\"file_properties_checksum_compare_prefix_match_format\">Samsvarer med starten av %1$s-sjekksumen</string>\n    <string name=\"file_properties_checksum_compare_no_match\">Fant ingen samsvarende sjekksum</string>\n    <string name=\"storage_file_system_root_title\">Rot</string>\n    <string name=\"storage_list_title\">Lagring</string>\n    <string name=\"storage_list_empty\">Ingen lagring</string>\n    <string name=\"storage_add_storage_title\">Legg til lagring</string>\n    <string name=\"storage_add_storage_document_tree\">Ekstern lagring</string>\n    <string name=\"storage_add_storage_ftp_server\">FTP-tjener</string>\n    <string name=\"storage_add_storage_sftp_server\">SFTP-tjener</string>\n    <string name=\"storage_add_storage_smb_server\">SMB-tjener</string>\n    <string name=\"storage_add_storage_webdav_server\">WebDAV-tjener</string>\n    <string name=\"storage_edit_device_storage_title\">Rediger lagring på enheten</string>\n    <string name=\"storage_edit_device_storage_name\">Navn</string>\n    <string name=\"storage_edit_device_storage_path\">Bane</string>\n    <string name=\"storage_add_external_storage_shortcut_title\">Legg til snarvei til ekstern lagring</string>\n    <string name=\"storage_edit_external_storage_shortcut_title\">Rediger snarvei til ekstern lagring</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id\">Lagringsvolum</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id_error_empty\">Angi et lagringsvolum</string>\n    <string name=\"storage_edit_external_storage_shortcut_path\">Bane</string>\n    <string name=\"storage_add_document_tree_title\">Legg til ekstern lagring</string>\n    <string name=\"storage_edit_document_tree_title\">Rediger ekstern lagring</string>\n    <string name=\"storage_edit_document_tree_name\">Navn</string>\n    <string name=\"storage_edit_document_tree_uri\">URI</string>\n    <string name=\"storage_edit_document_tree_path\">Bane</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">Rediger FTP-tjener</string>\n    <string name=\"storage_edit_ftp_server_title_add\">Legg til FTP-tjener</string>\n    <string name=\"storage_edit_ftp_server_host\">Vertsnavn</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">Skriv inn et vertsnavn</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">Ugyldig vertsnavn</string>\n    <string name=\"storage_edit_ftp_server_port\">Port</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">Ugyldig port</string>\n    <string name=\"storage_edit_ftp_server_path\">Bane</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">Kan være tomt</string>\n    <string name=\"storage_edit_ftp_server_name\">Navn</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">Bruk vertsnavn</string>\n    <string name=\"storage_edit_ftp_server_protocol\">Protokoll</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">Autentisering</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>Passord</item>\n        <item>Anonym</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">Brukernavn</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">Skriv inn et brukernavn</string>\n    <string name=\"storage_edit_ftp_server_password\">Passord</string>\n    <string name=\"storage_edit_ftp_server_mode\">Modus</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>Aktiv</item>\n        <item>Passiv</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">Koding</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">Koble til og legg til</string>\n    <string name=\"storage_edit_ftp_server_add\">Legg til</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">Rediger SFTP-tjener</string>\n    <string name=\"storage_edit_sftp_server_title_add\">Legg til SFTP-tjener</string>\n    <string name=\"storage_edit_sftp_server_host\">Vertsnavn</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">Skriv inn vertsnavn</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">Ugyldig vertsnavn</string>\n    <string name=\"storage_edit_sftp_server_port\">Port</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">Ugyldig port</string>\n    <string name=\"storage_edit_sftp_server_path\">Bane</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">Kan være tomt</string>\n    <string name=\"storage_edit_sftp_server_name\">Navn</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">Bruk vertsnavn</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">Autentisering</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>Passord</item>\n        <item>Offentlig nøkkel</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">Brukernavn</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">Skriv inn et brukernavn</string>\n    <string name=\"storage_edit_sftp_server_password\">Passord</string>\n    <string name=\"storage_edit_sftp_server_private_key\">Privat nøkkel</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">Åpne fil</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">Skriv inn en privat nøkkel</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">Ugyldig privat nøkkel</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">Passord for privat nøkkel</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">Kan være tomt</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">Ugyldig passord for privat nøkkel</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">Koble til og legg til</string>\n    <string name=\"storage_edit_sftp_server_add\">Legg til</string>\n    <string name=\"storage_add_lan_smb_server_loading\">Søker etter SMB-tjenere …</string>\n    <string name=\"storage_add_lan_smb_server_add\">Legg til manuelt</string>\n    <string name=\"storage_edit_smb_server_title_edit\">Rediger SMB-tjener</string>\n    <string name=\"storage_edit_smb_server_title_add\">Legg til SMB-tjener</string>\n    <string name=\"storage_edit_smb_server_host\">Vertsnavn</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">Skriv inn et vertsnavn</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">Ugyldig vertsnavn</string>\n    <string name=\"storage_edit_smb_server_port\">Port</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">Ugyldig port</string>\n    <string name=\"storage_edit_smb_server_path\">Bane</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">Kan være tomt</string>\n    <string name=\"storage_edit_smb_server_name\">Navn</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">Bruk vertsnavn</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">Autentisering</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>Passord</item>\n        <item>Gjest</item>\n        <item>Anonym</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">Brukernavn</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">Skriv inn et brukernavn</string>\n    <string name=\"storage_edit_smb_server_password\">Passord</string>\n    <string name=\"storage_edit_smb_server_domain\">Domene</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">Koble til og legg til</string>\n    <string name=\"storage_edit_smb_server_add\">Legg til</string>\n    <string name=\"storage_edit_webdav_server_title_edit\">Rediger WebDAV-tjener</string>\n    <string name=\"storage_edit_webdav_server_title_add\">Legg til WebDAV-tjener</string>\n    <string name=\"storage_edit_webdav_server_host\">Vertsnavn</string>\n    <string name=\"storage_edit_webdav_server_host_error_empty\">Skriv inn et vertsnavn</string>\n    <string name=\"storage_edit_webdav_server_host_error_invalid\">Ugyldig vertsnavn</string>\n    <string name=\"storage_edit_webdav_server_port\">Port</string>\n    <string name=\"storage_edit_webdav_server_port_error_invalid\">Ugyldig port</string>\n    <string name=\"storage_edit_webdav_server_path\">Bane</string>\n    <string name=\"storage_edit_webdav_server_path_placeholder\">Kan være tomt</string>\n    <string name=\"storage_edit_webdav_server_name\">Navn</string>\n    <string name=\"storage_edit_webdav_server_name_placeholder\">Bruk vertsnavn</string>\n    <string name=\"storage_edit_webdav_server_protocol\">Protokoll</string>\n    <string name=\"storage_edit_webdav_server_authentication_type\">Autentisering</string>\n    <string-array name=\"storage_edit_webdav_server_authentication_type_entries\">\n        <item>Passord</item>\n        <item>Tilgangstoken</item>\n        <item>Ingen</item>\n    </string-array>\n    <string name=\"storage_edit_webdav_server_username\">Brukernavn</string>\n    <string name=\"storage_edit_webdav_server_username_error_empty\">Skriv inn et brukernavn</string>\n    <string name=\"storage_edit_webdav_server_password\">Passord</string>\n    <string name=\"storage_edit_webdav_server_access_token\">Tilgangstoken</string>\n    <string name=\"storage_edit_webdav_server_access_token_error_empty\">Skriv inn et tilgangstoken</string>\n    <string name=\"storage_edit_webdav_server_connect_and_add\">Koble til og legg til</string>\n    <string name=\"storage_edit_webdav_server_add\">Legg til</string>\n\n    <string name=\"navigation_storage_subtitle_format\">%1$s av %2$s ledig</string>\n    <string name=\"navigation_add_storage\">Legg til lagring …</string>\n    <string name=\"navigation_standard_directory_alarms\">Alarmer</string>\n    <string name=\"navigation_standard_directory_dcim\">DCIM</string>\n    <string name=\"navigation_standard_directory_documents\">Dokumenter</string>\n    <string name=\"navigation_standard_directory_downloads\">Nedlastinger</string>\n    <string name=\"navigation_standard_directory_movies\">Filmer</string>\n    <string name=\"navigation_standard_directory_music\">Musikk</string>\n    <string name=\"navigation_standard_directory_notifications\">Varsler</string>\n    <string name=\"navigation_standard_directory_pictures\">Bilder</string>\n    <string name=\"navigation_standard_directory_podcasts\">Podcaster</string>\n    <string name=\"navigation_standard_directory_ringtones\">Ringetoner</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">Bokmerket mappe</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">Navn</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">Bane</string>\n    <string name=\"archive_viewer_title\">Arkivleser</string>\n    <string name=\"image_viewer_title\">Bildefremviser</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"save_as_title\">Lagre som</string>\n    <string name=\"save_as_error\">Feil under lagring av fil</string>\n    <string name=\"save_as_success_format\">«%1$s» er lagret i «%2$s»</string>\n    <string name=\"text_editor_title\">Tekstredigering</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">Lagret</string>\n    <string name=\"text_editor_reload_message\">Er du sikker på at du vil laste inn på nytt? Ulagrede endringer i dette dokumentet vil gå tapt.</string>\n    <string name=\"text_editor_encoding\">Tegnkoding</string>\n    <string name=\"text_editor_close_message\">Er du sikker på at du vil forkaste de ulagrede endringene i dette dokumentet?</string>\n\n    <string name=\"ftp_server_title\">FTP-tjener</string>\n    <string name=\"ftp_server_state_title\">Status</string>\n    <string name=\"ftp_server_state_summary_starting\">Starter …</string>\n    <string name=\"ftp_server_state_summary_running\">Kjører</string>\n    <string name=\"ftp_server_state_summary_stopping\">Stopper …</string>\n    <string name=\"ftp_server_state_summary_stopped\">Ikke startet</string>\n    <string name=\"ftp_server_url_title\">URL</string>\n    <string name=\"ftp_server_url_summary_no_local_inet_address\">Lokal IP-adresse er ukjent</string>\n    <string name=\"ftp_server_url_menu_copy_url\">Kopiér URL</string>\n    <string name=\"ftp_server_url_menu_copy_password\">Kopiér passord</string>\n    <string name=\"ftp_server_add_tile_title\">Legg til i hurtiginnstillingene</string>\n    <string name=\"ftp_server_add_tile_result_added\">«FTP-tjener» er lagt til i hurtiginnstillingene</string>\n    <string name=\"ftp_server_add_tile_result_already_added\">«FTP-tjener» er allerede lagt til i hurtiginnstillingene</string>\n    <string name=\"ftp_server_add_tile_result_error\">Feil under tilføying av «FTP-tjener» til hurtiginnstillingene</string>\n    <string name=\"ftp_server_configuration_title\">Konfigurasjon</string>\n    <string name=\"ftp_server_anonymous_login_title\">Anonym innlogging</string>\n    <string name=\"ftp_server_username_title\">Brukernavn</string>\n    <string name=\"ftp_server_password_title\">Passord</string>\n    <string name=\"ftp_server_port_title\">Port</string>\n    <string name=\"ftp_server_home_directory_title\">Rotmappen</string>\n    <string name=\"ftp_server_writable_title\">Tillat skriving</string>\n    <string name=\"settings_title\">Innstillinger</string>\n    <string name=\"settings_interface_title\">Grensesnitt</string>\n    <string name=\"settings_locale_title\">Språk</string>\n    <string name=\"settings_theme_color_title\">Temafarge</string>\n    <string name=\"settings_theme_color_summary\">Den mest brukte fargen i appen</string>\n    <string name=\"settings_material_design_3_title\">Material Design 3</string>\n    <string name=\"settings_night_mode_title\">Nattmodus</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>Følg systemet</item>\n        <item>Av</item>\n        <item>På</item>\n        <item>Basert på tid</item>\n        <item>Basert på batterisparing</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">Svart nattmodus</string>\n    <string name=\"settings_file_list_animation_title\">Animér fillisten</string>\n    <string name=\"settings_file_name_ellipsize_title\">Visning av lange filnavn</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>Ellipse i starten</item>\n        <item>Ellipse i midten</item>\n        <item>Ellipse på slutten</item>\n        <item>Rulletekst</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">Atferd</string>\n    <string name=\"settings_default_directory_title\">Startmappe</string>\n    <string name=\"settings_standard_directories_title\">Standardmapper</string>\n    <string name=\"settings_standard_directories_summary_empty\">Ingen standardmapper er aktivert</string>\n    <string name=\"settings_bookmark_directory_screenshots\">Skjermdumper</string>\n    <string name=\"settings_bookmark_directories_title\">Bokmerkede mapper</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">Ingen bokmerkede mapper</string>\n    <string name=\"settings_root_strategy_title\">Tilgang som rot</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"one\">Du kjører %1$,d filoperasjon; å endre tilgangsmodusen nå kan føre til uventede feil. Er du sikker på at du vil endre den nå?</item>\n        <item quantity=\"other\">Du kjører %1$,d filhandlinger; å endre tilgangsmodusen nå kan føre til uventede feil. Er du sikker på at du vil endre den nå?</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>Bare vanlig tilgang</item>\n        <item>Automatisk</item>\n        <item>Bare tilgang som rot</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">Tegnkoding for filnavn i arkiv</string>\n    <string name=\"settings_open_apk_default_action_title\">Åpne Android-pakke</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>Installer</item>\n        <item>Se innholdet</item>\n        <item>Spør</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_title\">Vis miniatyrbilde for PDF-filer</string>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">Kan gjøre appen ustabil på eldre Android-versjoner</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">Les eksterne filer for miniatyrbilder</string>\n\n    <string name=\"about_title\">Om appen</string>\n    <string name=\"about_version_title\">Versjon</string>\n    <string name=\"about_github_title\">Se på GitHub</string>\n    <string name=\"about_licenses_title\">Lisenser</string>\n    <string name=\"about_privacy_policy_title\">Personvernregler</string>\n    <string name=\"about_author_title\">Utvikler</string>\n    <string name=\"about_author_name_title\">Hai Zhang</string>\n    <string name=\"about_author_github_title\">Følg på GitHub</string>\n    <string name=\"about_author_twitter_title\">Følg på Twitter</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-night/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <color name=\"system_window_scrim\">@color/system_window_scrim_dark</color>\n\n    <color name=\"system_window_scrim_compat\">@color/system_window_scrim_dark</color>\n\n    <color name=\"color_primary_dark_black\">@color/system_window_scrim_dark_black</color>\n\n    <color name=\"color_primary\">@color/color_primary_dark</color>\n\n    <color name=\"color_surface\">@color/color_surface_dark</color>\n\n    <color name=\"activity_icon_tint\">@color/secondary_text_default_material_dark</color>\n\n    <color name=\"file_icon_light_blue\">#6F9EF3</color>\n    <color name=\"file_icon_blue\">#4285F4</color>\n    <color name=\"file_icon_cyan\">#78D9EC</color>\n    <color name=\"file_icon_light_green\">#81C995</color>\n    <color name=\"file_icon_green\">#4FB885</color>\n    <color name=\"file_icon_deep_green\">#3FB179</color>\n    <color name=\"file_icon_grey\">#9AA0A6</color>\n    <color name=\"file_icon_orange\">#FD9167</color>\n    <color name=\"file_icon_purple\">#C58AF9</color>\n    <color name=\"file_icon_red\">#F28B82</color>\n    <color name=\"file_icon_deep_red\">#E8877F</color>\n    <color name=\"file_icon_yellow\">#F8CF5C</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-night/colors_custom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<resources>\n    <color name=\"material_red\">@color/material_red_300</color>\n    <color name=\"material_pink\">@color/material_pink_300</color>\n    <color name=\"material_purple\">@color/material_purple_300</color>\n    <color name=\"material_deep_purple\">@color/material_deep_purple_300</color>\n    <color name=\"material_indigo\">@color/material_indigo_300</color>\n    <color name=\"material_blue\">@color/material_blue_300</color>\n    <color name=\"material_light_blue\">@color/material_light_blue_300</color>\n    <color name=\"material_cyan\">@color/material_cyan_300</color>\n    <color name=\"material_teal\">@color/material_teal_300</color>\n    <color name=\"material_green\">@color/material_green_300</color>\n    <color name=\"material_light_green\">@color/material_light_green_300</color>\n    <color name=\"material_lime\">@color/material_lime_300</color>\n    <color name=\"material_yellow\">@color/material_yellow_300</color>\n    <color name=\"material_amber\">@color/material_amber_300</color>\n    <color name=\"material_orange\">@color/material_orange_300</color>\n    <color name=\"material_deep_orange\">@color/material_deep_orange_300</color>\n    <color name=\"material_brown\">@color/material_brown_300</color>\n    <color name=\"material_grey\">@color/material_grey_300</color>\n    <color name=\"material_blue_grey\">@color/material_blue_grey_300</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-night/colors_material3.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <color name=\"system_window_scrim_material3\">@color/system_window_scrim_dark_material3</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-night/themes_custom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<resources>\n\n    <style name=\"Theme.MaterialFiles.color_primary.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.color_primary.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.color_primary.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_red.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_red.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_red.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_pink.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_pink.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_pink.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_purple.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_purple.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_purple.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_deep_purple.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_deep_purple.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_deep_purple.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_indigo.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_indigo.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_indigo.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_blue.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_blue.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_blue.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_light_blue.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_light_blue.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_light_blue.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_cyan.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_cyan.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_cyan.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_teal.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_teal.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_teal.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_green.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_green.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_green.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_light_green.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_light_green.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_light_green.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_lime.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_lime.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_lime.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_yellow.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_yellow.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_yellow.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_amber.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_amber.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_amber.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_orange.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_orange.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_orange.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_deep_orange.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_deep_orange.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_deep_orange.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_brown.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_brown.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_brown.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_grey.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_grey.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_grey.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.material_blue_grey.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Translucent.material_blue_grey.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Immersive.material_blue_grey.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-night/themes_material3.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <style name=\"Theme.MaterialFiles.Material3.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Material3.Translucent.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n    <style name=\"Theme.MaterialFiles.Material3.Immersive.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-night-v29/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <color name=\"color_primary_dark_black\">@android:color/transparent</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-nl/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">Bestand</string>\n    <string name=\"file_type_name_apk\">Android-pakket</string>\n    <string name=\"file_type_name_archive\">%1$s archief</string>\n    <string name=\"file_type_name_audio\">%1$s-audio</string>\n    <string name=\"file_type_name_calendar\">%1$s-agenda</string>\n    <string name=\"file_type_name_certificate\">%1$s-certificaat</string>\n    <string name=\"file_type_name_code\">%1$s-document</string>\n    <string name=\"file_type_name_contact\">Electronisch visitekaartje</string>\n    <string name=\"file_type_name_directory\">Map</string>\n    <string name=\"file_type_name_document\">%1$s-document</string>\n    <string name=\"file_type_name_ebook\">%1$se-boek</string>\n    <string name=\"file_type_name_email\">%1$s-e-mailbericht</string>\n    <string name=\"file_type_name_font\">%1$s-lettertype</string>\n    <string name=\"file_type_name_generic\">%1$s-bestand</string>\n    <string name=\"file_type_name_image\">%1$s-afbeelding</string>\n    <string name=\"file_type_name_pdf\">PDF-document</string>\n    <string name=\"file_type_name_presentation\">%1$s-presentatie</string>\n    <string name=\"file_type_name_spreadsheet\">%1$s-werkblad</string>\n    <string name=\"file_type_name_text\">%1$s-document</string>\n    <string name=\"file_type_name_text_plain\">Plattetekstdocument</string>\n    <string name=\"file_type_name_video\">%1$s-video</string>\n    <string name=\"file_type_name_word\">Word-document</string>\n    <string name=\"file_type_name_powerpoint\">PowerPoint-presentatie</string>\n    <string name=\"file_type_name_excel\">Excel-werkblad</string>\n    <string name=\"file_type_name_posix_character_device\">Tekenapparaat</string>\n    <string name=\"file_type_name_posix_block_device\">Blokapparaat</string>\n    <string name=\"file_type_name_posix_fifo\">Pijp</string>\n    <string name=\"file_type_name_posix_symbolic_link\">Verwijzing</string>\n    <string name=\"file_type_name_posix_socket\">Socket</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">Verwijzing (kapot)</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-nl/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <string name=\"app_name\">Material-bestanden</string>\n\n    <string name=\"close\">Sluiten</string>\n    <string name=\"copy\">Kopiëren</string>\n    <string name=\"cut\">Knippen</string>\n    <string name=\"default_\">Standaard</string>\n    <string name=\"delete\">Verwijderen</string>\n    <string name=\"discard\">Verwerpen</string>\n    <string name=\"edit\">Bewerken</string>\n    <string name=\"empty\">Leeg</string>\n    <string name=\"empty_placeholder\">(leeg)</string>\n    <string name=\"error\">Fout</string>\n    <string name=\"hide\">Verbergen</string>\n    <string name=\"install\">Installeren</string>\n    <string name=\"keep_editing\">Doorgaan met bewerken</string>\n    <string name=\"loading\">Bezig met laden…</string>\n    <string name=\"maybe_later\">Misschien later</string>\n    <string name=\"merge\">Samenvoegen</string>\n    <string name=\"none\">Geen</string>\n    <string name=\"open_navigation_drawer\">Navigatiemenu openen</string>\n    <string name=\"paste\">Plakken</string>\n    <string name=\"refresh\">Verversen</string>\n    <string name=\"reload\">Herladen</string>\n    <string name=\"remove\">Verwijderen</string>\n    <string name=\"rename\">Naam wijzigen</string>\n    <string name=\"replace\">Vervangen</string>\n    <string name=\"reset\">Standaardwaarden</string>\n    <string name=\"retry\">Opnieuw</string>\n    <string name=\"save\">Opslaan</string>\n    <string name=\"search\">Zoeken</string>\n    <string name=\"select\">Selecteren</string>\n    <string name=\"select_all\">Alles selecteren</string>\n    <string name=\"share\">Delen</string>\n    <string name=\"show\">Tonen</string>\n    <string name=\"skip\">Overslaan</string>\n    <string name=\"stop\">Stoppen</string>\n    <string name=\"system_default\">Systeemstandaard</string>\n    <string name=\"unknown\">Onbekend</string>\n    <string name=\"view\">Bekijken</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">Er is geen app die deze actie kan uitvoeren</string>\n    <string name=\"open_settings\">Instellingen openen</string>\n    <string name=\"shortcut_created\">De snelkoppeling is gemaakt</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"one\">%1$,d byte</item>\n        <item quantity=\"other\">%1$,d bytes</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">Deze app heeft toegang nodig tot je bestanden. Sta de toegang toe op het volgende scherm.</string>\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">Deze app heeft toegang nodig tot je bestanden. Druk op het volgende schermpje op ‘TOESTAAN’.</string>\n    <string name=\"storage_permission_permanently_denied_message\">Deze app heeft toegang nodig tot je bestanden. Verleen het recht ‘Opslag’ in de systeeminstellingen.</string>\n    <!-- The allow button is in sentence case now. -->\n    <string name=\"notification_permission_rationale_message\">Deze app heeft toegang nodig tot je meldingen. Druk op het volgende schermpje op ‘TOESTAAN’.</string>\n    <string name=\"notification_permission_permanently_denied_message\">Deze app heeft toegang nodig tot je meldingen. Verleen het recht ‘Meldingen’ in de systeeminstellingen.</string>\n\n    <string name=\"notification_channel_background_activity_start_name\">Achtergrondtaken</string>\n    <string name=\"notification_channel_background_activity_start_description\">Neem taken aan terwijl de app op de achtergrond draait</string>\n    <string name=\"notification_channel_file_job_name\">Bestandshandelingen</string>\n    <string name=\"notification_channel_file_job_description\">Toon en beheer bestandshandelingen</string>\n    <string name=\"notification_channel_ftp_server_name\">FTP-server</string>\n    <string name=\"notification_channel_ftp_server_description\">Toon en beheer de FTP-server</string>\n\n    <string name=\"file_action_archive_password_title\">Wachtwoord vereist</string>\n    <string name=\"file_action_archive_password_message_format\">‘%1$s’ is beveiligd met een wachtwoord.</string>\n    <string name=\"file_action_archive_password_error_empty\">Voer een wachtwoord in</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"one\">Bezig met voorbereiden van %1$,d bestand (%2$s)</item>\n        <item quantity=\"other\">Bezig met voorbereiden van %1$,d bestanden (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">Bezig met toevoegen van ‘%1$s’ aan ‘%2$s’</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"one\">Bezig met toevoegen van %1$,d bestand aan ‘%2$s’</item>\n        <item quantity=\"other\">Bezig met toevoegen van %1$,d bestanden aan ‘%2$s’</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"one\">Bezig met voorbereiden van %1$,d bestand (%2$s)</item>\n        <item quantity=\"other\">Bezig met voorbereiden van %1$,d bestanden (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">Bezig met kopiëren van ‘%1$s’ naar ‘%2$s’</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"one\">Bezig met kopiëren van %1$,d bestand naar ‘%2$s’</item>\n        <item quantity=\"other\">Bezig met kopiëren van %1$,d bestanden naar ‘%2$s’</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"one\">Bezig met voorbereiden van %1$,d bestand (%2$s)</item>\n        <item quantity=\"other\">Bezig met voorbereiden van %1$,d bestanden (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">Bezig met uitpakken van ‘%1$s’ naar ‘%2$s’</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"one\">Bezig met uitpakken van %1$,d bestand naar ‘%2$s’</item>\n        <item quantity=\"other\">Bezig met uitpakken van %1$,d bestanden naar ‘%2$s’</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"one\">Bezig met voorbereiden van %1$,d bestand (%2$s)</item>\n        <item quantity=\"other\">Bezig met voorbereiden van %1$,d bestanden (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">Bezig met verplaatsen van ‘%1$s’ naar ‘%2$s’</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"one\">Bezig met verplaatsen van %1$,d bestand naar ‘%2$s’</item>\n        <item quantity=\"other\">Bezig met verplaatsen van %1$,d bestanden naar ‘%2$s’</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"one\">Bezig met voorbereiden van %1$,d bestand</item>\n        <item quantity=\"other\">Bezig met voorbereiden van %1$,d bestanden</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">Bezig met verwijderen van ‘%1$s’</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"one\">Bezig met verwijderen van %1$,d bestand</item>\n        <item quantity=\"other\">Bezig met verwijderen van %1$,d bestanden</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"one\">Bezig met voorbereiden van %1$,d bestand</item>\n        <item quantity=\"other\">Bezig met voorbereiden van %1$,d bestanden</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">Bezig met veranderen van eigenaar van ‘%1$s’</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"one\">Bezig met veranderen van eigenaar van %1$,d bestand</item>\n        <item quantity=\"other\">Bezig met veranderen van eigenaar van %1$,d bestanden</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"one\">Bezig met voorbereiden van %1$,d bestand</item>\n        <item quantity=\"other\">Bezig met voorbereiden van %1$,d bestanden</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">Bezig met veranderen van groep van ‘%1$s’</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"one\">Bezig met veranderen van groep van %1$,d bestand</item>\n        <item quantity=\"other\">Bezig met veranderen van groep van %1$,d bestanden</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"one\">Bezig met voorbereiden van %1$,d bestand</item>\n        <item quantity=\"other\">Bezig met voorbereiden van %1$,d bestanden</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">Bezig met aanpassen van modus van ‘%1$s’</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"one\">Bezig met aanpassen van modus van %1$,d bestand</item>\n        <item quantity=\"other\">Bezig met aanpassen van modus van %1$,d bestanden</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Bezig met voorbereiden van %1$,d bestand</item>\n        <item quantity=\"other\">Bezig met voorbereiden van %1$,d bestanden</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">Bezig met aanpassen van SELinux-context van ‘%1$s’</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Bezig met aanpassen van SELinux-context van %1$,d bestand</item>\n        <item quantity=\"other\">Bezig met aanpassen van SELinux-context van %1$,d bestanden</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Bezig met voorbereiden van %1$,d bestand</item>\n        <item quantity=\"other\">Bezig met voorbereiden van %1$,d bestanden</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">Bezig met herstellen van SELinux-context van ‘%1$s’</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Bezig met herstellen van SELinux-context van %1$,d bestand</item>\n        <item quantity=\"other\">Bezig met herstellen van SELinux-context van %1$,d bestanden</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">Bezig met wegschrijven van ‘%1$s’</string>\n    <string name=\"file_job_create_error_title\">Fout tijdens aanmaken</string>\n    <string name=\"file_job_create_error_message_format\">Er is een fout opgetreden tijdens het aanmaken van ‘%1$s’.\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">Een map kan niet naar zichzelf worden gekopieerd</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">Een map kan niet naar zichzelf worden uitgepakt</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">Een map kan niet naar zichzelf worden verplaatst</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">De bestemmingsmap is in de bronmap.</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">Een bestand kan niet naar zichzelf worden gekopieerd</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">Een bestand kan niet naar zichzelf worden uitgepakt</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">Een bestand kan niet naar zichzelf worden verplaatst</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">Het bronbestand zou worden overschreven door de bestemming.</string>\n    <string name=\"file_job_replace_title_format\">Wil je ‘%1$s’ vervangen?</string>\n    <string name=\"file_job_replace_message_format\">Er is al een bestand met deze naam in ‘%1$s’.\\nDoor het te vervangen wordt de inhoud ervan overschreven.</string>\n    <string name=\"file_job_replace_target_name\">Oorspronkelijk bestand</string>\n    <string name=\"file_job_replace_source_name\">Vervangen door</string>\n    <string name=\"file_job_merge_title_format\">Wil je ‘%1$s’ samenvoegen?</string>\n    <string name=\"file_job_merge_copy_message_format\">Er wordt om bevestiging gevraagd alvorens het samenvoegen wegens mogelijke bestandsconflicten tijdens het kopiëren.</string>\n    <string name=\"file_job_merge_extract_message_format\">Er wordt om bevestiging gevraagd alvorens het samenvoegen wegens mogelijke bestandsconflicten tijdens het uitpakken.</string>\n    <string name=\"file_job_merge_move_message_format\">Er wordt om bevestiging gevraagd alvorens het samenvoegen wegens mogelijke bestandsconflicten tijdens het verplaatsen.</string>\n    <string name=\"file_job_merge_target_name\">Oorspronkelijke map</string>\n    <string name=\"file_job_merge_source_name\">Samenvoegen met</string>\n    <string name=\"file_job_conflict_show_name\">Voer een nieuwe naam in</string>\n    <string name=\"file_job_conflict_name_hint\">Nieuwe naam</string>\n    <string name=\"file_job_archive_error_title_format\">Fout tijdens inpakken van ‘%1$s’</string>\n    <string name=\"file_job_archive_error_message_format\">Er is een fout opgetreden tijdens het inpakken in ‘%1$s’.\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">Fout tijdens kopiëren van ‘%1$s’</string>\n    <string name=\"file_job_copy_error_message_format\">Er is een fout opgetreden tijdens het kopiëren naar ‘%1$s’.\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">Fout tijdens uitpakken van ‘%1$s’</string>\n    <string name=\"file_job_extract_error_message_format\">Er is een fout opgetreden tijdens het uitpakken naar ‘%1$s’.\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">Fout tijdens verplaatsen van ‘%1$s’</string>\n    <string name=\"file_job_move_error_message_format\">Er is een fout opgetreden tijdens het verplaatsen naar ‘%1$s’.\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">Fout tijdens verwijderen</string>\n    <string name=\"file_job_delete_error_message_format\">Er is een fout opgetreden tijdens het verwijderen van ‘%1$s’.\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">Fout tijdens verplaatsen van ‘%1$s’</string>\n    <string name=\"file_job_rename_error_message_format\">Er is een fout opgetreden tijdens het wijzigen van de naam in ‘%1$s’.\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">Fout tijdens veranderen van eigenaar van ‘%1$s’</string>\n    <string name=\"file_job_set_owner_error_message_format\">Er is een fout opgetreden tijdens het veranderen van de eigenaar naar ‘%1$s’.\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">Fout tijdens veranderen van groep van ‘%1$s’</string>\n    <string name=\"file_job_set_group_error_message_format\">Er is een fout opgetreden tijdens het veranderen van de groep naar ‘%1$s’.\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">Fout tijdens aanpassen van modus van ‘%1$s’</string>\n    <string name=\"file_job_set_mode_error_message_format\">Er is een fout opgetreden tijdens het aanpassen van de modus naar ‘%1$s’.\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">Fout tijdens aanpassen van SELinux-context van ‘%1$s’</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">Er is een fout opgetreden tijdens het aanpassen van de SELinux-context naar ‘%1$s’.\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">Fout tijdens herstellen van SELinux-context</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">Er is een fout opgetreden tijdens het herstellen van de SELinux-context van ‘%1$s’.\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">Fout tijdens wegschrijven</string>\n    <string name=\"file_job_write_error_message_format\">Er is een fout opgetreden tijdens het wegschrijven van ‘%1$s’.\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">Er is een fout opgetreden tijdens het verkrijgen van informatie over ‘%1$s’.</string>\n    <string name=\"file_job_remount_format\">‘%1$s’ opnieuw aankoppelen als beschrijfbaar</string>\n    <string name=\"file_job_remount_loading_format\">Bezig met opnieuw aankoppelen van ‘%1$s’…</string>\n    <string name=\"file_job_remount_success_format\">‘%1$s’ is opnieuw aangekoppeld als beschrijfbaar</string>\n    <string name=\"file_job_action_all\">Toepassen op alle bestanden</string>\n\n    <string name=\"file_open_apk_message\">Dit bestand is een Android-pakket. Wil je het installeren of de inhoud ervan bekijken?</string>\n    <string name=\"file_open_as_title\">Openen als…</string>\n    <string name=\"file_open_as_title_format\">‘%1$s’ openen als</string>\n    <string name=\"file_open_as_type_text\">Tekst</string>\n    <string name=\"file_open_as_type_image\">Afbeelding</string>\n    <string name=\"file_open_as_type_audio\">Audio</string>\n    <string name=\"file_open_as_type_video\">Video</string>\n    <string name=\"file_open_as_type_directory\">Map</string>\n    <string name=\"file_open_as_type_any\">Anders</string>\n    <string name=\"file_install_apk_from_background_title_format\">‘%1$s’ kan nu worden geïnstalleerd</string>\n    <string name=\"file_install_apk_from_background_text\">Druk om te installeren</string>\n    <string name=\"file_open_from_background_title_format\">‘%1$s’ kan nu worden geopend</string>\n    <string name=\"file_open_from_background_text\">Druk om te openen</string>\n    <string name=\"file_name_error_empty\">Voer een bestandsnaam in</string>\n    <string name=\"file_name_error_invalid\">Ongeldige bestandsnaam</string>\n    <string name=\"file_name_error_already_exists\">Er is al een bestand met deze naam</string>\n    <string name=\"file_replace_message_format\">Wil je ‘%1$s’ vervangen?</string>\n    <string name=\"file_delete_message_file_format\">‘%1$s’ verwijderen?</string>\n    <string name=\"file_delete_message_directory_format\">Wil je de map ‘%1$s’ en alle inhoud ervan verwijderen?</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"one\">%1$,d bestand verwijderen?</item>\n        <item quantity=\"other\">%1$,d bestanden verwijderen?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"one\">Wil je %1$,d map en alle inhoud ervan verwijderen?</item>\n        <item quantity=\"other\">Wil je %1$,d mappen en alle inhoud ervan verwijderen?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"one\">%1$,d item verwijderen?</item>\n        <item quantity=\"other\">%1$,d items verwijderen?</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">Archief samenstellen</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_create_archive_password\">Wachtwoord (optioneel)</string>\n    <string name=\"file_add_bookmark_success\">Bladwijzer is toegevoegd</string>\n    <string name=\"file_create_file_title\">Nieuw bestand</string>\n    <string name=\"file_create_directory_title\">Nieuwe map</string>\n\n    <string name=\"file_list_title\">Bestanden</string>\n    <plurals name=\"file_list_title_open_file\">\n        <item quantity=\"one\">Bestand selecteren</item>\n        <item quantity=\"other\">Bestanden selecteren</item>\n    </plurals>\n    <string name=\"file_list_title_create_file\">Bestand opslaan</string>\n    <plurals name=\"file_list_title_open_directory\">\n        <item quantity=\"one\">Map selecteren</item>\n        <item quantity=\"other\">Mappen selecteren</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"one\">%1$,d map</item>\n        <item quantity=\"other\">%1$,d mappen</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"one\">%1$,d bestand</item>\n        <item quantity=\"other\">%1$,d bestanden</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">,\\u0020</string>\n    <string name=\"file_list_path_error_empty\">Voer een locatie in</string>\n    <string name=\"file_list_path_error_invalid\">Ongeldige locatie</string>\n    <string name=\"file_list_action_view_sort\">Weergave en sortering</string>\n    <string name=\"file_list_action_view_list\">Lĳst</string>\n    <string name=\"file_list_action_view_grid\">Rooster</string>\n    <string name=\"file_list_action_sort_by_name\">Naam</string>\n    <string name=\"file_list_action_sort_by_type\">Soort</string>\n    <string name=\"file_list_action_sort_by_size\">Grootte</string>\n    <string name=\"file_list_action_sort_by_last_modified\">Aangepast op</string>\n    <string name=\"file_list_action_sort_order_ascending\">Oplopend</string>\n    <string name=\"file_list_action_sort_directories_first\">Mappen vóór bestanden</string>\n    <string name=\"file_list_action_view_sort_path_specific\">Alleen in deze map</string>\n    <string name=\"file_list_action_new_task\">Nieuw venster</string>\n    <string name=\"file_list_action_navigate_up\">Ga omhoog</string>\n    <string name=\"file_list_action_navigate_to\">Ga naar</string>\n    <string name=\"file_list_action_show_hidden_files\">Verborgen bestanden tonen</string>\n    <string name=\"file_list_action_copy_path\">Locatie kopiëren</string>\n    <string name=\"file_list_action_open_in_terminal\">Openen in terminalvenster</string>\n    <string name=\"file_list_action_add_bookmark\">Bladwijzer toevoegen</string>\n    <string name=\"file_list_action_create_shortcut\">Snelkoppeling maken</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">Openen in nieuw venster</string>\n    <string name=\"file_list_select_title_format\">%1$,d</string>\n    <string name=\"file_list_create_file_name_hint\">Bestandsnaam</string>\n    <string name=\"file_list_open_current_directory_format\">‘%1$s’ selecteren</string>\n    <string name=\"file_list_paste_move_title_format\">Bezig met verplaatsen van %1$,d</string>\n    <string name=\"file_list_paste_copy_title_format\">Bezig met kopiëren van %1$,d</string>\n    <string name=\"file_list_paste_extract_title_format\">Bezig met uitpakken van %1$,d</string>\n    <string name=\"file_list_paste_action_extract_here\">Hier uitpakken</string>\n    <string name=\"file_list_empty\">Geen bestanden</string>\n    <string name=\"file_list_action_create_file\">Bestand</string>\n    <string name=\"file_list_action_create_directory\">Map</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">Openen met</string>\n    <string name=\"file_item_action_extract\">Uitpakken</string>\n    <string name=\"file_item_action_archive\">Inpakken</string>\n    <string name=\"file_item_action_properties\">Eigenschappen</string>\n\n    <string name=\"file_properties_title_format\">Eigenschappen van ‘%1$s’</string>\n    <string name=\"file_properties_basic\">Informatie</string>\n    <string name=\"file_properties_basic_name\">Naam</string>\n    <string name=\"file_properties_basic_type\">Soort</string>\n    <string name=\"file_properties_basic_type_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">Verwijzen naar %1$s (%2$s)</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">Verwijsdoel</string>\n    <string name=\"file_properties_basic_size\">Grootte</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_contents\">Inhoud</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"one\">%1$,d item; in totaal %2$s</item>\n        <item quantity=\"other\">%1$,d items; in totaal %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">Aangepast op</string>\n    <string name=\"file_properties_basic_parent_directory\">Bovenliggende map</string>\n    <string name=\"file_properties_basic_archive_file\">Archiefbestand</string>\n    <string name=\"file_properties_basic_archive_entry\">Archiefitem</string>\n    <string name=\"file_properties_basic_free_space\">Vrije ruimte</string>\n    <string name=\"file_properties_permission\">Rechten</string>\n    <string name=\"file_properties_permission_owner\">Eigenaar</string>\n    <string name=\"file_properties_permission_group\">Groep</string>\n    <string name=\"file_properties_permission_principal_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_permission_set_owner_title\">Andere eigenaar kiezen</string>\n    <string name=\"file_properties_permission_set_group_title\">Andere groep kiezen</string>\n    <string name=\"file_properties_permission_set_principal_filter_hint\">Voer een naam of id in</string>\n    <string name=\"file_properties_permission_set_principal_system\">Systeem</string>\n    <string name=\"file_properties_permission_mode\">Modus</string>\n    <string name=\"file_properties_permission_mode_format\">%1$s (%2$04o)</string>\n    <string name=\"file_properties_permission_set_mode_title\">Modus aanpassen</string>\n    <string name=\"file_properties_permission_set_mode_others\">Anderen</string>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_file\">\n        <item>Uitlezen</item>\n        <item>Wegschrijven</item>\n        <item>Uitvoeren</item>\n    </string-array>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_directory\">\n        <item>Uitlezen</item>\n        <item>Wegschrijven</item>\n        <item>Zoeken</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_special\">Speciaal</string>\n    <string-array name=\"file_properties_permission_set_mode_special_mode_bits\">\n        <item>UID instellen</item>\n        <item>GID instellen</item>\n        <item>Vastgemaakt</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_uppercase_x\">Uitvoerrecht niet toepassen op bijgesloten bestanden</string>\n    <string name=\"file_properties_permission_selinux_context\">SELinux-context</string>\n    <string name=\"file_properties_permission_set_selinux_context_title\">SELinux-context aanpassen</string>\n    <string name=\"file_properties_permission_set_selinux_context_restore\">Herstellen</string>\n    <string name=\"file_properties_permission_recursive\">Toepassen op bijgesloten bestanden</string>\n    <string name=\"file_properties_image\">Afbeelding</string>\n    <string name=\"file_properties_media_dimensions\">Afmetingen</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">Genomen op</string>\n    <string name=\"file_properties_media_coordinates\">Coördinaten</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f, %2$.3f</string>\n    <string name=\"file_properties_media_address\">Locatie</string>\n    <string name=\"file_properties_image_gps_altitude\">Hoogte</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f m</string>\n    <string name=\"file_properties_image_equipment\">Camera</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">Diafragma</string>\n    <string name=\"file_properties_image_shutter_speed\">Sluitersnelheid</string>\n    <string name=\"file_properties_image_focal_length\">Brandpuntsafstand</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f mm</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">ISO-equivalent</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">Software</string>\n    <string name=\"file_properties_image_description\">Omschrijving</string>\n    <string name=\"file_properties_image_artist\">Maker</string>\n    <string name=\"file_properties_image_copyright\">Auteursrecht</string>\n    <string name=\"file_properties_audio\">Audio</string>\n    <string name=\"file_properties_media_title\">Titel</string>\n    <string name=\"file_properties_audio_artist\">Artiest</string>\n    <string name=\"file_properties_audio_album\">Album</string>\n    <string name=\"file_properties_audio_album_artist\">Albumartiest</string>\n    <string name=\"file_properties_audio_composer\">Componist</string>\n    <string name=\"file_properties_audio_disc_number\">Schijf</string>\n    <string name=\"file_properties_audio_track_number\">Nummer</string>\n    <string name=\"file_properties_audio_year\">Jaartal</string>\n    <string name=\"file_properties_audio_genre\">Genre</string>\n    <string name=\"file_properties_media_duration\">Duur</string>\n    <string name=\"file_properties_media_bit_rate\">Bitsnelheid</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d kbps</string>\n    <string name=\"file_properties_audio_sample_rate\">Samplesnelheid</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Hz</string>\n    <string name=\"file_properties_video\">Video</string>\n    <string name=\"file_properties_apk\">APK</string>\n    <string name=\"file_properties_apk_label\">Naam</string>\n    <string name=\"file_properties_apk_package_name\">Pakketnaam</string>\n    <string name=\"file_properties_apk_version\">Versie</string>\n    <string name=\"file_properties_apk_version_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_apk_min_sdk_version\">Minimale systeemvereiste</string>\n    <string name=\"file_properties_apk_target_sdk_version\">Doel-systeemvereiste</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s (%2$s, %3$d)</string>\n    <string name=\"file_properties_apk_requested_permissions\">Rechten</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">Geen rechten vereist</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"one\">%1$,d recht vereist</item>\n        <item quantity=\"other\">%1$,d rechten vereist</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">Ondertekeningen</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">Geen geldige ondertekeningen</string>\n    <string name=\"file_properties_apk_past_signature_digests\">Oude ondertekeningen</string>\n    <string name=\"file_properties_checksum\">Controlesommen</string>\n    <string name=\"file_properties_checksum_compare\">Vergelijken</string>\n    <string name=\"file_properties_checksum_compare_placeholder\">Iedere controlesom</string>\n    <string name=\"file_properties_checksum_compare_match_format\">Komt overeen met %1$s</string>\n    <string name=\"file_properties_checksum_compare_prefix_match_format\">Komt overeen met het voorvoegsel van %1$s</string>\n    <string name=\"file_properties_checksum_compare_no_match\">Er zijn geen overeenkomsten gevonden</string>\n    <string name=\"storage_file_system_root_title\">Root</string>\n    <string name=\"storage_list_title\">Opslag</string>\n    <string name=\"storage_list_empty\">Geen opslag</string>\n    <string name=\"storage_add_storage_title\">Opslagmedium toevoegen</string>\n    <string name=\"storage_add_storage_document_tree\">Externe opslag</string>\n    <string name=\"storage_add_storage_ftp_server\">FTP-server</string>\n    <string name=\"storage_add_storage_sftp_server\">SFTP-server</string>\n    <string name=\"storage_add_storage_smb_server\">SMB-server</string>\n    <string name=\"storage_add_storage_webdav_server\">WebDAV-server</string>\n    <string name=\"storage_edit_device_storage_title\">Apparaatopslag bewerken</string>\n    <string name=\"storage_edit_device_storage_name\">Naam</string>\n    <string name=\"storage_edit_device_storage_path\">Locatie</string>\n    <string name=\"storage_add_external_storage_shortcut_title\">Externe opslagsnelkoppeling toevoegen</string>\n    <string name=\"storage_edit_external_storage_shortcut_title\">Externe opslagsnelkoppeling bewerken</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id\">Opslagvolume</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id_error_empty\">Voer een opslagvolume in</string>\n    <string name=\"storage_edit_external_storage_shortcut_path\">Locatie</string>\n    <string name=\"storage_add_document_tree_title\">Externe opslag toevoegen</string>\n    <string name=\"storage_edit_document_tree_title\">Externe opslag bewerken</string>\n    <string name=\"storage_edit_document_tree_name\">Naam</string>\n    <string name=\"storage_edit_document_tree_uri\">URI</string>\n    <string name=\"storage_edit_document_tree_path\">Locatie</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">FTP-server bewerken</string>\n    <string name=\"storage_edit_ftp_server_title_add\">FTP-server toevoegen</string>\n    <string name=\"storage_edit_ftp_server_host\">Hostnaam</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">Voer een hostnaam in</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">Ongeldige hostnaam</string>\n    <string name=\"storage_edit_ftp_server_port\">Poort</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">Ongeldig poortnummer</string>\n    <string name=\"storage_edit_ftp_server_path\">Locatie</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">Mag leeg worden gelaten</string>\n    <string name=\"storage_edit_ftp_server_name\">Naam</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">Hostnaam gebruiken</string>\n    <string name=\"storage_edit_ftp_server_protocol\">Protocol</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">Authenticatie</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>Wachtwoord</item>\n        <item>Anoniem</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">Gebruikersnaam</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">Voer een gebruikersnaam in</string>\n    <string name=\"storage_edit_ftp_server_password\">Wachtwoord</string>\n    <string name=\"storage_edit_ftp_server_mode\">Modus</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>Actief</item>\n        <item>Passief</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">Versleuteling</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">Verbinden en toevoegen</string>\n    <string name=\"storage_edit_ftp_server_add\">Toevoegen</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">SFTP-server bewerken</string>\n    <string name=\"storage_edit_sftp_server_title_add\">SFTP-server toevoegen</string>\n    <string name=\"storage_edit_sftp_server_host\">Hostnaam</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">Voer een hostnaam in</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">Ongeldige hostnaam</string>\n    <string name=\"storage_edit_sftp_server_port\">Poort</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">Ongeldig poortnummer</string>\n    <string name=\"storage_edit_sftp_server_path\">Locatie</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">Mag leeg worden gelaten</string>\n    <string name=\"storage_edit_sftp_server_name\">Naam</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">Hostnaam gebruiken</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">Authenticatie</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>Wachtwoord</item>\n        <item>Publieke sleutel</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">Gebruikersnaam</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">Voer een gebruikersnaam in</string>\n    <string name=\"storage_edit_sftp_server_password\">Wachtwoord</string>\n    <string name=\"storage_edit_sftp_server_private_key\">Privésleutel</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">Bestand openen</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">Voer een privésleutel in</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">Ongeldige privésleutel</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">Privésleutel</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">Mag leeg worden gelaten</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">Ongeldige privésleutel</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">Verbinden en toevoegen</string>\n    <string name=\"storage_edit_sftp_server_add\">Toevoegen</string>\n    <string name=\"storage_add_lan_smb_server_loading\">Bezig met zoeken naar smb-servers…</string>\n    <string name=\"storage_add_lan_smb_server_add\">Handmatig toevoegen</string>\n    <string name=\"storage_edit_smb_server_title_edit\">SMB-server bewerken</string>\n    <string name=\"storage_edit_smb_server_title_add\">SMB-server toevoegen</string>\n    <string name=\"storage_edit_smb_server_host\">Hostnaam</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">Voer een hostnaam in</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">Ongeldige hostnaam</string>\n    <string name=\"storage_edit_smb_server_port\">Poort</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">Ongeldig poortnummer</string>\n    <string name=\"storage_edit_smb_server_path\">Locatie</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">Mag leeg worden gelaten</string>\n    <string name=\"storage_edit_smb_server_name\">Naam</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">Hostnaam gebruiken</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">Authenticatie</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>Wachtwoord</item>\n        <item>Gast</item>\n        <item>Anoniem</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">Gebruikersnaam</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">Voer een gebruikersnaam in</string>\n    <string name=\"storage_edit_smb_server_password\">Wachtwoord</string>\n    <string name=\"storage_edit_smb_server_domain\">Domein</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">Verbinden en toevoegen</string>\n    <string name=\"storage_edit_smb_server_add\">Toevoegen</string>\n    <string name=\"storage_edit_webdav_server_title_edit\">WebDAV-server bewerken</string>\n    <string name=\"storage_edit_webdav_server_title_add\">WebDAV-server toevoegen</string>\n    <string name=\"storage_edit_webdav_server_host\">Hostnaam</string>\n    <string name=\"storage_edit_webdav_server_host_error_empty\">Voer een hostnaam in</string>\n    <string name=\"storage_edit_webdav_server_host_error_invalid\">Ongeldige hostnaam</string>\n    <string name=\"storage_edit_webdav_server_port\">Poort</string>\n    <string name=\"storage_edit_webdav_server_port_error_invalid\">Ongeldig poortnummer</string>\n    <string name=\"storage_edit_webdav_server_path\">Locatie</string>\n    <string name=\"storage_edit_webdav_server_path_placeholder\">Mag leeg worden gelaten</string>\n    <string name=\"storage_edit_webdav_server_name\">Naam</string>\n    <string name=\"storage_edit_webdav_server_name_placeholder\">Hostnaam gebruiken</string>\n    <string name=\"storage_edit_webdav_server_protocol\">Protocol</string>\n    <string name=\"storage_edit_webdav_server_authentication_type\">Authenticatie</string>\n    <string-array name=\"storage_edit_webdav_server_authentication_type_entries\">\n        <item>Wachtwoord</item>\n        <item>Toegangssleutel</item>\n        <item>Geen</item>\n    </string-array>\n    <string name=\"storage_edit_webdav_server_username\">Gebruikersnaam</string>\n    <string name=\"storage_edit_webdav_server_username_error_empty\">Voer een gebruikersnaam in</string>\n    <string name=\"storage_edit_webdav_server_password\">Wachtwoord</string>\n    <string name=\"storage_edit_webdav_server_access_token\">Toegangssleutel</string>\n    <string name=\"storage_edit_webdav_server_access_token_error_empty\">Voer een toegangssleutel in</string>\n    <string name=\"storage_edit_webdav_server_connect_and_add\">Verbinden en toevoegen</string>\n    <string name=\"storage_edit_webdav_server_add\">Toevoegen</string>\n\n    <string name=\"navigation_storage_subtitle_format\">%1$s van %2$s vrij</string>\n    <string name=\"navigation_add_storage\">Opslagmedium toevoegen…</string>\n    <string name=\"navigation_standard_directory_alarms\">Wekkers</string>\n    <string name=\"navigation_standard_directory_dcim\">Camerafoto\\'s</string>\n    <string name=\"navigation_standard_directory_documents\">Documenten</string>\n    <string name=\"navigation_standard_directory_downloads\">Downloads</string>\n    <string name=\"navigation_standard_directory_movies\">Video\\'s</string>\n    <string name=\"navigation_standard_directory_music\">Muziek</string>\n    <string name=\"navigation_standard_directory_notifications\">Meldingen</string>\n    <string name=\"navigation_standard_directory_pictures\">Afbeeldingen</string>\n    <string name=\"navigation_standard_directory_podcasts\">Podcasts</string>\n    <string name=\"navigation_standard_directory_ringtones\">Beltonen</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">Toevoegen aan bladwijzers</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">Naam</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">Locatie</string>\n    <string name=\"archive_viewer_title\">Archiefweergave</string>\n    <string name=\"image_viewer_title\">Afbeeldingsweergave</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"save_as_title\">Opslaan als</string>\n    <string name=\"save_as_error\">Fout tijdens opslaan van bestand</string>\n    <string name=\"save_as_success_format\">“%1$s” is opgeslagen in ‘%2$s’</string>\n    <string name=\"text_editor_title\">Tekstbewerker</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">Opgeslagen</string>\n    <string name=\"text_editor_reload_message\">Weet je zeker dat je wilt herladen? Niet-opgeslagen wijzigingen gaan verloren.</string>\n    <string name=\"text_editor_encoding\">Versleuteling</string>\n    <string name=\"text_editor_close_message\">Weet je zeker dat je de niet-opgeslagen wijzigingen wilt verwerpen?</string>\n\n    <string name=\"ftp_server_title\">FTP-server</string>\n    <string name=\"ftp_server_state_title\">Status</string>\n    <string name=\"ftp_server_state_summary_starting\">Bezig met starten…</string>\n    <string name=\"ftp_server_state_summary_running\">Draait</string>\n    <string name=\"ftp_server_state_summary_stopping\">Bezig met stoppen…</string>\n    <string name=\"ftp_server_state_summary_stopped\">Niet gestart</string>\n    <string name=\"ftp_server_url_title\">URL</string>\n    <string name=\"ftp_server_url_summary_no_local_inet_address\">Het lokale ip-adres is onbekend</string>\n    <string name=\"ftp_server_url_menu_copy_url\">URL kopiëren</string>\n    <string name=\"ftp_server_url_menu_copy_password\">Wachtwoord kopiëren</string>\n    <string name=\"ftp_server_add_tile_title\">Toevoegen aan snelle instellingen</string>\n    <string name=\"ftp_server_add_tile_result_added\">De ftp-server is toegevoegd aan de snelle instellingen</string>\n    <string name=\"ftp_server_add_tile_result_already_added\">De ftp-server is al toegevoegd aan de snelle instellingen</string>\n    <string name=\"ftp_server_add_tile_result_error\">De ftp-server kan niet worden toegevoegd aan de snelle instellingen</string>\n    <string name=\"ftp_server_configuration_title\">Instellingen</string>\n    <string name=\"ftp_server_anonymous_login_title\">Anoniem inloggen</string>\n    <string name=\"ftp_server_username_title\">Gebruikersnaam</string>\n    <string name=\"ftp_server_password_title\">Wachtwoord</string>\n    <string name=\"ftp_server_port_title\">Poort</string>\n    <string name=\"ftp_server_home_directory_title\">Hoofdmap</string>\n    <string name=\"ftp_server_writable_title\">Wegschrijven toestaan</string>\n    <string name=\"settings_title\">Instellingen</string>\n    <string name=\"settings_interface_title\">Uiterlijk</string>\n    <string name=\"settings_locale_title\">Taal</string>\n    <string name=\"settings_theme_color_title\">Themakleur</string>\n    <string name=\"settings_theme_color_summary\">De kleur die het vaakst wordt gebruikt in de app</string>\n    <string name=\"settings_material_design_3_title\">Material Design 3</string>\n    <string name=\"settings_night_mode_title\">Nachtmodus</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>Systeeminstellingen gebruiken</item>\n        <item>Uit</item>\n        <item>Aan</item>\n        <item>Op een bepaald tijdstip</item>\n        <item>Ingesteld door batterijbesparing</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">Nachtmodus</string>\n    <string name=\"settings_file_list_animation_title\">Animatie tonen in bestandslijst</string>\n    <string name=\"settings_file_name_ellipsize_title\">Lange bestandsnamen tonen</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>Ellips toevoegen aan begin van naam</item>\n        <item>Ellips toevoegen middenin de naam</item>\n        <item>Ellips toevoegen aan einde van naam</item>\n        <item>Marquee</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">Gedrag</string>\n    <string name=\"settings_default_directory_title\">Standaardmap</string>\n    <string name=\"settings_standard_directories_title\">Standaardmappen</string>\n    <string name=\"settings_standard_directories_summary_empty\">Geen standaardmappen ingesteld</string>\n    <string name=\"settings_bookmark_directory_screenshots\">Schermfoto\\'s</string>\n    <string name=\"settings_bookmark_directories_title\">Bladwijzermappen</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">Geen bladwijzermappen</string>\n    <string name=\"settings_root_strategy_title\">Roottoegangsmodus</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"one\">Er wordt %1$,d bestandshandeling uitgevoerd. Als je nu de root-toegangsmodus aanpast, dan kan er een onverwachte fout optreden. Weet je zeker dat je wilt doorgaan?</item>\n        <item quantity=\"other\">Er worden %1$,d bestandshandelingen uitgevoerd. Als je nu de roottoegangsmodus aanpast, dan kan er een onverwachte fout optreden. Weet je zeker dat je wilt doorgaan?</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>Alleen reguliere toegang</item>\n        <item>Automatisch</item>\n        <item>Alleen roottoegang</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">Tekenreeks van archiefbestandsnaam</string>\n    <string name=\"settings_open_apk_default_action_title\">Android-pakket openen</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>Installeren</item>\n        <item>Inhoud bekijken</item>\n        <item>Vragen om een actie</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_title\">Miniaturen van pdf-bestanden tonen</string>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">Dit kan leiden tot instabiliteit op oudere Android-versies</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">Miniaturen laden van externe bestanden</string>\n\n    <string name=\"about_title\">Over</string>\n    <string name=\"about_version_title\">Versie</string>\n    <string name=\"about_github_title\">Bekijken op GitHub</string>\n    <string name=\"about_licenses_title\">Licenties</string>\n    <string name=\"about_privacy_policy_title\">Privacybeleid</string>\n    <string name=\"about_author_title\">Maker</string>\n    <string name=\"about_author_name_title\">Hai Zhang</string>\n    <string name=\"about_author_github_title\">Volgen op GitHub</string>\n    <string name=\"about_author_twitter_title\">Volgen op Twitter</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-pl/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">Plik</string>\n    <string name=\"file_type_name_apk\">Pakiet aplikacji systemu Android</string>\n    <string name=\"file_type_name_archive\">%1$s archiwum</string>\n    <string name=\"file_type_name_audio\">%1$s audio</string>\n    <string name=\"file_type_name_calendar\">%1$s kalendarz</string>\n    <string name=\"file_type_name_certificate\">%1$s certyfikat</string>\n    <string name=\"file_type_name_code\">%1$s dokument</string>\n    <string name=\"file_type_name_contact\">Wizytówka elektroniczna</string>\n    <string name=\"file_type_name_directory\">Katalog</string>\n    <string name=\"file_type_name_document\">%1$s dokument</string>\n    <string name=\"file_type_name_ebook\">%1$s e-book</string>\n    <string name=\"file_type_name_email\">%1$s wiadomość e-mail</string>\n    <string name=\"file_type_name_font\">%1$s czcionka</string>\n    <string name=\"file_type_name_generic\">%1$s plik</string>\n    <string name=\"file_type_name_image\">%1$s obraz</string>\n    <string name=\"file_type_name_pdf\">Dokument PDF</string>\n    <string name=\"file_type_name_presentation\">%1$s prezentacja</string>\n    <string name=\"file_type_name_spreadsheet\">%1$s arkusz kalkulacyjny</string>\n    <string name=\"file_type_name_text\">%1$s dokument</string>\n    <string name=\"file_type_name_text_plain\">Zwykły dokument tekstowy</string>\n    <string name=\"file_type_name_video\">%1$s wideo</string>\n    <string name=\"file_type_name_word\">Dokument Word</string>\n    <string name=\"file_type_name_powerpoint\">Prezentacja PowerPoint</string>\n    <string name=\"file_type_name_excel\">Arkusz kalkulacyjny Excel</string>\n    <string name=\"file_type_name_posix_character_device\">Urządzenie znakowe</string>\n    <string name=\"file_type_name_posix_block_device\">Urządzenie blokowe</string>\n    <string name=\"file_type_name_posix_fifo\">Potok</string>\n    <string name=\"file_type_name_posix_symbolic_link\">Link</string>\n    <string name=\"file_type_name_posix_socket\">Gniazdo</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">Link (uszkodzony)</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-pl/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <string name=\"app_name\">Material Files</string>\n\n    <string name=\"close\">Zamknij</string>\n    <string name=\"copy\">Kopiuj</string>\n    <string name=\"cut\">Wytnij</string>\n    <string name=\"default_\">Domyślny</string>\n    <string name=\"delete\">Usuń</string>\n    <string name=\"discard\">Odrzuć</string>\n    <string name=\"edit\">Edytuj</string>\n    <string name=\"empty\">Pusto</string>\n    <string name=\"empty_placeholder\">(Pusto)</string>\n    <string name=\"error\">Błąd</string>\n    <string name=\"hide\">Ukryj</string>\n    <string name=\"install\">Zainstaluj</string>\n    <string name=\"keep_editing\">Kontynuuj edycję</string>\n    <string name=\"loading\">Ładowanie…</string>\n    <string name=\"maybe_later\">Może później</string>\n    <string name=\"merge\">Scal</string>\n    <string name=\"none\">Żaden</string>\n    <string name=\"open_navigation_drawer\">Otwórz szufladę nawigacji</string>\n    <string name=\"paste\">Wklej</string>\n    <string name=\"refresh\">Odśwież</string>\n    <string name=\"reload\">Przeładuj</string>\n    <string name=\"remove\">Usuń</string>\n    <string name=\"rename\">Zmień nazwę</string>\n    <string name=\"replace\">Zastąp</string>\n    <string name=\"reset\">Zresetuj</string>\n    <string name=\"retry\">Spróbuj ponownie</string>\n    <string name=\"save\">Zapisz</string>\n    <string name=\"search\">Szukaj</string>\n    <string name=\"select\">Wybierz</string>\n    <string name=\"select_all\">Wybierz wszystko</string>\n    <string name=\"share\">Udostępnij </string>\n    <string name=\"show\">Pokaż</string>\n    <string name=\"skip\">Pomiń</string>\n    <string name=\"stop\">Zatrzymaj</string>\n    <string name=\"system_default\">Ustawienie domyślne systemu</string>\n    <string name=\"unknown\">Nieznany</string>\n    <string name=\"view\">Podgląd</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">Nie znaleziono aplikacji do wykonania tej akcji</string>\n    <string name=\"open_settings\">Otwórz ustawienia</string>\n    <string name=\"shortcut_created\">Skrót utworzony</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"one\">%1$,d bajt</item>\n        <item quantity=\"few\">%1$,d bajty</item>\n        <item quantity=\"many\">%1$,d bajtów</item>\n        <item quantity=\"other\">%1$,d bajtów</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">Aplikacja potrzebuje dostępu do zarządzania wszystkimi plikami. Zezwól na dostęp w następnym ustawieniu systemu.</string>\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">Aplikacja potrzebuje uprawnień dostępu do plików. Kliknij „ZEZWÓL” w następnym oknie dialogowym systemu.</string>\n    <string name=\"storage_permission_permanently_denied_message\">Aplikacja potrzebuje uprawnień dostępu do plików. Przyznaj uprawnienie dostępu dla „Pamięć” w ustawieniach systemu.</string>\n    <!-- The allow button is in sentence case now. -->\n    <string name=\"notification_permission_rationale_message\">Aplikacja potrzebuje uprawnień do publikowania powiadomień o operacjach na plikach. Kliknij „Zezwalaj” w następnym oknie dialogowym systemu.</string>\n    <string name=\"notification_permission_permanently_denied_message\">Aplikacja potrzebuje uprawnień do publikowania powiadomień o operacjach na plikach. Należy przyznać uprawnienia „Powiadomienia” w ustawieniach systemu.</string>\n\n    <string name=\"notification_channel_background_activity_start_name\">Działania w tle</string>\n    <string name=\"notification_channel_background_activity_start_description\">Podejmuj działania, gdy aplikacja działa w tle</string>\n    <string name=\"notification_channel_file_job_name\">Operacje na plikach</string>\n    <string name=\"notification_channel_file_job_description\">Wyświetlanie i sterowanie operacjami na plikach</string>\n    <string name=\"notification_channel_ftp_server_name\">Serwer FTP</string>\n    <string name=\"notification_channel_ftp_server_description\">Wyświetl i zarządzaj serwerem FTP</string>\n\n    <string name=\"file_action_archive_password_title\">Wymagane hasło</string>\n    <string name=\"file_action_archive_password_message_format\">„%1$s” jest chronione hasłem.</string>\n    <string name=\"file_action_archive_password_error_empty\">Hasło nie może być puste</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"one\">Trwa przygotowywanie do skompresowania %1$,d pliku (%2$s)</item>\n        <item quantity=\"few\">Trwa przygotowywanie do skompresowania %1$,d plików (%2$s)</item>\n        <item quantity=\"many\">Trwa przygotowywanie do skompresowania %1$,d plików (%2$s)</item>\n        <item quantity=\"other\">Trwa przygotowywanie do skompresowania %1$,d plików (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">Kompresowanie „%1$s” do „%2$s”</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"one\">Kompresowanie %1$,d pliku do „%2$s”</item>\n        <item quantity=\"few\">Kompresowanie %1$,d plików do „%2$s”</item>\n        <item quantity=\"many\">Kompresowanie %1$,d plików do „%2$s”</item>\n        <item quantity=\"other\">Kompresowanie %1$,d plików do „%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"one\">Trwa przygotowywanie do skopiowania %1$,d pliku (%2$s)</item>\n        <item quantity=\"few\">Trwa przygotowywanie do skopiowania %1$,d plików (%2$s)</item>\n        <item quantity=\"many\">Trwa przygotowywanie do skopiowania %1$,d plików (%2$s)</item>\n        <item quantity=\"other\">Trwa przygotowywanie do skopiowania %1$,d plików (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">Kopiowanie „%1$s” do „%2$s”</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"one\">Kopiowanie %1$,d pliku do „%2$s”</item>\n        <item quantity=\"few\">Kopiowanie %1$,d plików do „%2$s”</item>\n        <item quantity=\"many\">Kopiowanie %1$,d plików do „%2$s”</item>\n        <item quantity=\"other\">Kopiowanie %1$,d plików do „%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"one\">Trwa przygotowywanie do wypakowania %1$,d pliku (%2$s)</item>\n        <item quantity=\"few\">Trwa przygotowywanie do wypakowania %1$,d plików (%2$s)</item>\n        <item quantity=\"many\">Trwa przygotowywanie do wypakowania %1$,d plików (%2$s)</item>\n        <item quantity=\"other\">Trwa przygotowywanie do wypakowania %1$,d plików (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">Rozpakowywanie „%1$s” do „%2$s”</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"one\">Rozpakowywanie %1$,d pliku do „%2$s”</item>\n        <item quantity=\"few\">Rozpakowywanie %1$,d plików do „%2$s”</item>\n        <item quantity=\"many\">Rozpakowywanie %1$,d plików do „%2$s”</item>\n        <item quantity=\"other\">Rozpakowywanie %1$,d plików do „%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"one\">Trwa przygotowywanie do przeniesienia %1$,d pliku (%2$s)</item>\n        <item quantity=\"few\">Trwa przygotowywanie do przeniesienia %1$,d plików (%2$s)</item>\n        <item quantity=\"many\">Trwa przygotowywanie do przeniesienia %1$,d plików (%2$s)</item>\n        <item quantity=\"other\">Trwa przygotowywanie do przeniesienia %1$,d plików (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">Przenoszenie „%1$s” do „%2$s”</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"one\">Przenoszenie %1$,d pliku do „%2$s”</item>\n        <item quantity=\"few\">Przenoszenie %1$,d plików do „%2$s”</item>\n        <item quantity=\"many\">Przenoszenie %1$,d plików do „%2$s”</item>\n        <item quantity=\"other\">Przenoszenie %1$,d plików do „%2$s”</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"one\">Trwa przygotowywanie do usunięcia %1$,d pliku</item>\n        <item quantity=\"few\">Trwa przygotowywanie do usunięcia %1$,d plików</item>\n        <item quantity=\"many\">Trwa przygotowywanie do usunięcia %1$,d plików</item>\n        <item quantity=\"other\">Trwa przygotowywanie do usunięcia %1$,d plików</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">Usuwanie „%1$s”</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"one\">Usuwanie %1$,d pliku</item>\n        <item quantity=\"few\">Usuwanie %1$,d plików</item>\n        <item quantity=\"many\">Usuwanie %1$,d plików</item>\n        <item quantity=\"other\">Usuwanie %1$,d plików</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"one\">Trwa przygotowywanie do zmiany właściciela dla %1$,d pliku</item>\n        <item quantity=\"few\">Trwa przygotowywanie do zmiany właściciela dla %1$,d plików</item>\n        <item quantity=\"many\">Trwa przygotowywanie do zmiany właściciela dla %1$,d plików</item>\n        <item quantity=\"other\">Trwa przygotowywanie do zmiany właściciela dla %1$,d plików</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">Zmienianie właściciela dla „%1$s”</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"one\">Zmienianie właściciela dla %1$,d pliku</item>\n        <item quantity=\"few\">Zmienianie właściciela dla %1$,d plików</item>\n        <item quantity=\"many\">Zmienianie właściciela dla %1$,d plików</item>\n        <item quantity=\"other\">Zmienianie właściciela dla %1$,d plików</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"one\">Trwa przygotowywanie do zmiany grupy dla %1$,d pliku</item>\n        <item quantity=\"few\">Trwa przygotowywanie do zmiany grupy dla %1$,d plików</item>\n        <item quantity=\"many\">Trwa przygotowywanie do zmiany grupy dla %1$,d plików</item>\n        <item quantity=\"other\">Trwa przygotowywanie do zmiany grupy dla %1$,d plików</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">Zmiana grupy dla „%1$s”</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"one\">Zmienianie grupy dla %1$,d pliku</item>\n        <item quantity=\"few\">Zmienianie grupy dla %1$,d plików</item>\n        <item quantity=\"many\">Zmienianie grupy dla %1$,d plików</item>\n        <item quantity=\"other\">Zmienianie grupy dla %1$,d plików</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"one\">Przygotowywanie do zmiany atrybutu dla %1$,d pliku</item>\n        <item quantity=\"few\">Przygotowywanie do zmiany atrybutów dla %1$,d pliku</item>\n        <item quantity=\"many\">Przygotowywanie do zmiany atrybutów dla %1$,d plików</item>\n        <item quantity=\"other\">Przygotowywanie do zmiany atrybutów dla %1$,d plików</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">Zmienianie atrybutu dla „%1$s”</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"one\">Zmienianie atrybutu dla %1$,d pliku</item>\n        <item quantity=\"few\">Zmienianie atrybutów dla %1$,d plików</item>\n        <item quantity=\"many\">Zmienianie atrybutów dla %1$,d plików</item>\n        <item quantity=\"other\">Zmienianie atrybutów dla %1$,d plików</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Przygotowywanie do zmiany kontekstu SELinux dla %1$,d pliku</item>\n        <item quantity=\"few\">Przygotowywanie do zmiany kontekstu SELinux dla %1$,d plików</item>\n        <item quantity=\"many\">Przygotowywanie do zmiany kontekstu SELinux dla %1$,d plików</item>\n        <item quantity=\"other\">Przygotowywanie do zmiany kontekstu SELinux dla %1$,d plików</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">Zmienianie kontekstu SELinux dla „%1$s”</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Zmienianie kontekstu SELinux dla %1$,d pliku</item>\n        <item quantity=\"few\">Zmienianie kontekstu SELinux dla %1$,d plików</item>\n        <item quantity=\"many\">Zmienianie kontekstu SELinux dla %1$,d plików</item>\n        <item quantity=\"other\">Zmienianie kontekstu SELinux dla %1$,d plików</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Przygotowywanie do przywrócenia kontekstu SELinux dla %1$,d pliku</item>\n        <item quantity=\"few\">Przygotowywanie do przywrócenia kontekstu SELinux dla %1$,d plików</item>\n        <item quantity=\"many\">Przygotowywanie do przywrócenia kontekstu SELinux dla %1$,d plików</item>\n        <item quantity=\"other\">Przygotowywanie do przywrócenia kontekstu SELinux dla %1$,d plików</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">Przywracanie kontekstu SELinux dla „%1$s”</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Przywracanie kontekstu SELinux dla %1$,d pliku</item>\n        <item quantity=\"few\">Przywracanie kontekstu SELinux dla %1$,d plików</item>\n        <item quantity=\"many\">Przywracanie kontekstu SELinux dla %1$,d plików</item>\n        <item quantity=\"other\">Przywracanie kontekstu SELinux dla %1$,d plików</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">Zapisywanie „%1$s”</string>\n    <string name=\"file_job_create_error_title\">Błąd podczas tworzenia</string>\n    <string name=\"file_job_create_error_message_format\">Wystąpił błąd podczas tworzenia „%1$s”.\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">Nie można skopiować katalogu do tego samego</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">Nie można rozpakować katalogu do tego samego</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">Nie można przenieść katalogu do tego samego</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">Katalog docelowy znajduje się w katalogu źródłowym.</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">Nie można skopiować pliku do niego samego</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">Nie można rozpakować pliku do niego samego</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">Nie można przenieść pliku do niego samego</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">Plik źródłowy zostanie nadpisany przez docelowy.</string>\n    <string name=\"file_job_replace_title_format\">Zastąpić plik „%1$s”?</string>\n    <string name=\"file_job_replace_message_format\">Inny plik o tej nazwie już istnieje w „%1$s”.\\nZastąpienie go spowoduje nadpisanie jego zawartości.</string>\n    <string name=\"file_job_replace_target_name\">Oryginalny plik</string>\n    <string name=\"file_job_replace_source_name\">Zastąpić z</string>\n    <string name=\"file_job_merge_title_format\">Scalić katalog „%1$s”?</string>\n    <string name=\"file_job_merge_copy_message_format\">Operacja scalania poprosi o potwierdzenie przed zastąpieniem jakichkolwiek plików w katalogu, które powodują konflikt z kopiowanymi plikami.</string>\n    <string name=\"file_job_merge_extract_message_format\">Operacja scalania poprosi o potwierdzenie przed zastąpieniem jakichkolwiek plików w katalogu, które powodują konflikt z rozpakowywanymi plikami.</string>\n    <string name=\"file_job_merge_move_message_format\">Operacja scalania poprosi o potwierdzenie przed zastąpieniem jakichkolwiek plików w katalogu, które powodują konflikt z przenoszonymi plikami.</string>\n    <string name=\"file_job_merge_target_name\">Oryginalny katalog</string>\n    <string name=\"file_job_merge_source_name\">Scalić z</string>\n    <string name=\"file_job_conflict_show_name\">Wybierz nową nazwę miejsca docelowego</string>\n    <string name=\"file_job_conflict_name_hint\">Nowa nazwa</string>\n    <string name=\"file_job_archive_error_title_format\">Błąd podczas kompresowania „%1$s”</string>\n    <string name=\"file_job_archive_error_message_format\">Wystąpił błąd podczas kompresowania pliku do „%1$s”.\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">Błąd podczas kopiowania „%1$s”</string>\n    <string name=\"file_job_copy_error_message_format\">Wystąpił błąd podczas kopiowania pliku do „%1$s”.\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">Błąd podczas rozpakowywania „%1$s”</string>\n    <string name=\"file_job_extract_error_message_format\">Wystąpił błąd podczas rozpakowywania pliku do „%1$s”.\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">Błąd podczas przenoszenia „%1$s”</string>\n    <string name=\"file_job_move_error_message_format\">Wystąpił błąd podczas przenoszenia pliku do „%1$s”.\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">Błąd podczas usuwania</string>\n    <string name=\"file_job_delete_error_message_format\">Wystąpił błąd podczas usuwania „%1$s”.\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">Błąd podczas zmiany nazwy „%1$s”</string>\n    <string name=\"file_job_rename_error_message_format\">Wystąpił błąd podczas zmiany nazwy pliku do „%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">Błąd podczas zmiany właściciela dla „%1$s”</string>\n    <string name=\"file_job_set_owner_error_message_format\">Wystąpił błąd podczas zmiany właściciela na „%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">Błąd podczas zmiany grupy dla „%1$s”</string>\n    <string name=\"file_job_set_group_error_message_format\">Wystąpił błąd podczas zmiany grupy na „%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">Błąd podczas zmiany atrybutu dla „%1$s”</string>\n    <string name=\"file_job_set_mode_error_message_format\">Wystąpił błąd podczas zmiany atrybutu na „%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">Błąd podczas zmiany kontekstu SELinux dla „%1$s”</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">Wystąpił błąd podczas zmiany kontekstu SELinux na „%1$s”.\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">Błąd podczas przywracania kontekstu SELinux</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">Wystąpił błąd podczas przywracania kontekstu SELinux dla „%1$s”.\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">Błąd podczas zapisu</string>\n    <string name=\"file_job_write_error_message_format\">Wystąpił błąd podczas zapisu „%1$s”.\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">Wystąpił błąd podczas pobierania informacji o „%1$s”.</string>\n    <string name=\"file_job_remount_format\">Zamontuj ponownie „%1$s” z uprawnieniem do odczytu i zapisu</string>\n    <string name=\"file_job_remount_loading_format\">Ponowne montowanie „%1$s” z uprawnieniem do odczytu i zapisu</string>\n    <string name=\"file_job_remount_success_format\">„%1$s” został zamontowany ponownie z uprawnieniem do odczytu i zapisu</string>\n    <string name=\"file_job_action_all\">Zastosuj tę akcję do wszystkich plików</string>\n\n    <string name=\"file_open_apk_message\">Ten plik jest pakietem instalacyjnym dla systemu Android. Czy chcesz go zainstalować, czy wyświetlić jego zawartość?</string>\n    <string name=\"file_open_as_title\">Otwórz jako…</string>\n    <string name=\"file_open_as_title_format\">Otwórz „%1$s” jako</string>\n    <string name=\"file_open_as_type_text\">Tekst</string>\n    <string name=\"file_open_as_type_image\">Obraz</string>\n    <string name=\"file_open_as_type_audio\">Audio</string>\n    <string name=\"file_open_as_type_video\">Wideo</string>\n    <string name=\"file_open_as_type_directory\">Katalog</string>\n    <string name=\"file_open_as_type_any\">Inne</string>\n    <string name=\"file_install_apk_from_background_title_format\">„%1$s” jest gotowy do zainstalowania</string>\n    <string name=\"file_install_apk_from_background_text\">Kliknij, aby zainstalować</string>\n    <string name=\"file_open_from_background_title_format\">„%1$s” jest gotowy do otwarcia</string>\n    <string name=\"file_open_from_background_text\">Kliknij, aby otworzyć</string>\n    <string name=\"file_name_error_empty\">Nazwa pliku nie może być pusta</string>\n    <string name=\"file_name_error_invalid\">Nieprawidłowa nazwa pliku</string>\n    <string name=\"file_name_error_already_exists\">Plik o tej nazwie już istnieje</string>\n    <string name=\"file_replace_message_format\">Zastąpić „%1$s”?</string>\n    <string name=\"file_delete_message_file_format\">Usunąć „%1$s”?</string>\n    <string name=\"file_delete_message_directory_format\">Usunąć katalog „%1$s” i jego zawartość?</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"one\">Usunąć %1$,d plik?</item>\n        <item quantity=\"few\">Usunąć %1$,d pliki?</item>\n        <item quantity=\"many\">Usunąć %1$,d plików?</item>\n        <item quantity=\"other\">Usunąć %1$,d plików?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"one\">Usunąć %1$,d katalog i jego zawartość?</item>\n        <item quantity=\"few\">Usunąć %1$,d katalogi i ich zawartość?</item>\n        <item quantity=\"many\">Usunąć %1$,d katalogów i ich zawartość?</item>\n        <item quantity=\"other\">Usunąć %1$,d katalogów i ich zawartość?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"one\">Usunąć %1$,d pozycję?</item>\n        <item quantity=\"few\">Usunąć %1$,d pozycje?</item>\n        <item quantity=\"many\">Usunąć %1$,d pozycji?</item>\n        <item quantity=\"other\">Usunąć %1$,d pozycji?</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">Utwórz archiwum</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_create_archive_password\">Hasło (opcjonalnie)</string>\n    <string name=\"file_add_bookmark_success\">Zakładka dodana</string>\n    <string name=\"file_create_file_title\">Nowy plik</string>\n    <string name=\"file_create_directory_title\">Nowy katalog</string>\n\n    <string name=\"file_list_title\">Pliki</string>\n    <plurals name=\"file_list_title_open_file\">\n        <item quantity=\"one\">Wybierz plik</item>\n        <item quantity=\"few\">Wybierz pliki</item>\n        <item quantity=\"many\">Wybierz pliki</item>\n        <item quantity=\"other\">Wybierz pliki</item>\n    </plurals>\n    <string name=\"file_list_title_create_file\">Zapisz plik</string>\n    <plurals name=\"file_list_title_open_directory\">\n        <item quantity=\"one\">Wybierz katalog</item>\n        <item quantity=\"few\">Wybierz katalogi</item>\n        <item quantity=\"many\">Wybierz katalogi</item>\n        <item quantity=\"other\">Wybierz katalogi</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"one\">%1$,d katalog</item>\n        <item quantity=\"few\">%1$,d katalogi</item>\n        <item quantity=\"many\">%1$,d katalogów</item>\n        <item quantity=\"other\">%1$,d katalogów</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"one\">%1$,d plik</item>\n        <item quantity=\"few\">%1$,d pliki</item>\n        <item quantity=\"many\">%1$,d plików</item>\n        <item quantity=\"other\">%1$,d plików</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">,\\u0020</string>\n    <string name=\"file_list_path_error_empty\">Ścieżka nie może być pusta</string>\n    <string name=\"file_list_path_error_invalid\">Nieprawidłowa ścieżka</string>\n    <string name=\"file_list_action_view_sort\">Przeglądaj i sortuj</string>\n    <string name=\"file_list_action_view_list\">Lista</string>\n    <string name=\"file_list_action_view_grid\">Siatka</string>\n    <string name=\"file_list_action_sort_by_name\">Nazwa</string>\n    <string name=\"file_list_action_sort_by_type\">Typ</string>\n    <string name=\"file_list_action_sort_by_size\">Rozmiar</string>\n    <string name=\"file_list_action_sort_by_last_modified\">Ostatnia modyfikacja</string>\n    <string name=\"file_list_action_sort_order_ascending\">Rosnąco</string>\n    <string name=\"file_list_action_sort_directories_first\">Najpierw katalogi</string>\n    <string name=\"file_list_action_view_sort_path_specific\">Tylko dla tego folderu</string>\n    <string name=\"file_list_action_new_task\">Nowe okno</string>\n    <string name=\"file_list_action_navigate_up\">Idź w górę</string>\n    <string name=\"file_list_action_navigate_to\">Idź do</string>\n    <string name=\"file_list_action_show_hidden_files\">Pokazuj ukryte pliki</string>\n    <string name=\"file_list_action_copy_path\">Kopiuj ścieżkę</string>\n    <string name=\"file_list_action_open_in_terminal\">Otwórz w terminalu</string>\n    <string name=\"file_list_action_add_bookmark\">Dodaj zakładkę</string>\n    <string name=\"file_list_action_create_shortcut\">Utwórz skrót</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">Otwórz w nowym oknie</string>\n    <string name=\"file_list_select_title_format\">%1$,d</string>\n    <string name=\"file_list_create_file_name_hint\">Nazwa pliku</string>\n    <string name=\"file_list_open_current_directory_format\">Wybierz „%1$s”</string>\n    <string name=\"file_list_paste_move_title_format\">Przenoszenie %1$,d</string>\n    <string name=\"file_list_paste_copy_title_format\">Kopiowanie %1$,d</string>\n    <string name=\"file_list_paste_extract_title_format\">Rozpakowywanie %1$,d</string>\n    <string name=\"file_list_paste_action_extract_here\">Wypakuj tutaj</string>\n    <string name=\"file_list_empty\">Brak plików</string>\n    <string name=\"file_list_action_create_file\">Plik</string>\n    <string name=\"file_list_action_create_directory\">Katalog</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">Otwórz za pomocą</string>\n    <string name=\"file_item_action_extract\">Wypakuj</string>\n    <string name=\"file_item_action_archive\">Skompresuj plik</string>\n    <string name=\"file_item_action_properties\">Właściwości</string>\n\n    <string name=\"file_properties_title_format\">„%1$s” właściwości</string>\n    <string name=\"file_properties_basic\">Podstawowe</string>\n    <string name=\"file_properties_basic_name\">Nazwa</string>\n    <string name=\"file_properties_basic_type\">Typ</string>\n    <string name=\"file_properties_basic_type_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">Link do %1$s (%2$s)</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">Miejsce docelowe linku</string>\n    <string name=\"file_properties_basic_size\">Rozmiar</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_contents\">Zawartość</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"one\">%1$,d element, o rozmiarze %2$s</item>\n        <item quantity=\"few\">%1$,d elementy, suma rozmiaru %2$s</item>\n        <item quantity=\"many\">%1$,d elementów, suma rozmiaru %2$s</item>\n        <item quantity=\"other\">%1$,d elementów, suma rozmiaru %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">Ostatnia modyfikacja</string>\n    <string name=\"file_properties_basic_parent_directory\">Katalog nadrzędny</string>\n    <string name=\"file_properties_basic_archive_file\">Zarchiwizuj plik</string>\n    <string name=\"file_properties_basic_archive_entry\">Wpis archiwum</string>\n    <string name=\"file_properties_basic_free_space\">Wolna przestrzeń</string>\n    <string name=\"file_properties_permission\">Uprawnienia</string>\n    <string name=\"file_properties_permission_owner\">Właściciel</string>\n    <string name=\"file_properties_permission_group\">Grupa</string>\n    <string name=\"file_properties_permission_principal_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_permission_set_owner_title\">Zmień właściciela</string>\n    <string name=\"file_properties_permission_set_group_title\">Zmień grupę</string>\n    <string name=\"file_properties_permission_set_principal_filter_hint\">Wprowadź nazwę lub ID</string>\n    <string name=\"file_properties_permission_set_principal_system\">System</string>\n    <string name=\"file_properties_permission_mode\">Atrybut</string>\n    <string name=\"file_properties_permission_mode_format\">%1$s (%2$04o)</string>\n    <string name=\"file_properties_permission_set_mode_title\">Zmiana atrybutu</string>\n    <string name=\"file_properties_permission_set_mode_others\">Inni</string>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_file\">\n        <item>Odczyt</item>\n        <item>Zapis</item>\n        <item>Wykonanie</item>\n    </string-array>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_directory\">\n        <item>Odczyt</item>\n        <item>Zapis</item>\n        <item>Wyszukaj</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_special\">Specjalne</string>\n    <string-array name=\"file_properties_permission_set_mode_special_mode_bits\">\n        <item>Ustaw UID</item>\n        <item>Ustaw GID</item>\n        <item>Sticky bit</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_uppercase_x\">Nie dodawaj „Wykonaj” do załączonych plików</string>\n    <string name=\"file_properties_permission_selinux_context\">Kontekst SELinux</string>\n    <string name=\"file_properties_permission_set_selinux_context_title\">Zmień kontekst SELinux</string>\n    <string name=\"file_properties_permission_set_selinux_context_restore\">Przywróć</string>\n    <string name=\"file_properties_permission_recursive\">Zastosuj do załączonych plików</string>\n    <string name=\"file_properties_image\">Obraz</string>\n    <string name=\"file_properties_media_dimensions\">Wymiary</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">Data zrobienia zdjęcia</string>\n    <string name=\"file_properties_media_coordinates\">Współrzędne</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f, %2$.3f</string>\n    <string name=\"file_properties_media_address\">Lokalizacja</string>\n    <string name=\"file_properties_image_gps_altitude\">Wysokość</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f m</string>\n    <string name=\"file_properties_image_equipment\">Aparat</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">Przysłona</string>\n    <string name=\"file_properties_image_shutter_speed\">Czas naświetlania</string>\n    <string name=\"file_properties_image_focal_length\">Długość ogniskowa</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f mm</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">Odpowiednik ISO</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">Oprogramowanie</string>\n    <string name=\"file_properties_image_description\">Opis</string>\n    <string name=\"file_properties_image_artist\">Twórca</string>\n    <string name=\"file_properties_image_copyright\">Prawa autorskie</string>\n    <string name=\"file_properties_audio\">Audio</string>\n    <string name=\"file_properties_media_title\">Tytuł</string>\n    <string name=\"file_properties_audio_artist\">Wykonawca</string>\n    <string name=\"file_properties_audio_album\">Album</string>\n    <string name=\"file_properties_audio_album_artist\">Wykonawca albumu</string>\n    <string name=\"file_properties_audio_composer\">Kompozytor</string>\n    <string name=\"file_properties_audio_disc_number\">Numer płyty</string>\n    <string name=\"file_properties_audio_track_number\">Numer utworu</string>\n    <string name=\"file_properties_audio_year\">Rok</string>\n    <string name=\"file_properties_audio_genre\">Gatunek</string>\n    <string name=\"file_properties_media_duration\">Długość</string>\n    <string name=\"file_properties_media_bit_rate\">Przepływność</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d kbps</string>\n    <string name=\"file_properties_audio_sample_rate\">Częstotliwość próbkowania</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Hz</string>\n    <string name=\"file_properties_video\">Wideo</string>\n    <string name=\"file_properties_apk\">APK</string>\n    <string name=\"file_properties_apk_label\">Nazwa</string>\n    <string name=\"file_properties_apk_package_name\">Nazwa pakietu</string>\n    <string name=\"file_properties_apk_version\">Wersja</string>\n    <string name=\"file_properties_apk_version_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_apk_min_sdk_version\">Minimalna wersja systemu</string>\n    <string name=\"file_properties_apk_target_sdk_version\">Docelowa wersja systemu</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s (%2$s, %3$d)</string>\n    <string name=\"file_properties_apk_requested_permissions\">Uprawnienia</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">Żadne uprawnienia nie są wymagane</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"one\">%1$,d wymagane uprawnienie</item>\n        <item quantity=\"few\">%1$,d wymagane uprawnienia</item>\n        <item quantity=\"many\">%1$,d wymaganych uprawnień</item>\n        <item quantity=\"other\">%1$,d wymaganych uprawnień</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">Podpisy cyfrowe</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">Brak prawidłowych sygnatur</string>\n    <string name=\"file_properties_apk_past_signature_digests\">Starsze podpisy cyfrowe</string>\n    <string name=\"file_properties_checksum\">Sumy kontrolne</string>\n    <string name=\"file_properties_checksum_compare\">Porównanie</string>\n    <string name=\"file_properties_checksum_compare_placeholder\">Wprowadź dowolną sumę kontrolną</string>\n    <string name=\"file_properties_checksum_compare_match_format\">Pasuje do sumy kontrolnej %1$s</string>\n    <string name=\"file_properties_checksum_compare_prefix_match_format\">Pasuje do przedrostka sumy kontrolnej %1$s</string>\n    <string name=\"file_properties_checksum_compare_no_match\">Nie znaleziono pasującej sumy kontrolnej</string>\n    <string name=\"storage_file_system_root_title\">Root</string>\n    <string name=\"storage_list_title\">Pamięć</string>\n    <string name=\"storage_list_empty\">Brak nośników</string>\n    <string name=\"storage_add_storage_title\">Dodaj nośnik</string>\n    <string name=\"storage_add_storage_document_tree\">Zewnętrzny nośnik pamięci</string>\n    <string name=\"storage_add_storage_ftp_server\">Serwer FTP</string>\n    <string name=\"storage_add_storage_sftp_server\">Serwer SFTP</string>\n    <string name=\"storage_add_storage_smb_server\">Serwer SMB</string>\n    <string name=\"storage_add_storage_webdav_server\">Serwer WebDAV</string>\n    <string name=\"storage_edit_device_storage_title\">Edytuj pamięć urządzenia</string>\n    <string name=\"storage_edit_device_storage_name\">Nazwa</string>\n    <string name=\"storage_edit_device_storage_path\">Ścieżka</string>\n    <string name=\"storage_add_external_storage_shortcut_title\">Dodaj skrót do pamięci zewnętrznej</string>\n    <string name=\"storage_edit_external_storage_shortcut_title\">Edytuj skrót do pamięci zewnętrznej</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id\">Wolumin pamięci</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id_error_empty\">Podaj wolumin pamięci</string>\n    <string name=\"storage_edit_external_storage_shortcut_path\">Ścieżka</string>\n    <string name=\"storage_add_document_tree_title\">Dodaj nośnik zewnętrzny</string>\n    <string name=\"storage_edit_document_tree_title\">Edytuj nośnik zewnętrzny</string>\n    <string name=\"storage_edit_document_tree_name\">Nazwa</string>\n    <string name=\"storage_edit_document_tree_uri\">URI</string>\n    <string name=\"storage_edit_document_tree_path\">Ścieżka</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">Edytuj serwer FTP</string>\n    <string name=\"storage_edit_ftp_server_title_add\">Dodaj serwer FTP</string>\n    <string name=\"storage_edit_ftp_server_host\">Nazwa hosta</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">Wprowadź nazwę hosta</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">Nieprawidłowa nazwa hosta</string>\n    <string name=\"storage_edit_ftp_server_port\">Port</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">Nieprawidłowy port</string>\n    <string name=\"storage_edit_ftp_server_path\">Ścieżka</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">Można pozostawić puste</string>\n    <string name=\"storage_edit_ftp_server_name\">Nazwa</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">Użyj nazwy hosta</string>\n    <string name=\"storage_edit_ftp_server_protocol\">Protokół</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">Poświadczenie</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>Hasło</item>\n        <item>Anonimowe</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">Nazwa użytkownika</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">Wprowadź nazwę użytkownika</string>\n    <string name=\"storage_edit_ftp_server_password\">Hasło</string>\n    <string name=\"storage_edit_ftp_server_mode\">Tryb</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>Aktywny</item>\n        <item>Pasywny</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">Kodowanie</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">Połącz i dodaj</string>\n    <string name=\"storage_edit_ftp_server_add\">Dodaj</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">Edytuj serwer SFTP</string>\n    <string name=\"storage_edit_sftp_server_title_add\">Dodaj serwer SFTP</string>\n    <string name=\"storage_edit_sftp_server_host\">Nazwa hosta</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">Wprowadź nazwę hosta</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">Nieprawidłowa nazwa hosta</string>\n    <string name=\"storage_edit_sftp_server_port\">Port</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">Nieprawidłowy port</string>\n    <string name=\"storage_edit_sftp_server_path\">Ścieżka</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">Można pozostawić puste</string>\n    <string name=\"storage_edit_sftp_server_name\">Nazwa</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">Użyj nazwy hosta</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">Poświadczenie</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>Hasło</item>\n        <item>Klucz publiczny</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">Nazwa użytkownika</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">Wprowadź nazwę użytkownika</string>\n    <string name=\"storage_edit_sftp_server_password\">Hasło</string>\n    <string name=\"storage_edit_sftp_server_private_key\">Klucz prywatny</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">Otwórz plik</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">Wprowadź klucz prywatny</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">Nieprawidłowy klucz prywatny</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">Hasło klucza prywatnego</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">Można pozostawić puste</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">Nieprawidłowe hasło klucza prywatnego</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">Połącz i dodaj</string>\n    <string name=\"storage_edit_sftp_server_add\">Dodaj</string>\n    <string name=\"storage_add_lan_smb_server_loading\">Wyszukiwanie serwerów SMB…</string>\n    <string name=\"storage_add_lan_smb_server_add\">Dodaj ręcznie</string>\n    <string name=\"storage_edit_smb_server_title_edit\">Edytuj serwer SMB</string>\n    <string name=\"storage_edit_smb_server_title_add\">Dodaj serwer SMB</string>\n    <string name=\"storage_edit_smb_server_host\">Nazwa hosta</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">Wprowadź nazwę hosta</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">Nieprawidłowa nazwa hosta</string>\n    <string name=\"storage_edit_smb_server_port\">Port</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">Nieprawidłowy port</string>\n    <string name=\"storage_edit_smb_server_path\">Ścieżka</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">Można pozostawić puste</string>\n    <string name=\"storage_edit_smb_server_name\">Nazwa</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">Użyj nazwy hosta</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">Poświadczenie</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>Hasło</item>\n        <item>Gość</item>\n        <item>Anonimowe</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">Nazwa użytkownika</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">Wprowadź nazwę użytkownika</string>\n    <string name=\"storage_edit_smb_server_password\">Hasło</string>\n    <string name=\"storage_edit_smb_server_domain\">Domena</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">Połącz i dodaj</string>\n    <string name=\"storage_edit_smb_server_add\">Dodaj</string>\n    <string name=\"storage_edit_webdav_server_title_edit\">Edytuj serwer WebDAV</string>\n    <string name=\"storage_edit_webdav_server_title_add\">Dodaj serwer WebDAV</string>\n    <string name=\"storage_edit_webdav_server_host\">Nazwa hosta</string>\n    <string name=\"storage_edit_webdav_server_host_error_empty\">Podaj nazwę hosta</string>\n    <string name=\"storage_edit_webdav_server_host_error_invalid\">Nieprawidłowa nazwa hosta</string>\n    <string name=\"storage_edit_webdav_server_port\">Port</string>\n    <string name=\"storage_edit_webdav_server_port_error_invalid\">Nieprawidłowy port</string>\n    <string name=\"storage_edit_webdav_server_path\">Ścieżka</string>\n    <string name=\"storage_edit_webdav_server_path_placeholder\">Można pozostawić puste</string>\n    <string name=\"storage_edit_webdav_server_name\">Nazwa</string>\n    <string name=\"storage_edit_webdav_server_name_placeholder\">Użyj nazwy hosta</string>\n    <string name=\"storage_edit_webdav_server_protocol\">Protokół</string>\n    <string name=\"storage_edit_webdav_server_authentication_type\">Poświadczenie</string>\n    <string-array name=\"storage_edit_webdav_server_authentication_type_entries\">\n        <item>Hasło</item>\n        <item>Token dostępu</item>\n        <item>Żaden</item>\n    </string-array>\n    <string name=\"storage_edit_webdav_server_username\">Nazwa użytkownika</string>\n    <string name=\"storage_edit_webdav_server_username_error_empty\">Podaj nazwę użytkownika</string>\n    <string name=\"storage_edit_webdav_server_password\">Hasło</string>\n    <string name=\"storage_edit_webdav_server_access_token\">Token dostępu</string>\n    <string name=\"storage_edit_webdav_server_access_token_error_empty\">Podaj token dostępu</string>\n    <string name=\"storage_edit_webdav_server_connect_and_add\">Połącz i dodaj</string>\n    <string name=\"storage_edit_webdav_server_add\">Dodaj</string>\n\n    <string name=\"navigation_storage_subtitle_format\">%1$s wolne z %2$s</string>\n    <string name=\"navigation_add_storage\">Dodaj nośnik…</string>\n    <string name=\"navigation_standard_directory_alarms\">Alarmy</string>\n    <string name=\"navigation_standard_directory_dcim\">DCIM</string>\n    <string name=\"navigation_standard_directory_documents\">Dokumenty</string>\n    <string name=\"navigation_standard_directory_downloads\">Pobrane</string>\n    <string name=\"navigation_standard_directory_movies\">Filmy</string>\n    <string name=\"navigation_standard_directory_music\">Muzyka</string>\n    <string name=\"navigation_standard_directory_notifications\">Powiadomienia</string>\n    <string name=\"navigation_standard_directory_pictures\">Obrazy</string>\n    <string name=\"navigation_standard_directory_podcasts\">Podcasty</string>\n    <string name=\"navigation_standard_directory_ringtones\">Dzwonki</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">Katalog zakładek</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">Nazwa</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">Ścieżka</string>\n    <string name=\"archive_viewer_title\">Przeglądarka archiwum</string>\n    <string name=\"image_viewer_title\">Przeglądarka zdjęć</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"save_as_title\">Zapisz jako</string>\n    <string name=\"save_as_error\">Błąd podczas zapisywania pliku</string>\n    <string name=\"save_as_success_format\">„%1$s” zostało zapisane w „%2$s”</string>\n    <string name=\"text_editor_title\">Edytor tekstu</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">Zapisano</string>\n    <string name=\"text_editor_reload_message\">Czy na pewno chcesz załadować ponownie? Niezapisane zmiany w tym dokumencie zostaną utracone.</string>\n    <string name=\"text_editor_encoding\">Kodowanie</string>\n    <string name=\"text_editor_close_message\">Czy na pewno chcesz porzucić niezapisane zmiany w tym dokumencie?</string>\n\n    <string name=\"ftp_server_title\">Serwer FTP</string>\n    <string name=\"ftp_server_state_title\">Status</string>\n    <string name=\"ftp_server_state_summary_starting\">Uruchomianie…</string>\n    <string name=\"ftp_server_state_summary_running\">Uruchomiony</string>\n    <string name=\"ftp_server_state_summary_stopping\">Zatrzymywanie…</string>\n    <string name=\"ftp_server_state_summary_stopped\">Nieuruchomiony</string>\n    <string name=\"ftp_server_url_title\">Adres URL</string>\n    <string name=\"ftp_server_url_summary_no_local_inet_address\">Lokalny adres IP jest nieznany</string>\n    <string name=\"ftp_server_url_menu_copy_url\">Skopiuj adres URL</string>\n    <string name=\"ftp_server_url_menu_copy_password\">Skopiuj hasło</string>\n    <string name=\"ftp_server_add_tile_title\">Dodaj do Szybkich ustawień</string>\n    <string name=\"ftp_server_add_tile_result_added\">„Serwer FTP” został dodany do Szybkich ustawień</string>\n    <string name=\"ftp_server_add_tile_result_already_added\">„Serwer FTP” został już dodany do Szybkich ustawień</string>\n    <string name=\"ftp_server_add_tile_result_error\">Błąd podczas dodawania „Serwera FTP” do Szybkich ustawień</string>\n    <string name=\"ftp_server_configuration_title\">Konfiguracja</string>\n    <string name=\"ftp_server_anonymous_login_title\">Logowanie anonimowe</string>\n    <string name=\"ftp_server_username_title\">Nazwa użytkownika</string>\n    <string name=\"ftp_server_password_title\">Hasło</string>\n    <string name=\"ftp_server_port_title\">Port</string>\n    <string name=\"ftp_server_home_directory_title\">Katalog główny</string>\n    <string name=\"ftp_server_writable_title\">Zezwól na zapis</string>\n    <string name=\"settings_title\">Ustawienia</string>\n    <string name=\"settings_interface_title\">Interfejs</string>\n    <string name=\"settings_locale_title\">Język</string>\n    <string name=\"settings_theme_color_title\">Kolor motywu</string>\n    <string name=\"settings_theme_color_summary\">Kolor pojawiający się najczęściej w aplikacji</string>\n    <string name=\"settings_material_design_3_title\">Material Design 3</string>\n    <string name=\"settings_night_mode_title\">Ciemny motyw</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>Zgodnie z ustawieniem systemu</item>\n        <item>Wyłączony</item>\n        <item>Włączony</item>\n        <item>W oparciu o czas</item>\n        <item>W oparciu o oszczędzanie baterii</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">Czarny motyw ciemny</string>\n    <string name=\"settings_file_list_animation_title\">Animacja listy plików</string>\n    <string name=\"settings_file_name_ellipsize_title\">Wyświetlanie długiej nazwy pliku</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>Skrócona na początku</item>\n        <item>Skrócona w środku</item>\n        <item>Skrócona na końcu</item>\n        <item>Przewijanie nazwy</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">Zachowanie</string>\n    <string name=\"settings_default_directory_title\">Domyślny katalog</string>\n    <string name=\"settings_standard_directories_title\">Standardowe katalogi</string>\n    <string name=\"settings_standard_directories_summary_empty\">Nie włączono żadnych standardowych katalogów</string>\n    <string name=\"settings_bookmark_directory_screenshots\">Zrzuty ekranu</string>\n    <string name=\"settings_bookmark_directories_title\">Katalogi dodane do zakładek</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">Brak katalogów dodanych do zakładek</string>\n    <string name=\"settings_root_strategy_title\">Tryb dostępu do roota</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"one\">Masz uruchomione %1$,d operację na pliku, a zmiana trybu dostępu roota może teraz spowodować nieoczekiwany błąd. Czy na pewno chcesz to teraz zmienić?</item>\n        <item quantity=\"few\">Masz uruchomione %1$,d operacje na plikach, a zmiana trybu dostępu roota może teraz spowodować nieoczekiwany błąd. Czy na pewno chcesz to teraz zmienić?</item>\n        <item quantity=\"many\">Masz uruchomione %1$,d operacje na plikach, a zmiana trybu dostępu roota może teraz spowodować nieoczekiwany błąd. Czy na pewno chcesz to teraz zmienić?</item>\n        <item quantity=\"other\">Masz uruchomione %1$,d operacje na plikach, a zmiana trybu dostępu roota może teraz spowodować nieoczekiwany błąd. Czy na pewno chcesz to teraz zmienić?</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>Tylko dostęp normalny</item>\n        <item>Automatyczny</item>\n        <item>Tylko dostęp do roota</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">Kodowanie nazwy pliku archiwum</string>\n    <string name=\"settings_open_apk_default_action_title\">Działanie podczas otwarcia pakietu instalacyjnego</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>Zainstaluj</item>\n        <item>Wyświetl zawartość</item>\n        <item>Zapytaj, co zrobić</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_title\">Pokaż miniaturę dokumentów PDF</string>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">Może spowodować niestabilność aplikacji w starszych wersjach systemu Android</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">Odczytaj zdalne pliki w celu uzyskania miniatury</string>\n\n    <string name=\"about_title\">O aplikacji</string>\n    <string name=\"about_version_title\">Wersja</string>\n    <string name=\"about_github_title\">Zobacz na GitHubie</string>\n    <string name=\"about_licenses_title\">Licencje</string>\n    <string name=\"about_privacy_policy_title\">Polityka prywatności</string>\n    <string name=\"about_author_title\">Autor</string>\n    <string name=\"about_author_name_title\">Hai Zhang</string>\n    <string name=\"about_author_github_title\">Obserwuj na GitHubie</string>\n    <string name=\"about_author_twitter_title\">Obserwuj na Twitterze</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-pt-rBR/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">Arquivo</string>\n    <string name=\"file_type_name_apk\">Pacote do Android</string>\n    <string name=\"file_type_name_archive\">%1$s arquivo</string>\n    <string name=\"file_type_name_audio\">%1$s áudio</string>\n    <string name=\"file_type_name_calendar\">%1$s calendário</string>\n    <string name=\"file_type_name_certificate\">%1$s certificado</string>\n    <string name=\"file_type_name_code\">%1$s documento</string>\n    <string name=\"file_type_name_contact\">Cartão de visitas eletrônico</string>\n    <string name=\"file_type_name_directory\">Pasta</string>\n    <string name=\"file_type_name_document\">%1$s documento</string>\n    <string name=\"file_type_name_ebook\">%1$s e-book</string>\n    <string name=\"file_type_name_email\">%1$s mensagem de email</string>\n    <string name=\"file_type_name_font\">%1$s fonte</string>\n    <string name=\"file_type_name_generic\">%1$s arquivo</string>\n    <string name=\"file_type_name_image\">%1$s imagem</string>\n    <string name=\"file_type_name_pdf\">Documento PDF</string>\n    <string name=\"file_type_name_presentation\">%1$s apresentação</string>\n    <string name=\"file_type_name_spreadsheet\">%1$s planilha</string>\n    <string name=\"file_type_name_text\">%1$s documento</string>\n    <string name=\"file_type_name_text_plain\">Documento de texto simples</string>\n    <string name=\"file_type_name_video\">%1$s vídeo</string>\n    <string name=\"file_type_name_word\">Documento do Word</string>\n    <string name=\"file_type_name_powerpoint\">Apresentação do PowerPoint</string>\n    <string name=\"file_type_name_excel\">Planilha do Excel</string>\n    <string name=\"file_type_name_posix_character_device\">Dispositivo de caractere</string>\n    <string name=\"file_type_name_posix_block_device\">Dispositivo de bloco</string>\n    <string name=\"file_type_name_posix_fifo\">Pipe</string>\n    <string name=\"file_type_name_posix_symbolic_link\">Link</string>\n    <string name=\"file_type_name_posix_socket\">Soquete</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">Link (quebrado)</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-pt-rBR/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:ignore=\"ImpliedQuantity\">\n\n    <string name=\"app_name\">Material Files</string>\n\n    <string name=\"close\">Fechar</string>\n    <string name=\"copy\">Copiar</string>\n    <string name=\"cut\">Recortar</string>\n    <string name=\"default_\">Padrão</string>\n    <string name=\"delete\">Excluir</string>\n    <string name=\"discard\">Descartar</string>\n    <string name=\"edit\">Editar</string>\n    <string name=\"empty\">Vazio</string>\n    <string name=\"empty_placeholder\">(Vazio)</string>\n    <string name=\"error\">Erro</string>\n    <string name=\"hide\">Ocultar</string>\n    <string name=\"install\">Instalar</string>\n    <string name=\"keep_editing\">Continuar editando</string>\n    <string name=\"loading\">Carregando…</string>\n    <string name=\"maybe_later\">Tente mais tarde</string>\n    <string name=\"merge\">Mesclar</string>\n    <string name=\"none\">Nenhum</string>\n    <string name=\"open_navigation_drawer\">Abrir menu de navegação</string>\n    <string name=\"paste\">Colar</string>\n    <string name=\"refresh\">Atualizar</string>\n    <string name=\"reload\">Recarregar</string>\n    <string name=\"remove\">Remover</string>\n    <string name=\"rename\">Renomear</string>\n    <string name=\"replace\">Substituir</string>\n    <string name=\"reset\">Redefinir</string>\n    <string name=\"retry\">Repetir</string>\n    <string name=\"save\">Salvar</string>\n    <string name=\"search\">Procurar</string>\n    <string name=\"select\">Selecionar</string>\n    <string name=\"select_all\">Selecionar tudo</string>\n    <string name=\"share\">Compartilhar</string>\n    <string name=\"show\">Mostrar</string>\n    <string name=\"skip\">Próximo</string>\n    <string name=\"stop\">Parar</string>\n    <string name=\"system_default\">Padrão do sistema</string>\n    <string name=\"unknown\">Desconhecido</string>\n    <string name=\"view\">Visualizar</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">Nenhum aplicativo encontrado para lidar com essa ação</string>\n    <string name=\"open_settings\">Abrir configurações</string>\n    <string name=\"shortcut_created\">Atalho criado</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"one\">%1$,d byte</item>\n        <item quantity=\"many\">%1$,d bytes</item>\n        <item quantity=\"other\">%1$,d bytes</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">O aplicativo precisa de permissão para acessar os arquivos. Clique em \\\"PERMITIR\\\" na próxima caixa de diálogo do sistema.</string>\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">O aplicativo precisa de permissão para acessar os arquivos. Clique em \\\"PERMITIR\\\" na próxima caixa de diálogo do sistema.</string>\n    <string name=\"storage_permission_permanently_denied_message\">O aplicativo precisa de permissão para acessar os arquivos. Por favor, conceda a permissão de \\\"Armazenamento\\\" nas configurações do sistema.</string>\n    <!-- The allow button is in sentence case now. -->\n    <string name=\"notification_permission_rationale_message\">O app precisa de permissão para enviar notificações sobre operações de arquivos. Clique em \\\"Permitir\\\" no diálogo de sistema a seguir.</string>\n    <string name=\"notification_channel_background_activity_start_name\">Ações em segundo plano</string>\n    <string name=\"notification_channel_background_activity_start_description\">Realize ações enquanto o aplicativo estiver em segundo plano</string>\n    <string name=\"notification_channel_file_job_name\">Operações de arquivo</string>\n    <string name=\"notification_channel_file_job_description\">Exibir e controlar operações de arquivo</string>\n    <string name=\"notification_channel_ftp_server_name\">Servidor FTP</string>\n    <string name=\"notification_channel_ftp_server_description\">Exibir e controlar o servidor FTP</string>\n\n    <string name=\"file_action_archive_password_title\">Senha necessária</string>\n    <string name=\"file_action_archive_password_message_format\">“%1$s” é protegido por senha.</string>\n    <string name=\"file_action_archive_password_error_empty\">A senha não pode estar vazia</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"one\">Preparando para compactar %1$,d arquivo (%2$s)</item>\n        <item quantity=\"many\">Preparando para compactar %1$,d arquivos (%2$s)</item>\n        <item quantity=\"other\">Preparando para compactar %1$,d arquivos (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">Compactando “%1$s” em “%2$s”</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"one\">Compactando %1$,d arquivo em “%2$s”</item>\n        <item quantity=\"many\">Compactando %1$,d arquivos em “%2$s”</item>\n        <item quantity=\"other\">Compactando %1$,d arquivos em “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"one\">Preparando para copiar %1$,d arquivo (%2$s)</item>\n        <item quantity=\"many\">Preparando para copiar %1$,d arquivos (%2$s)</item>\n        <item quantity=\"other\">Preparando para copiar %1$,d arquivos (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">Copiando “%1$s” para “%2$s”</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"one\">Copiando %1$,d arquivo para “%2$s”</item>\n        <item quantity=\"many\">Copiando %1$,d arquivos para  “%2$s”</item>\n        <item quantity=\"other\">Copiando %1$,d arquivos para  “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"one\">Preparando para extrair %1$,d arquivo (%2$s)</item>\n        <item quantity=\"many\">Preparando para extrair %1$,d arquivos (%2$s)</item>\n        <item quantity=\"other\">Preparando para extrair %1$,d arquivos (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">Extraindo “%1$s” para  “%2$s”</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"one\">Extraindo %1$,d arquivo para \\\"%2$s\\\"</item>\n        <item quantity=\"many\">Extraindo %1$,d arquivos para \\\"%2$s\\\"</item>\n        <item quantity=\"other\">Extraindo %1$,d arquivos para “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"one\">Preparando para mover %1$,d arquivo (%2$s)</item>\n        <item quantity=\"many\">Preparando para mover %1$,d arquivos (%2$s)</item>\n        <item quantity=\"other\">Preparando para mover %1$,d arquivos (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">Movendo “%1$s” para “%2$s”</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"one\">Movendo %1$,d arquivo para \\\"%2$s\\\"</item>\n        <item quantity=\"many\">Movendo %1$,d arquivos para \\\"%2$s\\\"</item>\n        <item quantity=\"other\">Movendo %1$,d arquivos para “%2$s”</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"one\">Preparando para excluir %1$,d arquivo</item>\n        <item quantity=\"many\">Preparando para excluir %1$,d arquivos</item>\n        <item quantity=\"other\">Preparando para excluir %1$,d arquivos</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">Excluindo “%1$s”</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"one\">Excluindo %1$,d arquivo</item>\n        <item quantity=\"many\">Excluindo %1$,d arquivos</item>\n        <item quantity=\"other\">Excluindo %1$,d arquivos</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"one\">Preparando para mudar proprietário de %1$,d arquivo</item>\n        <item quantity=\"many\">Preparando para alterar proprietário de %1$,d arquivos</item>\n        <item quantity=\"other\">Preparando para alterar proprietário de %1$,d arquivos</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">Alterando proprietário de “%1$s”</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"one\">Alterando proprietário de %1$,d arquivo</item>\n        <item quantity=\"many\">Alterando proprietário de %1$,d arquivos</item>\n        <item quantity=\"other\">Alterando proprietário de %1$,d arquivos</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"one\">Preparando para alterar grupo de %1$,d arquivo</item>\n        <item quantity=\"many\">Preparando para alterar grupo de %1$,d arquivos</item>\n        <item quantity=\"other\">Preparando para alterar grupo de %1$,d arquivos</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">Alterando grupo de \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"one\">Alterando grupo de %1$,d arquivo</item>\n        <item quantity=\"many\">Alterando grupo de %1$,d arquivos</item>\n        <item quantity=\"other\">Alterando grupo de %1$,d arquivos</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"one\">Preparando para alterar modo de %1$,d arquivo</item>\n        <item quantity=\"many\">Preparando para alterar modo de %1$,d arquivos</item>\n        <item quantity=\"other\">Preparando para alterar modo de %1$,d arquivos</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">Alterando modo de \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"one\">Alterando modo de %1$,d arquivo</item>\n        <item quantity=\"many\">Alterando modo de %1$,d arquivos</item>\n        <item quantity=\"other\">Alterando modo de %1$,d arquivos</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Preparando para alterar o contexto do SELinux de %1$,d arquivo</item>\n        <item quantity=\"many\">Preparando para alterar o contexto do SELinux de %1$,d arquivos</item>\n        <item quantity=\"other\">Preparando para alterar o contexto do SELinux de %1$,d arquivos</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">Alterando o contexto do SELinux de \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Alterando o contexto do SELinux de %1$,d arquivo</item>\n        <item quantity=\"many\">Alterando o contexto do SELinux de %1$,d arquivos</item>\n        <item quantity=\"other\">Alterando o contexto do SELinux de %1$,d arquivos</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Preparando para restaurar o contexto do SELinux de %1$,d arquivo</item>\n        <item quantity=\"many\">Preparando para restaurar o contexto do SELinux de %1$,d arquivos</item>\n        <item quantity=\"other\">Preparando para restaurar o contexto do SELinux de %1$,d arquivos</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">Restaurando o contexto do SELinux de \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Restaurando o contexto do SELinux de %1$,d arquivo</item>\n        <item quantity=\"many\">Restaurando o contexto do SELinux de %1$,d arquivos</item>\n        <item quantity=\"other\">Restaurando o contexto do SELinux de %1$,d arquivos</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">Escrevendo \\\"%1$s\\\"</string>\n    <string name=\"file_job_create_error_title\">Erro ao criar</string>\n    <string name=\"file_job_create_error_message_format\">Houve um erro ao criar \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">Não é possível copiar uma pasta para si mesma</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">Não é possível extrair uma pasta para si mesma</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">Não é possível mover uma pasta para si mesma</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">A pasta de destino está dentro da pasta de origem.</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">Não é possível copiar um arquivo sobre si mesmo</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">Não é possível extrair um arquivo sobre si mesmo</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">Não é possível mover um arquivo sobre si mesmo</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">O arquivo de origem seria substituído pelo destino.</string>\n    <string name=\"file_job_replace_title_format\">Substituir arquivo \\\"%1$s\\\"?</string>\n    <string name=\"file_job_replace_message_format\">Outro arquivo com o mesmo nome já existe em \\\"%1$s\\\".\\nSubstituir irá sobrescrever seu conteúdo.</string>\n    <string name=\"file_job_replace_target_name\">Arquivo original</string>\n    <string name=\"file_job_replace_source_name\">Substituir com</string>\n    <string name=\"file_job_merge_title_format\">Mesclar pasta \\\"%1$s\\\"?</string>\n    <string name=\"file_job_merge_copy_message_format\">A mesclagem solicitará confirmação antes de substituir qualquer arquivo na pasta que entre em conflito com os arquivos que estão sendo copiados.</string>\n    <string name=\"file_job_merge_extract_message_format\">A mesclagem solicitará confirmação antes de substituir qualquer arquivo na pasta que entre em conflito com os arquivos que estão sendo extraídos.</string>\n    <string name=\"file_job_merge_move_message_format\">A mesclagem solicitará confirmação antes de substituir qualquer arquivo na pasta que entre em conflito com os arquivos que estão sendo movidos.</string>\n    <string name=\"file_job_merge_target_name\">Pasta original</string>\n    <string name=\"file_job_merge_source_name\">Mesclar com</string>\n    <string name=\"file_job_conflict_show_name\">Selecione um novo nome para o destino</string>\n    <string name=\"file_job_conflict_name_hint\">Novo nome</string>\n    <string name=\"file_job_archive_error_title_format\">Erro ao compactar \\\"%1$s\\\"</string>\n    <string name=\"file_job_archive_error_message_format\">Houve um erro ao compactar o arquivo em \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">Erro ao copiar \\\"%1$s\\\"</string>\n    <string name=\"file_job_copy_error_message_format\">Houve um erro ao copiar o arquivo em \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">Erro ao extrair \\\"%1$s\\\"</string>\n    <string name=\"file_job_extract_error_message_format\">Houve um erro ao extrair o arquivo em \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">Erro ao mover \\\"%1$s\\\"</string>\n    <string name=\"file_job_move_error_message_format\">Houve um erro ao mover o arquivo em \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">Erro ao excluir</string>\n    <string name=\"file_job_delete_error_message_format\">Houve um erro ao excluir \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">Erro ao renomear \\\"%1$s\\\"</string>\n    <string name=\"file_job_rename_error_message_format\">Ocorreu um erro ao renomear o arquivo para “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">Erro ao alterar proprietário de \\\"%1$s\\\"</string>\n    <string name=\"file_job_set_owner_error_message_format\">Houve um erro ao alterar o proprietário para \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">Erro ao alterar grupo de \\\"%1$s\\\"</string>\n    <string name=\"file_job_set_group_error_message_format\">Houve um erro ao alterar o grupo para \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">Erro ao alterar modo de \\\"%1$s\\\"</string>\n    <string name=\"file_job_set_mode_error_message_format\">Houve um erro ao alterar o modo para \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">Erro ao alterar o contexto do SELinux de \\\"%1$s\\\"</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">Houve um erro ao alterar o contexto do SELinux para \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">Erro ao restaurar o contexto do SELinux</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">Houve um erro ao restaurar o contexto do SELinux de \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">Erro ao escrever</string>\n    <string name=\"file_job_write_error_message_format\">Houve um erro ao escrever \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">Houve um erro ao obter informação sobre \\\"%1$s\\\".</string>\n    <string name=\"file_job_remount_format\">Remontar \\\"%1$s\\\" como read-write</string>\n    <string name=\"file_job_remount_loading_format\">Remontando \\\"%1$s\\\" como read-write…</string>\n    <string name=\"file_job_remount_success_format\">\\\"%1$s\\\" foi remontado como read-write</string>\n    <string name=\"file_job_action_all\">Aplique esta ação a todos os arquivos</string>\n\n    <string name=\"file_open_apk_message\">Este arquivo é um pacote do Android. Deseja instalá-lo ou visualizar seu conteúdo?</string>\n    <string name=\"file_open_as_title\">Abrir como…</string>\n    <string name=\"file_open_as_title_format\">Abrir “%1$s” como</string>\n    <string name=\"file_open_as_type_text\">Texto</string>\n    <string name=\"file_open_as_type_image\">Imagem</string>\n    <string name=\"file_open_as_type_audio\">Áudio</string>\n    <string name=\"file_open_as_type_video\">Vídeo</string>\n    <string name=\"file_open_as_type_directory\">Pasta</string>\n    <string name=\"file_open_as_type_any\">Outro</string>\n    <string name=\"file_install_apk_from_background_title_format\">\\\"%1$s\\\" está pronto para ser instalado</string>\n    <string name=\"file_install_apk_from_background_text\">Toque para instalar</string>\n    <string name=\"file_open_from_background_title_format\">\\\"%1$s\\\" está pronto para ser aberto</string>\n    <string name=\"file_open_from_background_text\">Toque para abrir</string>\n    <string name=\"file_name_error_empty\">O nome do arquivo não pode estar vazio</string>\n    <string name=\"file_name_error_invalid\">Nome do arquivo inválido</string>\n    <string name=\"file_name_error_already_exists\">Um arquivo com esse nome já existe</string>\n    <string name=\"file_delete_message_file_format\">Excluir \\\"%1$s\\\"?</string>\n    <string name=\"file_delete_message_directory_format\">Excluir a pasta \\\"%1$s\\\" e seu conteúdo?</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"one\">Excluir %1$,d arquivo?</item>\n        <item quantity=\"many\">Excluir %1$,d arquivos?</item>\n        <item quantity=\"other\">Excluir %1$,d arquivos?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"one\">Excluir %1$,d pasta e seu conteúdo?</item>\n        <item quantity=\"many\">Excluir %1$,d pastas e seus conteúdos?</item>\n        <item quantity=\"other\">Excluir %1$,d pastas e seus conteúdos?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"one\">Excluir %1$,d item?</item>\n        <item quantity=\"many\">Excluir %1$,d item?</item>\n        <item quantity=\"other\">Excluir %1$,d itens?</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">Criar arquivo</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_create_archive_password\">Senha (opcional)</string>\n    <string name=\"file_add_bookmark_success\">Favorito adicionado</string>\n    <string name=\"file_create_file_title\">Novo arquivo</string>\n    <string name=\"file_create_directory_title\">Nova pasta</string>\n\n    <string name=\"file_list_title\">Arquivos</string>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"one\">%1$,d pasta</item>\n        <item quantity=\"many\">%1$,d pastas</item>\n        <item quantity=\"other\">%1$,d pastas</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"one\">%1$,d arquivo</item>\n        <item quantity=\"many\">%1$,d arquivos</item>\n        <item quantity=\"other\">%1$,d arquivos</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">,\\u0020</string>\n    <string name=\"file_list_path_error_empty\">Caminho não pode estar vazio</string>\n    <string name=\"file_list_path_error_invalid\">Caminho inválido</string>\n    <string name=\"file_list_action_view_sort\">Visualizar e classificar</string>\n    <string name=\"file_list_action_view_list\">Lista</string>\n    <string name=\"file_list_action_view_grid\">Grade</string>\n    <string name=\"file_list_action_sort_by_name\">Nome</string>\n    <string name=\"file_list_action_sort_by_type\">Tipo</string>\n    <string name=\"file_list_action_sort_by_size\">Tamanho</string>\n    <string name=\"file_list_action_sort_by_last_modified\">Última modificação</string>\n    <string name=\"file_list_action_sort_order_ascending\">Crescente</string>\n    <string name=\"file_list_action_sort_directories_first\">Pastas primeiro</string>\n    <string name=\"file_list_action_view_sort_path_specific\">Apenas para esta pasta</string>\n    <string name=\"file_list_action_new_task\">Nova janela</string>\n    <string name=\"file_list_action_navigate_up\">Subir</string>\n    <string name=\"file_list_action_navigate_to\">Ir para</string>\n    <string name=\"file_list_action_show_hidden_files\">Mostrar arquivos ocultos</string>\n    <string name=\"file_list_action_copy_path\">Copiar caminho</string>\n    <string name=\"file_list_action_open_in_terminal\">Abrir no terminal</string>\n    <string name=\"file_list_action_add_bookmark\">Adicionar aos favoritos</string>\n    <string name=\"file_list_action_create_shortcut\">Criar atalho</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">Abrir em uma nova janela</string>\n    <string name=\"file_list_select_title_format\">%1$,d</string>\n    <string name=\"file_list_paste_move_title_format\">Movendo %1$,d</string>\n    <string name=\"file_list_paste_copy_title_format\">Copiando %1$,d</string>\n    <string name=\"file_list_paste_extract_title_format\">Extraindo %1$,d</string>\n    <string name=\"file_list_paste_action_extract_here\">Extrair aqui</string>\n    <string name=\"file_list_empty\">Nenhum arquivo</string>\n    <string name=\"file_list_action_create_file\">Arquivo</string>\n    <string name=\"file_list_action_create_directory\">Pasta</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">Abrir com</string>\n    <string name=\"file_item_action_extract\">Extrair</string>\n    <string name=\"file_item_action_archive\">Compactar</string>\n    <string name=\"file_item_action_properties\">Propriedades</string>\n\n    <string name=\"file_properties_title_format\">\\\"%1$s\\\" propriedades</string>\n    <string name=\"file_properties_basic\">Básico</string>\n    <string name=\"file_properties_basic_name\">Nome</string>\n    <string name=\"file_properties_basic_type\">Tipo</string>\n    <string name=\"file_properties_basic_type_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">Link para %1$s (%2$s)</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">Destino do Link</string>\n    <string name=\"file_properties_basic_size\">Tamanho</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_contents\">Conteúdo</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"one\">%1$,d item, com peso de %2$s</item>\n        <item quantity=\"many\">%1$,d itens, totalizando %2$s</item>\n        <item quantity=\"other\">%1$,d itens, totalizando %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">Última Modificação</string>\n    <string name=\"file_properties_basic_parent_directory\">Pasta Principal</string>\n    <string name=\"file_properties_basic_archive_file\">Arquivo</string>\n    <string name=\"file_properties_basic_archive_entry\">Entrada de Arquivo</string>\n    <string name=\"file_properties_basic_free_space\">Espaço Livre</string>\n    <string name=\"file_properties_image\">Imagem</string>\n    <string name=\"file_properties_media_dimensions\">Dimensões</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">Aceito</string>\n    <string name=\"file_properties_media_coordinates\">Coordenadas</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f, %2$.3f</string>\n    <string name=\"file_properties_media_address\">Localização</string>\n    <string name=\"file_properties_image_gps_altitude\">Altitude</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f m</string>\n    <string name=\"file_properties_image_equipment\">Câmera</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">Abertura</string>\n    <string name=\"file_properties_image_shutter_speed\">Velocidade do obturador</string>\n    <string name=\"file_properties_image_focal_length\">Comprimento focal</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f mm</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">Equivalente ISO</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">Software</string>\n    <string name=\"file_properties_image_description\">Descrição</string>\n    <string name=\"file_properties_image_artist\">Criador</string>\n    <string name=\"file_properties_image_copyright\">Copyright</string>\n    <string name=\"file_properties_audio\">Áudio</string>\n    <string name=\"file_properties_media_title\">Título</string>\n    <string name=\"file_properties_audio_artist\">Artista</string>\n    <string name=\"file_properties_audio_album\">Álbum</string>\n    <string name=\"file_properties_audio_album_artist\">Artista do álbum</string>\n    <string name=\"file_properties_audio_composer\">Compositor</string>\n    <string name=\"file_properties_audio_disc_number\">Disco</string>\n    <string name=\"file_properties_audio_track_number\">Faixa</string>\n    <string name=\"file_properties_audio_year\">Ano</string>\n    <string name=\"file_properties_audio_genre\">Gênero</string>\n    <string name=\"file_properties_media_duration\">Duração</string>\n    <string name=\"file_properties_media_bit_rate\">Bit rate</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d kbps</string>\n    <string name=\"file_properties_audio_sample_rate\">Taxa de amostragem</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Hz</string>\n    <string name=\"file_properties_video\">Vídeo</string>\n    <string name=\"file_properties_apk\">APK</string>\n    <string name=\"file_properties_apk_label\">Nome</string>\n    <string name=\"file_properties_apk_package_name\">Nome do pacote</string>\n    <string name=\"file_properties_apk_version\">Versão</string>\n    <string name=\"file_properties_apk_version_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_apk_min_sdk_version\">Versão mínima do sistema</string>\n    <string name=\"file_properties_apk_target_sdk_version\">Versão do sistema de destino</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s (%2$s, %3$d)</string>\n    <string name=\"file_properties_apk_requested_permissions\">Permissões</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">Nenhuma permissão solicitada</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"one\">%1$,d permissões solicitada</item>\n        <item quantity=\"many\">%1$,d permissões solicitadas</item>\n        <item quantity=\"other\">%1$,d permissões solicitadas</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">Assinaturas</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">Sem assinaturas válidas</string>\n    <string name=\"file_properties_apk_past_signature_digests\">Assinaturas antigas</string>\n    <string name=\"storage_file_system_root_title\">Root</string>\n    <string name=\"storage_list_title\">Armazenamento</string>\n    <string name=\"storage_list_empty\">Nenhum armazenamento</string>\n    <string name=\"storage_add_storage_title\">Adicionar armazenamento</string>\n    <string name=\"storage_add_storage_document_tree\">Armazenamento externo</string>\n    <string name=\"storage_add_storage_ftp_server\">Servidor FTP</string>\n    <string name=\"storage_add_storage_sftp_server\">Servidor SFTP</string>\n    <string name=\"storage_add_storage_smb_server\">Servidor SMB</string>\n    <string name=\"storage_add_storage_webdav_server\">Servidor WebDAV</string>\n    <string name=\"storage_edit_device_storage_title\">Editar armazenamento do dispositivo</string>\n    <string name=\"storage_edit_device_storage_name\">Nome</string>\n    <string name=\"storage_edit_device_storage_path\">Caminho</string>\n    <string name=\"storage_add_document_tree_title\">Adicionar armazenamento externo</string>\n    <string name=\"storage_edit_document_tree_title\">Editar armazenamento externo</string>\n    <string name=\"storage_edit_document_tree_name\">Nome</string>\n    <string name=\"storage_edit_document_tree_uri\">URI</string>\n    <string name=\"storage_edit_document_tree_path\">Caminho</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">Editar servidor FTP</string>\n    <string name=\"storage_edit_ftp_server_title_add\">Adicionar servidor FTP</string>\n    <string name=\"storage_edit_ftp_server_host\">Hostname</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">Insira o hostname</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">Hostname inválido</string>\n    <string name=\"storage_edit_ftp_server_port\">Porta</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">Porta inválida</string>\n    <string name=\"storage_edit_ftp_server_path\">Caminho</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">Pode ser deixado vazio</string>\n    <string name=\"storage_edit_ftp_server_name\">Nome</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">Usar hostname</string>\n    <string name=\"storage_edit_ftp_server_protocol\">Protocolo</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">Autenticação</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>Senha</item>\n        <item>Anônimo</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">Nome do usuário</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">Insira o nome do usuário</string>\n    <string name=\"storage_edit_ftp_server_password\">Senha</string>\n    <string name=\"storage_edit_ftp_server_mode\">Modo</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>Ativo</item>\n        <item>Passivo</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">Codificação</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">Conectar e adicionar</string>\n    <string name=\"storage_edit_ftp_server_add\">Adicionar</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">Editar servidor SFTP</string>\n    <string name=\"storage_edit_sftp_server_title_add\">Adicionar servidor SFTP</string>\n    <string name=\"storage_edit_sftp_server_host\">Hostname</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">Insira o hostname</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">Hostname inválido</string>\n    <string name=\"storage_edit_sftp_server_port\">Porta</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">Porta inválida</string>\n    <string name=\"storage_edit_sftp_server_path\">Caminho</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">Pode ser deixado vazio</string>\n    <string name=\"storage_edit_sftp_server_name\">Nome</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">Usar hostname</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">Autenticação</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>Senha</item>\n        <item>Chave pública</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">Nome do usuário</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">Insira o nome do usuário</string>\n    <string name=\"storage_edit_sftp_server_password\">Senha</string>\n    <string name=\"storage_edit_sftp_server_private_key\">Chave privada</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">Abrir arquivo</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">Insira a chave privada</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">Chave privada inválida</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">Senha chave privada</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">Pode ser deixado vazio</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">Senha de chave privada inválida</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">Conectar e adicionar</string>\n    <string name=\"storage_edit_sftp_server_add\">Adicionar</string>\n    <string name=\"storage_add_lan_smb_server_loading\">Procurando servidores SMB…</string>\n    <string name=\"storage_add_lan_smb_server_add\">Adicionar manualmente</string>\n    <string name=\"storage_edit_smb_server_title_edit\">Editar servidor SMB</string>\n    <string name=\"storage_edit_smb_server_title_add\">Adicionar servidor SMB</string>\n    <string name=\"storage_edit_smb_server_host\">Hostname</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">Insira o hostname</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">Hostname inválido</string>\n    <string name=\"storage_edit_smb_server_port\">Porta</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">Porta inválida</string>\n    <string name=\"storage_edit_smb_server_path\">Caminho</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">Pode ser deixado vazio</string>\n    <string name=\"storage_edit_smb_server_name\">Nome</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">Use hostname</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">Autenticação</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>Senha</item>\n        <item>Convidado</item>\n        <item>Anônimo</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">Nome do usuário</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">Insira o nome do usuário</string>\n    <string name=\"storage_edit_smb_server_password\">Senha</string>\n    <string name=\"storage_edit_smb_server_domain\">Domínio</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">Conecte e adicione</string>\n    <string name=\"storage_edit_smb_server_add\">Add</string>\n    <string name=\"storage_edit_webdav_server_title_edit\">Editar servidor WebDAV</string>\n    <string name=\"storage_edit_webdav_server_title_add\">Adicionar servidor WebDAV</string>\n    <string name=\"storage_edit_webdav_server_host_error_empty\">Insira o hostname</string>\n    <string name=\"storage_edit_webdav_server_port\">Porta</string>\n    <string name=\"storage_edit_webdav_server_port_error_invalid\">Porta inválida</string>\n    <string name=\"storage_edit_webdav_server_protocol\">Protocolo</string>\n    <string name=\"storage_edit_webdav_server_authentication_type\">Autenticação</string>\n    <string-array name=\"storage_edit_webdav_server_authentication_type_entries\">\n        <item>Senha</item>\n        <item>Token de acesso</item>\n    </string-array>\n    <string name=\"storage_edit_webdav_server_username_error_empty\">Insira o nome do usuário</string>\n    <string name=\"storage_edit_webdav_server_password\">Senha</string>\n    <string name=\"storage_edit_webdav_server_access_token\">Token de acesso</string>\n    <string name=\"storage_edit_webdav_server_access_token_error_empty\">Insira o token de acesso</string>\n    <string name=\"storage_edit_webdav_server_connect_and_add\">Conectar e adicionar</string>\n    <string name=\"storage_edit_webdav_server_add\">Adicionar</string>\n\n    <string name=\"navigation_storage_subtitle_format\">%1$s livre de %2$s</string>\n    <string name=\"navigation_add_storage\">Adicionar armazenamento…</string>\n    <string name=\"navigation_standard_directory_alarms\">Alarmes</string>\n    <string name=\"navigation_standard_directory_dcim\">DCIM</string>\n    <string name=\"navigation_standard_directory_documents\">Documentos</string>\n    <string name=\"navigation_standard_directory_downloads\">Downloads</string>\n    <string name=\"navigation_standard_directory_movies\">Vídeos</string>\n    <string name=\"navigation_standard_directory_music\">Músicas</string>\n    <string name=\"navigation_standard_directory_notifications\">Notificações</string>\n    <string name=\"navigation_standard_directory_pictures\">Imagens</string>\n    <string name=\"navigation_standard_directory_podcasts\">Podcasts</string>\n    <string name=\"navigation_standard_directory_ringtones\">Toques</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">Pasta de favoritos</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">Nome</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">Caminho</string>\n    <string name=\"archive_viewer_title\">Visualizador de arquivos</string>\n    <string name=\"image_viewer_title\">Visualizador de imagens</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"text_editor_title\">Editor de texto</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">Salvo</string>\n    <string name=\"text_editor_reload_message\">Tem certeza de que deseja recarregar? Alterações não salvas neste documento serão perdidas.</string>\n    <string name=\"text_editor_encoding\">Codificação</string>\n    <string name=\"text_editor_close_message\">Tem certeza de que deseja descartar alterações não salvas neste documento?</string>\n\n    <string name=\"ftp_server_title\">Servidor FTP</string>\n    <string name=\"ftp_server_state_title\">Status</string>\n    <string name=\"ftp_server_state_summary_starting\">Iniciando…</string>\n    <string name=\"ftp_server_state_summary_running\">Executando</string>\n    <string name=\"ftp_server_state_summary_stopping\">Parando…</string>\n    <string name=\"ftp_server_state_summary_stopped\">Não iniciado</string>\n    <string name=\"ftp_server_url_title\">URL</string>\n    <string name=\"ftp_server_url_summary_no_local_inet_address\">O endereço IP local é desconhecido</string>\n    <string name=\"ftp_server_url_menu_copy_url\">Copiar URL</string>\n    <string name=\"ftp_server_url_menu_copy_password\">Copiar senha</string>\n    <string name=\"ftp_server_add_tile_title\">Adicionar às configurações rápidas</string>\n    <string name=\"ftp_server_configuration_title\">Configuração</string>\n    <string name=\"ftp_server_anonymous_login_title\">Login anônimo</string>\n    <string name=\"ftp_server_username_title\">Nome do usuário</string>\n    <string name=\"ftp_server_password_title\">Senha</string>\n    <string name=\"ftp_server_port_title\">Porta</string>\n    <string name=\"ftp_server_home_directory_title\">Pasta raiz</string>\n    <string name=\"ftp_server_writable_title\">Permitir escrita</string>\n    <string name=\"settings_title\">Configurações</string>\n    <string name=\"settings_interface_title\">Interface</string>\n    <string name=\"settings_locale_title\">Idioma</string>\n    <string name=\"settings_theme_color_title\">Cor do tema</string>\n    <string name=\"settings_theme_color_summary\">Cor que aparece com mais frequência no aplicativo</string>\n    <string name=\"settings_material_design_3_title\">Material Design 3</string>\n    <string name=\"settings_night_mode_title\">Modo noturno</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>Seguir o sistema</item>\n        <item>Desativado</item>\n        <item>Ativado</item>\n        <item>Baseado na hora</item>\n        <item>Baseado na economia de bateria</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">Modo noite escura</string>\n    <string name=\"settings_file_list_animation_title\">Animação da lista de arquivos</string>\n    <string name=\"settings_file_name_ellipsize_title\">Exibir nome de arquivo longo</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>Elipsar o início</item>\n        <item>Elipsar o meio</item>\n        <item>Elipsar o final</item>\n        <item>Letreiro</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">Comportamento</string>\n    <string name=\"settings_default_directory_title\">Pasta padrão</string>\n    <string name=\"settings_standard_directories_title\">Pastas padrão</string>\n    <string name=\"settings_standard_directories_summary_empty\">Nenhuma pasta padrão ativada</string>\n    <string name=\"settings_bookmark_directory_screenshots\">Capturas de tela</string>\n    <string name=\"settings_bookmark_directories_title\">Pastas de favoritos</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">Nenhuma pasta de favoritos</string>\n    <string name=\"settings_root_strategy_title\">Modo de acesso root</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"one\">Você tem %1$,d operação de arquivo sendo executada e alterar o modo de acesso root agora pode resultar em erros inesperados. Tem certeza de que deseja alterar agora?</item>\n        <item quantity=\"many\">Você tem %1$,d operações de arquivos sendo executadas e alterar o modo de acesso root agora pode resultar em erros inesperados. Tem certeza de que deseja alterar agora?</item>\n        <item quantity=\"other\">Você tem %1$,d operações de arquivos sendo executadas e alterar o modo de acesso root agora pode resultar em erros inesperados. Tem certeza de que deseja alterar agora?</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>Apenas acesso normal</item>\n        <item>Automático</item>\n        <item>Apenas acesso root</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">Codificação do nome do arquivo</string>\n    <string name=\"settings_open_apk_default_action_title\">Abrir pacote do Android</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>Instalar</item>\n        <item>Visualizar conteúdo</item>\n        <item>Perguntar o que fazer</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">Pode tornar-se instável nas versões mais antigas do Android</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">Ler arquivos remotos para as miniaturas</string>\n\n    <string name=\"about_title\">Sobre</string>\n    <string name=\"about_version_title\">Versão</string>\n    <string name=\"about_github_title\">Ver no GitHub</string>\n    <string name=\"about_licenses_title\">Licenças</string>\n    <string name=\"about_privacy_policy_title\">Política de privacidade</string>\n    <string name=\"about_author_title\">Autor</string>\n    <string name=\"about_author_name_title\">Hai Zhang</string>\n    <string name=\"about_author_github_title\">Siga no GitHub</string>\n    <string name=\"about_author_twitter_title\">Siga no Twitter</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-pt-rPT/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">Ficheiro</string>\n    <string name=\"file_type_name_apk\">Pacote Android</string>\n    <string name=\"file_type_name_archive\">Arquivo %1$s</string>\n    <string name=\"file_type_name_audio\">Áudio %1$s</string>\n    <string name=\"file_type_name_calendar\">Calendário %1$s</string>\n    <string name=\"file_type_name_certificate\">Certificado %1$s</string>\n    <string name=\"file_type_name_code\">Documento %1$s</string>\n    <string name=\"file_type_name_contact\">Cartão de visita eletrónico</string>\n    <string name=\"file_type_name_directory\">Pasta</string>\n    <string name=\"file_type_name_document\">Documento %1$s</string>\n    <string name=\"file_type_name_ebook\">Livro eletrónico %1$s</string>\n    <string name=\"file_type_name_email\">Mensagem de e-mail %1$s</string>\n    <string name=\"file_type_name_font\">Tipo de letra %1$s</string>\n    <string name=\"file_type_name_generic\">Ficheiro %1$s</string>\n    <string name=\"file_type_name_image\">Imagem %1$s</string>\n    <string name=\"file_type_name_pdf\">Documento PDF</string>\n    <string name=\"file_type_name_presentation\">Apresentação %1$s</string>\n    <string name=\"file_type_name_spreadsheet\">Folha de cálculo %1$s</string>\n    <string name=\"file_type_name_text\">Documento %1$s</string>\n    <string name=\"file_type_name_text_plain\">Documento de texto simples</string>\n    <string name=\"file_type_name_video\">Vídeo %1$s</string>\n    <string name=\"file_type_name_word\">Documento do Word</string>\n    <string name=\"file_type_name_powerpoint\">Apresentação do PowerPoint</string>\n    <string name=\"file_type_name_excel\">Folha de cálculo do Excel</string>\n    <string name=\"file_type_name_posix_character_device\">Dispositivo de carateres</string>\n    <string name=\"file_type_name_posix_block_device\">Dispositivos de bloco</string>\n    <string name=\"file_type_name_posix_fifo\">Canal</string>\n    <string name=\"file_type_name_posix_symbolic_link\">Ligação</string>\n    <string name=\"file_type_name_posix_socket\">Socket</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">Ligação (danificada)</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-pt-rPT/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:ignore=\"ImpliedQuantity\">\n\n    <string name=\"app_name\">Material Files</string>\n\n    <string name=\"close\">Fechar</string>\n    <string name=\"copy\">Copiar</string>\n    <string name=\"cut\">Cortar</string>\n    <string name=\"default_\">Padrão</string>\n    <string name=\"delete\">Eliminar</string>\n    <string name=\"discard\">Descartar</string>\n    <string name=\"edit\">Editar</string>\n    <string name=\"empty\">Vazio</string>\n    <string name=\"empty_placeholder\">(Vazio)</string>\n    <string name=\"error\">Erro</string>\n    <string name=\"hide\">Ocultar</string>\n    <string name=\"install\">Instalar</string>\n    <string name=\"keep_editing\">Continuar edição</string>\n    <string name=\"loading\">A carregar…</string>\n    <string name=\"maybe_later\">Talvez mais tarde</string>\n    <string name=\"merge\">Juntar</string>\n    <string name=\"none\">Nenhum</string>\n    <string name=\"open_navigation_drawer\">Abrir menu de navegação</string>\n    <string name=\"paste\">Colar</string>\n    <string name=\"refresh\">Recarregar</string>\n    <string name=\"reload\">Recarregar</string>\n    <string name=\"remove\">Remover</string>\n    <string name=\"rename\">Mudar nome</string>\n    <string name=\"replace\">Substituir</string>\n    <string name=\"reset\">Repor</string>\n    <string name=\"retry\">Tentar novamente</string>\n    <string name=\"save\">Guardar</string>\n    <string name=\"search\">Pesquisar</string>\n    <string name=\"select\">Selecionar</string>\n    <string name=\"select_all\">Selecionar tudo</string>\n    <string name=\"share\">Partilhar</string>\n    <string name=\"show\">Mostrar</string>\n    <string name=\"skip\">Ignorar</string>\n    <string name=\"stop\">Parar</string>\n    <string name=\"system_default\">Definições do sistema</string>\n    <string name=\"unknown\">Desconhecido</string>\n    <string name=\"view\">Ver</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">Não existe uma aplicação para gerir esta ação</string>\n    <string name=\"open_settings\">Abrir definições</string>\n    <string name=\"shortcut_created\">Atalho criado</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"one\">%1$,d byte</item>\n        <item quantity=\"many\">%1$,d bytes</item>\n        <item quantity=\"other\">%1$,d bytes</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">A aplicação necessita do acesso para gerir os ficheiros/pastas. Conceda esse acesso na definição seguinte.</string>\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">A aplicação precisa da permissão para aceder aos ficheiros. Clique em “PERMITIR” na próxima caixa de diálogo.</string>\n    <string name=\"storage_permission_permanently_denied_message\">A aplicação precisa da permissão para aceder aos ficheiros. Por favor, conceda a permissão de “Armazenamento” nas definições de sistema.</string>\n    <!-- The allow button is in sentence case now. -->\n    <string name=\"notification_permission_rationale_message\">A aplicação precisa de aceder às notificações para poder mostrar as operações de ficheiros. Toque em \\\"Permitir\\\" no próximo ecrã.</string>\n    <string name=\"notification_permission_permanently_denied_message\">A aplicação precisa de aceder às notificações para poder mostrar as operações de ficheiros. Deve conceder a permissão \\\"Notificação\\\" nas definições do sistema.</string>\n\n    <string name=\"notification_channel_background_activity_start_name\">Ações em segundo plano</string>\n    <string name=\"notification_channel_background_activity_start_description\">Executar ações se a aplicação estiver em segundo plano</string>\n    <string name=\"notification_channel_file_job_name\">Operações de ficheiro</string>\n    <string name=\"notification_channel_file_job_description\">Mostrar e controlar operações de ficheiros</string>\n    <string name=\"notification_channel_ftp_server_name\">Servidor FTP</string>\n    <string name=\"notification_channel_ftp_server_description\">Mostrar e controlar servidores FTP</string>\n\n    <string name=\"file_action_archive_password_title\">Requer palavra-passe</string>\n    <string name=\"file_action_archive_password_message_format\">\\\"%1$s\\\" está protegido por palavra-passe.</string>\n    <string name=\"file_action_archive_password_error_empty\">A palavra-passe não pode estar vazia</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"one\">A preparar para comprimir %1$,d ficheiro (%2$s)</item>\n        <item quantity=\"many\">A preparar para comprimir %1$,d ficheiros (%2$s)</item>\n        <item quantity=\"other\">A preparar para comprimir %1$,d ficheiros (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">A comprimir “%1$s” para “%2$s”</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"one\">A comprimir %1$,d ficheiro para “%2$s”</item>\n        <item quantity=\"many\">A comprimir %1$,d ficheiros para “%2$s”</item>\n        <item quantity=\"other\">A comprimir %1$,d ficheiros para “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"one\">A preparar para copiar %1$,d ficheiro (%2$s)</item>\n        <item quantity=\"many\">A preparar para copiar %1$,d ficheiros (%2$s)</item>\n        <item quantity=\"other\">A preparar para copiar %1$,d ficheiros (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">A copiar “%1$s” para “%2$s”</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"one\">A copiar %1$,d ficheiro para “%2$s”</item>\n        <item quantity=\"many\">A copiar %1$,d ficheiros para “%2$s”</item>\n        <item quantity=\"other\">A copiar %1$,d ficheiros para “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"one\">A preparar para extrair %1$,d ficheiro (%2$s)</item>\n        <item quantity=\"many\">A preparar para extrair %1$,d ficheiros (%2$s)</item>\n        <item quantity=\"other\">A preparar para extrair %1$,d ficheiros (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">A extrair “%1$s” para “%2$s”</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"one\">A extrair %1$,d ficheiro para “%2$s”</item>\n        <item quantity=\"many\">A extrair %1$,d ficheiros para “%2$s”</item>\n        <item quantity=\"other\">A extrair %1$,d ficheiros para “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"one\">A preparar para mover %1$,d ficheiro (%2$s)</item>\n        <item quantity=\"many\">A preparar para mover %1$,d ficheiros (%2$s)</item>\n        <item quantity=\"other\">A preparar para mover %1$,d ficheiros (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">A mover “%1$s” para “%2$s”</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"one\">A mover %1$,d ficheiro para “%2$s”</item>\n        <item quantity=\"many\">A mover %1$,d ficheiros para “%2$s”</item>\n        <item quantity=\"other\">A mover %1$,d ficheiros para “%2$s”</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"one\">A preparar para eliminar %1$,d ficheiro</item>\n        <item quantity=\"many\">A preparar para eliminar %1$,d ficheiros</item>\n        <item quantity=\"other\">A preparar para eliminar %1$,d ficheiros</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">A eliminar “%1$s”</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"one\">A eliminar %1$,d ficheiro</item>\n        <item quantity=\"many\">A eliminar %1$,d ficheiros</item>\n        <item quantity=\"other\">A eliminar %1$,d ficheiros</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"one\">A preparar para alterar o proprietário de %1$,d ficheiro</item>\n        <item quantity=\"many\">A preparar para alterar o proprietário de %1$,d ficheiros</item>\n        <item quantity=\"other\">A preparar para alterar o proprietário de %1$,d ficheiros</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">A alterar proprietário de “%1$s”</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"one\">A alterar proprietário de %1$,d ficheiro</item>\n        <item quantity=\"many\">A alterar proprietário de %1$,d ficheiros</item>\n        <item quantity=\"other\">A alterar proprietário de %1$,d ficheiros</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"one\">A preparar parar alterar o grupo de %1$,d ficheiro</item>\n        <item quantity=\"many\">A preparar parar alterar o grupo de %1$,d ficheiros</item>\n        <item quantity=\"other\">A preparar parar alterar o grupo de %1$,d ficheiros</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">A alterar o grupo de “%1$s”</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"one\">A alterar o grupo de %1$,d ficheiro</item>\n        <item quantity=\"many\">A alterar o grupo de %1$,d ficheiros</item>\n        <item quantity=\"other\">A alterar o grupo de %1$,d ficheiros</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"one\">A preparar para alterar o modo de %1$,d ficheiro</item>\n        <item quantity=\"many\">A preparar para alterar o modo de %1$,d ficheiros</item>\n        <item quantity=\"other\">A preparar para alterar o modo de %1$,d ficheiros</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">A alterar modo de “%1$s”</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"one\">A alterar o modo de %1$,d ficheiro</item>\n        <item quantity=\"many\">A alterar o modo de %1$,d ficheiros</item>\n        <item quantity=\"other\">A alterar o modo de %1$,d ficheiros</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\"> A preparar para alterar o contexto SELinux de %1$,d ficheiro</item>\n        <item quantity=\"many\"> A preparar para alterar o contexto SELinux de %1$,d ficheiros</item>\n        <item quantity=\"other\"> A preparar para alterar o contexto SELinux de %1$,d ficheiros</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">A alterar contexto SELinux de “%1$s”</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">A alterar contexto SELinux de %1$,d ficheiro</item>\n        <item quantity=\"many\">A alterar contexto SELinux de %1$,d ficheiros</item>\n        <item quantity=\"other\">A alterar contexto SELinux de %1$,d ficheiros</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">A preparar para repor o contexto SELinux de %1$,d ficheiro</item>\n        <item quantity=\"many\">A preparar para repor o contexto SELinux de %1$,d ficheiros</item>\n        <item quantity=\"other\">A preparar para repor o contexto SELinux de %1$,d ficheiros</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">A repor contexto SELinux de “%1$s”</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">A repor contexto SELinux de %1$,d ficheiro</item>\n        <item quantity=\"many\">A repor contexto SELinux de %1$,d ficheiros</item>\n        <item quantity=\"other\">A repor contexto SELinux de %1$,d ficheiros</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">A escrever “%1$s”</string>\n    <string name=\"file_job_create_error_title\">Erro ao criar</string>\n    <string name=\"file_job_create_error_message_format\">Ocorreu um erro ao criar “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">Não é possível copiar uma pasta para si mesma</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">Não é possível extrair uma pasta para si mesma</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">Não é possível mover uma pasta para si mesma</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">A pasta de destino está dentro da pasta de origem.</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">Não é possível copiar um ficheiro para si mesmo</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">Não é possível extrair um ficheiro para si mesmo</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">Não é possível mover um ficheiro para si mesmo</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">O ficheiro de origem seria substituído pelo de destino. </string>\n    <string name=\"file_job_replace_title_format\">Substituir ficheiro “%1$s”?</string>\n    <string name=\"file_job_replace_message_format\">Já existe um ficheiro com este nome em “%1$s”.\\nSe o substituir perde o conteúdo do ficheiro existente.</string>\n    <string name=\"file_job_replace_target_name\">Ficheiro original</string>\n    <string name=\"file_job_replace_source_name\">Substituir por</string>\n    <string name=\"file_job_merge_title_format\">Juntar pasta “%1$s”?</string>\n    <string name=\"file_job_merge_copy_message_format\">A junção pede sempre uma confirmação antes de substituir os ficheiros da pasta que entrem em conflito com os que estão a ser copiados.</string>\n    <string name=\"file_job_merge_extract_message_format\">A junção pede sempre uma confirmação antes de substituir os ficheiros da pasta que entrem em conflito com os que estão a ser extraídos.</string>\n    <string name=\"file_job_merge_move_message_format\">A junção pede sempre uma confirmação antes de substituir os ficheiros da pasta que entrem em conflito com os que estão a ser movidos.</string>\n    <string name=\"file_job_merge_target_name\">Pasta original</string>\n    <string name=\"file_job_merge_source_name\">Juntar com</string>\n    <string name=\"file_job_conflict_show_name\">Selecione um novo nome para o destino</string>\n    <string name=\"file_job_conflict_name_hint\">Novo nome</string>\n    <string name=\"file_job_archive_error_title_format\">Erro ao compactar “%1$s”</string>\n    <string name=\"file_job_archive_error_message_format\">Ocorreu um erro ao compactar o ficheiro para “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">Erro ao copiar “%1$s”</string>\n    <string name=\"file_job_copy_error_message_format\">Ocorreu um erro ao copiar o ficheiro para “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">Erro ao extrair “%1$s”</string>\n    <string name=\"file_job_extract_error_message_format\">Ocorreu um erro ao extrair o ficheiro para “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">Erro ao mover “%1$s”</string>\n    <string name=\"file_job_move_error_message_format\">Ocorreu um erro ao mover o ficheiro para “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">Erro ao eliminar</string>\n    <string name=\"file_job_delete_error_message_format\">Ocorreu um erro ao eliminar o ficheiro em “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">Erro ao mudar o nome de “%1$s”</string>\n    <string name=\"file_job_rename_error_message_format\">Ocorreu um erro ao mudar o nome do ficheiro para “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">Erro ao alterar proprietário de “%1$s”</string>\n    <string name=\"file_job_set_owner_error_message_format\">Ocorreu um erro ao alterar o proprietário para “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">Erro ao alterar o grupo de “%1$s”</string>\n    <string name=\"file_job_set_group_error_message_format\">Ocorreu um erro ao alterar o grupo para “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">Erro ao alterar modo de “%1$s”</string>\n    <string name=\"file_job_set_mode_error_message_format\">Ocorreu um erro ao alterar o modo para “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">Erro ao alterar contexto SELinux para “%1$s”</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">Ocorreu um erro ao alterar o contexto SELinux para “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">Erro ao repor o contexto SELinux</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">Ocorreu um erro ao repor o contexto SELinux para “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">Erro ao escrever</string>\n    <string name=\"file_job_write_error_message_format\">Ocorreu um erro ao escrever “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">Ocorreu um erro ao obter informação de “%1$s”.</string>\n    <string name=\"file_job_remount_format\">Remontar “%1$s” como apenas leitura</string>\n    <string name=\"file_job_remount_loading_format\">A remontar “%1$s” como apenas leitura…</string>\n    <string name=\"file_job_remount_success_format\">“%1$s” foi remontado como apenas leitura</string>\n    <string name=\"file_job_action_all\">Aplicar esta ação para todos os ficheiros</string>\n\n    <string name=\"file_open_apk_message\">Este ficheiro é um pacote Android. Deseja instalar o pacote ou ver o seu conteúdo?</string>\n    <string name=\"file_open_as_title\">Abrir como…</string>\n    <string name=\"file_open_as_title_format\">Abrir “%1$s” como</string>\n    <string name=\"file_open_as_type_text\">Texto</string>\n    <string name=\"file_open_as_type_image\">Imagem</string>\n    <string name=\"file_open_as_type_audio\">Áudio</string>\n    <string name=\"file_open_as_type_video\">Vídeo</string>\n    <string name=\"file_open_as_type_directory\">Pasta</string>\n    <string name=\"file_open_as_type_any\">Outro</string>\n    <string name=\"file_install_apk_from_background_title_format\">“%1$s” está pronto para ser instalado</string>\n    <string name=\"file_install_apk_from_background_text\">Toque para instalar</string>\n    <string name=\"file_open_from_background_title_format\">“%1$s” está pronto para ser aberto</string>\n    <string name=\"file_open_from_background_text\">Toque para abrir</string>\n    <string name=\"file_name_error_empty\">O nome do ficheiro não pode estar vazio</string>\n    <string name=\"file_name_error_invalid\">Nome de ficheiro inválido</string>\n    <string name=\"file_name_error_already_exists\">Já existe um ficheiro com este nome</string>\n    <string name=\"file_delete_message_file_format\">Eliminar “%1$s”?</string>\n    <string name=\"file_delete_message_directory_format\">Eliminar a pasta “%1$s” e todo o seu conteúdo?</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"one\">Eliminar %1$,d ficheiro?</item>\n        <item quantity=\"many\">Eliminar %1$,d ficheiros?</item>\n        <item quantity=\"other\">Eliminar %1$,d ficheiros?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"one\">Eliminar %1$,d pasta e todo o seu conteúdo?</item>\n        <item quantity=\"many\">Eliminar %1$,d pastas e todo o seu conteúdo?</item>\n        <item quantity=\"other\">Eliminar %1$,d pastas e todo o seu conteúdo?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"one\">Eliminar %1$,d item?</item>\n        <item quantity=\"many\">Eliminar %1$,d itens?</item>\n        <item quantity=\"other\">Eliminar %1$,d itens?</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">Criar arquivo</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_create_archive_password\">Palavra-passe (opcional)</string>\n    <string name=\"file_add_bookmark_success\">Marcador adicionado</string>\n    <string name=\"file_create_file_title\">Novo ficheiro</string>\n    <string name=\"file_create_directory_title\">Nova pasta</string>\n\n    <string name=\"file_list_title\">Ficheiros</string>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"one\">%1$,d pasta</item>\n        <item quantity=\"many\">%1$,d pastas</item>\n        <item quantity=\"other\">%1$,d pastas</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"one\">%1$,d ficheiro</item>\n        <item quantity=\"many\">%1$,d ficheiros</item>\n        <item quantity=\"other\">%1$,d ficheiros</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">,\\u0020</string>\n    <string name=\"file_list_path_error_empty\">Caminho não pode estar vazio</string>\n    <string name=\"file_list_path_error_invalid\">Caminho inválido</string>\n    <string name=\"file_list_action_view_sort\">Vista e ordenação</string>\n    <string name=\"file_list_action_view_list\">Lista</string>\n    <string name=\"file_list_action_view_grid\">Grelha</string>\n    <string name=\"file_list_action_sort_by_name\">Nome</string>\n    <string name=\"file_list_action_sort_by_type\">Tipo</string>\n    <string name=\"file_list_action_sort_by_size\">Tamanho</string>\n    <string name=\"file_list_action_sort_by_last_modified\">Última modificação</string>\n    <string name=\"file_list_action_sort_order_ascending\">Ascendente</string>\n    <string name=\"file_list_action_sort_directories_first\">Pastas primeiro</string>\n    <string name=\"file_list_action_view_sort_path_specific\">Apenas para esta pasta</string>\n    <string name=\"file_list_action_new_task\">Nova janela</string>\n    <string name=\"file_list_action_navigate_up\">Subir</string>\n    <string name=\"file_list_action_navigate_to\">Ir para</string>\n    <string name=\"file_list_action_show_hidden_files\">Mostrar ficheiros ocultos</string>\n    <string name=\"file_list_action_copy_path\">Copiar caminho</string>\n    <string name=\"file_list_action_open_in_terminal\">Abrir no terminal</string>\n    <string name=\"file_list_action_add_bookmark\">Adicionar marcador</string>\n    <string name=\"file_list_action_create_shortcut\">Criar atalho</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">Abrir em nova janela</string>\n    <string name=\"file_list_select_title_format\">%1$,d</string>\n    <string name=\"file_list_paste_move_title_format\">A mover %1$,d</string>\n    <string name=\"file_list_paste_copy_title_format\">A copiar %1$,d</string>\n    <string name=\"file_list_paste_extract_title_format\">A extrair %1$,d</string>\n    <string name=\"file_list_paste_action_extract_here\">Extrair aqui</string>\n    <string name=\"file_list_empty\">Sem ficheiros</string>\n    <string name=\"file_list_action_create_file\">Ficheiro</string>\n    <string name=\"file_list_action_create_directory\">Pastas</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">Abrir com</string>\n    <string name=\"file_item_action_extract\">Extrair</string>\n    <string name=\"file_item_action_archive\">Comprimir</string>\n    <string name=\"file_item_action_properties\">Propriedades</string>\n\n    <string name=\"file_properties_title_format\">Propriedades de “%1$s”</string>\n    <string name=\"file_properties_basic\">Básico</string>\n    <string name=\"file_properties_basic_name\">Nome</string>\n    <string name=\"file_properties_basic_type\">Tipo</string>\n    <string name=\"file_properties_basic_type_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">Ligação para %1$s (%2$s)</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">Destino da ligação</string>\n    <string name=\"file_properties_basic_size\">Tamanho</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_contents\">Conteúdo</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"one\">%1$,d item, tamanho: %2$s</item>\n        <item quantity=\"many\">%1$,d itens, tamanho: %2$s</item>\n        <item quantity=\"other\">%1$,d itens, tamanho: %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">Última modificação</string>\n    <string name=\"file_properties_basic_parent_directory\">Pasta principal</string>\n    <string name=\"file_properties_basic_archive_file\">Arquivo</string>\n    <string name=\"file_properties_basic_archive_entry\">Entrada de arquivo</string>\n    <string name=\"file_properties_basic_free_space\">Espaço livre</string>\n    <string name=\"file_properties_image\">Imagem</string>\n    <string name=\"file_properties_media_dimensions\">Dimensões</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">Obtida em</string>\n    <string name=\"file_properties_media_coordinates\">Coordenadas</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f, %2$.3f</string>\n    <string name=\"file_properties_media_address\">Localização</string>\n    <string name=\"file_properties_image_gps_altitude\">Altitude</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f m</string>\n    <string name=\"file_properties_image_equipment\">Câmara</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">Abertura</string>\n    <string name=\"file_properties_image_shutter_speed\">Velocidade do obturador</string>\n    <string name=\"file_properties_image_focal_length\">Distância focal</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f mm</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">Equivalente ISO</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">Software</string>\n    <string name=\"file_properties_image_description\">Descrição</string>\n    <string name=\"file_properties_image_artist\">Criador</string>\n    <string name=\"file_properties_image_copyright\">Copyright</string>\n    <string name=\"file_properties_audio\">Áudio</string>\n    <string name=\"file_properties_media_title\">Título</string>\n    <string name=\"file_properties_audio_artist\">Artista</string>\n    <string name=\"file_properties_audio_album\">Álbum</string>\n    <string name=\"file_properties_audio_album_artist\">Artista do álbum</string>\n    <string name=\"file_properties_audio_composer\">Compositor</string>\n    <string name=\"file_properties_audio_disc_number\">Disco</string>\n    <string name=\"file_properties_audio_track_number\">Faixa</string>\n    <string name=\"file_properties_audio_year\">Ano</string>\n    <string name=\"file_properties_audio_genre\">Género</string>\n    <string name=\"file_properties_media_duration\">Duração</string>\n    <string name=\"file_properties_media_bit_rate\">Taxa de dados</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d kbps</string>\n    <string name=\"file_properties_audio_sample_rate\">Frequência</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Hz</string>\n    <string name=\"file_properties_video\">Vídeo</string>\n    <string name=\"file_properties_apk\">APK</string>\n    <string name=\"file_properties_apk_label\">Nome</string>\n    <string name=\"file_properties_apk_package_name\">Nome do pacote</string>\n    <string name=\"file_properties_apk_version\">Versão</string>\n    <string name=\"file_properties_apk_version_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_apk_min_sdk_version\">Versão mínima de sistema</string>\n    <string name=\"file_properties_apk_target_sdk_version\">Versão de sistema de destino</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s (%2$s, %3$d)</string>\n    <string name=\"file_properties_apk_requested_permissions\">Permissões</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">Não necessita de permissões</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"one\">Necessita de %1$,d permissão</item>\n        <item quantity=\"many\">Necessita de %1$,d permissões</item>\n        <item quantity=\"other\">Necessita de %1$,d permissões</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">Assinaturas</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">Não há assinaturas válidas</string>\n    <string name=\"file_properties_apk_past_signature_digests\">Assinaturas antigas</string>\n    <string name=\"storage_file_system_root_title\">Root</string>\n    <string name=\"storage_list_title\">Armazenamento</string>\n    <string name=\"storage_list_empty\">Nenhum armazenamento</string>\n    <string name=\"storage_add_storage_title\">Adicionar armazenamento</string>\n    <string name=\"storage_add_storage_document_tree\">Armazenamento externo</string>\n    <string name=\"storage_add_storage_ftp_server\">Servidor FTP</string>\n    <string name=\"storage_add_storage_sftp_server\">Servidor SFTP</string>\n    <string name=\"storage_add_storage_smb_server\">Servidor SMB</string>\n    <string name=\"storage_add_storage_webdav_server\">Servidor WebDAV</string>\n    <string name=\"storage_edit_device_storage_title\">Editar armazenamento</string>\n    <string name=\"storage_edit_device_storage_name\">Nome</string>\n    <string name=\"storage_edit_device_storage_path\">Caminho</string>\n    <string name=\"storage_add_document_tree_title\">Adicionar armazenamento externo</string>\n    <string name=\"storage_edit_document_tree_title\">Editar armazenamento externo</string>\n    <string name=\"storage_edit_document_tree_name\">Nome</string>\n    <string name=\"storage_edit_document_tree_uri\">URI</string>\n    <string name=\"storage_edit_document_tree_path\">Caminho</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">Editar servidor FTP</string>\n    <string name=\"storage_edit_ftp_server_title_add\">Adicionar servidor FTP</string>\n    <string name=\"storage_edit_ftp_server_host\">Nome do servidor</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">Indique um nome</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">Nome inválido</string>\n    <string name=\"storage_edit_ftp_server_port\">Porta</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">Porta inválida</string>\n    <string name=\"storage_edit_ftp_server_path\">Caminho</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">Pode ser deixado vazio</string>\n    <string name=\"storage_edit_ftp_server_name\">Nome</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">Utilizar nome do servidor</string>\n    <string name=\"storage_edit_ftp_server_protocol\">Protocolo</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">Autenticação</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>Palavra-passe</item>\n        <item>Anónimo</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">Nome de utilizador</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">Indique um nome de utilizador</string>\n    <string name=\"storage_edit_ftp_server_password\">Palavra-passe</string>\n    <string name=\"storage_edit_ftp_server_mode\">Modo</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>Ativo</item>\n        <item>Passivo</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">Codificação</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">Conectar e adicionar</string>\n    <string name=\"storage_edit_ftp_server_add\">Adicionar</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">Editar servidor SFTP</string>\n    <string name=\"storage_edit_sftp_server_title_add\">Adicionar servidor SFTP</string>\n    <string name=\"storage_edit_sftp_server_host\">Nome do servidor</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">Indique um nome</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">Nome inválido</string>\n    <string name=\"storage_edit_sftp_server_port\">Porta</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">Porta inválida</string>\n    <string name=\"storage_edit_sftp_server_path\">Caminho</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">Pode ser deixado vazio</string>\n    <string name=\"storage_edit_sftp_server_name\">Nome</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">Utilizar nome do servidor</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">Autenticação</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>Palavra-passe</item>\n        <item>Chave pública</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">Nome de utilizador</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">Indique um nome de utilizador</string>\n    <string name=\"storage_edit_sftp_server_password\">Palavra-passe</string>\n    <string name=\"storage_edit_sftp_server_private_key\">Chave privada</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">Abrir ficheiro</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">Indique a chave privada</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">Chave privada inválida</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">Palavra-passe da chave privada</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">Pode ser deixado vazio</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">Palavra-passe da chave privada inválida</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">Conectar e adicionar</string>\n    <string name=\"storage_edit_sftp_server_add\">Adicionar</string>\n    <string name=\"storage_add_lan_smb_server_loading\">A procurar servidores SMB…</string>\n    <string name=\"storage_add_lan_smb_server_add\">Adicionar manualmente</string>\n    <string name=\"storage_edit_smb_server_title_edit\">Editar servidor SMB</string>\n    <string name=\"storage_edit_smb_server_title_add\">Adicionar servidor SMB</string>\n    <string name=\"storage_edit_smb_server_host\">Nome do servidor</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">Introduza um nome</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">Nome inválido</string>\n    <string name=\"storage_edit_smb_server_port\">Porta</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">Porta inválida</string>\n    <string name=\"storage_edit_smb_server_path\">Caminho</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">Pode ser deixado vazio</string>\n    <string name=\"storage_edit_smb_server_name\">Nome</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">Utilizar nome do servidor</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">Autenticação</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>Palavra-passe</item>\n        <item>Convidado</item>\n        <item>Anónimo</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">Nome de utilizador</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">Introduza um nome de utilizador</string>\n    <string name=\"storage_edit_smb_server_password\">Palavra-passe</string>\n    <string name=\"storage_edit_smb_server_domain\">Domínio</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">Conectar e adicionar</string>\n    <string name=\"storage_edit_smb_server_add\">Adicionar</string>\n    <string name=\"storage_edit_webdav_server_title_edit\">Editar servidor WebDAV</string>\n    <string name=\"storage_edit_webdav_server_title_add\">Adicionar servidor WebDAV</string>\n    <string name=\"storage_edit_webdav_server_host\">Nome do servidor</string>\n    <string name=\"storage_edit_webdav_server_host_error_empty\">Introduza um nome</string>\n    <string name=\"storage_edit_webdav_server_host_error_invalid\">Nome inválido</string>\n    <string name=\"storage_edit_webdav_server_port\">Porta</string>\n    <string name=\"storage_edit_webdav_server_port_error_invalid\">Porta inválida</string>\n    <string name=\"storage_edit_webdav_server_path\">Caminho</string>\n    <string name=\"storage_edit_webdav_server_path_placeholder\">Pode ficar vazio</string>\n    <string name=\"storage_edit_webdav_server_name\">Nome</string>\n    <string name=\"storage_edit_webdav_server_name_placeholder\">Utilizar nome do servidor</string>\n    <string name=\"storage_edit_webdav_server_protocol\">Protocolo</string>\n    <string name=\"storage_edit_webdav_server_authentication_type\">Autenticação</string>\n    <string-array name=\"storage_edit_webdav_server_authentication_type_entries\">\n        <item>Palavra-passe</item>\n        <item>Token de acesso</item>\n        <item>Não usar</item>\n    </string-array>\n    <string name=\"storage_edit_webdav_server_username\">Nome de utilizador</string>\n    <string name=\"storage_edit_webdav_server_username_error_empty\">Introduza o nome de utilizador</string>\n    <string name=\"storage_edit_webdav_server_password\">Palavra-passe</string>\n    <string name=\"storage_edit_webdav_server_access_token\">Token de acesso</string>\n    <string name=\"storage_edit_webdav_server_access_token_error_empty\">Introduza o token de acesso</string>\n    <string name=\"storage_edit_webdav_server_connect_and_add\">Conectar e adicionar</string>\n    <string name=\"storage_edit_webdav_server_add\">Adicionar</string>\n\n    <string name=\"navigation_storage_subtitle_format\">%1$s livres de %2$s</string>\n    <string name=\"navigation_add_storage\">Adicionar armazenamento…</string>\n    <string name=\"navigation_standard_directory_alarms\">Alarmes</string>\n    <string name=\"navigation_standard_directory_dcim\">DCIM</string>\n    <string name=\"navigation_standard_directory_documents\">Documentos</string>\n    <string name=\"navigation_standard_directory_downloads\">Transferências</string>\n    <string name=\"navigation_standard_directory_movies\">Vídeos</string>\n    <string name=\"navigation_standard_directory_music\">Música</string>\n    <string name=\"navigation_standard_directory_notifications\">Notificações</string>\n    <string name=\"navigation_standard_directory_pictures\">Imagens</string>\n    <string name=\"navigation_standard_directory_podcasts\">Podcasts</string>\n    <string name=\"navigation_standard_directory_ringtones\">Toques</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">Pasta de marcadores</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">Nome</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">Caminho</string>\n    <string name=\"archive_viewer_title\">Visualizador de arquivos</string>\n    <string name=\"image_viewer_title\">Visualizador de imagens</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"text_editor_title\">Editor de texto</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">Guardado</string>\n    <string name=\"text_editor_reload_message\">Tem a certeza de que deseja recarregar? As alterações não guardadas serão perdidas.</string>\n    <string name=\"text_editor_encoding\">Codificação</string>\n    <string name=\"text_editor_close_message\">Tem certeza de que deseja descartar as alterações efetuadas a este documento?</string>\n\n    <string name=\"ftp_server_title\">Servidor FTP</string>\n    <string name=\"ftp_server_state_title\">Estado</string>\n    <string name=\"ftp_server_state_summary_starting\">A iniciar…</string>\n    <string name=\"ftp_server_state_summary_running\">Em execução</string>\n    <string name=\"ftp_server_state_summary_stopping\">A parar…</string>\n    <string name=\"ftp_server_state_summary_stopped\">Não iniciado</string>\n    <string name=\"ftp_server_url_title\">URL</string>\n    <string name=\"ftp_server_url_summary_no_local_inet_address\">Endereço local desconhecido</string>\n    <string name=\"ftp_server_url_menu_copy_url\">Copiar URL</string>\n    <string name=\"ftp_server_url_menu_copy_password\">Copiar palavra-passe</string>\n    <string name=\"ftp_server_add_tile_title\">Adicionar a Definições rápidas</string>\n    <string name=\"ftp_server_add_tile_result_added\">\\\"Servidor FTP\\\" foi adicionado às definições rápidas</string>\n    <string name=\"ftp_server_add_tile_result_already_added\">\\\"Servidor FTP\\\" já existe nas definições rápidas</string>\n    <string name=\"ftp_server_add_tile_result_error\">Erro ao adicionar “Servidor FTP” às definições rápidas</string>\n    <string name=\"ftp_server_configuration_title\">Configuração</string>\n    <string name=\"ftp_server_anonymous_login_title\">Sessão anónima</string>\n    <string name=\"ftp_server_username_title\">Nome de utilizador</string>\n    <string name=\"ftp_server_password_title\">Palavra-passe</string>\n    <string name=\"ftp_server_port_title\">Porta</string>\n    <string name=\"ftp_server_home_directory_title\">Pasta root</string>\n    <string name=\"ftp_server_writable_title\">Permitir escrita</string>\n    <string name=\"settings_title\">Definições</string>\n    <string name=\"settings_interface_title\">Interface</string>\n    <string name=\"settings_locale_title\">Idioma</string>\n    <string name=\"settings_theme_color_title\">Cor do tema</string>\n    <string name=\"settings_theme_color_summary\">A cor que aparece com mais frequência na aplicação</string>\n    <string name=\"settings_material_design_3_title\">Material Design 3</string>\n    <string name=\"settings_night_mode_title\">Modo noturno</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>Cor do sistema</item>\n        <item>Desligado</item>\n        <item>Ligado</item>\n        <item>Baseado na hora</item>\n        <item>Baseado na poupança de bateria</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">Modo noite escura</string>\n    <string name=\"settings_file_list_animation_title\">Animação da lista de ficheiros</string>\n    <string name=\"settings_file_name_ellipsize_title\">Mostrar nome longo</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>Elipse no início</item>\n        <item>Elipse no centro</item>\n        <item>Elipse no final</item>\n        <item>Marquee</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">Comportamento</string>\n    <string name=\"settings_default_directory_title\">Pasta padrão</string>\n    <string name=\"settings_standard_directories_title\">Pastas padrão</string>\n    <string name=\"settings_standard_directories_summary_empty\">Não existem pastas padrão</string>\n    <string name=\"settings_bookmark_directory_screenshots\">Capturas de ecrã</string>\n    <string name=\"settings_bookmark_directories_title\">Pastas de marcadores</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">Não existem pastas de marcadores</string>\n    <string name=\"settings_root_strategy_title\">Modo de acesso root</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"one\">Tem %1$,d operação de ficheiro em execução e se alterar o modo de acesso root agora podem ocorrer erros inesperados. Tem a certeza de que deseja alterar agora?</item>\n        <item quantity=\"many\">Tem %1$,d operações de ficheiro em execução e se alterar o modo de acesso root agora podem ocorrer erros inesperados. Tem a certeza de que deseja alterar agora?</item>\n        <item quantity=\"other\">Tem %1$,d operações de ficheiro em execução e se alterar o modo de acesso root agora podem ocorrer erros inesperados. Tem a certeza de que deseja alterar agora?</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>Apenas acesso normal</item>\n        <item>Automático</item>\n        <item>Apenas acesso root</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">Codificação para o nome do arquivo</string>\n    <string name=\"settings_open_apk_default_action_title\">Abrir pacote Android</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>Instalar</item>\n        <item>Ver conteúdo</item>\n        <item>Perguntar o que fazer</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">Pode tornar-se instável nas versões mais antigas do Android</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">Ler ficheiros remotos das miniaturas</string>\n\n    <string name=\"about_title\">Acerca</string>\n    <string name=\"about_version_title\">Versão</string>\n    <string name=\"about_github_title\">Ver no GitHub</string>\n    <string name=\"about_licenses_title\">Licenças</string>\n    <string name=\"about_privacy_policy_title\">Política de privacidade</string>\n    <string name=\"about_author_title\">Autor</string>\n    <string name=\"about_author_name_title\">Hai Zhang</string>\n    <string name=\"about_author_github_title\">Seguir em GitHub</string>\n    <string name=\"about_author_twitter_title\">Seguir em Twitter</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ro/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">Fişier</string>\n    <string name=\"file_type_name_apk\">Pachet Android</string>\n    <string name=\"file_type_name_archive\">%1$s arhivează</string>\n    <string name=\"file_type_name_audio\">%1$s audio</string>\n    <string name=\"file_type_name_calendar\">%1$s calendar</string>\n    <string name=\"file_type_name_certificate\">%1$s certificate</string>\n    <string name=\"file_type_name_code\">%1$s documente</string>\n    <string name=\"file_type_name_contact\">Carte de vizită electronică</string>\n    <string name=\"file_type_name_directory\">Dosar</string>\n    <string name=\"file_type_name_document\">%1$s document</string>\n    <string name=\"file_type_name_ebook\">%1$s e-book</string>\n    <string name=\"file_type_name_email\">%1$s mesaj email</string>\n    <string name=\"file_type_name_font\">%1$s font</string>\n    <string name=\"file_type_name_generic\">%1$s fişier</string>\n    <string name=\"file_type_name_image\">%1$s imagine</string>\n    <string name=\"file_type_name_pdf\">Document PDF</string>\n    <string name=\"file_type_name_presentation\">%1$s presentare</string>\n    <string name=\"file_type_name_spreadsheet\">%1$s tabel</string>\n    <string name=\"file_type_name_text\">%1$s document</string>\n    <string name=\"file_type_name_text_plain\">Document text</string>\n    <string name=\"file_type_name_video\">%1$s video</string>\n    <string name=\"file_type_name_word\">Document Word</string>\n    <string name=\"file_type_name_powerpoint\">Prezentare PowerPoint</string>\n    <string name=\"file_type_name_excel\">Tabel Excel</string>\n    <string name=\"file_type_name_posix_character_device\">Caracter dispozitov</string>\n    <string name=\"file_type_name_posix_block_device\">Dispozitiv block</string>\n    <string name=\"file_type_name_posix_fifo\">Pipe</string>\n    <string name=\"file_type_name_posix_symbolic_link\">Link</string>\n    <string name=\"file_type_name_posix_socket\">Socket</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">Link (stricat)</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ro/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <string name=\"app_name\">Material Files</string>\n\n    <string name=\"close\">Închide</string>\n    <string name=\"copy\">Copiază</string>\n    <string name=\"cut\">Decupează</string>\n    <string name=\"default_\">Implicit</string>\n    <string name=\"delete\">Șterge</string>\n    <string name=\"discard\">Aruncă</string>\n    <string name=\"edit\">Editează</string>\n    <string name=\"empty\">Gol</string>\n    <string name=\"empty_placeholder\">(Gol)</string>\n    <string name=\"error\">Eroare</string>\n    <string name=\"hide\">Ascunde</string>\n    <string name=\"install\">Instalează</string>\n    <string name=\"keep_editing\">Contină editarea</string>\n    <string name=\"loading\">Se încarcă…</string>\n    <string name=\"maybe_later\">Poate mai târziu</string>\n    <string name=\"merge\">Îmbinare</string>\n    <string name=\"none\">Niciunul</string>\n    <string name=\"open_navigation_drawer\">Deschide sertarul de navigare</string>\n    <string name=\"paste\">Lipește</string>\n    <string name=\"refresh\">Reîmprospătare</string>\n    <string name=\"reload\">Reîncarcă</string>\n    <string name=\"remove\">Elimină</string>\n    <string name=\"rename\">Redenumește</string>\n    <string name=\"replace\">Înlocuiește</string>\n    <string name=\"reset\">Resetare</string>\n    <string name=\"retry\">Reîncearcă</string>\n    <string name=\"save\">Salvează</string>\n    <string name=\"search\">Caută</string>\n    <string name=\"select\">Seleactează</string>\n    <string name=\"select_all\">Selectează tot</string>\n    <string name=\"share\">Partajează</string>\n    <string name=\"show\">Arată</string>\n    <string name=\"skip\">Omite</string>\n    <string name=\"stop\">Oprire</string>\n    <string name=\"system_default\">Prestabilit de sistem</string>\n    <string name=\"unknown\">Necunoscut</string>\n    <string name=\"view\">Vizualizează</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">Nu a fost găsită o aplicație pentru a gestiona această acțiune</string>\n    <string name=\"open_settings\">Deschide setările</string>\n    <string name=\"shortcut_created\">Scurtătură creată</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"one\">%1$,d octet</item>\n        <item quantity=\"few\">%1$,d octeți</item>\n        <item quantity=\"other\">%1$,d de octeți</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">Aplicația are nevoie de acces pentru a gestiona toate fișierele. Permite accesul în setarea de sistem viitoare.</string>\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">Aplicația necesită permisiunea de accesare a fișierelor. Apasă pe \\\"PERMITE\\\" în căsuța următoare.</string>\n    <string name=\"storage_permission_permanently_denied_message\">Aplicația are nevoie de permisiunea de a accesa fișierele. Acordă permisiunea „Stocare” în setările sistemului.</string>\n    <!-- The allow button is in sentence case now. -->\n    <string name=\"notification_permission_rationale_message\">Aplicația necesită permisiunea de a afișa notificări legate de operațiunile cu fișiere. Vă rugăm să apăsați pe \\\"Permite\\\" în fereastra următoare.</string>\n    <string name=\"notification_permission_permanently_denied_message\">Aplicația necesită permisiunea de a afișa notificări legate de operațiunile cu fișiere. Vă rugăm să permiteți afișarea notificărilor în setări.</string>\n\n    <string name=\"notification_channel_background_activity_start_name\">Acțiuni în fundal</string>\n    <string name=\"notification_channel_background_activity_start_description\">Efectuează acțiuni în timp ce aplicația este în fundal</string>\n    <string name=\"notification_channel_file_job_name\">Operațiuni asupra fișierelor</string>\n    <string name=\"notification_channel_file_job_description\">Afișează și controlează operațiunile fișierelor</string>\n    <string name=\"notification_channel_ftp_server_name\">Server FTP</string>\n    <string name=\"notification_channel_ftp_server_description\">Arată și controlează serverul FTP</string>\n\n    <string name=\"file_action_archive_password_title\">Parola este necesară</string>\n    <string name=\"file_action_archive_password_message_format\">\\\"%1$s\\\" este protejat de o parolă.</string>\n    <string name=\"file_action_archive_password_error_empty\">Câmpul parolă nu poate fi gol</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"one\">Ss pregătesc de compresie %1$,d fișier (%2$s)</item>\n        <item quantity=\"few\">Ss pregătesc de compresie %1$,d fișiere (%2$s)</item>\n        <item quantity=\"other\">Se pregătesc de comprimare %1$,d de fișiere (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">Se comprimă \\\"%1$s\\\" în \\\"%2$s\\\"</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"one\">Se comprimă %1$,d fișier în “1%2$s”</item>\n        <item quantity=\"few\">Se comprimă %1$,d fișiere în “1%2$s”</item>\n        <item quantity=\"other\">Se comprimă %1$,d de fișiere în “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"one\">Ss pregătește de copiere %1$,d fișier (%2$s)</item>\n        <item quantity=\"few\">Se pregătesc de copiere %1$,d fișiere (%2$s)</item>\n        <item quantity=\"other\">Se pregătesc de copiere %1$,d de fișiere (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">Se copiază \\\"%1$s\\\" în \\\"%2$s\\\"</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"one\">Se copiază %1$,d fișier în “1%2$s”</item>\n        <item quantity=\"few\">Se copiază %1$,d fișiere în “1%2$s”</item>\n        <item quantity=\"other\">Se copiază %1$,d de fișiere în “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"one\">Se pregătește de extragere %1$,d fișier (1%2$s)</item>\n        <item quantity=\"few\">Se pregătesc de extragere %1$,d fișiere (1%2$s)</item>\n        <item quantity=\"other\">Se pregătesc de extragere %1$,d de fișiere (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">Se extrage \\\"%1$s\\\" în \\\"%2$s\\\"</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"one\">Se extrage %1$,d fișier în “1%2$s”</item>\n        <item quantity=\"few\">Se extrag %1$,d fișiere în “1%2$s”</item>\n        <item quantity=\"other\">Se extrag %1$,d de fișiere în “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"one\">Se pregătește de mutare %1$,d fișier (1%2$s)</item>\n        <item quantity=\"few\">Se pregătesc de mutare %1$,d fișiere (1%2$s)</item>\n        <item quantity=\"other\">Se pregătesc de mutare %1$,d de fișiere (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">Se mută \\\"%1$s\\\" în \\\"%2$s\\\"</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"one\">Se mută %1$,d fișier în “1%2$s”</item>\n        <item quantity=\"few\">Se mută %1$,d fișiere în “1%2$s”</item>\n        <item quantity=\"other\">Se mută %1$,d de fișiere în “%2$s”</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"one\">Se pregătește de ștergere %1$,d fișier</item>\n        <item quantity=\"few\">Se pregătesc de ștergere %1$,d fișiere</item>\n        <item quantity=\"other\">Se pregătesc de ștergere %1$,d de fișiere</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">Se șterge \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"one\">Se șterge %1$,d fișier</item>\n        <item quantity=\"few\">Se șterg %1$,d fișiere</item>\n        <item quantity=\"other\">Se șterg  %1$,d de fișiere</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"one\">Se pregătește schimbarea deținătorului pentru %1$,d fișier</item>\n        <item quantity=\"few\">Se pregătește schimbarea deținătorului pentru %1$,d fișiere</item>\n        <item quantity=\"other\">Se pregătește schimbarea posesorului pentru %1$,d de fișiere</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">Se schimbă posesorul pentru \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"one\">Se schimbă posesorul pentru %1$,d fișier</item>\n        <item quantity=\"few\">Se schimbă posesorul pentru %1$,d fișiere</item>\n        <item quantity=\"other\">Se schimbă posesorul pentru %1$,d de fișiere</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"one\">Se pregătește schimbarea grupului pentru %1$,d fișier</item>\n        <item quantity=\"few\">Se pregătește schimbarea grupului pentru %1$,d fișiere</item>\n        <item quantity=\"other\">Se pregătește schimbarea grupului pentru %1$,d de fișiere</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">Se schimbă grupul pentru \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"one\">Se schimbă grupul pentru %1$,d fișier</item>\n        <item quantity=\"few\">Se schimbă grupul pentru %1$,d fișiere</item>\n        <item quantity=\"other\">Se schimbă grupul pentru %1$,d de fișiere</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"one\">Se pregătește schimbarea modului pentru %1$,d fișier</item>\n        <item quantity=\"few\">Se pregătește schimbarea modului pentru %1$,d fișiere</item>\n        <item quantity=\"other\">Se pregătește schimbarea modului pentru %1$,d de fișiere</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">Se schimbă modul pentru \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"one\">Se schimbă modul pentru %1$,d fișier</item>\n        <item quantity=\"few\">Se schimbă modul pentru %1$,d fișiere</item>\n        <item quantity=\"other\">Se schimbă modul pentru %1$,d de fișiere</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Se pregătește schimbarea contextului SELinux pentru %1$,d fișier</item>\n        <item quantity=\"few\">Se pregătește schimbarea contextului SELinux pentru %1$,d fișiere</item>\n        <item quantity=\"other\">Se pregătește schimbarea contextului SELinux pentru %1$,d de fișiere</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">Se schimbă contextul SELinux pentru \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Se schimbă contextul SELinux pentru %1$,d fișier</item>\n        <item quantity=\"few\">Se schimbă contextul SELinux pentru %1$,d files</item>\n        <item quantity=\"other\">Se schimbă contextul SELinux pentru %1$,d files</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Se pregătește restaurarea contextului SELinux pentru %1$,d fișier</item>\n        <item quantity=\"few\">Se pregătește restaurarea contextului SELinux pentru %1$,d fișiere</item>\n        <item quantity=\"other\">Se pregătește restaurarea contextului SELinux pentru %1$,d fișiere</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">Se restaurează contextul SELinux pentru \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Se restaurează contextul SELinux pentru %1$,d fișier</item>\n        <item quantity=\"few\">Se restaurează contextul SELinux pentru %1$,d fișiere</item>\n        <item quantity=\"other\">Se restaurează contextul SELinux pentru %1$,d fișiere</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">Se scrie \\\"%1$s\\\"</string>\n    <string name=\"file_job_create_error_title\">A apărut o eroare în timpul creării fișierului</string>\n    <string name=\"file_job_create_error_message_format\">A apărut o eroare în timpul creării “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">Nu se poate copia un dosar în același dosar</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">Nu se poate extrage un dosar în același dosar</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">Nu se poate muta un dosar în același dosar</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">Dosarul destinație este înăuntrul dosarului sursă.</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">Nu se poate copia un fișier peste sine</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">Nu se poat extrage un fișier peste același fișier</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">Nu se poate muta un fișier peste același fișier</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">Fișierul sursă va fii rescris de către fișierul destinație.</string>\n    <string name=\"file_job_replace_title_format\">Înlocuiești fișierul \\\"%1$s\\\"?</string>\n    <string name=\"file_job_replace_message_format\">Un alt fișier cu un același nume există deja în \\\"%1$s\\\".\\nÎnlocuirea lui va suprascrie conținutul acestuia.</string>\n    <string name=\"file_job_replace_target_name\">Fișier original</string>\n    <string name=\"file_job_replace_source_name\">Înlocuiește cu</string>\n    <string name=\"file_job_merge_title_format\">Îmbini dosarul \\\"%1$s\\\"?</string>\n    <string name=\"file_job_merge_copy_message_format\">Îmbinarea va cere confirmarea înainte de a înlocui orice fișiere din dosar ce sunt în conflict cu fișierele copiate.</string>\n    <string name=\"file_job_merge_extract_message_format\">Îmbinarea va cere confirmarea înainte de a înlocui orice fișier din dosar ce intra în conflict cu fișierele care sunt extrase.</string>\n    <string name=\"file_job_merge_move_message_format\">Îmbinarea va cere confirmarea înainte de a înlocui orice fișier din dosar ce intra în conflict cu fișierele mutate.</string>\n    <string name=\"file_job_merge_target_name\">Dosar original</string>\n    <string name=\"file_job_merge_source_name\">Îmbină cu</string>\n    <string name=\"file_job_conflict_show_name\">Selectează un nume nou pentru destinație</string>\n    <string name=\"file_job_conflict_name_hint\">Nume nou</string>\n    <string name=\"file_job_archive_error_title_format\">A apărut o eroare în timpul compresiei \\\"%1$s\\\"</string>\n    <string name=\"file_job_archive_error_message_format\">A apărut o eroare în timpul compresiei fișierului în \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">A apărut o eroare în timpul copierii \\\"%1$s\\\"</string>\n    <string name=\"file_job_copy_error_message_format\">A apărut o eroare în timpul copierii fișierului în \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">A apărut o eroare în timpul extragerii \\\"%1$s\\\"</string>\n    <string name=\"file_job_extract_error_message_format\">A apărut o eroare în timpul extragerii fișierului în \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">A apărut o eroare în timpul mutării \\\"%1$s\\\"</string>\n    <string name=\"file_job_move_error_message_format\">A apărut o eroare în timpul mutării fișierului în \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">Eroare în timpul ștergerii</string>\n    <string name=\"file_job_delete_error_message_format\">A apărut o eroare în timpul ștergerii “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">A apărut o eroare în timpul redenumirii \\\"%1$s\\\"</string>\n    <string name=\"file_job_rename_error_message_format\">A apărut o eroare în timpul redenumirii fișierului în \\\"%1$s\\\".\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">A apărut o eroare în timpul schimbării posesorului pentru \\\"%1$s\\\"</string>\n    <string name=\"file_job_set_owner_error_message_format\">A apărut o eroare în timpul schimbării posesorului la \\\"%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">A apărut o eroare în timpul schimbării grupului pentru \\\"%1$s\\\"</string>\n    <string name=\"file_job_set_group_error_message_format\">A apărut o eroare în timpul schimbării grupului la \\\"%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">A apărut o eroare în timpul schimbării modului pentru \\\"%1$s\\\"</string>\n    <string name=\"file_job_set_mode_error_message_format\">A apărut o eroare în timpul schimbării modului la \\\"%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">A apărut o eroare în timpul schimbării contextului SELinux pentru \\\"%1$s\\\"</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">A apărut o eroare în timpul schimbării contextului SELinux la \\\"%1$s”.\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">A apărut o eroare în timpul restabilirii contextului SELinux</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">A apărut o eroare în timpul schimbării contextului SELinux la \\\"%1$s”.\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">Eroare în timpul scrierii</string>\n    <string name=\"file_job_write_error_message_format\">A apărut o eroare în timpul scrierii “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">A apărut o eroare în timpul obținerii informațiilor despre \\\"%1$s\\\".</string>\n    <string name=\"file_job_remount_format\">Remontează \\\"%1$s\\\" cu permisiuni de citire și scriere</string>\n    <string name=\"file_job_remount_loading_format\">Se remontează  \\\"%1$s\\\" cu permisiuni de citire și scriere…</string>\n    <string name=\"file_job_remount_success_format\">\\\"%1$s\\\" a fost remontat cu permisiuni de citire și scriere</string>\n    <string name=\"file_job_action_all\">Aplică această setare tuturor fișierelor</string>\n\n    <string name=\"file_open_apk_message\">Acest fișier este un pachet Android. Dorești să-l instalezi sau să vizualizezi conținutul?</string>\n    <string name=\"file_open_as_title\">Deschide ca…</string>\n    <string name=\"file_open_as_title_format\">Deschide \\\"%1$s\\\" ca</string>\n    <string name=\"file_open_as_type_text\">Text</string>\n    <string name=\"file_open_as_type_image\">Imagine</string>\n    <string name=\"file_open_as_type_audio\">Audio</string>\n    <string name=\"file_open_as_type_video\">Video</string>\n    <string name=\"file_open_as_type_directory\">Dosar</string>\n    <string name=\"file_open_as_type_any\">Altele</string>\n    <string name=\"file_install_apk_from_background_title_format\">\\\"%1$s\\\" este gata de instalare</string>\n    <string name=\"file_install_apk_from_background_text\">Apasă pentru a instala</string>\n    <string name=\"file_open_from_background_title_format\">\\\"%1$s\\\" este gata de deschidere</string>\n    <string name=\"file_open_from_background_text\">Apasă pentru a deschide</string>\n    <string name=\"file_name_error_empty\">Numele fișierului nu poate fi gol</string>\n    <string name=\"file_name_error_invalid\">Numele fișierului este greșit</string>\n    <string name=\"file_name_error_already_exists\">Un fișier cu același nume există deja</string>\n    <string name=\"file_delete_message_file_format\">Ștergi \\\"%1$s\\\"?</string>\n    <string name=\"file_delete_message_directory_format\">Ștergi dosarul \\\"%1$s\\\" și conținutul acestuia?</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"one\">Șterge %1$,d fișier?</item>\n        <item quantity=\"few\">Șterge %1$,d fișiere?</item>\n        <item quantity=\"other\">Șterge %1$,d de fișiere?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"one\">Ștergi %1$,d fișiereși conținutul său ?</item>\n        <item quantity=\"few\">Ștergi %1$,d fișiere și conținutul lor ?</item>\n        <item quantity=\"other\">Ștergi %1$,d de fișiere și conținutul lor ?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"one\">Ștergi %1$,d fișier ?</item>\n        <item quantity=\"few\">Ștergi %1$,d fișiere ?</item>\n        <item quantity=\"other\">Ștergi %1$,d de fișiere ?</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">Creează arhiva</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_create_archive_password\">Parolă (opțional)</string>\n    <string name=\"file_add_bookmark_success\">Marcaj adăugat</string>\n    <string name=\"file_create_file_title\">Fișier nou</string>\n    <string name=\"file_create_directory_title\">Dosar nou</string>\n\n    <string name=\"file_list_title\">Fișiere</string>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"one\">%1$,d dosar</item>\n        <item quantity=\"few\">%1$,d dosare</item>\n        <item quantity=\"other\">%1$,d de dosare</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"one\">%1$,d fișier</item>\n        <item quantity=\"few\">%1$,d fișiere</item>\n        <item quantity=\"other\">%1$,d de fișiere</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">,\\u0020</string>\n    <string name=\"file_list_path_error_empty\">Calea nu poate fi liberă</string>\n    <string name=\"file_list_path_error_invalid\">Cale invalidă</string>\n    <string name=\"file_list_action_view_sort\">Vizualizare și sortare</string>\n    <string name=\"file_list_action_view_list\">Listă</string>\n    <string name=\"file_list_action_view_grid\">Tabel</string>\n    <string name=\"file_list_action_sort_by_name\">Nume</string>\n    <string name=\"file_list_action_sort_by_type\">Tip</string>\n    <string name=\"file_list_action_sort_by_size\">Mărime</string>\n    <string name=\"file_list_action_sort_by_last_modified\">Ultima modificare</string>\n    <string name=\"file_list_action_sort_order_ascending\">Crescător</string>\n    <string name=\"file_list_action_sort_directories_first\">Mai întâi dosarele</string>\n    <string name=\"file_list_action_view_sort_path_specific\">Doar pentru acest dosar</string>\n    <string name=\"file_list_action_new_task\">Fereastră nouă</string>\n    <string name=\"file_list_action_navigate_up\">Dute sus</string>\n    <string name=\"file_list_action_navigate_to\">Sari la</string>\n    <string name=\"file_list_action_show_hidden_files\">Arată fișiere ascunse</string>\n    <string name=\"file_list_action_copy_path\">Copiază calea</string>\n    <string name=\"file_list_action_open_in_terminal\">Deschide în terminal</string>\n    <string name=\"file_list_action_add_bookmark\">Adaugă marcaj</string>\n    <string name=\"file_list_action_create_shortcut\">Creează scurtătură</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">Deschide într-o fereastră nouă</string>\n    <string name=\"file_list_select_title_format\">%1$,d</string>\n    <string name=\"file_list_paste_move_title_format\">Se mută %1$,d</string>\n    <string name=\"file_list_paste_copy_title_format\">Se copiază %1$,d</string>\n    <string name=\"file_list_paste_extract_title_format\">Se extrag %1$,d</string>\n    <string name=\"file_list_paste_action_extract_here\">Extrage aici</string>\n    <string name=\"file_list_empty\">Fără fișiere</string>\n    <string name=\"file_list_action_create_file\">Fișier</string>\n    <string name=\"file_list_action_create_directory\">Dosar</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">Deschide cu</string>\n    <string name=\"file_item_action_extract\">Extrage</string>\n    <string name=\"file_item_action_archive\">Comprimă</string>\n    <string name=\"file_item_action_properties\">Proprietăți</string>\n\n    <string name=\"file_properties_title_format\">Proprietăți pentru “%1$s”</string>\n    <string name=\"file_properties_basic\">De bază</string>\n    <string name=\"file_properties_basic_name\">Nume</string>\n    <string name=\"file_properties_basic_type\">Tip</string>\n    <string name=\"file_properties_basic_type_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">Link către %1$s (%2$s)</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">Destinația linkului</string>\n    <string name=\"file_properties_basic_size\">Dimensiune</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_contents\">Conținut</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"one\">%1$,d articol, totalizând %2$s</item>\n        <item quantity=\"few\">%1$,d articole, totalizând 1%2$s</item>\n        <item quantity=\"other\">%1$,d de articole, totalizând %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">Modificat ultima dată</string>\n    <string name=\"file_properties_basic_parent_directory\">Dosar părinte</string>\n    <string name=\"file_properties_basic_archive_file\">Arhivează fișierul</string>\n    <string name=\"file_properties_basic_archive_entry\">Articol din arhivă</string>\n    <string name=\"file_properties_basic_free_space\">Spațiu liber</string>\n    <string name=\"file_properties_image\">Imagine</string>\n    <string name=\"file_properties_media_dimensions\">Dimensiuni</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">Fotografiat pe</string>\n    <string name=\"file_properties_media_coordinates\">Coordonate</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f, %2$.3f</string>\n    <string name=\"file_properties_media_address\">Locație</string>\n    <string name=\"file_properties_image_gps_altitude\">Altitudine</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f m</string>\n    <string name=\"file_properties_image_equipment\">Camera</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">Diafragmă</string>\n    <string name=\"file_properties_image_shutter_speed\">Viteza obturatorului</string>\n    <string name=\"file_properties_image_focal_length\">Distanță focală</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f mm</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">Echivalent ISO</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">Software</string>\n    <string name=\"file_properties_image_description\">Descriere</string>\n    <string name=\"file_properties_image_artist\">Autor</string>\n    <string name=\"file_properties_image_copyright\">Drepturi de autor</string>\n    <string name=\"file_properties_audio\">Audio</string>\n    <string name=\"file_properties_media_title\">Titlu</string>\n    <string name=\"file_properties_audio_artist\">Artist</string>\n    <string name=\"file_properties_audio_album\">Album</string>\n    <string name=\"file_properties_audio_album_artist\">Artistul albumului</string>\n    <string name=\"file_properties_audio_composer\">Compozitor</string>\n    <string name=\"file_properties_audio_disc_number\">Disc</string>\n    <string name=\"file_properties_audio_track_number\">Piesă</string>\n    <string name=\"file_properties_audio_year\">An</string>\n    <string name=\"file_properties_audio_genre\">Gen</string>\n    <string name=\"file_properties_media_duration\">Durată</string>\n    <string name=\"file_properties_media_bit_rate\">Rată de biți</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d kbps</string>\n    <string name=\"file_properties_audio_sample_rate\">Frecvență eșantionare</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Hz</string>\n    <string name=\"file_properties_video\">Video</string>\n    <string name=\"file_properties_apk\">APK</string>\n    <string name=\"file_properties_apk_label\">Nume</string>\n    <string name=\"file_properties_apk_package_name\">Numele pachetului</string>\n    <string name=\"file_properties_apk_version\">Versiune</string>\n    <string name=\"file_properties_apk_version_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_apk_min_sdk_version\">Versiunea minimă a sistemului</string>\n    <string name=\"file_properties_apk_target_sdk_version\">Versiunea sistemului țintă</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s (%2$s, %3$d)</string>\n    <string name=\"file_properties_apk_requested_permissions\">Permisiuni</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">Nu au fost cerute permisiuni</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"one\">%1$,d permisiune cerută</item>\n        <item quantity=\"few\">%1$,d permisiunile cerute</item>\n        <item quantity=\"other\">%1$,d permisiunile cerute</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">Semnături</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">Nu există semnături valide</string>\n    <string name=\"file_properties_apk_past_signature_digests\">Semnături vechi</string>\n    <string name=\"storage_file_system_root_title\">Root</string>\n    <string name=\"storage_list_title\">Stocare</string>\n    <string name=\"storage_list_empty\">Nu există stocare</string>\n    <string name=\"storage_add_storage_title\">Adaugă stocare</string>\n    <string name=\"storage_add_storage_document_tree\">Stocare externă</string>\n    <string name=\"storage_add_storage_ftp_server\">Server FTP</string>\n    <string name=\"storage_add_storage_sftp_server\">Server SFTP</string>\n    <string name=\"storage_add_storage_smb_server\">Server SMB</string>\n    <string name=\"storage_edit_device_storage_title\">Editează stocarea dispozitivului</string>\n    <string name=\"storage_edit_device_storage_name\">Nume</string>\n    <string name=\"storage_edit_device_storage_path\">Cale</string>\n    <string name=\"storage_add_document_tree_title\">Adaugă stocare externă</string>\n    <string name=\"storage_edit_document_tree_title\">Editează stocarea externă</string>\n    <string name=\"storage_edit_document_tree_name\">Nume</string>\n    <string name=\"storage_edit_document_tree_uri\">URI</string>\n    <string name=\"storage_edit_document_tree_path\">Cale</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">Editează serverul FTP</string>\n    <string name=\"storage_edit_ftp_server_title_add\">Adaugă serverul FTP</string>\n    <string name=\"storage_edit_ftp_server_host\">Hostname</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">Editează hostname</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">Hostname invalid</string>\n    <string name=\"storage_edit_ftp_server_port\">Port</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">Port invalid</string>\n    <string name=\"storage_edit_ftp_server_path\">Cale</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">Poate fi lăsat liber</string>\n    <string name=\"storage_edit_ftp_server_name\">Nume</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">Folosește hostname</string>\n    <string name=\"storage_edit_ftp_server_protocol\">Protocol</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">Autentificare</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>Parolă</item>\n        <item>Anonim</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">Nume de utilizator</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">Introdu un nume de utilizator</string>\n    <string name=\"storage_edit_ftp_server_password\">Parolă</string>\n    <string name=\"storage_edit_ftp_server_mode\">Mod</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>Activ</item>\n        <item>Pasiv</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">Encodare</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">Conectează și adaugă</string>\n    <string name=\"storage_edit_ftp_server_add\">Adaugă</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">Editează serverul SFTP</string>\n    <string name=\"storage_edit_sftp_server_title_add\">Adaugă server SFTP</string>\n    <string name=\"storage_edit_sftp_server_host\">Nume de gazdă</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">Introdu un nume de gazdă</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">Hostname invalid</string>\n    <string name=\"storage_edit_sftp_server_port\">Port</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">Port greșit</string>\n    <string name=\"storage_edit_sftp_server_path\">Cale</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">Poate fi lăsat gol</string>\n    <string name=\"storage_edit_sftp_server_name\">Nume</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">Folosește nume de gazdă</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">Autentificare</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>Parolă</item>\n        <item>Cheie publică</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">Nume de utilizator</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">Introdu un nume de utilizator</string>\n    <string name=\"storage_edit_sftp_server_password\">Parolă</string>\n    <string name=\"storage_edit_sftp_server_private_key\">Cheie privată</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">Deschide fișier</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">Introdu o cheie privată</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">Cheie privată greșită</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">Parola cheii private</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">Poate fi lăsat liber</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">Parola cheii private este invalidă</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">Conectează și adaugă</string>\n    <string name=\"storage_edit_sftp_server_add\">Adaugă</string>\n    <string name=\"storage_add_lan_smb_server_loading\">Se caută servere SMB…</string>\n    <string name=\"storage_add_lan_smb_server_add\">Adaugă manual</string>\n    <string name=\"storage_edit_smb_server_title_edit\">Editează serverul SMB</string>\n    <string name=\"storage_edit_smb_server_title_add\">Adaugă server SMB</string>\n    <string name=\"storage_edit_smb_server_host\">Nume de gazdă</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">Introdu un nume de gazdă</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">Hostname invalid</string>\n    <string name=\"storage_edit_smb_server_port\">Port</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">Port greșit</string>\n    <string name=\"storage_edit_smb_server_path\">Cale</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">Poate fi lăsat gol</string>\n    <string name=\"storage_edit_smb_server_name\">Nume</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">Folosește nume de gazdă</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">Autentificare</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>Parolă</item>\n        <item>Vizitator</item>\n        <item>Anonim</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">Nume utilizator</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">Introdu un nume de utilizator</string>\n    <string name=\"storage_edit_smb_server_password\">Parolă</string>\n    <string name=\"storage_edit_smb_server_domain\">Domeniu</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">Conectează și adaugă</string>\n    <string name=\"storage_edit_smb_server_add\">Adaugă</string>\n    <string name=\"navigation_storage_subtitle_format\">%1$s liber din %2$s</string>\n    <string name=\"navigation_add_storage\">Adăugare stocare…</string>\n    <string name=\"navigation_standard_directory_alarms\">Alarme</string>\n    <string name=\"navigation_standard_directory_dcim\">DCIM</string>\n    <string name=\"navigation_standard_directory_documents\">Documente</string>\n    <string name=\"navigation_standard_directory_downloads\">Descărcări</string>\n    <string name=\"navigation_standard_directory_movies\">Filme</string>\n    <string name=\"navigation_standard_directory_music\">Muzică</string>\n    <string name=\"navigation_standard_directory_notifications\">Notificări</string>\n    <string name=\"navigation_standard_directory_pictures\">Imagini</string>\n    <string name=\"navigation_standard_directory_podcasts\">Podcasturi</string>\n    <string name=\"navigation_standard_directory_ringtones\">Tonuri de apel</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">Dosar cu marcaje</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">Nume</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">Cale</string>\n    <string name=\"archive_viewer_title\">Arhivează ce se vede pe ecran</string>\n    <string name=\"image_viewer_title\">Vizualizator de imagini</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"text_editor_title\">Editor de text</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">Salvat</string>\n    <string name=\"text_editor_reload_message\">Ești sigur că dorești să reîncarci? Schimbările nesalvate la acest document vor fi pierdute.</string>\n    <string name=\"text_editor_encoding\">Codificare</string>\n    <string name=\"text_editor_close_message\">Ești sigur că dorești să renunți schimbările nesalvate la cest document?</string>\n\n    <string name=\"ftp_server_title\">Server FTP</string>\n    <string name=\"ftp_server_state_title\">Stare</string>\n    <string name=\"ftp_server_state_summary_starting\">Pornește…</string>\n    <string name=\"ftp_server_state_summary_running\">Funcționează</string>\n    <string name=\"ftp_server_state_summary_stopping\">Se oprește…</string>\n    <string name=\"ftp_server_state_summary_stopped\">Nu este pornit</string>\n    <string name=\"ftp_server_url_title\">URL</string>\n    <string name=\"ftp_server_url_summary_no_local_inet_address\">Adresa IP locală nu este cunoscută</string>\n    <string name=\"ftp_server_url_menu_copy_url\">Copiază URL</string>\n    <string name=\"ftp_server_url_menu_copy_password\">Copiază parola</string>\n    <string name=\"ftp_server_add_tile_title\">Adaugă la Setările Rapide</string>\n    <string name=\"ftp_server_configuration_title\">Configurare</string>\n    <string name=\"ftp_server_anonymous_login_title\">Autentificare anonimă</string>\n    <string name=\"ftp_server_username_title\">Nume utilizator</string>\n    <string name=\"ftp_server_password_title\">Parolă</string>\n    <string name=\"ftp_server_port_title\">Port</string>\n    <string name=\"ftp_server_home_directory_title\">Dosar rădăcină</string>\n    <string name=\"ftp_server_writable_title\">Permite scrierea</string>\n    <string name=\"settings_title\">Setări</string>\n    <string name=\"settings_interface_title\">Interfață</string>\n    <string name=\"settings_locale_title\">Limbă</string>\n    <string name=\"settings_theme_color_title\">Culoarea temei</string>\n    <string name=\"settings_theme_color_summary\">Culoarea ce apare cel mai frecvent în aplicație</string>\n    <string name=\"settings_material_design_3_title\">Material Design 3</string>\n    <string name=\"settings_night_mode_title\">Mod de noapte</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>Urmează sistemul</item>\n        <item>Oprit</item>\n        <item>Pornit</item>\n        <item>În funcție de timp</item>\n        <item>În funcție de economizorul de baterie</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">Mod de noapte negru</string>\n    <string name=\"settings_file_list_animation_title\">Animații liste fișiere</string>\n    <string name=\"settings_file_name_ellipsize_title\">Arată numele lung al fișierului</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>Elipsizează începutul</item>\n        <item>Elipsizează mijlocul</item>\n        <item>Elipsizează finalul</item>\n        <item>Marcaj</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">Comportament</string>\n    <string name=\"settings_default_directory_title\">Dosar implicit</string>\n    <string name=\"settings_standard_directories_title\">Dosare standard</string>\n    <string name=\"settings_standard_directories_summary_empty\">Dosarele standard nu sunt pornite</string>\n    <string name=\"settings_bookmark_directory_screenshots\">Captură de ecran</string>\n    <string name=\"settings_bookmark_directories_title\">Dosare cu marcaje</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">Nu există dosare cu marcaje</string>\n    <string name=\"settings_root_strategy_title\">Mod de acces root</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"one\">Aveți %1$,d operațiune cu fișiere în desfășurare și schimbarea accesului de root acum ar putea rezulta o eroare neaștetată. Ești sigur că dorești să facia ceastă schimbare acum ?</item>\n        <item quantity=\"few\">Aveți %1$,d operațiuni cu fișiere în desfășurare și schimbarea accesului de root acum ar putea rezulta o eroare neaștetată. Ești sigur că dorești să facia ceastă schimbare acum ?</item>\n        <item quantity=\"other\">Ai %1$,d operațiuni cu fișiere în desfășurare și schimbarea accesului la root acum ar putea rezulta o eroare neașteptată. Ești sigur că dorești să faci această schimbare acum?</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>Numai acces normal</item>\n        <item>Automat</item>\n        <item>Numai acces root</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">Codificarea numelui fișierului arhivă</string>\n    <string name=\"settings_open_apk_default_action_title\">Deschide pachetul Android</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>Instalează</item>\n        <item>Vizualizează conținutul</item>\n        <item>Întreabă ce să fac</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">Ar putea face aplicația instabilă pe versiunile vechi de Android</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">Citește fișiere la distanță pentru miniaturi</string>\n\n    <string name=\"about_title\">Despre</string>\n    <string name=\"about_version_title\">Versiune</string>\n    <string name=\"about_github_title\">Vizualizează pe GitHub</string>\n    <string name=\"about_licenses_title\">Licențe</string>\n    <string name=\"about_privacy_policy_title\">Politică de Confidențialitate</string>\n    <string name=\"about_author_title\">Autor</string>\n    <string name=\"about_author_name_title\">Hai Zhang</string>\n    <string name=\"about_author_github_title\">Urmărește pe GitHub</string>\n    <string name=\"about_author_twitter_title\">Urmărește pe Twitter</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ru/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">Файл</string>\n    <string name=\"file_type_name_apk\">Android пакет</string>\n    <string name=\"file_type_name_archive\">%1$s архив</string>\n    <string name=\"file_type_name_audio\">%1$s аудио</string>\n    <string name=\"file_type_name_calendar\">%1$s календарь</string>\n    <string name=\"file_type_name_certificate\">%1$s сертификат</string>\n    <string name=\"file_type_name_code\">%1$s документ</string>\n    <string name=\"file_type_name_contact\">Электронная визитка</string>\n    <string name=\"file_type_name_directory\">Папка</string>\n    <string name=\"file_type_name_document\">%1$s документ</string>\n    <string name=\"file_type_name_ebook\">%1$s электронная книга</string>\n    <string name=\"file_type_name_email\">%1$s e-mail сообщение</string>\n    <string name=\"file_type_name_font\">%1$s шрифт</string>\n    <string name=\"file_type_name_generic\">%1$s файл</string>\n    <string name=\"file_type_name_image\">%1$s изображение</string>\n    <string name=\"file_type_name_pdf\">PDF документ</string>\n    <string name=\"file_type_name_presentation\">%1$s презентация</string>\n    <string name=\"file_type_name_spreadsheet\">%1$s таблица</string>\n    <string name=\"file_type_name_text\">%1$s документ</string>\n    <string name=\"file_type_name_text_plain\">Текстовый документ</string>\n    <string name=\"file_type_name_video\">%1$s видео</string>\n    <string name=\"file_type_name_word\">Документ Word</string>\n    <string name=\"file_type_name_powerpoint\">Презентация PowerPoint</string>\n    <string name=\"file_type_name_excel\">Таблица Excel</string>\n    <string name=\"file_type_name_posix_character_device\">Символьное устройство</string>\n    <string name=\"file_type_name_posix_block_device\">Блочное устройство</string>\n    <string name=\"file_type_name_posix_fifo\">Канал</string>\n    <string name=\"file_type_name_posix_symbolic_link\">Ссылка</string>\n    <string name=\"file_type_name_posix_socket\">Сокет</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">Ссылка (сломанная)</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-ru/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:ignore=\"ImpliedQuantity\">\n\n    <string name=\"app_name\">Material Файлы</string>\n\n    <string name=\"close\">Закрыть</string>\n    <string name=\"copy\">Копировать</string>\n    <string name=\"cut\">Вырезать</string>\n    <string name=\"default_\">По умолчанию</string>\n    <string name=\"delete\">Удалить</string>\n    <string name=\"discard\">Отказаться</string>\n    <string name=\"edit\">Редактировать</string>\n    <string name=\"empty\">Пусто</string>\n    <string name=\"empty_placeholder\">(Пусто)</string>\n    <string name=\"error\">Ошибка</string>\n    <string name=\"hide\">Скрыть</string>\n    <string name=\"install\">Установить</string>\n    <string name=\"keep_editing\">Продолжить редактирование</string>\n    <string name=\"loading\">Загрузка…</string>\n    <string name=\"maybe_later\">Возможно позже</string>\n    <string name=\"merge\">Объединить</string>\n    <string name=\"none\">Нет</string>\n    <string name=\"open_navigation_drawer\">Открыть боковое меню</string>\n    <string name=\"paste\">Вставить</string>\n    <string name=\"refresh\">Обновить</string>\n    <string name=\"reload\">Перезагрузить</string>\n    <string name=\"remove\">Удалить</string>\n    <string name=\"rename\">Переименовать</string>\n    <string name=\"replace\">Заменить</string>\n    <string name=\"reset\">Сбросить</string>\n    <string name=\"retry\">Повторить</string>\n    <string name=\"save\">Сохранить</string>\n    <string name=\"search\">Поиск</string>\n    <string name=\"select\">Выбрать</string>\n    <string name=\"select_all\">Выбрать всё</string>\n    <string name=\"share\">Поделиться</string>\n    <string name=\"show\">Показать</string>\n    <string name=\"skip\">Пропустить</string>\n    <string name=\"stop\">Остановить</string>\n    <string name=\"system_default\">Системный</string>\n    <string name=\"unknown\">Неизвестно</string>\n    <string name=\"view\">Просмотр</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">Приложение не найдено для обработки данного действия</string>\n    <string name=\"open_settings\">Открыть настройки</string>\n    <string name=\"shortcut_created\">Ярлык создан</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"one\">%1$,d байт</item>\n        <item quantity=\"few\">%1$,d байт</item>\n        <item quantity=\"many\">%1$,d байт</item>\n        <item quantity=\"other\">%1$,d байт</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">Приложению требуется доступ для управления всеми файлами. Пожалуйста, разрешите доступ в появившемся окне настроек системы.</string>\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">Приложению требуется разрешение для доступа к файлам. Пожалуйста, нажмите «РАЗРЕШИТЬ» в появившемся системном диалоговом окне.</string>\n    <string name=\"storage_permission_permanently_denied_message\">Приложению требуется разрешение для доступа к файлам. Пожалуйста, предоставьте разрешение «Хранилище» в настройках системы.</string>\n    <!-- The allow button is in sentence case now. -->\n    <string name=\"notification_permission_rationale_message\">Приложению требуется разрешение на отправку уведомлений об операциях с файлами. Пожалуйста, нажмите “Разрешить” в появившемся диалоговом окне.</string>\n    <string name=\"notification_permission_permanently_denied_message\">Приложению требуется разрешение на отправку уведомлений о файловых операциях. Пожалуйста, предоставьте разрешение на “Уведомления” в системных настройках.</string>\n\n    <string name=\"notification_channel_background_activity_start_name\">Действия в фоновом режиме</string>\n    <string name=\"notification_channel_background_activity_start_description\">Разрешить действия, пока приложение находится в фоновом режиме</string>\n    <string name=\"notification_channel_file_job_name\">Операции с файлами</string>\n    <string name=\"notification_channel_file_job_description\">Отображать и управлять операциями с файлами</string>\n    <string name=\"notification_channel_ftp_server_name\">FTP-сервер</string>\n    <string name=\"notification_channel_ftp_server_description\">Отображать и управлять FTP-сервером</string>\n\n    <string name=\"file_action_archive_password_title\">Нужен пароль</string>\n    <string name=\"file_action_archive_password_message_format\">“%1$s” защищённый паролем.</string>\n    <string name=\"file_action_archive_password_error_empty\">Пароль не может быть пустым</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"one\">Подготовка к сжатию %1$,d файла (%2$s)</item>\n        <item quantity=\"few\">Подготовка к сжатию %1$,d файлов (%2$s)</item>\n        <item quantity=\"many\">Подготовка к сжатию %1$,d файлов (%2$s)</item>\n        <item quantity=\"other\">Подготовка к сжатию %1$,d файлов (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">Сжатие “%1$s” в “%2$s”</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"one\">Сжатие %1$,d файла в “%2$s”</item>\n        <item quantity=\"few\">Сжатие %1$,d файлов в “%2$s”</item>\n        <item quantity=\"many\">Сжатие %1$,d файлов в “%2$s”</item>\n        <item quantity=\"other\">Сжатие %1$,d файлов в “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"one\">Подготовка к копированию %1$,d файла (%2$s)</item>\n        <item quantity=\"few\">Подготовка к копированию %1$,d файлов (%2$s)</item>\n        <item quantity=\"many\">Подготовка к копированию %1$,d файлов (%2$s)</item>\n        <item quantity=\"other\">Подготовка к копированию %1$,d файлов (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">Копирование “%1$s” в “%2$s”</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"one\">Копирование %1$,d файла в “%2$s”</item>\n        <item quantity=\"few\">Копирование %1$,d файлов в “%2$s”</item>\n        <item quantity=\"many\">Копирование %1$,d файлов в “%2$s”</item>\n        <item quantity=\"other\">Копирование %1$,d файлов в “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"one\">Подготовка к извлечению %1$,d файла (%2$s)</item>\n        <item quantity=\"few\">Подготовка к извлечению %1$,d файлов (%2$s)</item>\n        <item quantity=\"many\">Подготовка к извлечению %1$,d файлов (%2$s)</item>\n        <item quantity=\"other\">Подготовка к извлечению %1$,d файлов (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">Извлечение “%1$s” в “%2$s”</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"one\">Извлечение %1$,d файла в “%2$s”</item>\n        <item quantity=\"few\">Извлечение %1$,d файлов в “%2$s”</item>\n        <item quantity=\"many\">Извлечение %1$,d файлов в “%2$s”</item>\n        <item quantity=\"other\">Извлечение %1$,d файлов в “%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"one\">Подготовка к перемещению %1$,d файла (%2$s)</item>\n        <item quantity=\"few\">Подготовка к перемещению %1$,d файлов (%2$s)</item>\n        <item quantity=\"many\">Подготовка к перемещению %1$,d файлов (%2$s)</item>\n        <item quantity=\"other\">Подготовка к перемещению %1$,d файлов (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">Перемещение “%1$s” в “%2$s”</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"one\">Перемещение %1$,d файла в “%2$s”</item>\n        <item quantity=\"few\">Перемещение %1$,d файлов в “%2$s”</item>\n        <item quantity=\"many\">Перемещение %1$,d файлов в “%2$s”</item>\n        <item quantity=\"other\">Перемещение %1$,d файлов в “%2$s”</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"one\">Подготовка к удалению %1$,d файла</item>\n        <item quantity=\"few\">Подготовка к удалению %1$,d файлов</item>\n        <item quantity=\"many\">Подготовка к удалению %1$,d файлов</item>\n        <item quantity=\"other\">Подготовка к удалению %1$,d файлов</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">Удаление “%1$s”</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"one\">Удаление %1$,d файла</item>\n        <item quantity=\"few\">Удаление %1$,d файлов</item>\n        <item quantity=\"many\">Удаление %1$,d файлов</item>\n        <item quantity=\"other\">Удаление %1$,d файлов</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"one\">Подготовка к смене владельца для %1$,d файла</item>\n        <item quantity=\"few\">Подготовка к смене владельца для %1$,d файлов</item>\n        <item quantity=\"many\">Подготовка к смене владельца для %1$,d файлов</item>\n        <item quantity=\"other\">Подготовка к смене владельца для %1$,d файлов</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">Смена владельца для “%1$s”</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"one\">Смена владельца для %1$,d файла</item>\n        <item quantity=\"few\">Смена владельца для %1$,d файлов</item>\n        <item quantity=\"many\">Смена владельца для %1$,d файлов</item>\n        <item quantity=\"other\">Смена владельца для %1$,d файлов</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"one\">Подготовка к смене группы для %1$,d файла</item>\n        <item quantity=\"few\">Подготовка к смене группы для %1$,d файлов</item>\n        <item quantity=\"many\">Подготовка к смене группы для %1$,d файлов</item>\n        <item quantity=\"other\">Подготовка к смене группы для %1$,d файлов</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">Смена группы для “%1$s”</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"one\">Смена группы для %1$,d файла</item>\n        <item quantity=\"few\">Смена группы для %1$,d файлов</item>\n        <item quantity=\"many\">Смена группы для %1$,d файлов</item>\n        <item quantity=\"other\">Смена группы для %1$,d файлов</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"one\">Подготовка к изменению прав доступа для %1$,d файла</item>\n        <item quantity=\"few\">Подготовка к изменению прав доступа для %1$,d файлов</item>\n        <item quantity=\"many\">Подготовка к изменению прав доступа для %1$,d файлов</item>\n        <item quantity=\"other\">Подготовка к изменению прав доступа для %1$,d файлов</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">Изменение прав доступа для “%1$s”</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"one\">Изменение прав доступа для %1$,d файла</item>\n        <item quantity=\"few\">Изменение прав доступа для %1$,d файлов</item>\n        <item quantity=\"many\">Изменение прав доступа для %1$,d файлов</item>\n        <item quantity=\"other\">Изменение прав доступа для %1$,d файлов</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Подготовка к изменению контекста SELinux для %1$,d файла</item>\n        <item quantity=\"few\">Подготовка к изменению контекста SELinux для %1$,d файлов</item>\n        <item quantity=\"many\">Подготовка к изменению контекста SELinux для %1$,d файлов</item>\n        <item quantity=\"other\">Подготовка к изменению контекста SELinux для %1$,d файлов</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">Изменение контекста SELinux для “%1$s”</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Изменение контекста SELinux для %1$,d файла</item>\n        <item quantity=\"few\">Изменение контекста SELinux для %1$,d файлов</item>\n        <item quantity=\"many\">Изменение контекста SELinux для %1$,d файлов</item>\n        <item quantity=\"other\">Изменение контекста SELinux для %1$,d файлов</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Подготовка к восстановлению контекста SELinux для %1$,d файла</item>\n        <item quantity=\"few\">Подготовка к восстановлению контекста SELinux для %1$,d файлов</item>\n        <item quantity=\"many\">Подготовка к восстановлению контекста SELinux для %1$,d файлов</item>\n        <item quantity=\"other\">Подготовка к восстановлению контекста SELinux для %1$,d файлов</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">Восстановление контекста SELinux для “%1$s”</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Восстановление контекста SELinux для %1$,d файла</item>\n        <item quantity=\"few\">Восстановление контекста SELinux для %1$,d файлов</item>\n        <item quantity=\"many\">Восстановление контекста SELinux для %1$,d файлов</item>\n        <item quantity=\"other\">Восстановление контекста SELinux для %1$,d файлов</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">Запись “%1$s”</string>\n    <string name=\"file_job_create_error_title\">Ошибка во время создания</string>\n    <string name=\"file_job_create_error_message_format\">Произошла ошибка при создании “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">Невозможно скопировать папку саму в себя</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">Невозможно извлечь папку саму в себя</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">Невозможно переместить папку саму в себя</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">Папка назначения находится внутри исходной папки.</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">Невозможно скопировать файл поверх самого себя</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">Невозможно извлечь файл поверх самого себя</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">Невозможно переместить файл поверх самого себя</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">Исходный файл будет перезаписан файлов назначения.</string>\n    <string name=\"file_job_replace_title_format\">Заменить файл “%1$s”?</string>\n    <string name=\"file_job_replace_message_format\">Другой файл с таким именем уже существует в “%1$s”.\\nЗамена перезапишет его содержимое.</string>\n    <string name=\"file_job_replace_target_name\">Исходный файл</string>\n    <string name=\"file_job_replace_source_name\">Заменить на</string>\n    <string name=\"file_job_merge_title_format\">Объединить папку “%1$s”?</string>\n    <string name=\"file_job_merge_copy_message_format\">Объединение будет запрашивать подтверждение перед заменой любых файлов в папке, конфликтующих с копируемыми файлами.</string>\n    <string name=\"file_job_merge_extract_message_format\">Объединение будет запрашивать подтверждение перед заменой любых файлов в папке, конфликтующих с извлекаемыми файлами.</string>\n    <string name=\"file_job_merge_move_message_format\">Объединение будет запрашивать подтверждение перед заменой любых файлов в папке, конфликтующих с перемещаемыми файлами.</string>\n    <string name=\"file_job_merge_target_name\">Исходная папка</string>\n    <string name=\"file_job_merge_source_name\">Объединить с</string>\n    <string name=\"file_job_conflict_show_name\">Выберите новое имя для файла назначения</string>\n    <string name=\"file_job_conflict_name_hint\">Новое имя</string>\n    <string name=\"file_job_archive_error_title_format\">Ошибка при сжатии “%1$s”</string>\n    <string name=\"file_job_archive_error_message_format\">При сжатии файла в “%1$s” произошла ошибка.\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">Ошибка при копировании “%1$s”</string>\n    <string name=\"file_job_copy_error_message_format\">При копировании файла в “%1$s” произошла ошибка.\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">Ошибка при извлечении “%1$s”</string>\n    <string name=\"file_job_extract_error_message_format\">При извлечении файла в “%1$s” произошла ошибка.\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">Ошибка при перемещении “%1$s”</string>\n    <string name=\"file_job_move_error_message_format\">При перемещении файла в “%1$s” произошла ошибка.\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">Ошибка при удалении</string>\n    <string name=\"file_job_delete_error_message_format\">Произошла ошибка при удалении “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">Ошибка переименования “%1$s”</string>\n    <string name=\"file_job_rename_error_message_format\">Произошла ошибка при переименовании файла в “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">Ошибка при смене владельца для “%1$s”</string>\n    <string name=\"file_job_set_owner_error_message_format\">Произошла ошибка при смене владельца на “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">Ошибка при изменении группы для “%1$s”</string>\n    <string name=\"file_job_set_group_error_message_format\">Произошла ошибка при изменении группы на “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">Ошибка при изменении режима для “%1$s”</string>\n    <string name=\"file_job_set_mode_error_message_format\">Произошла ошибка при изменении режима на “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">Ошибка при изменении контекста SELinux для “%1$s”</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">Произошла ошибка при изменении контекста SELinux на “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">Ошибка при восстановлении контекста SELinux</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">Произошла ошибка при восстановлении контекста SELinux для “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">Ошибка во время записи</string>\n    <string name=\"file_job_write_error_message_format\">Произошла ошибка записи “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">Произошла ошибка при получении информации о “%1$s”.</string>\n    <string name=\"file_job_remount_format\">Перемонтировать “%1$s” в режим чтение-запись</string>\n    <string name=\"file_job_remount_loading_format\">Перемонтирование “%1$s” в режим чтение-запись…</string>\n    <string name=\"file_job_remount_success_format\">“%1$s” был перемонтирован в режим чтение-запись</string>\n    <string name=\"file_job_action_all\">Применить действие ко всем файлам</string>\n\n    <string name=\"file_open_apk_message\">Этот файл представляет собой Android-пакет. Вы хотите установить его или посмотреть его содержимое?</string>\n    <string name=\"file_open_as_title\">Открыть как…</string>\n    <string name=\"file_open_as_title_format\">Открыть “%1$s” как</string>\n    <string name=\"file_open_as_type_text\">Текст</string>\n    <string name=\"file_open_as_type_image\">Изображение</string>\n    <string name=\"file_open_as_type_audio\">Аудио</string>\n    <string name=\"file_open_as_type_video\">Видео</string>\n    <string name=\"file_open_as_type_directory\">Папка</string>\n    <string name=\"file_open_as_type_any\">Другое</string>\n    <string name=\"file_install_apk_from_background_title_format\">“%1$s” готов к установке</string>\n    <string name=\"file_install_apk_from_background_text\">Нажмите, чтобы установить</string>\n    <string name=\"file_open_from_background_title_format\">“%1$s” готов к открытию</string>\n    <string name=\"file_open_from_background_text\">Нажмите, чтобы открыть</string>\n    <string name=\"file_name_error_empty\">Имя файла не может быть пустым</string>\n    <string name=\"file_name_error_invalid\">Неверное имя файла</string>\n    <string name=\"file_name_error_already_exists\">Файл с таким именем уже существует</string>\n    <string name=\"file_replace_message_format\">Заменить “%1$s”?</string>\n    <string name=\"file_delete_message_file_format\">Удалить “%1$s”?</string>\n    <string name=\"file_delete_message_directory_format\">Удалить папку “%1$s” и её содержимое?</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"one\">Удалить %1$,d файл?</item>\n        <item quantity=\"few\">Удалить %1$,d файла?</item>\n        <item quantity=\"many\">Удалить %1$,d файлов?</item>\n        <item quantity=\"other\">Удалить %1$,d файлов?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"one\">Удалить %1$,d папку и её содержимое?</item>\n        <item quantity=\"few\">Удалить %1$,d папки и их содержимое?</item>\n        <item quantity=\"many\">Удалить %1$,d папок и их содержимое?</item>\n        <item quantity=\"other\">Удалить %1$,d папок и их содержимое?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"one\">Удалить %1$,d элемент?</item>\n        <item quantity=\"few\">Удалить %1$,d элемента?</item>\n        <item quantity=\"many\">Удалить %1$,d элементов?</item>\n        <item quantity=\"other\">Удалить %1$,d элементов?</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">Создать архив</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_create_archive_password\">Пароль (необязательно)</string>\n    <string name=\"file_add_bookmark_success\">Закладка добавлена</string>\n    <string name=\"file_create_file_title\">Новый файл</string>\n    <string name=\"file_create_directory_title\">Новая папка</string>\n\n    <string name=\"file_list_title\">Файлы</string>\n    <plurals name=\"file_list_title_open_file\">\n        <item quantity=\"one\">Выбрать файл</item>\n        <item quantity=\"few\">Выбрать файлы</item>\n        <item quantity=\"many\">Выбрать файлы</item>\n        <item quantity=\"other\">Выбрать файлы</item>\n    </plurals>\n    <string name=\"file_list_title_create_file\">Сохранить файл</string>\n    <plurals name=\"file_list_title_open_directory\">\n        <item quantity=\"one\">Выбрать папку</item>\n        <item quantity=\"few\">Выбрать папки</item>\n        <item quantity=\"many\">Выбрать папки</item>\n        <item quantity=\"other\">Выбрать папки</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"one\">%1$,d папка</item>\n        <item quantity=\"few\">%1$,d папки</item>\n        <item quantity=\"many\">%1$,d папок</item>\n        <item quantity=\"other\">%1$,d папки</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"one\">%1$,d файл</item>\n        <item quantity=\"few\">%1$,d файла</item>\n        <item quantity=\"many\">%1$,d файлов</item>\n        <item quantity=\"other\">%1$,d файлов</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">,\\u0020</string>\n    <string name=\"file_list_path_error_empty\">Путь не может быть пустым</string>\n    <string name=\"file_list_path_error_invalid\">Неверный путь</string>\n    <string name=\"file_list_action_view_sort\">Настройки просмотра</string>\n    <string name=\"file_list_action_view_list\">Список</string>\n    <string name=\"file_list_action_view_grid\">Сетка</string>\n    <string name=\"file_list_action_sort_by_name\">Имя</string>\n    <string name=\"file_list_action_sort_by_type\">Тип</string>\n    <string name=\"file_list_action_sort_by_size\">Размер</string>\n    <string name=\"file_list_action_sort_by_last_modified\">Последнее изменение</string>\n    <string name=\"file_list_action_sort_order_ascending\">По возрастанию</string>\n    <string name=\"file_list_action_sort_directories_first\">Папки первые</string>\n    <string name=\"file_list_action_view_sort_path_specific\">Только для этой папки</string>\n    <string name=\"file_list_action_new_task\">Новое окно</string>\n    <string name=\"file_list_action_navigate_up\">Вверх</string>\n    <string name=\"file_list_action_navigate_to\">Перейти к</string>\n    <string name=\"file_list_action_show_hidden_files\">Показывать скрытые файлы</string>\n    <string name=\"file_list_action_copy_path\">Скопировать путь</string>\n    <string name=\"file_list_action_open_in_terminal\">Открыть в терминале</string>\n    <string name=\"file_list_action_add_bookmark\">Добавить закладку</string>\n    <string name=\"file_list_action_create_shortcut\">Создать ярлык</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">Открыть в новом окне</string>\n    <string name=\"file_list_select_title_format\">%1$,d</string>\n    <string name=\"file_list_create_file_name_hint\">Имя файла</string>\n    <string name=\"file_list_open_current_directory_format\">Выбрать “%1$s”</string>\n    <string name=\"file_list_paste_move_title_format\">Перемещение %1$,d</string>\n    <string name=\"file_list_paste_copy_title_format\">Копирование %1$,d</string>\n    <string name=\"file_list_paste_extract_title_format\">Извлечение %1$,d</string>\n    <string name=\"file_list_paste_action_extract_here\">Извлечь здесь</string>\n    <string name=\"file_list_empty\">Нет файлов</string>\n    <string name=\"file_list_action_create_file\">Файл</string>\n    <string name=\"file_list_action_create_directory\">Папка</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">Открыть с помощью</string>\n    <string name=\"file_item_action_extract\">Извлечь</string>\n    <string name=\"file_item_action_archive\">Сжать</string>\n    <string name=\"file_item_action_properties\">Свойства</string>\n\n    <string name=\"file_properties_title_format\">Свойства “%1$s”</string>\n    <string name=\"file_properties_basic\">Основное</string>\n    <string name=\"file_properties_basic_name\">Имя</string>\n    <string name=\"file_properties_basic_type\">Тип</string>\n    <string name=\"file_properties_basic_type_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">Ссылка на %1$s (%2$s)</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">Цель ссылки</string>\n    <string name=\"file_properties_basic_size\">Размер</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_contents\">Содержимое</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"one\">%1$,d элемент, размер %2$s</item>\n        <item quantity=\"few\">%1$,d элемента, суммарный размер %2$s</item>\n        <item quantity=\"many\">%1$,d элементов, суммарный размер %2$s</item>\n        <item quantity=\"other\">%1$,d элементов, суммарный размер %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">Последнее изменение</string>\n    <string name=\"file_properties_basic_parent_directory\">Родительская папка</string>\n    <string name=\"file_properties_basic_archive_file\">Файл архива</string>\n    <string name=\"file_properties_basic_archive_entry\">Элемент архива</string>\n    <string name=\"file_properties_basic_free_space\">Свободное место</string>\n    <string name=\"file_properties_permission\">Разрешения</string>\n    <string name=\"file_properties_permission_owner\">Владелец</string>\n    <string name=\"file_properties_permission_group\">Группа</string>\n    <string name=\"file_properties_permission_principal_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_permission_set_owner_title\">Изменить владельца</string>\n    <string name=\"file_properties_permission_set_group_title\">Изменить группу</string>\n    <string name=\"file_properties_permission_set_principal_filter_hint\">Введите имя или идентификатор</string>\n    <string name=\"file_properties_permission_set_principal_system\">Система</string>\n    <string name=\"file_properties_permission_mode\">Режим</string>\n    <string name=\"file_properties_permission_mode_format\">%1$s (%2$04o)</string>\n    <string name=\"file_properties_permission_set_mode_title\">Изменить режим</string>\n    <string name=\"file_properties_permission_set_mode_others\">Другие</string>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_file\">\n        <item>Чтение</item>\n        <item>Запись</item>\n        <item>Выполнение</item>\n    </string-array>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_directory\">\n        <item>Чтение</item>\n        <item>Запись</item>\n        <item>Поиск</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_special\">Специальный</string>\n    <string-array name=\"file_properties_permission_set_mode_special_mode_bits\">\n        <item>Установить UID</item>\n        <item>Установить GID</item>\n        <item>Sticky-бит</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_uppercase_x\">Не устанавливать “Выполнить” для вложенных файлов</string>\n    <string name=\"file_properties_permission_selinux_context\">Контекст SELinux</string>\n    <string name=\"file_properties_permission_set_selinux_context_title\">Изменить контекст SELinux</string>\n    <string name=\"file_properties_permission_set_selinux_context_restore\">Восстановить</string>\n    <string name=\"file_properties_permission_recursive\">Применить ко вложенным файлам</string>\n    <string name=\"file_properties_image\">Изображение</string>\n    <string name=\"file_properties_media_dimensions\">Размеры</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">Время съёмки</string>\n    <string name=\"file_properties_media_coordinates\">Координаты</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f, %2$.3f</string>\n    <string name=\"file_properties_media_address\">Местоположение</string>\n    <string name=\"file_properties_image_gps_altitude\">Высота</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f м</string>\n    <string name=\"file_properties_image_equipment\">Камера</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">Апертура</string>\n    <string name=\"file_properties_image_shutter_speed\">Скорость затвора</string>\n    <string name=\"file_properties_image_focal_length\">Фокусное расстояние</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f мм</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">Чувствительность ISO</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">Программное обеспечение</string>\n    <string name=\"file_properties_image_description\">Описание</string>\n    <string name=\"file_properties_image_artist\">Автор</string>\n    <string name=\"file_properties_image_copyright\">Авторское право</string>\n    <string name=\"file_properties_audio\">Аудио</string>\n    <string name=\"file_properties_media_title\">Название</string>\n    <string name=\"file_properties_audio_artist\">Исполнитель</string>\n    <string name=\"file_properties_audio_album\">Альбом</string>\n    <string name=\"file_properties_audio_album_artist\">Исполнитель альбома</string>\n    <string name=\"file_properties_audio_composer\">Композитор</string>\n    <string name=\"file_properties_audio_disc_number\">Диск</string>\n    <string name=\"file_properties_audio_track_number\">Трек</string>\n    <string name=\"file_properties_audio_year\">Год</string>\n    <string name=\"file_properties_audio_genre\">Жанр</string>\n    <string name=\"file_properties_media_duration\">Длительность</string>\n    <string name=\"file_properties_media_bit_rate\">Битрейт</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d кбит/с</string>\n    <string name=\"file_properties_audio_sample_rate\">Частота дискретизации</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Гц</string>\n    <string name=\"file_properties_video\">Видео</string>\n    <string name=\"file_properties_apk\">APK</string>\n    <string name=\"file_properties_apk_label\">Имя</string>\n    <string name=\"file_properties_apk_package_name\">Имя пакета</string>\n    <string name=\"file_properties_apk_version\">Версия</string>\n    <string name=\"file_properties_apk_version_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_apk_min_sdk_version\">Минимальная версия системы</string>\n    <string name=\"file_properties_apk_target_sdk_version\">Целевая версия системы</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s (%2$s, %3$d)</string>\n    <string name=\"file_properties_apk_requested_permissions\">Разрешения</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">Разрешения не запрашиваются</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"one\">Запрошено %1$,d разрешение</item>\n        <item quantity=\"few\">Запрошено %1$,d разрешения</item>\n        <item quantity=\"many\">Запрошено %1$,d разрешений</item>\n        <item quantity=\"other\">Запрошено %1$,d разрешений</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">Подписи</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">Нет действительных подписей</string>\n    <string name=\"file_properties_apk_past_signature_digests\">Старые подписи</string>\n    <string name=\"file_properties_checksum\">Контрольные суммы</string>\n    <string name=\"file_properties_checksum_compare\">Сравнить</string>\n    <string name=\"file_properties_checksum_compare_placeholder\">Любая контрольная сумма</string>\n    <string name=\"file_properties_checksum_compare_match_format\">Контрольная сумма %1$s совпадений</string>\n    <string name=\"file_properties_checksum_compare_prefix_match_format\">Совпадение префикса контрольной суммы %1$s</string>\n    <string name=\"file_properties_checksum_compare_no_match\">Соответствующая контрольная сумма не найдена</string>\n    <string name=\"storage_file_system_root_title\">Root</string>\n    <string name=\"storage_list_title\">Накопитель</string>\n    <string name=\"storage_list_empty\">Нет накопителей</string>\n    <string name=\"storage_add_storage_title\">Добавить накопитель</string>\n    <string name=\"storage_add_storage_document_tree\">Внешний накопитель</string>\n    <string name=\"storage_add_storage_ftp_server\">FTP-сервер</string>\n    <string name=\"storage_add_storage_sftp_server\">SFTP сервер</string>\n    <string name=\"storage_add_storage_smb_server\">SMB-сервер</string>\n    <string name=\"storage_add_storage_webdav_server\">WebDAV сервер</string>\n    <string name=\"storage_edit_device_storage_title\">Редактировать накопитель</string>\n    <string name=\"storage_edit_device_storage_name\">Имя</string>\n    <string name=\"storage_edit_device_storage_path\">Путь</string>\n    <string name=\"storage_add_external_storage_shortcut_title\">Добавить ярлык внешнего хранилища</string>\n    <string name=\"storage_edit_external_storage_shortcut_title\">Изменить ярлык внешнего хранилища</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id\">Объем хранилища</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id_error_empty\">Введите объем хранилища</string>\n    <string name=\"storage_edit_external_storage_shortcut_path\">Путь</string>\n    <string name=\"storage_add_document_tree_title\">Добавить внешний накопитель</string>\n    <string name=\"storage_edit_document_tree_title\">Редактировать внешний накопитель</string>\n    <string name=\"storage_edit_document_tree_name\">Имя</string>\n    <string name=\"storage_edit_document_tree_uri\">URI</string>\n    <string name=\"storage_edit_document_tree_path\">Путь</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">Редактировать FTP сервер</string>\n    <string name=\"storage_edit_ftp_server_title_add\">Добавить FTP сервер</string>\n    <string name=\"storage_edit_ftp_server_host\">Имя хоста</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">Введите имя хоста</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">Неверное имя хоста</string>\n    <string name=\"storage_edit_ftp_server_port\">Порт</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">Неверный порт</string>\n    <string name=\"storage_edit_ftp_server_path\">Путь</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">Можно оставить пустым</string>\n    <string name=\"storage_edit_ftp_server_name\">Имя</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">Используйте имя хоста</string>\n    <string name=\"storage_edit_ftp_server_protocol\">Протокол</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">Авторизация</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>Пароль</item>\n        <item>Анонимно</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">Имя пользователя</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">Введите имя пользователя</string>\n    <string name=\"storage_edit_ftp_server_password\">Пароль</string>\n    <string name=\"storage_edit_ftp_server_mode\">Режим</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>Активный</item>\n        <item>Пассивный</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">Кодировка</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">Соединиться и добавить</string>\n    <string name=\"storage_edit_ftp_server_add\">Добавить</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">Редактировать SFTP сервер</string>\n    <string name=\"storage_edit_sftp_server_title_add\">Добавить SFTP сервер</string>\n    <string name=\"storage_edit_sftp_server_host\">Имя хоста</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">Введите имя хоста</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">Неверное имя хоста</string>\n    <string name=\"storage_edit_sftp_server_port\">Порт</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">Неверный порт</string>\n    <string name=\"storage_edit_sftp_server_path\">Путь</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">Можно оставить пустым</string>\n    <string name=\"storage_edit_sftp_server_name\">Имя</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">Используйте имя хоста</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">Авторизация</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>Пароль</item>\n        <item>Публичный ключ</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">Имя пользователя</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">Введите имя пользователя</string>\n    <string name=\"storage_edit_sftp_server_password\">Пароль</string>\n    <string name=\"storage_edit_sftp_server_private_key\">Приватный ключ</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">Открыть файл</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">Введите приватный ключ</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">Неверный приватный ключ</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">Пароль закрытого ключа</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">Можно оставить пустым</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">Неверный пароль закрытого ключа</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">Соединиться и добавить</string>\n    <string name=\"storage_edit_sftp_server_add\">Добавить</string>\n    <string name=\"storage_add_lan_smb_server_loading\">Поиск SMB-серверов…</string>\n    <string name=\"storage_add_lan_smb_server_add\">Добавить вручную</string>\n    <string name=\"storage_edit_smb_server_title_edit\">Редактировать SMB-сервер</string>\n    <string name=\"storage_edit_smb_server_title_add\">Добавить SMB-сервер</string>\n    <string name=\"storage_edit_smb_server_host\">Имя хоста</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">Введите имя хоста</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">Неверное имя хоста</string>\n    <string name=\"storage_edit_smb_server_port\">Порт</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">Неверный порт</string>\n    <string name=\"storage_edit_smb_server_path\">Путь</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">Можно оставить пустым</string>\n    <string name=\"storage_edit_smb_server_name\">Имя</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">Используйте имя хоста</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">Авторизация</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>Пароль</item>\n        <item>Гость</item>\n        <item>Анонимно</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">Имя пользователя</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">Введите имя пользователя</string>\n    <string name=\"storage_edit_smb_server_password\">Пароль</string>\n    <string name=\"storage_edit_smb_server_domain\">Домен</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">Добавить и соединиться</string>\n    <string name=\"storage_edit_smb_server_add\">Добавить</string>\n    <string name=\"storage_edit_webdav_server_title_edit\">Редактировать WebDAV сервер</string>\n    <string name=\"storage_edit_webdav_server_title_add\">Добавить WebDAV сервер</string>\n    <string name=\"storage_edit_webdav_server_host\">Название хоста</string>\n    <string name=\"storage_edit_webdav_server_host_error_empty\">Введите название хоста</string>\n    <string name=\"storage_edit_webdav_server_host_error_invalid\">Недействительное название хоста</string>\n    <string name=\"storage_edit_webdav_server_port\">Порт</string>\n    <string name=\"storage_edit_webdav_server_port_error_invalid\">Недействительный порт</string>\n    <string name=\"storage_edit_webdav_server_path\">Путь</string>\n    <string name=\"storage_edit_webdav_server_path_placeholder\">Может быть пустым</string>\n    <string name=\"storage_edit_webdav_server_name\">Имя</string>\n    <string name=\"storage_edit_webdav_server_name_placeholder\">Использовать название хоста</string>\n    <string name=\"storage_edit_webdav_server_protocol\">Протокол</string>\n    <string name=\"storage_edit_webdav_server_authentication_type\">Аутентификация</string>\n    <string-array name=\"storage_edit_webdav_server_authentication_type_entries\">\n        <item>Пароль</item>\n        <item>Токен доступа</item>\n        <item>Нет</item>\n    </string-array>\n    <string name=\"storage_edit_webdav_server_username\">Имя пользователя</string>\n    <string name=\"storage_edit_webdav_server_username_error_empty\">Введите имя пользователя</string>\n    <string name=\"storage_edit_webdav_server_password\">Пароль</string>\n    <string name=\"storage_edit_webdav_server_access_token\">Токен доступа</string>\n    <string name=\"storage_edit_webdav_server_access_token_error_empty\">Введите токен доступа</string>\n    <string name=\"storage_edit_webdav_server_connect_and_add\">Соединиться и добавить</string>\n    <string name=\"storage_edit_webdav_server_add\">Добавить</string>\n\n    <string name=\"navigation_storage_subtitle_format\">%1$s свободно из %2$s</string>\n    <string name=\"navigation_add_storage\">Добавить накопитель…</string>\n    <string name=\"navigation_standard_directory_alarms\">Будильник</string>\n    <string name=\"navigation_standard_directory_dcim\">DCIM</string>\n    <string name=\"navigation_standard_directory_documents\">Документы</string>\n    <string name=\"navigation_standard_directory_downloads\">Загрузки</string>\n    <string name=\"navigation_standard_directory_movies\">Фильмы</string>\n    <string name=\"navigation_standard_directory_music\">Музыка</string>\n    <string name=\"navigation_standard_directory_notifications\">Уведомления</string>\n    <string name=\"navigation_standard_directory_pictures\">Изображения</string>\n    <string name=\"navigation_standard_directory_podcasts\">Подкасты</string>\n    <string name=\"navigation_standard_directory_ringtones\">Мелодии</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">Закладка папки</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">Имя</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">Путь</string>\n    <string name=\"archive_viewer_title\">Просмотрщик архивов</string>\n    <string name=\"image_viewer_title\">Просмотрщик изображений</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"save_as_title\">Сохранить как</string>\n    <string name=\"save_as_error\">Ошибка при сохранении файла</string>\n    <string name=\"save_as_success_format\">“%1$s” было сохранено в “%2$s”</string>\n    <string name=\"text_editor_title\">Текстовый редактор</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">Сохранено</string>\n    <string name=\"text_editor_reload_message\">Вы уверены что хотите перезагрузить? Несохраненные изменения в  документе будут потеряны.</string>\n    <string name=\"text_editor_encoding\">Кодировка</string>\n    <string name=\"text_editor_close_message\">Вы уверены что хотите отказаться от несохраненных изменений в документе?</string>\n\n    <string name=\"ftp_server_title\">FTP-сервер</string>\n    <string name=\"ftp_server_state_title\">Статус</string>\n    <string name=\"ftp_server_state_summary_starting\">Запуск…</string>\n    <string name=\"ftp_server_state_summary_running\">Запущен</string>\n    <string name=\"ftp_server_state_summary_stopping\">Остановка…</string>\n    <string name=\"ftp_server_state_summary_stopped\">Не запущен</string>\n    <string name=\"ftp_server_url_title\">URL</string>\n    <string name=\"ftp_server_url_summary_no_local_inet_address\">Локальный IP-адрес неизвестен</string>\n    <string name=\"ftp_server_url_menu_copy_url\">Скопировать URL</string>\n    <string name=\"ftp_server_url_menu_copy_password\">Скопировать пароль</string>\n    <string name=\"ftp_server_add_tile_title\">Добавить в Быстрые настройки</string>\n    <string name=\"ftp_server_add_tile_result_added\">“FTP server” был добавлен в Быстрые настройки</string>\n    <string name=\"ftp_server_add_tile_result_already_added\">“FTP server” уже добавлен в Быстрые настройки</string>\n    <string name=\"ftp_server_add_tile_result_error\">Ошибка при добавлении “FTP server” в Быстрые настройки</string>\n    <string name=\"ftp_server_configuration_title\">Конфигурация</string>\n    <string name=\"ftp_server_anonymous_login_title\">Анонимный вход</string>\n    <string name=\"ftp_server_username_title\">Имя пользователя</string>\n    <string name=\"ftp_server_password_title\">Пароль</string>\n    <string name=\"ftp_server_port_title\">Порт</string>\n    <string name=\"ftp_server_home_directory_title\">Корневой каталог</string>\n    <string name=\"ftp_server_writable_title\">Разрешить запись</string>\n    <string name=\"settings_title\">Настройки</string>\n    <string name=\"settings_interface_title\">Интерфейс</string>\n    <string name=\"settings_locale_title\">Язык</string>\n    <string name=\"settings_theme_color_title\">Цвет темы</string>\n    <string name=\"settings_theme_color_summary\">Цвет который чаще всего используется в приложении</string>\n    <string name=\"settings_material_design_3_title\">Material Design 3</string>\n    <string name=\"settings_night_mode_title\">Ночной режим</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>Использовать системную тему</item>\n        <item>Выключить</item>\n        <item>Включить</item>\n        <item>На основе времени</item>\n        <item>На основе энергосберегающего режима</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">Ночная тема</string>\n    <string name=\"settings_file_list_animation_title\">Анимация списка файлов</string>\n    <string name=\"settings_file_name_ellipsize_title\">Отображать длинные имена файлов</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>Многоточие в начале</item>\n        <item>Многоточие в середине</item>\n        <item>Многоточие в конце</item>\n        <item>Бегущая строка</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">Поведение</string>\n    <string name=\"settings_default_directory_title\">Папка по умолчанию</string>\n    <string name=\"settings_standard_directories_title\">Стандартные папки</string>\n    <string name=\"settings_standard_directories_summary_empty\">Стандартные папки не включены</string>\n    <string name=\"settings_bookmark_directory_screenshots\">Скриншоты</string>\n    <string name=\"settings_bookmark_directories_title\">Закладки папок</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">Нет закладок папок</string>\n    <string name=\"settings_root_strategy_title\">Root-доступ</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"one\">У вас запущена %1$,d файловая операция и изменение режима root-доступа может привести к непредвиденной ошибке. Вы уверены, что хотите изменить сейчас?</item>\n        <item quantity=\"few\">У вас запущены %1$,d файловые операции и изменение режима root-доступа может привести к непредвиденной ошибке. Вы уверены, что хотите изменить сейчас?</item>\n        <item quantity=\"many\">У вас запущены %1$,d файловых операций и изменение режима root-доступа может привести к непредвиденной ошибке. Вы уверены, что хотите изменить сейчас?</item>\n        <item quantity=\"other\">У вас запущены %1$,d файловых операций и изменение режима root-доступа может привести к непредвиденной ошибке. Вы уверены, что хотите изменить сейчас?</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>Только обычный доступ</item>\n        <item>Автоматически</item>\n        <item>Только root-доступ</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">Кодировка имени файла архива</string>\n    <string name=\"settings_open_apk_default_action_title\">Открытие Android-пакета</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>Установка</item>\n        <item>Просмотр содержимого</item>\n        <item>Спрашивать что делать</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_title\">Показывать миниатюры для документов PDF</string>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">Приложение может стать нестабильным на старых версиях Android</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">Считывать удалённые (сетевые) файлы для миниатюр</string>\n\n    <string name=\"about_title\">О программе</string>\n    <string name=\"about_version_title\">Версия</string>\n    <string name=\"about_github_title\">Посмотреть на GitHub</string>\n    <string name=\"about_licenses_title\">Лицензии</string>\n    <string name=\"about_privacy_policy_title\">Политика конфиденциальности</string>\n    <string name=\"about_author_title\">Автор</string>\n    <string name=\"about_author_name_title\">Hai Zhang</string>\n    <string name=\"about_author_github_title\">Подписаться на GitHub</string>\n    <string name=\"about_author_twitter_title\">Подписаться в Twitter</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-sw360dp-v13/values_preference.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources xmlns:tools=\"http://schemas.android.com/tools\">\n    <bool name=\"config_materialPreferenceIconSpaceReserved\" tools:ignore=\"MissingDefaultResource,PrivateResource\">false</bool>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-sw600dp/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <dimen name=\"screen_edge_margin\">24dp</dimen>\n    <dimen name=\"screen_edge_margin_minus_4dp\">20dp</dimen>\n    <dimen name=\"screen_edge_margin_minus_8dp\">16dp</dimen>\n    <dimen name=\"screen_edge_margin_minus_12dp\">12dp</dimen>\n    <dimen name=\"screen_edge_margin_minus_12dp_at_least_8dp\">12dp</dimen>\n    <dimen name=\"screen_edge_margin_minus_16dp\">8dp</dimen>\n    <dimen name=\"content_start_margin\">80dp</dimen>\n    <dimen name=\"content_start_margin_minus_12dp\">68dp</dimen>\n    <dimen name=\"content_start_from_screen_edge_margin_minus_36dp_considering_at_least_8dp\">20dp</dimen>\n    <dimen name=\"navigation_header_height\">192dp</dimen>\n    <dimen name=\"navigation_item_horizontal_padding\">32dp</dimen>\n    <dimen name=\"list_bottom_padding_with_fab\">104dp</dimen>\n\n    <dimen name=\"file_list_toolbar_padding_start\">16dp</dimen>\n    <dimen name=\"file_list_toolbar_padding_end_no_overflow\">22dp</dimen>\n    <dimen name=\"file_list_toolbar_padding_end_with_overflow\">14dp</dimen>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-sw600dp/dimens_material3.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2022 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <dimen name=\"navigation_item_horizontal_padding_material3\">32dp</dimen>\n    <dimen name=\"navigation_item_icon_padding_material3\">24dp</dimen>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-sw600dp/integers.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <integer name=\"file_list_toolbar_scroll_flags\">@integer/scrollFlags_noScroll</integer>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-tr/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">Dosya</string>\n    <string name=\"file_type_name_apk\">APK dosyası</string>\n    <string name=\"file_type_name_archive\">%1$s arşivi</string>\n    <string name=\"file_type_name_audio\">%1$s sesi</string>\n    <string name=\"file_type_name_calendar\">%1$s takvimi</string>\n    <string name=\"file_type_name_certificate\">%1$s sertifikası</string>\n    <string name=\"file_type_name_code\">%1$s belgesi</string>\n    <string name=\"file_type_name_contact\">Elektronik kartvizit</string>\n    <string name=\"file_type_name_directory\">Klasör</string>\n    <string name=\"file_type_name_document\">%1$s belgesi</string>\n    <string name=\"file_type_name_ebook\">%1$s e-kitabı</string>\n    <string name=\"file_type_name_email\">%1$s e-posta mesajı</string>\n    <string name=\"file_type_name_font\">%1$s fontu</string>\n    <string name=\"file_type_name_generic\">%1$s dosyası</string>\n    <string name=\"file_type_name_image\">%1$s görüntüsü</string>\n    <string name=\"file_type_name_pdf\">PDF belgesi</string>\n    <string name=\"file_type_name_presentation\">%1$s sunumu</string>\n    <string name=\"file_type_name_spreadsheet\">%1$s tablosu</string>\n    <string name=\"file_type_name_text\">%1$s belgesi</string>\n    <string name=\"file_type_name_text_plain\">Düz metin belgesi</string>\n    <string name=\"file_type_name_video\">%1$s videosu</string>\n    <string name=\"file_type_name_word\">Word belgesi</string>\n    <string name=\"file_type_name_powerpoint\">PowerPoint sunumu</string>\n    <string name=\"file_type_name_excel\">Excel tablosu</string>\n    <string name=\"file_type_name_posix_character_device\">Karakter cihazı</string>\n    <string name=\"file_type_name_posix_block_device\">Blok cihazı</string>\n    <string name=\"file_type_name_posix_fifo\">Pipe</string>\n    <string name=\"file_type_name_posix_symbolic_link\">Bağlantı</string>\n    <string name=\"file_type_name_posix_socket\">Soket</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">Bağlantı (kırık)</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-tr/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <string name=\"app_name\">Materyal Dosyalar</string>\n\n    <string name=\"close\">Kapat</string>\n    <string name=\"copy\">Kopyala</string>\n    <string name=\"cut\">Kes</string>\n    <string name=\"default_\">Varsayılan</string>\n    <string name=\"delete\">Sil</string>\n    <string name=\"discard\">Geri al</string>\n    <string name=\"edit\">Düzenle</string>\n    <string name=\"empty\">Boş</string>\n    <string name=\"empty_placeholder\">(Boş)</string>\n    <string name=\"error\">Hata</string>\n    <string name=\"hide\">Gizle</string>\n    <string name=\"install\">Yükle</string>\n    <string name=\"keep_editing\">Düzenlemeye devam et</string>\n    <string name=\"loading\">Yükleniyor…</string>\n    <string name=\"maybe_later\">Belki sonra</string>\n    <string name=\"merge\">Birleştir</string>\n    <string name=\"none\">Hiçbiri</string>\n    <string name=\"paste\">Yapıştır</string>\n    <string name=\"refresh\">Yenile</string>\n    <string name=\"reload\">Yeniden yükle</string>\n    <string name=\"remove\">Kaldır</string>\n    <string name=\"rename\">Yeniden adlandır</string>\n    <string name=\"replace\">Değiştir</string>\n    <string name=\"reset\">Sıfırla</string>\n    <string name=\"retry\">Yeniden dene</string>\n    <string name=\"save\">Kaydet</string>\n    <string name=\"search\">Ara</string>\n    <string name=\"select\">Seç</string>\n    <string name=\"select_all\">Tümünü seç</string>\n    <string name=\"share\">Paylaş</string>\n    <string name=\"show\">Göster</string>\n    <string name=\"skip\">Atla</string>\n    <string name=\"stop\">Durdur</string>\n    <string name=\"unknown\">Bilinmiyor</string>\n    <string name=\"view\">Görüntüle</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">Bu eylemi gerçekleştirecek uygulama bulunamadı</string>\n    <string name=\"open_settings\">Ayarları aç</string>\n    <string name=\"shortcut_created\">Kısayol oluşturuldu</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"one\">%1$,d bayt</item>\n        <item quantity=\"other\">%1$,d bayt</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">Uygulamanın tüm dosyaları yönetmek için erişime ihtiyacı var. Lütfen açılan sistem ayarından erişime izin verin.</string>\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">Uygulamanın dosyalara erişmek için izne ihtiyacı var. Lütfen açılan sistem iletişim kutusunda “İZİN VER” seçeneğini tıklayın.</string>\n    <string name=\"storage_permission_permanently_denied_message\">Uygulamanın dosyalara erişmek için izne ihtiyacı var. Lütfen sistem ayarlarında “Depolama” iznini verin.</string>\n    <string name=\"notification_channel_background_activity_start_name\">Arka plandaki eylemler</string>\n    <string name=\"notification_channel_background_activity_start_description\">Uygulama arka plandayken işlem yapın</string>\n    <string name=\"notification_channel_file_job_name\">Dosya işlemleri</string>\n    <string name=\"notification_channel_file_job_description\">Dosya işlemlerini görüntüle ve yönet</string>\n    <string name=\"notification_channel_ftp_server_name\">FTP sunucusu</string>\n    <string name=\"notification_channel_ftp_server_description\">FTP sunucusunu görüntüle ve yönet</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"one\">%1$,d dosya sıkıştırılmaya hazırlanıyor (%2$s)</item>\n        <item quantity=\"other\">%1$,d dosya sıkıştırılmaya hazırlanıyor (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">“%1$s” “%2$s” içine sıkıştırılıyor</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"one\">%1$,d dosya “%2$s” içine sıkıştırılıyor</item>\n        <item quantity=\"other\">%1$,d dosya “%2$s” içine sıkıştırılıyor</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"one\">%1$,d dosya kopyalanmaya hazırlanıyor (%2$s)</item>\n        <item quantity=\"other\">%1$,d dosya kopyalanmaya hazırlanıyor (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">“%1$s” “%2$s” klasörüne kopyalanıyor</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"one\">%1$,d dosya “%2$s” klasörüne kopyalanıyor</item>\n        <item quantity=\"other\">%1$,d dosya “%2$s” klasörüne kopyalanıyor</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"one\">%1$,d dosya çıkartılmaya hazırlanıyor (%2$s)</item>\n        <item quantity=\"other\">%1$,d dosya çıkartılmaya hazırlanıyor (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">“%1$s” “%2$s” klasörüne çıkartılıyor</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"one\">%1$,d dosya “%2$s” klasörüne çıkartılıyor</item>\n        <item quantity=\"other\">%1$,d dosya “%2$s” klasörüne çıkartılıyor</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"one\">%1$,d dosya taşınmaya hazırlanıyor (%2$s)</item>\n        <item quantity=\"other\">%1$,d dosya taşınmaya hazırlanıyor (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">“%1$s” “%2$s” klasörüne taşınıyor</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"one\">%1$,d dosya “%2$s” klasörüne taşınıyor</item>\n        <item quantity=\"other\">%1$,d dosya “%2$s” klasörüne taşınıyor</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"one\">%1$,d dosya silinmeye hazırlanıyor</item>\n        <item quantity=\"other\">%1$,d dosya silinmeye hazırlanıyor</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">“%1$s” siliniyor</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"one\">%1$,d dosya siliniyor</item>\n        <item quantity=\"other\">%1$,d dosya siliniyor</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"one\">%1$,d dosyanın sahibi değiştirilmeye hazırlanıyor</item>\n        <item quantity=\"other\">%1$,d dosyanın sahibi değiştirilmeye hazırlanıyor</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">“%1$s” için sahip değiştiriliyor</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"one\">%1$,d dosyanın sahibi değiştiriliyor</item>\n        <item quantity=\"other\">%1$,d dosyanın sahibi değiştiriliyor</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"one\">%1$,d dosya için grup değiştirilmeye hazırlanılıyor</item>\n        <item quantity=\"other\">%1$,d dosya için grup değiştirilmeye hazırlanılıyor</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">“%1$s” için grup değiştiriliyor</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"one\">%1$,d dosya için grup değiştiriliyor</item>\n        <item quantity=\"other\">%1$,d dosya için grup değiştiriliyor</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"one\">%1$,d dosya için mod değiştirilmeye hazırlanılıyor</item>\n        <item quantity=\"other\">%1$,d dosya için mod değiştirilmeye hazırlanılıyor</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">“%1$s” için mod değiştiriliyor</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"one\">%1$,d dosya için mod değiştiriliyor</item>\n        <item quantity=\"other\">%1$,d dosya için mod değiştiriliyor</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">%1$,d dosya için SELinux durumunu değiştirmeye hazırlanıyor</item>\n        <item quantity=\"other\">%1$,d dosya için SELinux durumunu değiştirmeye hazırlanıyor</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">“%1$s” için SELinux bağlamı değiştiririliyor</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">%1$,d dosya için SELinux durumu değiştiriliyor</item>\n        <item quantity=\"other\">%1$,d dosya için SELinux durumu değiştiriliyor</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">%1$,d dosya için SELinux durumu geri yüklemeye hazırlanıyor</item>\n        <item quantity=\"other\">%1$,d dosya için SELinux durumu geri yüklemeye hazırlanıyor</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">“%1$s” için SELinux durumu geri yükleniyor</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">%1$,d dosya için SELinux durumu geri yükleniyor</item>\n        <item quantity=\"other\">%1$,d dosya için SELinux durumu geri yükleniyor</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">“%1$s” yazılıyor</string>\n    <string name=\"file_job_create_error_title\">Oluşturulurken hata oluştu</string>\n    <string name=\"file_job_create_error_message_format\">“%1$s” oluşturulurken bir hata oluştu.\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">Bir klasör kendi içine kopyalanamaz</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">Bir klasör kendi içine çıkartılamaz</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">Bir klasör kendi içine taşınamaz</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">Hedef klasör kaynak klasörün içinde.</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">Bir dosya kendi üzerine kopyalanamaz</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">Bir dosya kendi üzerine çıkartılamaz</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">Bir dosya kendi üzerine taşınamaz</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">Hedef tarafından kaynak dosyanın üzerine yazılır.</string>\n    <string name=\"file_job_replace_title_format\">“%1$s” dosyası değiştirilsin mi?</string>\n    <string name=\"file_job_replace_message_format\">“%1$s” içinde aynı isme sahip başka bir dosya mevcut.\\nİçeriğinin üzerine yazılacaktır.</string>\n    <string name=\"file_job_replace_target_name\">Orijinal dosya</string>\n    <string name=\"file_job_replace_source_name\">Şununla değiştir</string>\n    <string name=\"file_job_merge_title_format\">“%1$s” klasörü birleştirilsin mi?</string>\n    <string name=\"file_job_merge_copy_message_format\">Birleştirme işlemi kopyalanan dosyalarla çakışan klasördeki herhangi bir dosyayı değiştirmeden önce onay isteyecektir.</string>\n    <string name=\"file_job_merge_extract_message_format\">Birleştirme işlemi çıkartılan dosyalarla çakışan klasördeki herhangi bir dosyayı değiştirmeden önce onay isteyecektir.</string>\n    <string name=\"file_job_merge_move_message_format\">Birleştirme işlemi taşınan dosyalarla çakışan klasördeki herhangi bir dosyayı değiştirmeden önce onay isteyecektir.</string>\n    <string name=\"file_job_merge_target_name\">Orijinal klasör</string>\n    <string name=\"file_job_merge_source_name\">Birleştir</string>\n    <string name=\"file_job_conflict_show_name\">Hedef için yeni bir isim seçin</string>\n    <string name=\"file_job_conflict_name_hint\">Yeni isim</string>\n    <string name=\"file_job_archive_error_title_format\">“%1$s” sıkıştırılırken hata oluştu</string>\n    <string name=\"file_job_archive_error_message_format\">Dosya “%1$s” içine sıkıştırılırken bir sorun oluştu.\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">“%1$s” kopyalanırken hata oluştu</string>\n    <string name=\"file_job_copy_error_message_format\">Dosya “%1$s” içine kopyalanırken bir sorun oluştu.\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">“%1$s” çıkartılırken hata oluştu</string>\n    <string name=\"file_job_extract_error_message_format\">Dosya “%1$s” içine çıkartılırken bir sorun oluştu.\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">“%1$s” taşınırken hata oluştu</string>\n    <string name=\"file_job_move_error_message_format\">Dosya “%1$s” içine taşınırken bir sorun oluştu.\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">Silinirken hata oluştu</string>\n    <string name=\"file_job_delete_error_message_format\">“%1$s” silinirken bir hata oluştu.\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">“%1$s” yeniden adlandırılırken hata oluştu</string>\n    <string name=\"file_job_rename_error_message_format\">Dosya “%1$s” olarak yeniden adlandırılırken bir hata oluştu.\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">“%1$s” için sahip değiştirilirken hata oluştu</string>\n    <string name=\"file_job_set_owner_error_message_format\">Sahip “%1$s” olarak değiştirilirken bir hata oluştu.\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">“%1$s” için grup değiştirilirken hata oluştu</string>\n    <string name=\"file_job_set_group_error_message_format\">Grup “%1$s” olarak değiştirilirken bir hata oluştu.\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">“%1$s” için mod değiştirilirken hata oluştu</string>\n    <string name=\"file_job_set_mode_error_message_format\">Mod “%1$s” olarak değiştirilirken bir hata oluştu.\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">“%1$s” için SELinux durumu değiştirilirken hata oluştu</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">SELinux durumu “%1$s” olarak değiştirilirken bir hata oluştu.\\n %2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">SELinux durumu geri yüklenirken hata oluştu</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">“%1$s” için SELinux durumu geri yüklenirken bir hata oluştu.\\n %2$s</string>\n    <string name=\"file_job_write_error_title\">Yazılırken hata oluştu</string>\n    <string name=\"file_job_write_error_message_format\">“%1$s” yazılırken bir hata oluştu.\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">“%1$s” ile ilgili bilgi alınırken hata oluştu.</string>\n    <string name=\"file_job_remount_format\">“%1$s”`i okuma ve yazma modunda tekrar bağla</string>\n    <string name=\"file_job_remount_loading_format\">“%1$s” okuma ve yazma modunda tekrar bağlanıyor…</string>\n    <string name=\"file_job_remount_success_format\">“%1$s” okuma ve yazma modunda tekrar bağlandı</string>\n    <string name=\"file_job_action_all\">Bu eylemi tüm dosyalara uygula</string>\n\n    <string name=\"file_open_apk_message\">Bu dosya bir Android paketidir. Yüklemek mi yoksa içeriğini görmek mi istiyorsunuz?</string>\n    <string name=\"file_open_as_title\">Farklı aç…</string>\n    <string name=\"file_open_as_title_format\">“%1$s” olarak aç</string>\n    <string name=\"file_open_as_type_text\">Metin</string>\n    <string name=\"file_open_as_type_image\">Görüntü</string>\n    <string name=\"file_open_as_type_audio\">Ses</string>\n    <string name=\"file_open_as_type_video\">Video</string>\n    <string name=\"file_open_as_type_directory\">Klasör</string>\n    <string name=\"file_open_as_type_any\">Diğer</string>\n    <string name=\"file_install_apk_from_background_title_format\">“%1$s” yüklenmek için hazır</string>\n    <string name=\"file_install_apk_from_background_text\">Yüklemek için dokunun</string>\n    <string name=\"file_open_from_background_title_format\">“%1$s” açılmak için hazır</string>\n    <string name=\"file_open_from_background_text\">Açmak için dokunun</string>\n    <string name=\"file_name_error_empty\">Dosya adı boş olamaz</string>\n    <string name=\"file_name_error_invalid\">Geçersiz dosya adı</string>\n    <string name=\"file_name_error_already_exists\">Bu isimde bir dosya zaten mevcut</string>\n    <string name=\"file_delete_message_file_format\">“%1$s” silinsin mi?</string>\n    <string name=\"file_delete_message_directory_format\">“%1$s” klasörü ve içeriği silinsin mi?</string>\n    <string name=\"file_create_archive_title\">Arşiv oluştur</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_add_bookmark_success\">Yer işareti eklendi</string>\n    <string name=\"file_create_file_title\">Yeni dosya</string>\n    <string name=\"file_create_directory_title\">Yeni klasör</string>\n\n    <string name=\"file_list_title\">Dosyalar</string>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"one\">%1$,d klasör</item>\n        <item quantity=\"other\">%1$,d klasör</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"one\">%1$,d dosya</item>\n        <item quantity=\"other\">%1$,d dosya</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">,\\u0020</string>\n    <string name=\"file_list_action_sort_by_name\">İsim</string>\n    <string name=\"file_list_action_sort_by_type\">Tür</string>\n    <string name=\"file_list_action_sort_by_size\">Boyut</string>\n    <string name=\"file_list_action_sort_by_last_modified\">Son düzenleme</string>\n    <string name=\"file_list_action_sort_order_ascending\">Artan</string>\n    <string name=\"file_list_action_sort_directories_first\">Önce klasörler</string>\n    <string name=\"file_list_action_new_task\">Yeni pencere</string>\n    <string name=\"file_list_action_navigate_up\">Yukarı git</string>\n    <string name=\"file_list_action_show_hidden_files\">Gizli dosyaları göster</string>\n    <string name=\"file_list_action_copy_path\">Yolu kopyala</string>\n    <string name=\"file_list_action_open_in_terminal\">Terminalde aç</string>\n    <string name=\"file_list_action_add_bookmark\">Yer işareti ekle</string>\n    <string name=\"file_list_action_create_shortcut\">Kısayol oluştur</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">Yeni pencerede aç</string>\n    <string name=\"file_list_paste_move_title_format\">%1$,d taşınıyor</string>\n    <string name=\"file_list_paste_copy_title_format\">%1$,d kopyalanıyor</string>\n    <string name=\"file_list_paste_extract_title_format\">%1$,d çıkartılıyor</string>\n    <string name=\"file_list_paste_action_extract_here\">Buraya çıkart</string>\n    <string name=\"file_list_empty\">Dosya yok</string>\n    <string name=\"file_list_action_create_file\">Dosya</string>\n    <string name=\"file_list_action_create_directory\">Klasör</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">Birlikte aç</string>\n    <string name=\"file_item_action_extract\">Çıkart</string>\n    <string name=\"file_item_action_archive\">Sıkıştır</string>\n    <string name=\"file_item_action_properties\">Özellikler</string>\n\n    <string name=\"file_properties_title_format\">“%1$s” özellikleri</string>\n    <string name=\"file_properties_basic\">Temel</string>\n    <string name=\"file_properties_basic_name\">İsim</string>\n    <string name=\"file_properties_basic_type\">Tür</string>\n    <string name=\"file_properties_basic_type_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">%1$s bağlantısı (%2$s)</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">Bağlantı hedefi</string>\n    <string name=\"file_properties_basic_size\">Boyut</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_contents\">İçerik</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"one\">%1$,d öge, %2$s boyutunda</item>\n        <item quantity=\"other\">%1$,d öge, toplamda %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">Son düzenleme</string>\n    <string name=\"file_properties_basic_parent_directory\">Ana Klasör</string>\n    <string name=\"file_properties_basic_archive_file\">Arşiv dosyası</string>\n    <string name=\"file_properties_basic_archive_entry\">Arşiv Girdisi</string>\n    <string name=\"file_properties_basic_free_space\">Boş alan</string>\n    <string name=\"file_properties_image\">Görüntü</string>\n    <string name=\"file_properties_media_dimensions\">Boyutlar</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">Tarih</string>\n    <string name=\"file_properties_media_coordinates\">Koordinatlar</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f, %2$.3f</string>\n    <string name=\"file_properties_media_address\">Konum</string>\n    <string name=\"file_properties_image_gps_altitude\">Rakım</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f m</string>\n    <string name=\"file_properties_image_equipment\">Kamera</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">Diyafram açıklığı</string>\n    <string name=\"file_properties_image_shutter_speed\">Deklanşör hızı</string>\n    <string name=\"file_properties_image_focal_length\">Odak uzaklığı</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f mm</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">ISO değeri</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">Yazılım</string>\n    <string name=\"file_properties_image_description\">Açıklama</string>\n    <string name=\"file_properties_image_artist\">Yaratıcı</string>\n    <string name=\"file_properties_image_copyright\">Telif hakkı</string>\n    <string name=\"file_properties_audio\">Ses</string>\n    <string name=\"file_properties_media_title\">Başlık</string>\n    <string name=\"file_properties_audio_artist\">Sanatçı</string>\n    <string name=\"file_properties_audio_album\">Albüm</string>\n    <string name=\"file_properties_audio_album_artist\">Albüm sanatçısı</string>\n    <string name=\"file_properties_audio_composer\">Besteci</string>\n    <string name=\"file_properties_audio_disc_number\">Disk</string>\n    <string name=\"file_properties_audio_track_number\">Parça</string>\n    <string name=\"file_properties_audio_year\">Yıl</string>\n    <string name=\"file_properties_audio_genre\">Tarz</string>\n    <string name=\"file_properties_media_duration\">Uzunluk</string>\n    <string name=\"file_properties_media_bit_rate\">Bit hızı</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d kbps</string>\n    <string name=\"file_properties_audio_sample_rate\">Örnekleme hızı</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Hz</string>\n    <string name=\"file_properties_video\">Video</string>\n    <string name=\"file_properties_apk\">APK</string>\n    <string name=\"file_properties_apk_label\">İsim</string>\n    <string name=\"file_properties_apk_package_name\">Paket adı</string>\n    <string name=\"file_properties_apk_version\">Sürüm</string>\n    <string name=\"file_properties_apk_version_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_apk_min_sdk_version\">Minimum sistem sürümü</string>\n    <string name=\"file_properties_apk_target_sdk_version\">Hedef sistem sürümü</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s (%2$s, %3$d)</string>\n    <string name=\"file_properties_apk_requested_permissions\">İzinler</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">Hiçbir izin istenmedi</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"one\">%1$,d izin istendi</item>\n        <item quantity=\"other\">%1$,d izin istendi</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">İmzalar</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">Uygun imza bulunamadı</string>\n    <string name=\"file_properties_apk_past_signature_digests\">Eski imzalar</string>\n    <string name=\"storage_file_system_root_title\">Kök</string>\n    <string name=\"storage_list_title\">Depolama</string>\n    <string name=\"storage_list_empty\">Depolama yok</string>\n    <string name=\"storage_add_storage_title\">Depolama ekle</string>\n    <string name=\"storage_add_storage_document_tree\">Harici depolama</string>\n    <string name=\"storage_add_storage_ftp_server\">FTP sunucu</string>\n    <string name=\"storage_add_storage_sftp_server\">SFTP sunucusu</string>\n    <string name=\"storage_add_storage_smb_server\">SMB sunucusu</string>\n    <string name=\"storage_edit_device_storage_title\">Aygıt depolamasını düzenle</string>\n    <string name=\"storage_edit_device_storage_name\">İsim</string>\n    <string name=\"storage_edit_device_storage_path\">Yol</string>\n    <string name=\"storage_add_document_tree_title\">Harici depolama ekle</string>\n    <string name=\"storage_edit_document_tree_title\">Harici depolamayı düzenle</string>\n    <string name=\"storage_edit_document_tree_name\">İsim</string>\n    <string name=\"storage_edit_document_tree_uri\">URI</string>\n    <string name=\"storage_edit_document_tree_path\">Yol</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">FTP sunucuyu düzenle</string>\n    <string name=\"storage_edit_ftp_server_title_add\">FTP sunucu ekle</string>\n    <string name=\"storage_edit_ftp_server_host\">Cihaz adı</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">Cihaz adı gir</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">Geçersiz cihaz adı</string>\n    <string name=\"storage_edit_ftp_server_port\">Bağlantı noktası</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">Geçersiz bağlantı noktası</string>\n    <string name=\"storage_edit_ftp_server_path\">Yol</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">Boş bırakılabilir</string>\n    <string name=\"storage_edit_ftp_server_name\">İsim</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">Cihaz adını kullan</string>\n    <string name=\"storage_edit_ftp_server_protocol\">Protokol</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">Doğrulama</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>Parola</item>\n        <item>Anonim</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">Kullanıcı adı</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">Bir kullanıcı adı gir</string>\n    <string name=\"storage_edit_ftp_server_password\">Parola</string>\n    <string name=\"storage_edit_ftp_server_mode\">Mod</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>Aktif</item>\n        <item>Pasif</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">Kodlama</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">Bağlan ve ekle</string>\n    <string name=\"storage_edit_ftp_server_add\">Ekle</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">SFTP sunucusunu düzenle</string>\n    <string name=\"storage_edit_sftp_server_title_add\">SFTP sunucusu ekle</string>\n    <string name=\"storage_edit_sftp_server_host\">Hostname</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">Hostname gir</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">Geçersiz cihaz adı</string>\n    <string name=\"storage_edit_sftp_server_port\">Port</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">Geçersiz port</string>\n    <string name=\"storage_edit_sftp_server_path\">Yol</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">Boş bırakılabilir</string>\n    <string name=\"storage_edit_sftp_server_name\">Ad</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">Hostname`i kullan</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">Doğrulama</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>Şifre</item>\n        <item>Genel anahtar</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">Kullanıcı adı</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">Bir kullanıcı adı gir</string>\n    <string name=\"storage_edit_sftp_server_password\">Şifre</string>\n    <string name=\"storage_edit_sftp_server_private_key\">Özel anahtar</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">Dosya aç</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">Bir özel anahtar gir</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">Geçersiz özel anahtar</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">Bağlan ve ekle</string>\n    <string name=\"storage_edit_sftp_server_add\">Ekle</string>\n    <string name=\"storage_add_lan_smb_server_loading\">SMB sunucuları aranıyor…</string>\n    <string name=\"storage_add_lan_smb_server_add\">Elle ekle</string>\n    <string name=\"storage_edit_smb_server_title_edit\">SMB sunucusunu düzenle</string>\n    <string name=\"storage_edit_smb_server_title_add\">SMB sunucusu ekle</string>\n    <string name=\"storage_edit_smb_server_host\">Hostname</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">Hostname gir</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">Geçersiz cihaz adı</string>\n    <string name=\"storage_edit_smb_server_port\">Port</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">Geçersiz port</string>\n    <string name=\"storage_edit_smb_server_path\">Yol</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">Boş bırakılabilir</string>\n    <string name=\"storage_edit_smb_server_name\">İsim</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">Hostname kullan</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">Yetkilendirme</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>Şifre</item>\n        <item>Misafir</item>\n        <item>Anonim</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">Kullanıcı adı</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">Bir kullanıcı adı gir</string>\n    <string name=\"storage_edit_smb_server_password\">Şifre</string>\n    <string name=\"storage_edit_smb_server_domain\">Alan adı</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">Bağlan ve ekle</string>\n    <string name=\"storage_edit_smb_server_add\">Ekle</string>\n    <string name=\"navigation_storage_subtitle_format\">%2$s alandan %1$s boş</string>\n    <string name=\"navigation_add_storage\">Depolama ekle…</string>\n    <string name=\"navigation_standard_directory_alarms\">Alarmlar</string>\n    <string name=\"navigation_standard_directory_dcim\">DCIM</string>\n    <string name=\"navigation_standard_directory_documents\">Belgeler</string>\n    <string name=\"navigation_standard_directory_downloads\">İndirilenler</string>\n    <string name=\"navigation_standard_directory_movies\">Filmler</string>\n    <string name=\"navigation_standard_directory_music\">Müzik</string>\n    <string name=\"navigation_standard_directory_notifications\">Bildirimler</string>\n    <string name=\"navigation_standard_directory_pictures\">Resimler</string>\n    <string name=\"navigation_standard_directory_podcasts\">Podcast\\'ler</string>\n    <string name=\"navigation_standard_directory_ringtones\">Zil sesleri</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">Yer işareti klasörü</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">İsim</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">Yol</string>\n    <string name=\"archive_viewer_title\">Arşiv görüntüleyici</string>\n    <string name=\"image_viewer_title\">Resim görüntüleyici</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"text_editor_title\">Metin düzenleyici</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">Kaydedildi</string>\n    <string name=\"text_editor_reload_message\">Yeniden yüklemek istediğinizden emin misiniz? Bu belgedeki kaydedilmemiş değişiklikler kaybedilecek.</string>\n    <string name=\"text_editor_encoding\">Kodlama</string>\n    <string name=\"text_editor_close_message\">Bu belgedeki kaydedilmemiş değişiklikleri atmak istediğinizden emin misiniz?</string>\n\n    <string name=\"ftp_server_title\">FTP sunucusu</string>\n    <string name=\"ftp_server_state_title\">Durum</string>\n    <string name=\"ftp_server_state_summary_starting\">Başlatılıyor…</string>\n    <string name=\"ftp_server_state_summary_running\">Çalışıyor</string>\n    <string name=\"ftp_server_state_summary_stopping\">Durduruluyor…</string>\n    <string name=\"ftp_server_state_summary_stopped\">Başlatılmadı</string>\n    <string name=\"ftp_server_url_title\">URL</string>\n    <string name=\"ftp_server_url_menu_copy_url\">URL\\'yi kopyala</string>\n    <string name=\"ftp_server_url_menu_copy_password\">Şifreyi kopyala</string>\n    <string name=\"ftp_server_configuration_title\">Yapılandırma</string>\n    <string name=\"ftp_server_anonymous_login_title\">Anonim oturum açma</string>\n    <string name=\"ftp_server_username_title\">Kullanıcı adı</string>\n    <string name=\"ftp_server_password_title\">Şifre</string>\n    <string name=\"ftp_server_port_title\">Port</string>\n    <string name=\"ftp_server_home_directory_title\">Kök klasör</string>\n    <string name=\"ftp_server_writable_title\">Yazmaya izin ver</string>\n    <string name=\"settings_title\">Ayarlar</string>\n    <string name=\"settings_interface_title\">Arayüz</string>\n    <string name=\"settings_theme_color_title\">Tema rengi</string>\n    <string name=\"settings_theme_color_summary\">Uygulamada en sık görünen renk</string>\n    <string name=\"settings_material_design_3_title\">Materyal Tasarım 3</string>\n    <string name=\"settings_night_mode_title\">Gece modu</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>Sistemi takip et</item>\n        <item>Kapalı</item>\n        <item>Açık</item>\n        <item>Zamana göre</item>\n        <item>Pil tasarrufuna göre</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">Siyah gece modu</string>\n    <string name=\"settings_file_list_animation_title\">Dosya listesi animasyonu</string>\n    <string name=\"settings_file_name_ellipsize_title\">Uzun dosya adı görüntüle</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>Baştan kısalt</item>\n        <item>Ortadan kısalt</item>\n        <item>Sondan kısalt</item>\n        <item>Kayan yazı (Hepsi)</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">Davranış</string>\n    <string name=\"settings_default_directory_title\">Varsayılan klasör</string>\n    <string name=\"settings_standard_directories_title\">Standart klasörler</string>\n    <string name=\"settings_standard_directories_summary_empty\">Hiçbir standart klasör etkinleştirilmedi</string>\n    <string name=\"settings_bookmark_directory_screenshots\">Ekran görüntüleri</string>\n    <string name=\"settings_bookmark_directories_title\">Yer işareti klasörleri</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">Yer işareti klasörü yok</string>\n    <string name=\"settings_root_strategy_title\">Kök erişim modu</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"one\">Çalışan %1$,d dosya işleminiz var ve şimdi kök erişim modunu değiştirmek beklenmeyen hatalara neden olabilir. Şimdi değiştirmek istediğinizden emin misiniz?</item>\n        <item quantity=\"other\">Çalışan %1$,d dosya işleminiz var ve şimdi kök erişim modunu değiştirmek beklenmeyen hatalara neden olabilir. Şimdi değiştirmek istediğinizden emin misiniz?</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>Yalnızca normal erişim</item>\n        <item>Otomatik</item>\n        <item>Yalnızca kök erişimi</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">Arşiv dosya adı kodlaması</string>\n    <string name=\"settings_open_apk_default_action_title\">Android paketini aç</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>Yükle</item>\n        <item>İçeriği görüntüle</item>\n        <item>Ne yapılacağını sor</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">Uygulamayı Android\\'in eski versiyonlarında kararsız yapabilir</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">Küçük resim için uzak dosyaları okuyun</string>\n\n    <string name=\"about_title\">Hakkında</string>\n    <string name=\"about_version_title\">Sürüm</string>\n    <string name=\"about_github_title\">GitHub\\'da görüntüle</string>\n    <string name=\"about_licenses_title\">Lisanslar</string>\n    <string name=\"about_privacy_policy_title\">Gizlilik politikası</string>\n    <string name=\"about_author_title\">Yazar</string>\n    <string name=\"about_author_name_title\">Hai Zhang</string>\n    <string name=\"about_author_github_title\">GitHub\\'da takip edin</string>\n    <string name=\"about_author_twitter_title\">Twitter\\'da takip edin</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-uk/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">Файл</string>\n    <string name=\"file_type_name_apk\">Пакунок Android</string>\n    <string name=\"file_type_name_archive\">%1$s архів</string>\n    <string name=\"file_type_name_audio\">%1$s звук</string>\n    <string name=\"file_type_name_calendar\">%1$s календар</string>\n    <string name=\"file_type_name_certificate\">%1$s сертифікат</string>\n    <string name=\"file_type_name_code\">%1$s документ</string>\n    <string name=\"file_type_name_contact\">Електронна візитка</string>\n    <string name=\"file_type_name_directory\">Тека</string>\n    <string name=\"file_type_name_document\">%1$s документ</string>\n    <string name=\"file_type_name_ebook\">%1$s ел. книга</string>\n    <string name=\"file_type_name_email\">%1$s ел. лист</string>\n    <string name=\"file_type_name_font\">%1$s шрифт</string>\n    <string name=\"file_type_name_generic\">%1$s файл</string>\n    <string name=\"file_type_name_image\">%1$s зображення</string>\n    <string name=\"file_type_name_pdf\">Документ PDF</string>\n    <string name=\"file_type_name_presentation\">%1$s презентація</string>\n    <string name=\"file_type_name_spreadsheet\">%1$s таблиця</string>\n    <string name=\"file_type_name_text\">%1$s документ</string>\n    <string name=\"file_type_name_text_plain\">Звичайний текстовий документ</string>\n    <string name=\"file_type_name_video\">%1$s відеозапис</string>\n    <string name=\"file_type_name_word\">Документ Word</string>\n    <string name=\"file_type_name_powerpoint\">Презентація PowerPoint</string>\n    <string name=\"file_type_name_excel\">Таблиця Excel</string>\n    <string name=\"file_type_name_posix_character_device\">Символьний пристрій</string>\n    <string name=\"file_type_name_posix_block_device\">Блоковий пристрій</string>\n    <string name=\"file_type_name_posix_fifo\">Канал</string>\n    <string name=\"file_type_name_posix_symbolic_link\">Посилання</string>\n    <string name=\"file_type_name_posix_socket\">Сокет</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">Посилання (зламане)</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-uk/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:ignore=\"ImpliedQuantity\">\n\n    <string name=\"app_name\">Material Files</string>\n\n    <string name=\"close\">Закрити</string>\n    <string name=\"copy\">Копіювати</string>\n    <string name=\"cut\">Вирізати</string>\n    <string name=\"default_\">За замовченням</string>\n    <string name=\"delete\">Видалити</string>\n    <string name=\"discard\">Відхилити</string>\n    <string name=\"edit\">Змінити</string>\n    <string name=\"empty\">Порожньо</string>\n    <string name=\"empty_placeholder\">(Порожньо)</string>\n    <string name=\"error\">Помилка</string>\n    <string name=\"hide\">Сховати</string>\n    <string name=\"install\">Встановити</string>\n    <string name=\"keep_editing\">Продовжити редагувати</string>\n    <string name=\"loading\">Завантаження…</string>\n    <string name=\"maybe_later\">Може пізніше</string>\n    <string name=\"merge\">Об\\'єднати</string>\n    <string name=\"none\">Жодного</string>\n    <string name=\"open_navigation_drawer\">Відкрити панель навігації</string>\n    <string name=\"paste\">Вставити</string>\n    <string name=\"refresh\">Оновити</string>\n    <string name=\"reload\">Перезавантажити</string>\n    <string name=\"remove\">Вилучити</string>\n    <string name=\"rename\">Перейменувати</string>\n    <string name=\"replace\">Замінити</string>\n    <string name=\"reset\">Скинути</string>\n    <string name=\"retry\">Повторити</string>\n    <string name=\"save\">Зберегти</string>\n    <string name=\"search\">Пошук</string>\n    <string name=\"select\">Вибрати</string>\n    <string name=\"select_all\">Вибрати все</string>\n    <string name=\"share\">Поширити</string>\n    <string name=\"show\">Показати</string>\n    <string name=\"skip\">Пропустити</string>\n    <string name=\"stop\">Зупинити</string>\n    <string name=\"system_default\">Системна за замовчуванням</string>\n    <string name=\"unknown\">Невідомий</string>\n    <string name=\"view\">Вигляд</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">Для обробки цієї дії не знайдено програми</string>\n    <string name=\"open_settings\">Відкрити налаштування</string>\n    <string name=\"shortcut_created\">Скорочення створено</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"one\">%1$,d байт</item>\n        <item quantity=\"few\">%1$,d байти</item>\n        <item quantity=\"many\">%1$,d байтів</item>\n        <item quantity=\"other\">%1$,d байтів</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">Програмі потрібен дозвіл на керування файлами. Будь ласка, надайте дозвіл у майбутніх налаштуваннях системи.</string>\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">Застосунку потрібен дозвіл на керування файлами. Будь ласка, натисніть «Дозволити» у діалозі.</string>\n    <string name=\"storage_permission_permanently_denied_message\">Програма потребує дозволу на доступ до файлів. Будь ласка, надайте доступ до «Cховища» в налаштуваннях системи.</string>\n    <!-- The allow button is in sentence case now. -->\n    <string name=\"notification_permission_rationale_message\">Додатку потрібен дозвіл на відправку сповіщень про операції з файлами. Будь ласка, натисніть \\\"Дозволити\\\" у діалоговому вікні, що з\\'явиться.</string>\n    <string name=\"notification_permission_permanently_denied_message\">Додатку потрібен дозвіл на відправку сповіщень про операції з файлами. Будь ласка, надайте дозвіл \\\"Сповіщення\\\" у налаштуваннях системи.</string>\n\n    <string name=\"notification_channel_background_activity_start_name\">Фонові дії</string>\n    <string name=\"notification_channel_background_activity_start_description\">Робити дії, коли програма у фоновому режимі</string>\n    <string name=\"notification_channel_file_job_name\">Операції з файлами</string>\n    <string name=\"notification_channel_file_job_description\">Відображати та контролювати файлові операції</string>\n    <string name=\"notification_channel_ftp_server_name\">FTP-сервер</string>\n    <string name=\"notification_channel_ftp_server_description\">Відображати та контролювати FTP сервер</string>\n\n    <string name=\"file_action_archive_password_title\">Потрібен пароль</string>\n    <string name=\"file_action_archive_password_message_format\">“%1$s” захищений паролем.</string>\n    <string name=\"file_action_archive_password_error_empty\">Пароль не може бути порожнім</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"one\">Підготовка до стиснення %1$,d файлу (%2$s)</item>\n        <item quantity=\"few\">Підготовка до стиснення %1$,d файлів (%2$s)</item>\n        <item quantity=\"many\">Підготовка до стиснення %1$,d файлів (%2$s)</item>\n        <item quantity=\"other\">Підготовка до стиснення %1$,d файлів (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">Стиснення «%1$s» у «%2$s»</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"one\">Стиснення %1$,d файлу в «%2$s»</item>\n        <item quantity=\"few\">Стиснення %1$,d файлів у «%2$s»</item>\n        <item quantity=\"many\">Стиснення %1$,d файлів у «%2$s»</item>\n        <item quantity=\"other\">Стиснення %1$,d файлів у «%2$s»</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"one\">Підготовка до копіювання %1$,d файлу (%2$s)</item>\n        <item quantity=\"few\">Підготовка до копіювання %1$,d файлів (%2$s)</item>\n        <item quantity=\"many\">Підготовка до копіювання %1$,d файлів (%2$s)</item>\n        <item quantity=\"other\">Підготовка до копіювання %1$,d файлів (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">Копіювання «%1$s» у «%2$s».</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"one\">Копіювання %1$,d файлу в «%2$s»</item>\n        <item quantity=\"few\">Копіювання %1$,d файлів у «%2$s»</item>\n        <item quantity=\"many\">Копіювання %1$,d файлів у «%2$s»</item>\n        <item quantity=\"other\">Копіювання %1$,d файлів у «%2$s»</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"one\">Підготовка до видобування %1$,d файлу (%2$s)</item>\n        <item quantity=\"few\">Підготовка до видобування %1$,d файлів (%2$s)</item>\n        <item quantity=\"many\">Підготовка до видобування %1$,d файлів (%2$s)</item>\n        <item quantity=\"other\">Підготовка до видобування %1$,d файлів (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">Видобування «%1$s» до «%2$s»</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"one\">Видобування %1$,d файлу до «%2$s»</item>\n        <item quantity=\"few\">Видобування %1$,d файлів «%2$s»</item>\n        <item quantity=\"many\">Видобування %1$,d файлів до «%2$s»</item>\n        <item quantity=\"other\">Видобування %1$,d файлів до «%2$s»</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"one\">Підготовка до переміщення %1$,d файлу (%2$s)</item>\n        <item quantity=\"few\">Підготовка до переміщення %1$,d файлів (%2$s)</item>\n        <item quantity=\"many\">Підготовка до переміщення %1$,d файлів (%2$s)</item>\n        <item quantity=\"other\">Підготовка до переміщення %1$,d файлів (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">Переміщення «%1$s» у «%2$s»</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"one\">Переміщення %1$,d файлів у «%2$s»</item>\n        <item quantity=\"few\">Переміщення %1$,d файлів у «%2$s»</item>\n        <item quantity=\"many\">Переміщення %1$,d файлів у «%2$s»</item>\n        <item quantity=\"other\">Переміщення %1$,d файлів у «%2$s»</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"one\">Підготовка до видалення %1$,d файлу</item>\n        <item quantity=\"few\">Підготовка до видалення %1$,d файлів</item>\n        <item quantity=\"many\">Підготовка до видалення %1$,d файлів</item>\n        <item quantity=\"other\">Підготовка до видалення %1$,d файлів</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">Видалення “%1$s”</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"one\">Видалення %1$,d файлу</item>\n        <item quantity=\"few\">Видалення %1$,d файлів</item>\n        <item quantity=\"many\">Видалення %1$,d файлів</item>\n        <item quantity=\"other\">Видалення %1$,d файлів</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"one\">Підготовка до зміни власника для %1$,d файлу</item>\n        <item quantity=\"few\">Підготовка до зміни власника для %1$,d файлів</item>\n        <item quantity=\"many\">Підготовка до зміни власника для %1$,d файлів</item>\n        <item quantity=\"other\">Підготовка до зміни власника для %1$,d файлів</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">Зміна власника на “%1$s”</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"one\">Зміна власника для %1$,d файлу</item>\n        <item quantity=\"few\">Зміна власника для %1$,d файлів</item>\n        <item quantity=\"many\">Зміна власника для %1$,d файлів</item>\n        <item quantity=\"other\">Зміна власника для %1$,d файлів</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"one\">Підготовка до зміни групи для %1$,d файлу</item>\n        <item quantity=\"few\">Підготовка до зміни групи для %1$,d файлів</item>\n        <item quantity=\"many\">Підготовка до зміни групи для %1$,d файлів</item>\n        <item quantity=\"other\">Підготовка до зміни групи для %1$,d файлів</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">Зміна групи на “%1$s”</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"one\">Зміна групи для %1$,d файлу</item>\n        <item quantity=\"few\">Зміна групи для %1$,d файлів</item>\n        <item quantity=\"many\">Зміна групи для %1$,d файлів</item>\n        <item quantity=\"other\">Зміна групи для %1$,d файлів</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"one\">Підготовка до зміни режиму для %1$,d файлу</item>\n        <item quantity=\"few\">Підготовка до зміни режиму для %1$,d файлів</item>\n        <item quantity=\"many\">Підготовка до зміни режиму для %1$,d файлів</item>\n        <item quantity=\"other\">Підготовка до зміни режиму для %1$,d файлів</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">Зміна режиму для “%1$s”</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"one\">Зміна режиму для %1$,d файлу</item>\n        <item quantity=\"few\">Зміна режиму для %1$,d файлів</item>\n        <item quantity=\"many\">Зміна режиму для %1$,d файлів</item>\n        <item quantity=\"other\">Зміна режиму для %1$,d файлів</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Підготовка до зміни контексту SELinux %1$,d файлу</item>\n        <item quantity=\"few\">Підготовка до зміни контексту SELinux %1$,d файлів</item>\n        <item quantity=\"many\">Підготовка до зміни контексту SELinux %1$,d файлів</item>\n        <item quantity=\"other\">Підготовка до зміни контексту SELinux %1$,d файлів</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">Зміна контексту SELinux для “%1$s”</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Зміна контексту SELinux для %1$,d файлу</item>\n        <item quantity=\"few\">Зміна контексту SELinux для %1$,d файлів</item>\n        <item quantity=\"many\">Зміна контексту SELinux для %1$,d файлів</item>\n        <item quantity=\"other\">Зміна контексту SELinux для %1$,d файлів</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"one\">Підготовка до відновлення контексту SELinux для %1$,d файлу</item>\n        <item quantity=\"few\">Підготовка до відновлення контексту SELinux для %1$,d файлів</item>\n        <item quantity=\"many\">Підготовка до відновлення контексту SELinux для %1$,d файлів</item>\n        <item quantity=\"other\">Підготовка до відновлення контексту SELinux для %1$,d файлів</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">Відновлення контексту SELinux для “%1$s”</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"one\">Відновлення контексту SELinux для %1$,d файлу</item>\n        <item quantity=\"few\">Відновлення контексту SELinux для %1$,d файлів</item>\n        <item quantity=\"many\">Відновлення контексту SELinux для %1$,d файлів</item>\n        <item quantity=\"other\">Відновлення контексту SELinux для %1$,d файлів</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">Записується «%1$s»</string>\n    <string name=\"file_job_create_error_title\">Помилка під час створення</string>\n    <string name=\"file_job_create_error_message_format\">Сталася помилка під час створення «%1$s».\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">Неможливо копіювати теку в себе</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">Неможливо видобути теку в себе</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">Неможливо перемістити теку в себе</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">Цільова тека всередині вихідної теки.</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">Неможливо копіювати файл у себе</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">Неможливо видобути файл у себе</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">Неможливо перемістити файл у себе</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">Вихідний файл перезапишеться цільовим.</string>\n    <string name=\"file_job_replace_title_format\">Замінити файл «%1$s»?</string>\n    <string name=\"file_job_replace_message_format\">Файл з такою назвою вже існує у “%1$s”.\\nЗамінивши його, ви перезапишете його вміст.</string>\n    <string name=\"file_job_replace_target_name\">Оригінальний файл</string>\n    <string name=\"file_job_replace_source_name\">Замінити на</string>\n    <string name=\"file_job_merge_title_format\">Об\\'єднати теку “%1$s”?</string>\n    <string name=\"file_job_merge_copy_message_format\">Під час об\\'єднання запитуватиметься підтвердження перед копіюванням файлів, які конфліктують із тими, що вже є у теці.</string>\n    <string name=\"file_job_merge_extract_message_format\">Під час об\\'єднання запитуватиметься підтвердження перед розпакуванням файлів, які конфліктують із тими, що вже є у теці.</string>\n    <string name=\"file_job_merge_move_message_format\">Під час об\\'єднання запитуватиметься підтвердження перед заміною файлів, які конфліктують із тими, що вже є у теці.</string>\n    <string name=\"file_job_merge_target_name\">Оригінальна тека</string>\n    <string name=\"file_job_merge_source_name\">Об\\'єднати з</string>\n    <string name=\"file_job_conflict_show_name\">Виберіть нове ім\\'я для цілі</string>\n    <string name=\"file_job_conflict_name_hint\">Нова назва</string>\n    <string name=\"file_job_archive_error_title_format\">Помилка під час стиснення «%1$s»</string>\n    <string name=\"file_job_archive_error_message_format\">Сталася помилка під час стиснення файлу в «%1$s».\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">Помилка під час копіювання «%1$s»</string>\n    <string name=\"file_job_copy_error_message_format\">Сталася помилка копіювання файлу в «%1$s».\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">Помилка під час видобування «%1$s»</string>\n    <string name=\"file_job_extract_error_message_format\">Сталася помилка під час видобування файлу в «%1$s».\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">Помилка під час переміщення «%1$s»</string>\n    <string name=\"file_job_move_error_message_format\">Сталася помилка переміщення файлу в «%1$s».\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">Помилка під час видалення</string>\n    <string name=\"file_job_delete_error_message_format\">Сталася помилка видалення «%1$s».\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">Помилка під час перейменування «%1$s»</string>\n    <string name=\"file_job_rename_error_message_format\">Сталася помилка перейменування файлу на «%1$s».\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">Помилка під час зміни власника на «%1$s»</string>\n    <string name=\"file_job_set_owner_error_message_format\">Сталася помилка зміни власника на «%1$s».\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">Помилка зміни групи на «%1$s»</string>\n    <string name=\"file_job_set_group_error_message_format\">Сталася помилка зміни групи на «%1$s».\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">Помилка зміни режиму на «%1$s»</string>\n    <string name=\"file_job_set_mode_error_message_format\">Сталася помилка зміни режиму на «%1$s».\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">Помилка під час зміни контексту SELinux для “%1$s”</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">Сталася помилка під час зміни контексту SELinux на “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">Помилка під час відновлення контексту SELinux</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">Сталася помилка під час відновлення контексту SELinux для “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">Помилка під час запису</string>\n    <string name=\"file_job_write_error_message_format\">Сталася помилка під час запису «%1$s».\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">Сталася помилка під час отримання інформації про «%1$s».</string>\n    <string name=\"file_job_remount_format\">Перемонтувати “%1$s” для читання/запису</string>\n    <string name=\"file_job_remount_loading_format\">“%1$s” перемонтовується для читання/запису…</string>\n    <string name=\"file_job_remount_success_format\">“%1$s” перемонтовано для читання/запису</string>\n    <string name=\"file_job_action_all\">Застосувати цю дію для всіх файлів</string>\n\n    <string name=\"file_open_apk_message\">Цей файл є пакетом Android. Бажаєте встановити його чи переглянути його вміст?</string>\n    <string name=\"file_open_as_title\">Відкрити як…</string>\n    <string name=\"file_open_as_title_format\">Відкрити «%1$s» як</string>\n    <string name=\"file_open_as_type_text\">Текст</string>\n    <string name=\"file_open_as_type_image\">Зображення</string>\n    <string name=\"file_open_as_type_audio\">Звук</string>\n    <string name=\"file_open_as_type_video\">Відео</string>\n    <string name=\"file_open_as_type_directory\">Тека</string>\n    <string name=\"file_open_as_type_any\">Інше</string>\n    <string name=\"file_install_apk_from_background_title_format\">“%1$s” готовий до встановлення</string>\n    <string name=\"file_install_apk_from_background_text\">Торкніться, щоб встановити</string>\n    <string name=\"file_open_from_background_title_format\">“%1$s” готовий до відкриття</string>\n    <string name=\"file_open_from_background_text\">Торкніться, щоб відкрити</string>\n    <string name=\"file_name_error_empty\">Назва файлу не може бути порожньою</string>\n    <string name=\"file_name_error_invalid\">Недійсне ім\\'я файлу</string>\n    <string name=\"file_name_error_already_exists\">Файл з такою назвою вже існує</string>\n    <string name=\"file_replace_message_format\">Замінити “%1$s”?</string>\n    <string name=\"file_delete_message_file_format\">Видалити «%1$s»?</string>\n    <string name=\"file_delete_message_directory_format\">Видалити теку «%1$s» і її вміст?</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"one\">Видалити %1$,d файл?</item>\n        <item quantity=\"few\">Видалити %1$,d файли?</item>\n        <item quantity=\"many\">Видалити %1$,d файлів?</item>\n        <item quantity=\"other\">Видалити %1$,d файлів?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"one\">Видалити %1$,d теку і її вміст?</item>\n        <item quantity=\"few\">Видалити %1$,d теки і їхній вміст?</item>\n        <item quantity=\"many\">Видалити %1$,d тек і їхній вміст?</item>\n        <item quantity=\"other\">Видалити %1$,d тек і їхній вміст?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"one\">Видалити %1$,d елемент?</item>\n        <item quantity=\"few\">Видалити %1$,d елементи?</item>\n        <item quantity=\"many\">Видалити %1$,d елементів?</item>\n        <item quantity=\"other\">Видалити %1$,d елементів?</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">Створити архів</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_create_archive_password\">Пароль (необов\\'язково)</string>\n    <string name=\"file_add_bookmark_success\">Закладку додано</string>\n    <string name=\"file_create_file_title\">Новий файл</string>\n    <string name=\"file_create_directory_title\">Нова тека</string>\n\n    <string name=\"file_list_title\">Файли</string>\n    <plurals name=\"file_list_title_open_file\">\n        <item quantity=\"one\">Вибрати файл</item>\n        <item quantity=\"few\">Вибрати файли</item>\n        <item quantity=\"many\">Вибрати файли</item>\n        <item quantity=\"other\">Вибрати файли</item>\n    </plurals>\n    <string name=\"file_list_title_create_file\">Зберегти файл</string>\n    <plurals name=\"file_list_title_open_directory\">\n        <item quantity=\"one\">Вибарти теку</item>\n        <item quantity=\"few\">Вибрати теки</item>\n        <item quantity=\"many\">Вибрати теки</item>\n        <item quantity=\"other\">Вибрати теки</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"one\">%1$,d тека</item>\n        <item quantity=\"few\">%1$,d теки</item>\n        <item quantity=\"many\">%1$,d тек</item>\n        <item quantity=\"other\">%1$,d тек</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"one\">%1$,d файл</item>\n        <item quantity=\"few\">%1$,d файли</item>\n        <item quantity=\"many\">%1$,d файлів</item>\n        <item quantity=\"other\">%1$,d файлів</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">,\\u0020</string>\n    <string name=\"file_list_path_error_empty\">Шлях не може бути порожнім</string>\n    <string name=\"file_list_path_error_invalid\">Недійсний шлях</string>\n    <string name=\"file_list_action_view_sort\">Вигляд та сортування</string>\n    <string name=\"file_list_action_view_list\">Список</string>\n    <string name=\"file_list_action_view_grid\">Сітка</string>\n    <string name=\"file_list_action_sort_by_name\">Назва</string>\n    <string name=\"file_list_action_sort_by_type\">Тип</string>\n    <string name=\"file_list_action_sort_by_size\">Розмір</string>\n    <string name=\"file_list_action_sort_by_last_modified\">Востаннє змінено</string>\n    <string name=\"file_list_action_sort_order_ascending\">Зростання</string>\n    <string name=\"file_list_action_sort_directories_first\">Теки спочатку</string>\n    <string name=\"file_list_action_view_sort_path_specific\">Тільки для цієї теки</string>\n    <string name=\"file_list_action_new_task\">Нове вікно</string>\n    <string name=\"file_list_action_navigate_up\">Перейти вверх</string>\n    <string name=\"file_list_action_navigate_to\">Перейти в</string>\n    <string name=\"file_list_action_show_hidden_files\">Показати приховані файли</string>\n    <string name=\"file_list_action_copy_path\">Копіювати шлях</string>\n    <string name=\"file_list_action_open_in_terminal\">Відкрити в терміналі</string>\n    <string name=\"file_list_action_add_bookmark\">Додати закладку</string>\n    <string name=\"file_list_action_create_shortcut\">Створити скорочення</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">Відкрити в новому вікні</string>\n    <string name=\"file_list_select_title_format\">%1$,d</string>\n    <string name=\"file_list_create_file_name_hint\">Назва файлу</string>\n    <string name=\"file_list_open_current_directory_format\">Вибрати “%1$s”</string>\n    <string name=\"file_list_paste_move_title_format\">Переміщення %1$,d</string>\n    <string name=\"file_list_paste_copy_title_format\">Копіювання %1$,d</string>\n    <string name=\"file_list_paste_extract_title_format\">Видобування %1$,d</string>\n    <string name=\"file_list_paste_action_extract_here\">Видобути тут</string>\n    <string name=\"file_list_empty\">Нема файлів</string>\n    <string name=\"file_list_action_create_file\">Файл</string>\n    <string name=\"file_list_action_create_directory\">Тека</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">Відкрити через</string>\n    <string name=\"file_item_action_extract\">Видобути</string>\n    <string name=\"file_item_action_archive\">Стиснути</string>\n    <string name=\"file_item_action_properties\">Властивості</string>\n\n    <string name=\"file_properties_title_format\">Властивості “%1$s”</string>\n    <string name=\"file_properties_basic\">Основні</string>\n    <string name=\"file_properties_basic_name\">Назва</string>\n    <string name=\"file_properties_basic_type\">Тип</string>\n    <string name=\"file_properties_basic_type_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">Посилається до %1$s(%2$s)</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">Ціль посилання</string>\n    <string name=\"file_properties_basic_size\">Розмір</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_contents\">Вміст</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"one\">%1$,d предмет, з розміром %2$s</item>\n        <item quantity=\"few\">%1$,d предмети, всього %2$s</item>\n        <item quantity=\"many\">%1$,d предметів, всього %2$s</item>\n        <item quantity=\"other\">Предметів: %1$,d, всього %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">Востаннє змінено</string>\n    <string name=\"file_properties_basic_parent_directory\">Батьківська тека</string>\n    <string name=\"file_properties_basic_archive_file\">Файл архіву</string>\n    <string name=\"file_properties_basic_archive_entry\">Точка входу в архіві</string>\n    <string name=\"file_properties_basic_free_space\">Вільний простір</string>\n    <string name=\"file_properties_permission\">Дозволи</string>\n    <string name=\"file_properties_permission_owner\">Власник</string>\n    <string name=\"file_properties_permission_group\">Група</string>\n    <string name=\"file_properties_permission_principal_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_permission_set_owner_title\">Змінити власника</string>\n    <string name=\"file_properties_permission_set_group_title\">Змінити групу</string>\n    <string name=\"file_properties_permission_set_principal_filter_hint\">Введіть ім\\'я або ID</string>\n    <string name=\"file_properties_permission_set_principal_system\">Система</string>\n    <string name=\"file_properties_permission_mode\">Режим</string>\n    <string name=\"file_properties_permission_mode_format\">%1$s (%2$04o)</string>\n    <string name=\"file_properties_permission_set_mode_title\">Змінити режим</string>\n    <string name=\"file_properties_permission_set_mode_others\">Інші</string>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_file\">\n        <item>Читання</item>\n        <item>Запис</item>\n        <item>Виконання</item>\n    </string-array>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_directory\">\n        <item>Читання</item>\n        <item>Запис</item>\n        <item>Пошук</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_special\">Особливий</string>\n    <string-array name=\"file_properties_permission_set_mode_special_mode_bits\">\n        <item>Встановити UID</item>\n        <item>Встановити GID</item>\n        <item>Sticky bit</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_uppercase_x\">Не додавати «Виконання» для вкладених файлів</string>\n    <string name=\"file_properties_permission_selinux_context\">Контекст SELinux</string>\n    <string name=\"file_properties_permission_set_selinux_context_title\">Змінити контекст SELinux</string>\n    <string name=\"file_properties_permission_set_selinux_context_restore\">Відновити</string>\n    <string name=\"file_properties_permission_recursive\">Застосувати до вкладених файлів</string>\n    <string name=\"file_properties_image\">Зображення</string>\n    <string name=\"file_properties_media_dimensions\">Розміри</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">Знято</string>\n    <string name=\"file_properties_media_coordinates\">Координати</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f, %2$.3f</string>\n    <string name=\"file_properties_media_address\">Розташування</string>\n    <string name=\"file_properties_image_gps_altitude\">Висота</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f м</string>\n    <string name=\"file_properties_image_equipment\">Камера</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">Апертура</string>\n    <string name=\"file_properties_image_shutter_speed\">Швидкість затвору</string>\n    <string name=\"file_properties_image_focal_length\">Фокусна відстань</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f мм</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">ISO</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">ПЗ</string>\n    <string name=\"file_properties_image_description\">Опис</string>\n    <string name=\"file_properties_image_artist\">Автор</string>\n    <string name=\"file_properties_image_copyright\">Авторські права</string>\n    <string name=\"file_properties_audio\">Аудіо</string>\n    <string name=\"file_properties_media_title\">Заголовок</string>\n    <string name=\"file_properties_audio_artist\">Виконавець</string>\n    <string name=\"file_properties_audio_album\">Альбом</string>\n    <string name=\"file_properties_audio_album_artist\">Артист</string>\n    <string name=\"file_properties_audio_composer\">Композитор</string>\n    <string name=\"file_properties_audio_disc_number\">Диск</string>\n    <string name=\"file_properties_audio_track_number\">Доріжка</string>\n    <string name=\"file_properties_audio_year\">Рік</string>\n    <string name=\"file_properties_audio_genre\">Жанр</string>\n    <string name=\"file_properties_media_duration\">Тривалість</string>\n    <string name=\"file_properties_media_bit_rate\">Бітрейт</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d кб/с</string>\n    <string name=\"file_properties_audio_sample_rate\">Частота дискретизації</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Гц</string>\n    <string name=\"file_properties_video\">Відео</string>\n    <string name=\"file_properties_apk\">APK</string>\n    <string name=\"file_properties_apk_label\">Назва</string>\n    <string name=\"file_properties_apk_package_name\">Назва пакунка</string>\n    <string name=\"file_properties_apk_version\">Версія</string>\n    <string name=\"file_properties_apk_version_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_apk_min_sdk_version\">Найменша версія системи</string>\n    <string name=\"file_properties_apk_target_sdk_version\">Цільова версія системи</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s (%2$s, %3$d)</string>\n    <string name=\"file_properties_apk_requested_permissions\">Дозволи</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">Дозволів не запитано</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"one\">Запитаний %1$,d дозвіл</item>\n        <item quantity=\"few\">Запитано %1$,d дозволи</item>\n        <item quantity=\"many\">Запитано %1$,d дозволів</item>\n        <item quantity=\"other\">%1$,d дозволів запитано</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">Підписи</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">Немає дійсних підписів</string>\n    <string name=\"file_properties_apk_past_signature_digests\">Старі підписи</string>\n    <string name=\"file_properties_checksum\">Контрольні суми</string>\n    <string name=\"file_properties_checksum_compare\">Порівняти</string>\n    <string name=\"file_properties_checksum_compare_placeholder\">Довільна контрольна сума</string>\n    <string name=\"file_properties_checksum_compare_match_format\">Збігається з контрольною сумою %1$s</string>\n    <string name=\"file_properties_checksum_compare_prefix_match_format\">Збігається з префіксом контрольної суми %1$s</string>\n    <string name=\"file_properties_checksum_compare_no_match\">Відповідної контрольної суми не знайдено</string>\n    <string name=\"storage_file_system_root_title\">Root</string>\n    <string name=\"storage_list_title\">Сховище</string>\n    <string name=\"storage_list_empty\">Нема сховища</string>\n    <string name=\"storage_add_storage_title\">Додати сховище</string>\n    <string name=\"storage_add_storage_document_tree\">Зовнішнє сховище</string>\n    <string name=\"storage_add_storage_ftp_server\">FTP-сервер</string>\n    <string name=\"storage_add_storage_sftp_server\">SFTP-сервер</string>\n    <string name=\"storage_add_storage_smb_server\">SMB-сервер</string>\n    <string name=\"storage_add_storage_webdav_server\">WebDAV сервер</string>\n    <string name=\"storage_edit_device_storage_title\">Змінити сховище пристрою</string>\n    <string name=\"storage_edit_device_storage_name\">Назва</string>\n    <string name=\"storage_edit_device_storage_path\">Шлях</string>\n    <string name=\"storage_add_external_storage_shortcut_title\">Додати ярлик зовнішнього сховища</string>\n    <string name=\"storage_edit_external_storage_shortcut_title\">Редагувати ярлик зовнішнього сховища</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id\">Об\\'єм сховища</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id_error_empty\">Введіть об\\'єм сховища</string>\n    <string name=\"storage_edit_external_storage_shortcut_path\">Шлях</string>\n    <string name=\"storage_add_document_tree_title\">Додати зовнішнє сховище</string>\n    <string name=\"storage_edit_document_tree_title\">Змінити зовнішнє сховище</string>\n    <string name=\"storage_edit_document_tree_name\">Назва</string>\n    <string name=\"storage_edit_document_tree_uri\">URI</string>\n    <string name=\"storage_edit_document_tree_path\">Шлях</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">Змінити FTP-сервер</string>\n    <string name=\"storage_edit_ftp_server_title_add\">Додати FTP-сервер</string>\n    <string name=\"storage_edit_ftp_server_host\">Назва хоста</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">Уведіть назву хоста</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">Недійсна назва хоста</string>\n    <string name=\"storage_edit_ftp_server_port\">Порт</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">Недійсний порт</string>\n    <string name=\"storage_edit_ftp_server_path\">Шлях</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">Може бути порожнім</string>\n    <string name=\"storage_edit_ftp_server_name\">Назва</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">Використовувати назву хоста</string>\n    <string name=\"storage_edit_ftp_server_protocol\">Протокол</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">Автентифікація</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>Пароль</item>\n        <item>Анонім</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">Імʼя користувача</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">Уведіть імʼя користувача</string>\n    <string name=\"storage_edit_ftp_server_password\">Пароль</string>\n    <string name=\"storage_edit_ftp_server_mode\">Режим</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>Активний</item>\n        <item>Пасивний</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">Кодування</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">З\\'єднатися та додати</string>\n    <string name=\"storage_edit_ftp_server_add\">Додати</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">Змінити SFTP-сервер</string>\n    <string name=\"storage_edit_sftp_server_title_add\">Додати SFTP-сервер</string>\n    <string name=\"storage_edit_sftp_server_host\">Назва хоста</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">Уведіть назву хоста</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">Недійсна назва хоста</string>\n    <string name=\"storage_edit_sftp_server_port\">Порт</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">Недійсний порт</string>\n    <string name=\"storage_edit_sftp_server_path\">Шлях</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">Може бути порожнім</string>\n    <string name=\"storage_edit_sftp_server_name\">Назва</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">Використовувати назву хоста</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">Автентифікація</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>Пароль</item>\n        <item>Відкритий ключ</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">Імʼя користувача</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">Уведіть імʼя користувача</string>\n    <string name=\"storage_edit_sftp_server_password\">Пароль</string>\n    <string name=\"storage_edit_sftp_server_private_key\">Закритий ключ</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">Відкрити файл</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">Уведіть закритий ключ</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">Недійсний закритий ключ</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">Пароль закритого ключа</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">Може бути порожнім</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">Недійсний пароль закритого ключа</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">З\\'єднатися та додати</string>\n    <string name=\"storage_edit_sftp_server_add\">Додати</string>\n    <string name=\"storage_add_lan_smb_server_loading\">Пошук SMB-серверів…</string>\n    <string name=\"storage_add_lan_smb_server_add\">Додати вручну</string>\n    <string name=\"storage_edit_smb_server_title_edit\">Змінити SMB-сервер</string>\n    <string name=\"storage_edit_smb_server_title_add\">Додати SMB-сервер</string>\n    <string name=\"storage_edit_smb_server_host\">Назва хоста</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">Уведіть назву хоста</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">Недійсна назва хоста</string>\n    <string name=\"storage_edit_smb_server_port\">Порт</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">Недійсний порт</string>\n    <string name=\"storage_edit_smb_server_path\">Шлях</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">Може бути порожнім</string>\n    <string name=\"storage_edit_smb_server_name\">Назва</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">Використовувати назву хоста</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">Автентифікація</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>Пароль</item>\n        <item>Гість</item>\n        <item>Анонім</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">Імʼя користувача</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">Введіть ім\\'я користувача</string>\n    <string name=\"storage_edit_smb_server_password\">Пароль</string>\n    <string name=\"storage_edit_smb_server_domain\">Домен</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">З\\'єднатися та додати</string>\n    <string name=\"storage_edit_smb_server_add\">Додати</string>\n    <string name=\"storage_edit_webdav_server_title_edit\">Змінити WebDAV сервер</string>\n    <string name=\"storage_edit_webdav_server_title_add\">Додати WebDAV сервер</string>\n    <string name=\"storage_edit_webdav_server_host\">Назва хоста</string>\n    <string name=\"storage_edit_webdav_server_host_error_empty\">Уведіть назву хоста</string>\n    <string name=\"storage_edit_webdav_server_host_error_invalid\">Недійсна назва хоста</string>\n    <string name=\"storage_edit_webdav_server_port\">Порт</string>\n    <string name=\"storage_edit_webdav_server_port_error_invalid\">Недійсний порт</string>\n    <string name=\"storage_edit_webdav_server_path\">Шлях</string>\n    <string name=\"storage_edit_webdav_server_path_placeholder\">Може бути порожнім</string>\n    <string name=\"storage_edit_webdav_server_name\">Назва</string>\n    <string name=\"storage_edit_webdav_server_name_placeholder\">Використовувати назву хоста</string>\n    <string name=\"storage_edit_webdav_server_protocol\">Протокол</string>\n    <string name=\"storage_edit_webdav_server_authentication_type\">Автентифікація</string>\n    <string-array name=\"storage_edit_webdav_server_authentication_type_entries\">\n        <item>Пароль</item>\n        <item>Токен доступу</item>\n        <item>Жодного</item>\n    </string-array>\n    <string name=\"storage_edit_webdav_server_username\">Імʼя користувача</string>\n    <string name=\"storage_edit_webdav_server_username_error_empty\">Введіть ім\\'я користувача</string>\n    <string name=\"storage_edit_webdav_server_password\">Пароль</string>\n    <string name=\"storage_edit_webdav_server_access_token\">Токен доступу</string>\n    <string name=\"storage_edit_webdav_server_access_token_error_empty\">Введіть токен доступу</string>\n    <string name=\"storage_edit_webdav_server_connect_and_add\">З\\'єднатися та додати</string>\n    <string name=\"storage_edit_webdav_server_add\">Додати</string>\n\n    <string name=\"navigation_storage_subtitle_format\">%1$s вільно з %2$s</string>\n    <string name=\"navigation_add_storage\">Додати сховище…</string>\n    <string name=\"navigation_standard_directory_alarms\">Нагадування</string>\n    <string name=\"navigation_standard_directory_dcim\">DCIM</string>\n    <string name=\"navigation_standard_directory_documents\">Документи</string>\n    <string name=\"navigation_standard_directory_downloads\">Завантаження</string>\n    <string name=\"navigation_standard_directory_movies\">Фільми</string>\n    <string name=\"navigation_standard_directory_music\">Музика</string>\n    <string name=\"navigation_standard_directory_notifications\">Сповіщення</string>\n    <string name=\"navigation_standard_directory_pictures\">Фотографії</string>\n    <string name=\"navigation_standard_directory_podcasts\">Подкасти</string>\n    <string name=\"navigation_standard_directory_ringtones\">Рінгтони</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">Тека закладки</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">Назва</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">Шлях</string>\n    <string name=\"archive_viewer_title\">Переглядач архівів</string>\n    <string name=\"image_viewer_title\">Переглядач зображень</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"save_as_title\">Зберегти як</string>\n    <string name=\"save_as_error\">Помилка при збереженні файлу</string>\n    <string name=\"save_as_success_format\">“%1$s” збережено у “%2$s”</string>\n    <string name=\"text_editor_title\">Текстовий редактор</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">Збережено</string>\n    <string name=\"text_editor_reload_message\">Ви впевнені, що хочете перезавантажити? Незбережені зміни в цьому документі буде втрачено.</string>\n    <string name=\"text_editor_encoding\">Кодування</string>\n    <string name=\"text_editor_close_message\">Ви впевнені, що бажаєте скасувати незбережені зміни в цьому документі?</string>\n\n    <string name=\"ftp_server_title\">FTP-сервер</string>\n    <string name=\"ftp_server_state_title\">Стан</string>\n    <string name=\"ftp_server_state_summary_starting\">Запуск…</string>\n    <string name=\"ftp_server_state_summary_running\">Працює</string>\n    <string name=\"ftp_server_state_summary_stopping\">Зупинка…</string>\n    <string name=\"ftp_server_state_summary_stopped\">Не запущено</string>\n    <string name=\"ftp_server_url_title\">URL</string>\n    <string name=\"ftp_server_url_summary_no_local_inet_address\">Локальна IP-адреса невідома</string>\n    <string name=\"ftp_server_url_menu_copy_url\">Копіювати URL</string>\n    <string name=\"ftp_server_url_menu_copy_password\">Копіювати пароль</string>\n    <string name=\"ftp_server_add_tile_title\">Додати до швидких налаштувань</string>\n    <string name=\"ftp_server_add_tile_result_added\">“FTP сервер” доданий до швидких налаштувань</string>\n    <string name=\"ftp_server_add_tile_result_already_added\">“FTP сервер” вже доданий до швидких налаштувань</string>\n    <string name=\"ftp_server_add_tile_result_error\">Помилка під час додавання “FTP серверу” до швидких налаштувань</string>\n    <string name=\"ftp_server_configuration_title\">Конфігурація</string>\n    <string name=\"ftp_server_anonymous_login_title\">Анонімний вхід</string>\n    <string name=\"ftp_server_username_title\">Імʼя користувача</string>\n    <string name=\"ftp_server_password_title\">Пароль</string>\n    <string name=\"ftp_server_port_title\">Порт</string>\n    <string name=\"ftp_server_home_directory_title\">Тека Root</string>\n    <string name=\"ftp_server_writable_title\">Дозволити запис</string>\n    <string name=\"settings_title\">Налаштування</string>\n    <string name=\"settings_interface_title\">Інтерфейс</string>\n    <string name=\"settings_locale_title\">Мова</string>\n    <string name=\"settings_theme_color_title\">Колір теми</string>\n    <string name=\"settings_theme_color_summary\">Колір, який найчастіше з’являється у застосунку</string>\n    <string name=\"settings_material_design_3_title\">Material Design 3</string>\n    <string name=\"settings_night_mode_title\">Нічний режим</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>Дотримуватись системи</item>\n        <item>Вимкнути</item>\n        <item>Увімкнути</item>\n        <item>На основі часу</item>\n        <item>На основі режиму економії батареї</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">Чорний нічний режим</string>\n    <string name=\"settings_file_list_animation_title\">Анімація списку файлів</string>\n    <string name=\"settings_file_name_ellipsize_title\">Відображати довгі назви файлів</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>Еліпсувати початок</item>\n        <item>Еліпсувати середину</item>\n        <item>Еліпсувати кінець</item>\n        <item>\\\"Бігуча строка\\\"</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">Поведінка</string>\n    <string name=\"settings_default_directory_title\">Типова тека</string>\n    <string name=\"settings_standard_directories_title\">Стандартні теки</string>\n    <string name=\"settings_standard_directories_summary_empty\">Нема ввімкнених стандартних тек</string>\n    <string name=\"settings_bookmark_directory_screenshots\">Знімки екрана</string>\n    <string name=\"settings_bookmark_directories_title\">Теки закладок</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">Нема тек закладок</string>\n    <string name=\"settings_root_strategy_title\">Режим доступу root</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"one\">У вас запущена %1$,d файлова операція, і зміна режиму root доступу зараз може призвести до неочікуваної помилки. Ви впевнені, що хочете змінити його зараз?</item>\n        <item quantity=\"few\">У вас запущено %1$,d файлові операції, і зміна режиму root доступу зараз може призвести до неочікуваної помилки. Ви впевнені, що хочете змінити його зараз?</item>\n        <item quantity=\"many\">У вас запущено %1$,d файлових операцій, і зміна режиму root доступу зараз може призвести до неочікуваної помилки. Ви впевнені, що хочете змінити його зараз?</item>\n        <item quantity=\"other\">У вас запущено %1$,d файлових операцій, і зміна режиму root доступу зараз може призвести до неочікуваної помилки. Ви впевнені, що хочете змінити його зараз?</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>Лише звичайний доступ</item>\n        <item>Автоматично</item>\n        <item>Лише доступ root</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">Кодування назви файлу для архіву</string>\n    <string name=\"settings_open_apk_default_action_title\">Відкривати пакунок Android</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>Встановити</item>\n        <item>Переглянути вміст</item>\n        <item>Запитати що робити</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_title\">Показувати прев\\'ю для PDF-документів</string>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">Може зробити застосунок нестабільним на старих версіях Android</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">Зчитувати віддалені файли для ескізів</string>\n\n    <string name=\"about_title\">Про застосунок</string>\n    <string name=\"about_version_title\">Версія</string>\n    <string name=\"about_github_title\">Переглянути на GitHub</string>\n    <string name=\"about_licenses_title\">Ліцензії</string>\n    <string name=\"about_privacy_policy_title\">Політика приватності</string>\n    <string name=\"about_author_title\">Автор</string>\n    <string name=\"about_author_name_title\">Hai Zhang</string>\n    <string name=\"about_author_github_title\">Стежити на GitHub</string>\n    <string name=\"about_author_twitter_title\">Стежити у Twitter</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-v23/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <style name=\"Platform.V23.Theme.MaterialFiles\" parent=\"Platform.V21.Theme.MaterialFiles\">\n        <item name=\"colorPrimaryDark\">@color/system_window_scrim</item>\n        <item name=\"android:windowLightStatusBar\">?isLightTheme</item>\n    </style>\n    <style name=\"Platform.Theme.MaterialFiles\" parent=\"Platform.V23.Theme.MaterialFiles\" />\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-v23/themes_material3.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <style name=\"Platform.V23.Theme.MaterialFiles.Material3\" parent=\"Platform.V21.Theme.MaterialFiles.Material3\">\n        <item name=\"colorPrimaryDark\">@color/system_window_scrim_material3</item>\n        <item name=\"android:windowLightStatusBar\">?isLightTheme</item>\n    </style>\n    <style name=\"Platform.Theme.MaterialFiles.Material3\" parent=\"Platform.V23.Theme.MaterialFiles.Material3\" />\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-v24/integers.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <!-- SevenZOutputFile requires SeekableByteChannel which requires Android N. -->\n    <integer name=\"create_archive_type_seven_z_visibility\">@integer/visibility_visible</integer>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-v27/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <style name=\"Platform.V27.Theme.MaterialFiles\" parent=\"Platform.V23.Theme.MaterialFiles\">\n        <item name=\"android:navigationBarColor\">?colorPrimaryDark</item>\n        <item name=\"android:windowLayoutInDisplayCutoutMode\">shortEdges</item>\n        <item name=\"android:windowLightNavigationBar\">?isLightTheme</item>\n    </style>\n    <style name=\"Platform.Theme.MaterialFiles\" parent=\"Platform.V27.Theme.MaterialFiles\" />\n\n    <style name=\"Platform.V27.Theme.MaterialFiles.Immersive\" parent=\"Platform.V21.Theme.MaterialFiles.Immersive\">\n        <item name=\"android:windowLayoutInDisplayCutoutMode\">shortEdges</item>\n    </style>\n    <style name=\"Platform.Theme.MaterialFiles.Immersive\" parent=\"Platform.V27.Theme.MaterialFiles.Immersive\" />\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-v27/themes_material3.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <style name=\"Platform.V27.Theme.MaterialFiles.Material3\" parent=\"Platform.V23.Theme.MaterialFiles.Material3\">\n        <item name=\"android:navigationBarColor\">?colorPrimaryDark</item>\n        <item name=\"android:windowLayoutInDisplayCutoutMode\">shortEdges</item>\n        <item name=\"android:windowLightNavigationBar\">?isLightTheme</item>\n    </style>\n    <style name=\"Platform.Theme.MaterialFiles.Material3\" parent=\"Platform.V27.Theme.MaterialFiles.Material3\" />\n\n    <style name=\"Platform.V27.Theme.MaterialFiles.Material3.Immersive\" parent=\"Platform.V21.Theme.MaterialFiles.Material3.Immersive\">\n        <item name=\"android:windowLayoutInDisplayCutoutMode\">shortEdges</item>\n    </style>\n    <style name=\"Platform.Theme.MaterialFiles.Material3.Immersive\" parent=\"Platform.V27.Theme.MaterialFiles.Material3.Immersive\" />\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-v28/donottranslate_prefs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <bool name=\"pref_visible_show_pdf_thumbnail_pre_28\">false</bool>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-v29/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <style name=\"Platform.V29.Theme.MaterialFiles\" parent=\"Platform.V27.Theme.MaterialFiles\">\n        <item name=\"android:forceDarkAllowed\">false</item>\n\n        <item name=\"colorPrimaryDark\">@android:color/transparent</item>\n    </style>\n    <style name=\"Platform.Theme.MaterialFiles\" parent=\"Platform.V29.Theme.MaterialFiles\" />\n\n    <style name=\"Platform.V29.Theme.MaterialFiles.Immersive\" parent=\"Platform.V27.Theme.MaterialFiles.Immersive\">\n        <item name=\"android:forceDarkAllowed\">false</item>\n\n        <item name=\"colorPrimaryDark\">@android:color/transparent</item>\n    </style>\n    <style name=\"Platform.Theme.MaterialFiles.Immersive\" parent=\"Platform.V29.Theme.MaterialFiles.Immersive\" />\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-v29/themes_material3.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <style name=\"Platform.V29.Theme.MaterialFiles.Material3\" parent=\"Platform.V27.Theme.MaterialFiles.Material3\">\n        <item name=\"android:forceDarkAllowed\">false</item>\n\n        <item name=\"colorPrimaryDark\">@android:color/transparent</item>\n    </style>\n    <style name=\"Platform.Theme.MaterialFiles.Material3\" parent=\"Platform.V29.Theme.MaterialFiles.Material3\" />\n\n    <style name=\"Platform.V29.Theme.MaterialFiles.Material3.Immersive\" parent=\"Platform.V27.Theme.MaterialFiles.Material3.Immersive\">\n        <item name=\"android:forceDarkAllowed\">false</item>\n\n        <item name=\"colorPrimaryDark\">@android:color/transparent</item>\n    </style>\n    <style name=\"Platform.Theme.MaterialFiles.Material3.Immersive\" parent=\"Platform.V29.Theme.MaterialFiles.Material3.Immersive\" />\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-v30/themes.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <style name=\"Platform.V30.Theme.MaterialFiles\" parent=\"Platform.V29.Theme.MaterialFiles\">\n        <item name=\"android:windowLayoutInDisplayCutoutMode\">always</item>\n    </style>\n    <style name=\"Platform.Theme.MaterialFiles\" parent=\"Platform.V30.Theme.MaterialFiles\" />\n\n    <style name=\"Platform.V30.Theme.MaterialFiles.Immersive\" parent=\"Platform.V29.Theme.MaterialFiles.Immersive\">\n        <item name=\"android:windowLayoutInDisplayCutoutMode\">always</item>\n    </style>\n    <style name=\"Platform.Theme.MaterialFiles.Immersive\" parent=\"Platform.V30.Theme.MaterialFiles.Immersive\" />\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-v30/themes_material3.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <style name=\"Platform.V30.Theme.MaterialFiles.Material3\" parent=\"Platform.V29.Theme.MaterialFiles.Material3\">\n        <item name=\"android:windowLayoutInDisplayCutoutMode\">always</item>\n    </style>\n    <style name=\"Platform.Theme.MaterialFiles.Material3\" parent=\"Platform.V30.Theme.MaterialFiles.Material3\" />\n\n    <style name=\"Platform.V30.Theme.MaterialFiles.Material3.Immersive\" parent=\"Platform.V29.Theme.MaterialFiles.Material3.Immersive\">\n        <item name=\"android:windowLayoutInDisplayCutoutMode\">always</item>\n    </style>\n    <style name=\"Platform.Theme.MaterialFiles.Material3.Immersive\" parent=\"Platform.V30.Theme.MaterialFiles.Material3.Immersive\" />\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-v31/donottranslate_prefs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2021 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <bool name=\"pref_default_value_material_design_3\">true</bool>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-v33/donottranslate_prefs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2023 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <bool name=\"pref_visible_ftp_server_add_tile\">true</bool>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-vi/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">Tệp</string>\n    <string name=\"file_type_name_apk\">Gói cài đặt Android</string>\n    <string name=\"file_type_name_archive\">Tệp nén %1$s</string>\n    <string name=\"file_type_name_audio\">Âm thanh %1$s</string>\n    <string name=\"file_type_name_calendar\">Lịch %1$s</string>\n    <string name=\"file_type_name_certificate\">Chứng chỉ %1$s</string>\n    <string name=\"file_type_name_code\">Tài liệu %1$s</string>\n    <string name=\"file_type_name_contact\">Danh thiếp điện tử</string>\n    <string name=\"file_type_name_directory\">Thư mục</string>\n    <string name=\"file_type_name_document\">Tài liệu %1$s</string>\n    <string name=\"file_type_name_ebook\">Sách điện tử %1$s</string>\n    <string name=\"file_type_name_email\">Email %1$s</string>\n    <string name=\"file_type_name_font\">Phông chữ %1$s</string>\n    <string name=\"file_type_name_generic\">Tệp %1$s</string>\n    <string name=\"file_type_name_image\">Hình ảnh %1$s</string>\n    <string name=\"file_type_name_pdf\">Tài liệu PDF</string>\n    <string name=\"file_type_name_presentation\">Bản trình chiếu %1$s</string>\n    <string name=\"file_type_name_spreadsheet\">Bảng tính %1$s</string>\n    <string name=\"file_type_name_text\">Tài liệu %1$s</string>\n    <string name=\"file_type_name_text_plain\">Tài liệu văn bản thuần</string>\n    <string name=\"file_type_name_video\">Video %1$s</string>\n    <string name=\"file_type_name_word\">Tài liệu Word</string>\n    <string name=\"file_type_name_powerpoint\">Bản trình chiếu PowerPoint</string>\n    <string name=\"file_type_name_excel\">Bảng tính Excel</string>\n    <string name=\"file_type_name_posix_character_device\">Thiết bị kí tự</string>\n    <string name=\"file_type_name_posix_block_device\">Thiết bị khối</string>\n    <string name=\"file_type_name_posix_fifo\">Pipe</string>\n    <string name=\"file_type_name_posix_symbolic_link\">Liên kết</string>\n    <string name=\"file_type_name_posix_socket\">Socket</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">Liên kết (bị hỏng)</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-vi/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <string name=\"app_name\">Material Files</string>\n\n    <string name=\"close\">Đóng</string>\n    <string name=\"copy\">Sao chép</string>\n    <string name=\"cut\">Cắt</string>\n    <string name=\"default_\">Mặc định</string>\n    <string name=\"delete\">Xoá</string>\n    <string name=\"discard\">Bỏ</string>\n    <string name=\"edit\">Chỉnh sửa</string>\n    <string name=\"empty\">Trống</string>\n    <string name=\"empty_placeholder\">(trống)</string>\n    <string name=\"error\">Lỗi</string>\n    <string name=\"hide\">Ẩn</string>\n    <string name=\"install\">Cài đặt</string>\n    <string name=\"keep_editing\">Tiếp tục sửa đổi</string>\n    <string name=\"loading\">Đang tải…</string>\n    <string name=\"maybe_later\">Có lẽ để sau</string>\n    <string name=\"merge\">Hợp nhất</string>\n    <string name=\"none\">Không có</string>\n    <string name=\"open_navigation_drawer\">Mở ngăn điều hướng</string>\n    <string name=\"paste\">Dán</string>\n    <string name=\"refresh\">Làm mới</string>\n    <string name=\"reload\">Tải lại</string>\n    <string name=\"remove\">Gỡ bỏ</string>\n    <string name=\"rename\">Đổi tên</string>\n    <string name=\"replace\">Thay thế</string>\n    <string name=\"reset\">Đặt lại</string>\n    <string name=\"retry\">Thử lại</string>\n    <string name=\"save\">Lưu</string>\n    <string name=\"search\">Tìm kiếm</string>\n    <string name=\"select\">Chọn</string>\n    <string name=\"select_all\">Chọn tất cả</string>\n    <string name=\"share\">Chia sẻ</string>\n    <string name=\"show\">Hiện</string>\n    <string name=\"skip\">Bỏ qua</string>\n    <string name=\"stop\">Dừng</string>\n    <string name=\"unknown\">Không rõ</string>\n    <string name=\"view\">Xem</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">Không tìm thấy ứng dụng nào để xử lý hành động này</string>\n    <string name=\"open_settings\">Mở cài đặt</string>\n    <string name=\"shortcut_created\">Đã tạo lối tắt</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"other\">%1$,d byte</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">Ứng dụng cần quyền quản lý tất cả tệp. Vui lòng cấp quyền ở cài đặt hệ thống tiếp theo.</string>\n    <!-- The allow button before Android 10 was in all-caps. -->\n    <string name=\"storage_permission_rationale_message\">Ứng dụng cần quyền truy cập tệp. Hãy nhấn \\\"CHO PHÉP\\\" trong hộp thoại hệ thống sắp hiện ra sau đây.</string>\n    <string name=\"storage_permission_permanently_denied_message\">Ứng dụng cần quyền truy cập tệp. Hãy cấp quyền \\\"Bộ nhớ\\\" trong cài đặt hệ thống.</string>\n    <string name=\"notification_channel_background_activity_start_name\">Hành động khi chạy nền</string>\n    <string name=\"notification_channel_background_activity_start_description\">Thực hiện các hành động khi ứng dụng chạy nền</string>\n    <string name=\"notification_channel_file_job_name\">Thao tác với tệp</string>\n    <string name=\"notification_channel_file_job_description\">Hiển thị và điều khiển các thao tác với tệp</string>\n    <string name=\"notification_channel_ftp_server_name\">Máy chủ FTP</string>\n    <string name=\"notification_channel_ftp_server_description\">Hiển thị và điều khiển máy chủ FTP</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"other\">Đang chuẩn bị nén %1$,d tệp (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">Đang nén \\\"%1$s\\\" thành \\\"%2$s\\\"</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"other\">Đang nén %1$,d tệp thành \\\"%2$s\\\"</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"other\">Đang chuẩn bị sao chép %1$,d tệp (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">Đang sao chép \\\"%1$s\\\" vào \\\"%2$s\\\"</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"other\">Đang sao chép %1$,d tệp vào \\\"%2$s\\\"</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"other\">Đang chuẩn bị giải nén %1$,d tệp (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">Đang giải nén \\\"%1$s\\\" vào \\\"%2$s\\\"</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"other\">Đang giải nén %1$,d tệp vào \\\"%2$s\\\"</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"other\">Đang chuẩn bị di chuyển %1$,d tệp (%2$s)</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">Đang di chuyển \\\"%1$s\\\" đến \\\"%2$s\\\"</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"other\">Đang di chuyển %1$,d tệp đến \\\"%2$s\\\"</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"other\">Đang chuẩn bị xoá %1$,d tệp</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">Đang xoá \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"other\">Đang xoá %1$,d tệp</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"other\">Đang chuẩn bị thay đổi chủ sở hữu cho %1$,d tệp</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">Đang thay đổi chủ sở hữu cho \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"other\">Đang thay đổi chủ sở hữu cho %1$,d tệp</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"other\">Đang chuẩn bị thay đổi nhóm cho %1$,d tệp</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">Đang thay đổi nhóm cho \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"other\">Đang thay đổi nhóm cho %1$,d tệp</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"other\">Đang chuẩn bị thay đổi chế độ cho %1$,d tệp</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">Đang thay đổi chế độ cho \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"other\">Đang thay đổi chế độ cho %1$,d tệp</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"other\">Đang chuẩn bị thay đổi hoàn cảnh SELinux cho %1$,d tệp</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">Đang thay đổi hoàn cảnh SELinux cho \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"other\">Đang thay đổi hoàn cảnh SELinux cho %1$,d tệp</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"other\">Đang chuẩn bị khôi phục hoàn cảnh SELinux cho %1$,d tệp</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">Đang khôi phục hoàn cảnh SELinux cho \\\"%1$s\\\"</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"other\">Đang khôi phục hoàn cảnh SELinux cho %1$,d tệp</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">Đang ghi \\\"%1$s\\\"</string>\n    <string name=\"file_job_create_error_title\">Lỗi khi tạo</string>\n    <string name=\"file_job_create_error_message_format\">Đã có lỗi khi tạo “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">Không thể sao chép thư mục vào trong chính nó</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">Không thể giải nén thư mục vào trong chính nó</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">Không thể di chuyển thư mục vào trong chính nó</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">Thư mục đích ở trong thư mục nguồn.</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">Không thể sao chép một tệp lên chính nó</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">Không thể giải nén một tệp lên chính nó</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">Không thể di chuyển một tệp lên chính nó</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">Tệp nguồn sẽ bị tệp đích ghi đè.</string>\n    <string name=\"file_job_replace_title_format\">Thay thế tệp “%1$s”?</string>\n    <string name=\"file_job_replace_message_format\">Một tệp khác có cùng tên đã tồn tại trong “%1$s”.\\nViệc thay thế sẽ ghi đè lên nội dung của nó.</string>\n    <string name=\"file_job_replace_target_name\">Tệp gốc</string>\n    <string name=\"file_job_replace_source_name\">Thay bằng</string>\n    <string name=\"file_job_merge_title_format\">Hợp nhất thư mục “%1$s”?</string>\n    <string name=\"file_job_merge_copy_message_format\">Việc hợp nhất sẽ hỏi xác nhận trước khi thay thế bất kỳ tệp nào trong thư mục mà chúng xung đột với các tệp đang được sao chép.</string>\n    <string name=\"file_job_merge_extract_message_format\">Việc hợp nhất sẽ hỏi xác nhận trước khi thay thế bất kỳ tệp nào trong thư mục mà chúng xung đột với các tệp đang được giải nén.</string>\n    <string name=\"file_job_merge_move_message_format\">Việc hợp nhất sẽ hỏi xác nhận trước khi thay thế bất kỳ tệp nào trong thư mục mà chúng xung đột với các tệp đang được di chuyển.</string>\n    <string name=\"file_job_merge_target_name\">Thư mục gốc</string>\n    <string name=\"file_job_merge_source_name\">Hợp nhất với</string>\n    <string name=\"file_job_conflict_show_name\">Chọn tên mới cho tệp đích</string>\n    <string name=\"file_job_conflict_name_hint\">Tên mới</string>\n    <string name=\"file_job_archive_error_title_format\">Lỗi khi nén “%1$s”</string>\n    <string name=\"file_job_archive_error_message_format\">Đã có lỗi khi nén tệp thành “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">Lỗi khi sao chép “%1$s”</string>\n    <string name=\"file_job_copy_error_message_format\">Đã có lỗi khi sao chép tệp vào “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">Lỗi khi giải nén “%1$s”</string>\n    <string name=\"file_job_extract_error_message_format\">Đã có lỗi khi giải nén tệp vào “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">Lỗi khi di chuyển “%1$s”</string>\n    <string name=\"file_job_move_error_message_format\">Đã có lỗi khi di chuyển tệp vào “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">Lỗi khi xoá</string>\n    <string name=\"file_job_delete_error_message_format\">Đã có lỗi khi xoá “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">Lỗi khi đổi tên \\\"%1$s\\\"</string>\n    <string name=\"file_job_rename_error_message_format\">Đã có lỗi khi đổi tên tệp thành \\\"%1$s\\\". \\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">Lỗi khi thay đổi chủ sở hữu của “%1$s”</string>\n    <string name=\"file_job_set_owner_error_message_format\">Đã có lỗi khi thay đổi chủ sở hữu thành “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">Lỗi khi thay đổi nhóm người dùng của “%1$s”</string>\n    <string name=\"file_job_set_group_error_message_format\">Xảy ra lỗi khi thay đổi nhóm người dùng thành “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">Lỗi khi thay đổi chế độ của “%1$s”</string>\n    <string name=\"file_job_set_mode_error_message_format\">Đã có lỗi khi thay đổi chế độ thành “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">Lỗi khi thay đổi hoàn cảnh SELinux của “%1$s”</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">Đã có lỗi khi thay đổi hoàn cảnh SELinux thành “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">Lỗi khi khổi phục hoàn cảnh SELinux</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">Xảy ra lỗi khi khôi phục hoàn cảnh SELinux của “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">Lỗi khi ghi</string>\n    <string name=\"file_job_write_error_message_format\">Đã có lỗi khi ghi “%1$s”.\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">Đã có lỗi khi lấy thông tin về “%1$s”.</string>\n    <string name=\"file_job_remount_format\">Đã mount lại “%1$s” thành thiết bị đọc-ghi</string>\n    <string name=\"file_job_remount_loading_format\">Đang mount lại “%1$s” thành thiết bị đọc-ghi…</string>\n    <string name=\"file_job_remount_success_format\">“%1$s” đã được mount lại thành thiết bị đọc-ghi</string>\n    <string name=\"file_job_action_all\">Áp dụng hành động này cho tất cả tệp</string>\n\n    <string name=\"file_open_apk_message\">Tệp này là một gói cài đặt Android. Bạn muốn cài đặt hay xem nội dung của nó?</string>\n    <string name=\"file_open_as_title\">Mở dưới dạng…</string>\n    <string name=\"file_open_as_title_format\">Mở “%1$s” dưới dạng</string>\n    <string name=\"file_open_as_type_text\">Văn bản</string>\n    <string name=\"file_open_as_type_image\">Hình ảnh</string>\n    <string name=\"file_open_as_type_audio\">Âm thanh</string>\n    <string name=\"file_open_as_type_video\">Video</string>\n    <string name=\"file_open_as_type_directory\">Thư mục</string>\n    <string name=\"file_open_as_type_any\">Khác</string>\n    <string name=\"file_install_apk_from_background_title_format\">“%1$s” đã sẵn sàng cài đặt</string>\n    <string name=\"file_install_apk_from_background_text\">Nhấn để cài đặt</string>\n    <string name=\"file_open_from_background_title_format\">“%1$s” đã sẵn sàng để mở</string>\n    <string name=\"file_open_from_background_text\">Nhấn để mở</string>\n    <string name=\"file_name_error_empty\">Tên tệp không được trống</string>\n    <string name=\"file_name_error_invalid\">Tên tệp không hợp lệ</string>\n    <string name=\"file_name_error_already_exists\">Một tệp có tên này đã tồn tại</string>\n    <string name=\"file_delete_message_file_format\">Xoá “%1$s”?</string>\n    <string name=\"file_delete_message_directory_format\">Xoá thư mục “%1$s” và nội dung của nó?</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"other\">Xoá %1$,d tệp?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"other\">Xoá %1$,d thư mục và nội dung của chúng?</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"other\">Xoá %1$,d mục?</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">Tạo tệp nén</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_add_bookmark_success\">Đã thêm đánh dấu</string>\n    <string name=\"file_create_file_title\">Tệp mới</string>\n    <string name=\"file_create_directory_title\">Thư mục mới</string>\n\n    <string name=\"file_list_title\">Tệp</string>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"other\">%1$,d thư mục</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"other\">%1$,d tệp</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">,\\u0020</string>\n    <string name=\"file_list_path_error_empty\">Đường dẫn không thể trống</string>\n    <string name=\"file_list_path_error_invalid\">Đường dẫn không hợp lệ</string>\n    <string name=\"file_list_action_sort_by_name\">Tên</string>\n    <string name=\"file_list_action_sort_by_type\">Loại</string>\n    <string name=\"file_list_action_sort_by_size\">Kích cỡ</string>\n    <string name=\"file_list_action_sort_by_last_modified\">Thay đổi lần cuối</string>\n    <string name=\"file_list_action_sort_order_ascending\">Tăng dần</string>\n    <string name=\"file_list_action_sort_directories_first\">Thư mục lên trước</string>\n    <string name=\"file_list_action_new_task\">Cửa sổ mới</string>\n    <string name=\"file_list_action_navigate_up\">Đi lên</string>\n    <string name=\"file_list_action_navigate_to\">Đi đến</string>\n    <string name=\"file_list_action_show_hidden_files\">Hiện các tệp ẩn</string>\n    <string name=\"file_list_action_copy_path\">Sao chép đường dẫn</string>\n    <string name=\"file_list_action_open_in_terminal\">Mở trong cửa sổ dòng lệnh</string>\n    <string name=\"file_list_action_add_bookmark\">Thêm đánh dấu</string>\n    <string name=\"file_list_action_create_shortcut\">Tạo lối tắt</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">Mở trong cửa sổ mới</string>\n    <string name=\"file_list_paste_move_title_format\">Đang di chuyển %1$,d</string>\n    <string name=\"file_list_paste_copy_title_format\">Đang sao chép %1$,d</string>\n    <string name=\"file_list_paste_extract_title_format\">Đang giải nén %1$,d</string>\n    <string name=\"file_list_paste_action_extract_here\">Giải nén vào đây</string>\n    <string name=\"file_list_empty\">Không có tệp</string>\n    <string name=\"file_list_action_create_file\">Tệp</string>\n    <string name=\"file_list_action_create_directory\">Thư mục</string>\n\n    <string name=\"file_item_description_separator\">\\u0020\\u0020\\u0020\\u0020</string>\n    <string name=\"file_item_action_open_with\">Mở bằng</string>\n    <string name=\"file_item_action_extract\">Giải nén</string>\n    <string name=\"file_item_action_archive\">Nén</string>\n    <string name=\"file_item_action_properties\">Thuộc tính</string>\n\n    <string name=\"file_properties_title_format\">Thuộc tính của “%1$s”</string>\n    <string name=\"file_properties_basic\">Cơ bản</string>\n    <string name=\"file_properties_basic_name\">Tên</string>\n    <string name=\"file_properties_basic_type\">Loại</string>\n    <string name=\"file_properties_basic_type_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">Liên kết tới %1$s (%2$s)</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">Đích đến liên kết</string>\n    <string name=\"file_properties_basic_size\">Kích cỡ</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s (%2$s)</string>\n    <string name=\"file_properties_basic_contents\">Nội dung</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"other\">%1$,d mục, tổng cộng %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">Thay đổi lần cuối</string>\n    <string name=\"file_properties_basic_parent_directory\">Thư mục mẹ</string>\n    <string name=\"file_properties_basic_archive_file\">Tệp nén</string>\n    <string name=\"file_properties_basic_archive_entry\">Mục lưu trữ nén</string>\n    <string name=\"file_properties_basic_free_space\">Không gian trống</string>\n    <string name=\"file_properties_image\">Hình ảnh</string>\n    <string name=\"file_properties_media_dimensions\">Kích thước</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">Chụp vào</string>\n    <string name=\"file_properties_media_coordinates\">Toạ độ</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f, %2$.3f</string>\n    <string name=\"file_properties_media_address\">Vị trí</string>\n    <string name=\"file_properties_image_gps_altitude\">Độ cao</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f m</string>\n    <string name=\"file_properties_image_equipment\">Máy ảnh</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">Khẩu độ</string>\n    <string name=\"file_properties_image_shutter_speed\">Tốc độ màn trập</string>\n    <string name=\"file_properties_image_focal_length\">Tiêu cự</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f mm</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">Mức ISO</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">Phần mềm</string>\n    <string name=\"file_properties_image_description\">Mô tả</string>\n    <string name=\"file_properties_image_artist\">Tác giả</string>\n    <string name=\"file_properties_image_copyright\">Bản quyền</string>\n    <string name=\"file_properties_audio\">Âm thanh</string>\n    <string name=\"file_properties_media_title\">Tiêu đề</string>\n    <string name=\"file_properties_audio_artist\">Nghệ sĩ</string>\n    <string name=\"file_properties_audio_album\">Album</string>\n    <string name=\"file_properties_audio_album_artist\">Nghệ sĩ của album</string>\n    <string name=\"file_properties_audio_composer\">Nhạc sĩ</string>\n    <string name=\"file_properties_audio_disc_number\">Đĩa</string>\n    <string name=\"file_properties_audio_track_number\">Bài số</string>\n    <string name=\"file_properties_audio_year\">Năm</string>\n    <string name=\"file_properties_audio_genre\">Thể loại</string>\n    <string name=\"file_properties_media_duration\">Độ dài</string>\n    <string name=\"file_properties_media_bit_rate\">Tốc độ bit</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d kbps </string>\n    <string name=\"file_properties_audio_sample_rate\">Tần số lấy mẫu</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Hz</string>\n    <string name=\"file_properties_video\">Video</string>\n    <string name=\"file_properties_apk\">APK</string>\n    <string name=\"file_properties_apk_label\">Tên</string>\n    <string name=\"file_properties_apk_package_name\">Tên gói</string>\n    <string name=\"file_properties_apk_version\">Phiên bản</string>\n    <string name=\"file_properties_apk_version_format\">%1$s (%2$d)</string>\n    <string name=\"file_properties_apk_min_sdk_version\">Phiên bản hệ thống tối thiểu</string>\n    <string name=\"file_properties_apk_target_sdk_version\">Phiên bản hệ thống mục tiêu</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s (%2$s, %3$d)</string>\n    <string name=\"file_properties_apk_requested_permissions\">Quyền</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">Không có quyền được yêu cầu</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"other\">%1$,d quyền được yêu cầu</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">Chữ kí</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">Không có chữ ký hợp lệ</string>\n    <string name=\"file_properties_apk_past_signature_digests\">Chữ kí cũ</string>\n    <string name=\"storage_file_system_root_title\">Root</string>\n    <string name=\"storage_list_title\">Kho lưu trữ</string>\n    <string name=\"storage_list_empty\">Không có kho lưu trữ</string>\n    <string name=\"storage_add_storage_title\">Thêm kho lưu trữ</string>\n    <string name=\"storage_add_storage_document_tree\">Bộ nhớ ngoài</string>\n    <string name=\"storage_add_storage_ftp_server\">Máy chủ FTP</string>\n    <string name=\"storage_add_storage_sftp_server\">Máy chủ SFTP</string>\n    <string name=\"storage_add_storage_smb_server\">Máy chủ SMB</string>\n    <string name=\"storage_edit_device_storage_title\">Chỉnh sửa kho lưu trữ thiết bị</string>\n    <string name=\"storage_edit_device_storage_name\">Tên</string>\n    <string name=\"storage_edit_device_storage_path\">Đường dẫn</string>\n    <string name=\"storage_add_document_tree_title\">Thêm kho lưu trữ ngoài</string>\n    <string name=\"storage_edit_document_tree_title\">Chỉnh sửa kho lưu trữ ngoài</string>\n    <string name=\"storage_edit_document_tree_name\">Tên</string>\n    <string name=\"storage_edit_document_tree_uri\">URI</string>\n    <string name=\"storage_edit_document_tree_path\">Đường dẫn</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">Chỉnh sửa máy chủ FTP</string>\n    <string name=\"storage_edit_ftp_server_title_add\">Thêm máy chủ FTP</string>\n    <string name=\"storage_edit_ftp_server_host\">Tên miền</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">Nhập tên miền</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">Tên miền không hợp lệ</string>\n    <string name=\"storage_edit_ftp_server_port\">Cổng</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">Cổng không hợp lệ</string>\n    <string name=\"storage_edit_ftp_server_path\">Đường dẫn</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">Có thể để trống</string>\n    <string name=\"storage_edit_ftp_server_name\">Tên</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">Dùng tên miền</string>\n    <string name=\"storage_edit_ftp_server_protocol\">Giao thức</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">Xác thực</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>Mật khẩu</item>\n        <item>Ẩn danh</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">Tên người dùng</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">Nhập tên người dùng</string>\n    <string name=\"storage_edit_ftp_server_password\">Mật khẩu</string>\n    <string name=\"storage_edit_ftp_server_mode\">Chế độ</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>Hoạt động</item>\n        <item>Bị động</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">Mã hoá</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">Kết nối và thêm</string>\n    <string name=\"storage_edit_ftp_server_add\">Thêm</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">Chỉnh sửa máy chủ SFTP</string>\n    <string name=\"storage_edit_sftp_server_title_add\">Thêm máy chủ SFTP</string>\n    <string name=\"storage_edit_sftp_server_host\">Tên máy chủ</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">Nhập tên máy chủ</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">Tên miền không hợp lệ</string>\n    <string name=\"storage_edit_sftp_server_port\">Cổng</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">Cổng không hợp lệ</string>\n    <string name=\"storage_edit_sftp_server_path\">Đường dẫn</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">Có thể để trống</string>\n    <string name=\"storage_edit_sftp_server_name\">Tên</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">Sử dụng tên máy chủ</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">Xác thực</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>Mật khẩu</item>\n        <item>Mã khóa công khai</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">Tên người dùng</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">Nhập tên người dùng</string>\n    <string name=\"storage_edit_sftp_server_password\">Mật khẩu</string>\n    <string name=\"storage_edit_sftp_server_private_key\">Mã khóa riêng</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">Mở tệp</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">Nhập mã khóa riêng</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">Mã khóa riêng không hợp lệ</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">Mật khẩu mã riêng tư</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">Có thể để trống</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">Mật khẩu mã riêng tư không hợp lệ</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">Kết nối và thêm</string>\n    <string name=\"storage_edit_sftp_server_add\">Thêm</string>\n    <string name=\"storage_add_lan_smb_server_loading\">Đang tìm kiếm máy chủ SMB…</string>\n    <string name=\"storage_add_lan_smb_server_add\">Thêm thủ công</string>\n    <string name=\"storage_edit_smb_server_title_edit\">Chỉnh sửa máy chủ SMB</string>\n    <string name=\"storage_edit_smb_server_title_add\">Thêm máy chủ SMB</string>\n    <string name=\"storage_edit_smb_server_host\">Tên máy chủ</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">Nhập tên máy chủ</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">Tên miền không hợp lệ</string>\n    <string name=\"storage_edit_smb_server_port\">Cổng</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">Cổng không hợp lệ</string>\n    <string name=\"storage_edit_smb_server_path\">Đường dẫn</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">Có thể để trống</string>\n    <string name=\"storage_edit_smb_server_name\">Tên</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">Sử dụng tên máy chủ</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">Xác thực</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>Mật khẩu</item>\n        <item>Khách</item>\n        <item>Nặc danh</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">Tên người dùng</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">Nhập tên người dùng</string>\n    <string name=\"storage_edit_smb_server_password\">Mật khẩu</string>\n    <string name=\"storage_edit_smb_server_domain\">Miền</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">Kết nối và thêm</string>\n    <string name=\"storage_edit_smb_server_add\">Thêm</string>\n    <string name=\"navigation_storage_subtitle_format\">%1$s trống trong số %2$s</string>\n    <string name=\"navigation_add_storage\">Thêm kho lưu trữ…</string>\n    <string name=\"navigation_standard_directory_alarms\">Báo thức</string>\n    <string name=\"navigation_standard_directory_dcim\">Máy ảnh</string>\n    <string name=\"navigation_standard_directory_documents\">Tài liệu</string>\n    <string name=\"navigation_standard_directory_downloads\">Tải xuống</string>\n    <string name=\"navigation_standard_directory_movies\">Phim</string>\n    <string name=\"navigation_standard_directory_music\">Âm nhạc</string>\n    <string name=\"navigation_standard_directory_notifications\">Âm thông báo</string>\n    <string name=\"navigation_standard_directory_pictures\">Hình ảnh</string>\n    <string name=\"navigation_standard_directory_podcasts\">Podcast</string>\n    <string name=\"navigation_standard_directory_ringtones\">Nhạc chuông</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">WeChat</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">Đánh dấu thư mục</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">Tên</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">Đường dẫn</string>\n    <string name=\"archive_viewer_title\">Trình xem tệp nén</string>\n    <string name=\"image_viewer_title\">Trình xem hình ảnh</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"text_editor_title\">Trình chỉnh sửa văn bản</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">* %1$s</string>\n    <string name=\"text_editor_save_success\">Đã lưu</string>\n    <string name=\"text_editor_reload_message\">Bạn có chắc chắn muốn tải lại không? Những thay đổi chưa lưu sẽ bị mất.</string>\n    <string name=\"text_editor_encoding\">Bảng mã kí tự</string>\n    <string name=\"text_editor_close_message\">Bạn có chắc chắn muốn huỷ bỏ những thay đổi chưa lưu đối với tài liệu này?</string>\n\n    <string name=\"ftp_server_title\">Máy chủ FTP</string>\n    <string name=\"ftp_server_state_title\">Trạng thái</string>\n    <string name=\"ftp_server_state_summary_starting\">Đang khởi động…</string>\n    <string name=\"ftp_server_state_summary_running\">Đang chạy</string>\n    <string name=\"ftp_server_state_summary_stopping\">Đang dừng…</string>\n    <string name=\"ftp_server_state_summary_stopped\">Chưa khởi động</string>\n    <string name=\"ftp_server_url_title\">URL</string>\n    <string name=\"ftp_server_url_menu_copy_url\">Sao chép URL</string>\n    <string name=\"ftp_server_url_menu_copy_password\">Sao chép mật khẩu</string>\n    <string name=\"ftp_server_configuration_title\">Thiết lập</string>\n    <string name=\"ftp_server_anonymous_login_title\">Đăng nhập nặc danh</string>\n    <string name=\"ftp_server_username_title\">Tên đăng nhập</string>\n    <string name=\"ftp_server_password_title\">Mật khẩu</string>\n    <string name=\"ftp_server_port_title\">Cổng</string>\n    <string name=\"ftp_server_home_directory_title\">Thư mục gốc</string>\n    <string name=\"ftp_server_writable_title\">Cho phép ghi</string>\n    <string name=\"settings_title\">Cài đặt</string>\n    <string name=\"settings_interface_title\">Giao diện</string>\n    <string name=\"settings_theme_color_title\">Màu chủ đề</string>\n    <string name=\"settings_theme_color_summary\">Màu thường xuyên xuất hiện nhất trong ứng dụng</string>\n    <string name=\"settings_material_design_3_title\">Thiết kế Material 3</string>\n    <string name=\"settings_night_mode_title\">Chế độ ban đêm</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>Theo hệ thống</item>\n        <item>Tắt</item>\n        <item>Bật</item>\n        <item>Dựa theo thời gian</item>\n        <item>Dựa theo chế độ tiết kiệm pin</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">Chế độ ban đêm đen</string>\n    <string name=\"settings_file_list_animation_title\">Hoạt ảnh danh sách tệp</string>\n    <string name=\"settings_file_name_ellipsize_title\">Hiển thị tên tệp dài</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>Rút ngắn ở đầu</item>\n        <item>Rút ngắn ở giữa</item>\n        <item>Rút ngắn ở cuối</item>\n        <item>Marquee</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">Hành vi</string>\n    <string name=\"settings_default_directory_title\">Thư mục mặc định</string>\n    <string name=\"settings_standard_directories_title\">Các thư mục tiêu chuẩn</string>\n    <string name=\"settings_standard_directories_summary_empty\">Không bật thư mục tiêu chuẩn nào</string>\n    <string name=\"settings_bookmark_directory_screenshots\">Ảnh chụp màn hình</string>\n    <string name=\"settings_bookmark_directories_title\">Đánh dấu thư mục</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">Không có thư mục nào được đánh dấu</string>\n    <string name=\"settings_root_strategy_title\">Chế độ truy cập root</string>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"other\">Bạn có %1$,d thao tác với tệp đang chạy và việc thay đổi chế độ truy cập root ngay bây giờ có thể gây ra lỗi không mong đợi. Bạn có chắc bạn muốn thay đổi ngay không?</item>\n    </plurals>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>Chỉ dùng quyền truy cập thông thường</item>\n        <item>Tự động</item>\n        <item>Chỉ dùng quyền truy cập root</item>\n    </string-array>\n    <string name=\"settings_archive_file_name_encoding_title\">Bảng mã kí tự của tên tệp nén</string>\n    <string name=\"settings_open_apk_default_action_title\">Mở gói cài đặt Android</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>Cài đặt</item>\n        <item>Xem nội dung</item>\n        <item>Hỏi xem sẽ làm gì</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">Có thể làm ứng dụng không ổn định trên các phiên bản Android thấp hơn</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">Đọc tệp từ xa để lấy ảnh xem trước</string>\n\n    <string name=\"about_title\">Giới thiệu</string>\n    <string name=\"about_version_title\">Phiên bản</string>\n    <string name=\"about_github_title\">Xem trên GitHub</string>\n    <string name=\"about_licenses_title\">Giấy phép</string>\n    <string name=\"about_privacy_policy_title\">Chính sách riêng tư</string>\n    <string name=\"about_author_title\">Tác giả</string>\n    <string name=\"about_author_name_title\">Hai Zhang</string>\n    <string name=\"about_author_github_title\">Theo dõi trên GitHub</string>\n    <string name=\"about_author_twitter_title\">Theo dõi trên Twitter</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-zh-rCN/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">文件</string>\n    <string name=\"file_type_name_apk\">Android 安装包</string>\n    <string name=\"file_type_name_archive\">%1$s 归档文件</string>\n    <string name=\"file_type_name_audio\">%1$s 音频</string>\n    <string name=\"file_type_name_calendar\">%1$s 日历</string>\n    <string name=\"file_type_name_certificate\">%1$s 证书</string>\n    <string name=\"file_type_name_code\">%1$s 文档</string>\n    <string name=\"file_type_name_contact\">电子名片</string>\n    <string name=\"file_type_name_directory\">文件夹</string>\n    <string name=\"file_type_name_document\">%1$s 文档</string>\n    <string name=\"file_type_name_ebook\">%1$s 电子书</string>\n    <string name=\"file_type_name_email\">%1$s 电子邮件</string>\n    <string name=\"file_type_name_font\">%1$s 字体</string>\n    <string name=\"file_type_name_generic\">%1$s 文件</string>\n    <string name=\"file_type_name_image\">%1$s 图片</string>\n    <string name=\"file_type_name_pdf\">PDF 文档</string>\n    <string name=\"file_type_name_presentation\">%1$s 演示文稿</string>\n    <string name=\"file_type_name_spreadsheet\">%1$s 电子表格</string>\n    <string name=\"file_type_name_text\">%1$s 文档</string>\n    <string name=\"file_type_name_text_plain\">纯文本文档</string>\n    <string name=\"file_type_name_video\">%1$s 视频</string>\n    <string name=\"file_type_name_word\">Word 文档</string>\n    <string name=\"file_type_name_powerpoint\">PowerPoint 演示文稿</string>\n    <string name=\"file_type_name_excel\">Excel 电子表格</string>\n    <string name=\"file_type_name_posix_character_device\">字符设备</string>\n    <string name=\"file_type_name_posix_block_device\">块设备</string>\n    <string name=\"file_type_name_posix_fifo\">管道</string>\n    <string name=\"file_type_name_posix_symbolic_link\">链接</string>\n    <string name=\"file_type_name_posix_socket\">套接字</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">链接（已断）</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-zh-rCN/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <string name=\"app_name\">质感文件</string>\n\n    <string name=\"close\">关闭</string>\n    <string name=\"copy\">复制</string>\n    <string name=\"cut\">剪切</string>\n    <string name=\"default_\">默认</string>\n    <string name=\"delete\">删除</string>\n    <string name=\"discard\">舍弃</string>\n    <string name=\"edit\">编辑</string>\n    <string name=\"empty\">空</string>\n    <string name=\"empty_placeholder\">（空）</string>\n    <string name=\"error\">错误</string>\n    <string name=\"hide\">隐藏</string>\n    <string name=\"install\">安装</string>\n    <string name=\"keep_editing\">继续编辑</string>\n    <string name=\"loading\">正在加载…</string>\n    <string name=\"maybe_later\">以后再说</string>\n    <string name=\"merge\">合并</string>\n    <string name=\"none\">无</string>\n    <string name=\"open_navigation_drawer\">打开抽屉式导航栏</string>\n    <string name=\"paste\">粘贴</string>\n    <string name=\"refresh\">刷新</string>\n    <string name=\"reload\">重新加载</string>\n    <string name=\"remove\">移除</string>\n    <string name=\"rename\">重命名</string>\n    <string name=\"replace\">替换</string>\n    <string name=\"reset\">重置</string>\n    <string name=\"retry\">重试</string>\n    <string name=\"save\">保存</string>\n    <string name=\"search\">搜索</string>\n    <string name=\"select\">选择</string>\n    <string name=\"select_all\">全选</string>\n    <string name=\"share\">分享</string>\n    <string name=\"show\">显示</string>\n    <string name=\"skip\">跳过</string>\n    <string name=\"stop\">停止</string>\n    <string name=\"system_default\">系统默认</string>\n    <string name=\"unknown\">未知</string>\n    <string name=\"view\">查看</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">未找到可处理此操作的应用</string>\n    <string name=\"open_settings\">打开设置</string>\n    <string name=\"shortcut_created\">已创建快捷方式</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"other\">%1$,d 字节</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">应用需要管理所有文件的权限。请在接下来的系统设置中授予权限。</string>\n    <string name=\"storage_permission_rationale_message\">应用需要访问文件的权限。请在接下来的系统对话框中点击“允许”。</string>\n    <string name=\"storage_permission_permanently_denied_message\">应用需要访问文件的权限。请在系统设置中授予“存储空间”权限。</string>\n    <string name=\"notification_permission_rationale_message\">应用需要发布文件操作相关通知的权限。请在接下来的系统对话框中点击“允许”。</string>\n    <string name=\"notification_permission_permanently_denied_message\">应用需要发布文件操作相关通知的权限。请在系统设置中授予“通知”权限。</string>\n\n    <string name=\"notification_channel_background_activity_start_name\">后台期间动作</string>\n    <string name=\"notification_channel_background_activity_start_description\">在应用处于后台期间采取动作</string>\n    <string name=\"notification_channel_file_job_name\">文件操作</string>\n    <string name=\"notification_channel_file_job_description\">显示和控制文件操作</string>\n    <string name=\"notification_channel_ftp_server_name\">FTP 服务器</string>\n    <string name=\"notification_channel_ftp_server_description\">显示和控制 FTP 服务器</string>\n\n    <string name=\"file_action_archive_password_title\">需要密码</string>\n    <string name=\"file_action_archive_password_message_format\">“%1$s”受密码保护。</string>\n    <string name=\"file_action_archive_password_error_empty\">密码不能为空</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"other\">正在准备压缩 %1$,d 个文件（%2$s）</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">正在将“%1$s”压缩进“%2$s”</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"other\">正在将 %1$,d 个文件压缩进“%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"other\">正在准备复制 %1$,d 个文件（%2$s）</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">正在将“%1$s”复制到“%2$s”</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"other\">正在将 %1$,d 个文件复制到“%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"other\">正在准备提取 %1$,d 个文件（%2$s）</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">正在将“%1$s”提取到“%2$s”</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"other\">正在将 %1$,d 个文件提取到“%2$s”</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"other\">正在准备移动 %1$,d 个文件（%2$s）</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">正在将“%1$s”移动到“%2$s”</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"other\">正在将 %1$,d 个文件移动到“%2$s”</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"other\">正在准备删除 %1$,d 个文件</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">正在删除“%1$s”</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"other\">正在删除 %1$,d 个文件</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"other\">正在准备更改 %1$,d 个文件的所有者</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">正在更改“%1$s”的所有者</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"other\">正在更改 %1$,d 个文件的所有者</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"other\">正在准备更改 %1$,d 个文件的组</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">正在更改“%1$s”的组</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"other\">正在更改 %1$,d 个文件的组</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"other\">正在准备更改 %1$,d 个文件的模式</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">正在更改“%1$s”的模式</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"other\">正在更改 %1$,d 个文件的模式</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"other\">正在准备更改 %1$,d 个文件的 SELinux 上下文</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">正在更改“%1$s”的 SELinux 上下文</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"other\">正在更改 %1$,d 个文件的 SELinux 上下文</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"other\">正在准备还原 %1$,d 个文件的 SELinux 上下文</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">正在还原“%1$s”的 SELinux 上下文</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"other\">正在还原 %1$,d 个文件的 SELinux 上下文</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">正在写入“%1$s”</string>\n    <string name=\"file_job_create_error_title\">创建时发生错误</string>\n    <string name=\"file_job_create_error_message_format\">在创建“%1$s”时发生错误。\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">无法将文件夹复制到自身</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">无法将文件夹提取到自身</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">无法将文件夹移动到自身</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">目标文件夹位于来源文件夹内。</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">无法将文件复制到自身</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">无法将文件提取到自身</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">无法将文件移动到自身</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">来源文件将被目标覆盖。</string>\n    <string name=\"file_job_replace_title_format\">替换文件“%1$s”？</string>\n    <string name=\"file_job_replace_message_format\">在“%1$s”中存在相同名称的另一个文件。\\n替换将覆盖它的内容。</string>\n    <string name=\"file_job_replace_target_name\">原始文件</string>\n    <string name=\"file_job_replace_source_name\">替换为</string>\n    <string name=\"file_job_merge_title_format\">合并文件夹“%1$s”？</string>\n    <string name=\"file_job_merge_copy_message_format\">文件中有文件与正在被复制的文件冲突时，合并将在替换前请求确认。</string>\n    <string name=\"file_job_merge_extract_message_format\">文件中有文件与正在被提取的文件冲突时，合并将在替换前请求确认。</string>\n    <string name=\"file_job_merge_move_message_format\">文件中有文件与正在被移动的文件冲突时，合并将在替换前请求确认。</string>\n    <string name=\"file_job_merge_target_name\">原始文件夹</string>\n    <string name=\"file_job_merge_source_name\">合并</string>\n    <string name=\"file_job_conflict_show_name\">为目标选择新的名称</string>\n    <string name=\"file_job_conflict_name_hint\">新名称</string>\n    <string name=\"file_job_archive_error_title_format\">压缩“%1$s”时发生错误</string>\n    <string name=\"file_job_archive_error_message_format\">将文件压缩进“%1$s”时发生错误。\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">复制“%1$s”时发生错误</string>\n    <string name=\"file_job_copy_error_message_format\">将文件复制到“%1$s”时发生错误。\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">提取“%1$s”时发生错误</string>\n    <string name=\"file_job_extract_error_message_format\">将文件提取到“%1$s”时发生错误。\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">移动“%1$s”时发生错误</string>\n    <string name=\"file_job_move_error_message_format\">将文件移动到“%1$s”时发生错误。\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">删除时发生错误</string>\n    <string name=\"file_job_delete_error_message_format\">在删除“%1$s”时发生错误。\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">重命名“%1$s”时发生错误</string>\n    <string name=\"file_job_rename_error_message_format\">将文件重命名为“%1$s”时发生错误。\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">更改“%1$s”的所有者时发生错误</string>\n    <string name=\"file_job_set_owner_error_message_format\">将所有者更改为“%1$s”时发生错误。\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">更改“%1$s”的组时发生错误</string>\n    <string name=\"file_job_set_group_error_message_format\">将组更改为“%1$s”时发生错误。\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">更改“%1$s”的模式时发生错误</string>\n    <string name=\"file_job_set_mode_error_message_format\">将模式更改为“%1$s”时发生错误。\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">更改“%1$s”的 SELinux 上下文时发生错误</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">将 SELinux 上下文更改为“%1$s”时发生错误。\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">还原 SELinux 上下文时发生错误</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">在还原“%1$s”的 SELinux 上下文时发生错误。\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">写入时发生错误</string>\n    <string name=\"file_job_write_error_message_format\">在写入“%1$s”时发生错误。\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">在获取“%1$s”的信息时发生错误。</string>\n    <string name=\"file_job_remount_format\">重新挂载“%1$s”为读写模式</string>\n    <string name=\"file_job_remount_loading_format\">正在重新挂载“%1$s”为读写模式…</string>\n    <string name=\"file_job_remount_success_format\">“%1$s”已被重新挂载为读写模式</string>\n    <string name=\"file_job_action_all\">应用此操作到所有文件</string>\n\n    <string name=\"file_open_apk_message\">这个文件是一个 Android 安装包。您想要安装它，还是查看它的内容？</string>\n    <string name=\"file_open_as_title\">打开为…</string>\n    <string name=\"file_open_as_title_format\">将“%1$s”打开为</string>\n    <string name=\"file_open_as_type_text\">文本</string>\n    <string name=\"file_open_as_type_image\">图片</string>\n    <string name=\"file_open_as_type_audio\">音频</string>\n    <string name=\"file_open_as_type_video\">视频</string>\n    <string name=\"file_open_as_type_directory\">文件夹</string>\n    <string name=\"file_open_as_type_any\">其他</string>\n    <string name=\"file_install_apk_from_background_title_format\">“%1$s”已可安装</string>\n    <string name=\"file_install_apk_from_background_text\">点按即可安装</string>\n    <string name=\"file_open_from_background_title_format\">“%1$s”已可打开</string>\n    <string name=\"file_open_from_background_text\">点按即可打开</string>\n    <string name=\"file_name_error_empty\">文件名不能为空</string>\n    <string name=\"file_name_error_invalid\">无效文件名</string>\n    <string name=\"file_name_error_already_exists\">已存在同名的文件</string>\n    <string name=\"file_replace_message_format\">替换“%1$s”？</string>\n    <string name=\"file_delete_message_file_format\">删除“%1$s”？</string>\n    <string name=\"file_delete_message_directory_format\">删除文件夹“%1$s”和它的内容？</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"other\">删除 %1$,d 个文件？</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"other\">删除 %1$,d 个文件夹和它们的内容？</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"other\">删除 %1$,d 个文件和文件夹？</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">创建压缩文件</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_create_archive_password\">密码（可选）</string>\n    <string name=\"file_add_bookmark_success\">已添加书签</string>\n    <string name=\"file_create_file_title\">新建文件</string>\n    <string name=\"file_create_directory_title\">新建文件夹</string>\n\n    <string name=\"file_list_title\">文件</string>\n    <plurals name=\"file_list_title_open_file\">\n        <item quantity=\"other\">选择文件</item>\n    </plurals>\n    <string name=\"file_list_title_create_file\">保存文件</string>\n    <plurals name=\"file_list_title_open_directory\">\n        <item quantity=\"other\">选择文件夹</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"other\">%1$,d 个文件夹</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"other\">%1$,d 个文件</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">，</string>\n    <string name=\"file_list_path_error_empty\">路径不能为空</string>\n    <string name=\"file_list_path_error_invalid\">无效路径</string>\n    <string name=\"file_list_action_view_sort\">视图和排序</string>\n    <string name=\"file_list_action_view_list\">列表</string>\n    <string name=\"file_list_action_view_grid\">网格</string>\n    <string name=\"file_list_action_sort_by_name\">名称</string>\n    <string name=\"file_list_action_sort_by_type\">类型</string>\n    <string name=\"file_list_action_sort_by_size\">大小</string>\n    <string name=\"file_list_action_sort_by_last_modified\">最后修改</string>\n    <string name=\"file_list_action_sort_order_ascending\">升序</string>\n    <string name=\"file_list_action_sort_directories_first\">文件夹优先</string>\n    <string name=\"file_list_action_view_sort_path_specific\">仅用于此文件夹</string>\n    <string name=\"file_list_action_new_task\">新建窗口</string>\n    <string name=\"file_list_action_navigate_up\">向上</string>\n    <string name=\"file_list_action_navigate_to\">转到</string>\n    <string name=\"file_list_action_show_hidden_files\">显示隐藏文件</string>\n    <string name=\"file_list_action_copy_path\">复制路径</string>\n    <string name=\"file_list_action_open_in_terminal\">在终端中打开</string>\n    <string name=\"file_list_action_add_bookmark\">添加书签</string>\n    <string name=\"file_list_action_create_shortcut\">创建快捷方式</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">在新窗口中打开</string>\n    <string name=\"file_list_select_title_format\">%1$,d</string>\n    <string name=\"file_list_create_file_name_hint\">文件名</string>\n    <string name=\"file_list_open_current_directory_format\">选择“%1$s”</string>\n    <string name=\"file_list_paste_move_title_format\">移动 %1$,d</string>\n    <string name=\"file_list_paste_copy_title_format\">复制 %1$,d</string>\n    <string name=\"file_list_paste_extract_title_format\">提取 %1$,d</string>\n    <string name=\"file_list_paste_action_extract_here\">提取到此处</string>\n    <string name=\"file_list_empty\">无文件</string>\n    <string name=\"file_list_action_create_file\">文件</string>\n    <string name=\"file_list_action_create_directory\">文件夹</string>\n\n    <string name=\"file_item_description_separator\">\"    \"</string>\n    <string name=\"file_item_action_open_with\">打开方式</string>\n    <string name=\"file_item_action_extract\">提取</string>\n    <string name=\"file_item_action_archive\">压缩</string>\n    <string name=\"file_item_action_properties\">属性</string>\n\n    <string name=\"file_properties_title_format\">“%1$s”属性</string>\n    <string name=\"file_properties_basic\">基本</string>\n    <string name=\"file_properties_basic_name\">名称</string>\n    <string name=\"file_properties_basic_type\">类型</string>\n    <string name=\"file_properties_basic_type_format\">%1$s（%2$s）</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">到 %1$s 的链接（%2$s）</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">链接目标</string>\n    <string name=\"file_properties_basic_size\">大小</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s（%2$s）</string>\n    <string name=\"file_properties_basic_contents\">内容</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"other\">%1$,d 项，共 %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">最后修改</string>\n    <string name=\"file_properties_basic_parent_directory\">父文件夹</string>\n    <string name=\"file_properties_basic_archive_file\">归档文件</string>\n    <string name=\"file_properties_basic_archive_entry\">归档条目</string>\n    <string name=\"file_properties_basic_free_space\">可用空间</string>\n    <string name=\"file_properties_permission\">权限</string>\n    <string name=\"file_properties_permission_owner\">所有者</string>\n    <string name=\"file_properties_permission_group\">组</string>\n    <string name=\"file_properties_permission_principal_format\">%1$s（%2$d）</string>\n    <string name=\"file_properties_permission_set_owner_title\">更改所有者</string>\n    <string name=\"file_properties_permission_set_group_title\">更改组</string>\n    <string name=\"file_properties_permission_set_principal_filter_hint\">输入名称或 ID</string>\n    <string name=\"file_properties_permission_set_principal_system\">系统</string>\n    <string name=\"file_properties_permission_mode\">模式</string>\n    <string name=\"file_properties_permission_mode_format\">%1$s（%2$04o）</string>\n    <string name=\"file_properties_permission_set_mode_title\">更改模式</string>\n    <string name=\"file_properties_permission_set_mode_others\">其他</string>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_file\">\n        <item>读取</item>\n        <item>写入</item>\n        <item>执行</item>\n    </string-array>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_directory\">\n        <item>读取</item>\n        <item>写入</item>\n        <item>搜索</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_special\">特殊</string>\n    <string-array name=\"file_properties_permission_set_mode_special_mode_bits\">\n        <item>设置 UID</item>\n        <item>设置 GID</item>\n        <item>粘滞位</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_uppercase_x\">不要为包含的文件添加“执行”</string>\n    <string name=\"file_properties_permission_selinux_context\">SELinux 上下文</string>\n    <string name=\"file_properties_permission_set_selinux_context_title\">更改 SELinux 上下文</string>\n    <string name=\"file_properties_permission_set_selinux_context_restore\">还原</string>\n    <string name=\"file_properties_permission_recursive\">应用到包含的文件</string>\n    <string name=\"file_properties_image\">图片</string>\n    <string name=\"file_properties_media_dimensions\">尺寸</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">拍摄时间</string>\n    <string name=\"file_properties_media_coordinates\">坐标</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f，%2$.3f</string>\n    <string name=\"file_properties_media_address\">位置</string>\n    <string name=\"file_properties_image_gps_altitude\">海拔</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f 米</string>\n    <string name=\"file_properties_image_equipment\">相机</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">光圈</string>\n    <string name=\"file_properties_image_shutter_speed\">快门速度</string>\n    <string name=\"file_properties_image_focal_length\">焦距</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f 毫米</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">ISO 等效感光度</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">软件</string>\n    <string name=\"file_properties_image_description\">描述</string>\n    <string name=\"file_properties_image_artist\">创建者</string>\n    <string name=\"file_properties_image_copyright\">版权</string>\n    <string name=\"file_properties_audio\">音频</string>\n    <string name=\"file_properties_media_title\">标题</string>\n    <string name=\"file_properties_audio_artist\">艺术家</string>\n    <string name=\"file_properties_audio_album\">专辑</string>\n    <string name=\"file_properties_audio_album_artist\">专辑艺术家</string>\n    <string name=\"file_properties_audio_composer\">作曲家</string>\n    <string name=\"file_properties_audio_disc_number\">光盘编号</string>\n    <string name=\"file_properties_audio_track_number\">曲目编号</string>\n    <string name=\"file_properties_audio_year\">年份</string>\n    <string name=\"file_properties_audio_genre\">流派</string>\n    <string name=\"file_properties_media_duration\">时长</string>\n    <string name=\"file_properties_media_bit_rate\">比特率</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d kbps</string>\n    <string name=\"file_properties_audio_sample_rate\">采样率</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Hz</string>\n    <string name=\"file_properties_video\">视频</string>\n    <string name=\"file_properties_apk\">安装包</string>\n    <string name=\"file_properties_apk_label\">名称</string>\n    <string name=\"file_properties_apk_package_name\">软件包名称</string>\n    <string name=\"file_properties_apk_version\">版本</string>\n    <string name=\"file_properties_apk_version_format\">%1$s（%2$d）</string>\n    <string name=\"file_properties_apk_min_sdk_version\">最低系统版本</string>\n    <string name=\"file_properties_apk_target_sdk_version\">目标系统版本</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s（%2$s，%3$d）</string>\n    <string name=\"file_properties_apk_requested_permissions\">权限</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">未请求任何权限</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"other\">已请求 %1$,d 项权限</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">签名</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">无有效签名</string>\n    <string name=\"file_properties_apk_past_signature_digests\">旧签名</string>\n    <string name=\"file_properties_checksum\">校验和</string>\n    <string name=\"file_properties_checksum_compare\">比较</string>\n    <string name=\"file_properties_checksum_compare_placeholder\">任意校验和</string>\n    <string name=\"file_properties_checksum_compare_match_format\">与 %1$s 校验和匹配</string>\n    <string name=\"file_properties_checksum_compare_prefix_match_format\">与 %1$s 校验和的前缀匹配</string>\n    <string name=\"file_properties_checksum_compare_no_match\">未找到匹配的校验和</string>\n\n    <string name=\"storage_file_system_root_title\">根目录</string>\n    <string name=\"storage_list_title\">存储空间</string>\n    <string name=\"storage_list_empty\">无存储空间</string>\n    <string name=\"storage_add_storage_title\">添加存储空间</string>\n    <string name=\"storage_add_storage_document_tree\">外部存储空间</string>\n    <string name=\"storage_add_storage_ftp_server\">FTP 服务器</string>\n    <string name=\"storage_add_storage_sftp_server\">SFTP 服务器</string>\n    <string name=\"storage_add_storage_smb_server\">SMB 服务器</string>\n    <string name=\"storage_add_storage_webdav_server\">WebDAV 服务器</string>\n    <string name=\"storage_edit_device_storage_title\">编辑设备存储空间</string>\n    <string name=\"storage_edit_device_storage_name\">名称</string>\n    <string name=\"storage_edit_device_storage_path\">路径</string>\n    <string name=\"storage_add_external_storage_shortcut_title\">添加外部存储空间快捷方式</string>\n    <string name=\"storage_edit_external_storage_shortcut_title\">编辑外部存储空间快捷方式</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id\">存储卷</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id_error_empty\">输入存储卷</string>\n    <string name=\"storage_edit_external_storage_shortcut_path\">路径</string>\n    <string name=\"storage_add_document_tree_title\">添加外部存储空间</string>\n    <string name=\"storage_edit_document_tree_title\">编辑外部存储空间</string>\n    <string name=\"storage_edit_document_tree_name\">名称</string>\n    <string name=\"storage_edit_document_tree_uri\">URI</string>\n    <string name=\"storage_edit_document_tree_path\">路径</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">编辑 FTP 服务器</string>\n    <string name=\"storage_edit_ftp_server_title_add\">添加 FTP 服务器</string>\n    <string name=\"storage_edit_ftp_server_host\">主机名</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">输入主机名</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">无效主机名</string>\n    <string name=\"storage_edit_ftp_server_port\">端口</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">无效端口</string>\n    <string name=\"storage_edit_ftp_server_path\">路径</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">可留空</string>\n    <string name=\"storage_edit_ftp_server_name\">名称</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">使用主机名</string>\n    <string name=\"storage_edit_ftp_server_protocol\">协议</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">验证</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>密码</item>\n        <item>匿名</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">用户名</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">输入用户名</string>\n    <string name=\"storage_edit_ftp_server_password\">密码</string>\n    <string name=\"storage_edit_ftp_server_mode\">模式</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>主动</item>\n        <item>被动</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">编码</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">连接并添加</string>\n    <string name=\"storage_edit_ftp_server_add\">添加</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">编辑 SFTP 服务器</string>\n    <string name=\"storage_edit_sftp_server_title_add\">添加 SFTP 服务器</string>\n    <string name=\"storage_edit_sftp_server_host\">主机名</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">输入主机名</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">无效主机名</string>\n    <string name=\"storage_edit_sftp_server_port\">端口</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">无效端口</string>\n    <string name=\"storage_edit_sftp_server_path\">路径</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">可留空</string>\n    <string name=\"storage_edit_sftp_server_name\">名称</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">使用主机名</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">验证</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>密码</item>\n        <item>公钥</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">用户名</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">输入用户名</string>\n    <string name=\"storage_edit_sftp_server_password\">密码</string>\n    <string name=\"storage_edit_sftp_server_private_key\">私钥</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">打开文件</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">输入私钥</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">无效私钥</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">私钥密码</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">可留空</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">无效私钥密码</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">连接并添加</string>\n    <string name=\"storage_edit_sftp_server_add\">添加</string>\n    <string name=\"storage_add_lan_smb_server_loading\">正在搜索 SMB 服务器…</string>\n    <string name=\"storage_add_lan_smb_server_add\">手动添加</string>\n    <string name=\"storage_edit_smb_server_title_edit\">编辑 SMB 服务器</string>\n    <string name=\"storage_edit_smb_server_title_add\">添加 SMB 服务器</string>\n    <string name=\"storage_edit_smb_server_host\">主机名</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">输入主机名</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">无效主机名</string>\n    <string name=\"storage_edit_smb_server_port\">端口</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">无效端口</string>\n    <string name=\"storage_edit_smb_server_path\">路径</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">可留空</string>\n    <string name=\"storage_edit_smb_server_name\">名称</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">使用主机名</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">验证</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>密码</item>\n        <item>来宾</item>\n        <item>匿名</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">用户名</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">输入用户名</string>\n    <string name=\"storage_edit_smb_server_password\">密码</string>\n    <string name=\"storage_edit_smb_server_domain\">域</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">连接并添加</string>\n    <string name=\"storage_edit_smb_server_add\">添加</string>\n    <string name=\"storage_edit_webdav_server_title_edit\">编辑 WebDAV 服务器</string>\n    <string name=\"storage_edit_webdav_server_title_add\">添加 WebDAV 服务器</string>\n    <string name=\"storage_edit_webdav_server_host\">主机名</string>\n    <string name=\"storage_edit_webdav_server_host_error_empty\">输入主机名</string>\n    <string name=\"storage_edit_webdav_server_host_error_invalid\">无效主机名</string>\n    <string name=\"storage_edit_webdav_server_port\">端口</string>\n    <string name=\"storage_edit_webdav_server_port_error_invalid\">无效端口</string>\n    <string name=\"storage_edit_webdav_server_path\">路径</string>\n    <string name=\"storage_edit_webdav_server_path_placeholder\">可留空</string>\n    <string name=\"storage_edit_webdav_server_name\">名称</string>\n    <string name=\"storage_edit_webdav_server_name_placeholder\">使用主机名</string>\n    <string name=\"storage_edit_webdav_server_protocol\">协议</string>\n    <string name=\"storage_edit_webdav_server_authentication_type\">验证</string>\n    <string-array name=\"storage_edit_webdav_server_authentication_type_entries\">\n        <item>密码</item>\n        <item>访问令牌</item>\n        <item>无</item>\n    </string-array>\n    <string name=\"storage_edit_webdav_server_username\">用户名</string>\n    <string name=\"storage_edit_webdav_server_username_error_empty\">输入用户名</string>\n    <string name=\"storage_edit_webdav_server_password\">密码</string>\n    <string name=\"storage_edit_webdav_server_access_token\">访问令牌</string>\n    <string name=\"storage_edit_webdav_server_access_token_error_empty\">输入访问令牌</string>\n    <string name=\"storage_edit_webdav_server_connect_and_add\">连接并添加</string>\n    <string name=\"storage_edit_webdav_server_add\">添加</string>\n\n    <string name=\"navigation_storage_subtitle_format\">共 %2$s，剩余 %1$s</string>\n    <string name=\"navigation_add_storage\">添加存储空间…</string>\n    <string name=\"navigation_standard_directory_alarms\">闹钟</string>\n    <string name=\"navigation_standard_directory_dcim\">相片</string>\n    <string name=\"navigation_standard_directory_documents\">文档</string>\n    <string name=\"navigation_standard_directory_downloads\">下载</string>\n    <string name=\"navigation_standard_directory_movies\">电影</string>\n    <string name=\"navigation_standard_directory_music\">音乐</string>\n    <string name=\"navigation_standard_directory_notifications\">通知</string>\n    <string name=\"navigation_standard_directory_pictures\">图片</string>\n    <string name=\"navigation_standard_directory_podcasts\">播客</string>\n    <string name=\"navigation_standard_directory_ringtones\">铃声</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">微信</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">书签文件夹</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">名称</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">路径</string>\n\n    <string name=\"archive_viewer_title\">归档文件查看器</string>\n    <string name=\"image_viewer_title\">图片查看器</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"save_as_title\">另存为</string>\n    <string name=\"save_as_error\">保存时发生错误</string>\n    <string name=\"save_as_success_format\">已将“%1$s”保存到“%2$s”</string>\n    <string name=\"text_editor_title\">文本编辑器</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">已保存</string>\n    <string name=\"text_editor_reload_message\">您确定要重新加载吗？对此文档未保存的变更将丢失。</string>\n    <string name=\"text_editor_encoding\">编码</string>\n    <string name=\"text_editor_close_message\">您确定要舍弃对此文档未保存的变更吗？</string>\n\n    <string name=\"ftp_server_title\">FTP 服务器</string>\n    <string name=\"ftp_server_state_title\">状态</string>\n    <string name=\"ftp_server_state_summary_starting\">正在启动…</string>\n    <string name=\"ftp_server_state_summary_running\">正在运行</string>\n    <string name=\"ftp_server_state_summary_stopping\">正在停止…</string>\n    <string name=\"ftp_server_state_summary_stopped\">未启动</string>\n    <string name=\"ftp_server_url_title\">网址</string>\n    <string name=\"ftp_server_url_summary_no_local_inet_address\">本地 IP 地址未知</string>\n    <string name=\"ftp_server_url_menu_copy_url\">复制网址</string>\n    <string name=\"ftp_server_url_menu_copy_password\">复制密码</string>\n    <string name=\"ftp_server_add_tile_title\">添加到“快捷设置”</string>\n    <string name=\"ftp_server_add_tile_result_added\">已将“FTP 服务器”添加到“快捷设置”</string>\n    <string name=\"ftp_server_add_tile_result_already_added\">“FTP 服务器”已被添加到“快捷设置”</string>\n    <string name=\"ftp_server_add_tile_result_error\">将“FTP 服务器”添加到“快捷设置”时发生错误</string>\n    <string name=\"ftp_server_configuration_title\">配置</string>\n    <string name=\"ftp_server_anonymous_login_title\">匿名登录</string>\n    <string name=\"ftp_server_username_title\">用户名</string>\n    <string name=\"ftp_server_password_title\">密码</string>\n    <string name=\"ftp_server_port_title\">端口</string>\n    <string name=\"ftp_server_home_directory_title\">根文件夹</string>\n    <string name=\"ftp_server_writable_title\">允许写入</string>\n\n    <string name=\"settings_title\">设置</string>\n    <string name=\"settings_interface_title\">界面</string>\n    <string name=\"settings_locale_title\">语言</string>\n    <string name=\"settings_theme_color_title\">主题色</string>\n    <string name=\"settings_theme_color_summary\">应用中最常见的颜色</string>\n    <string name=\"settings_material_design_3_title\">质感设计 3</string>\n    <string name=\"settings_night_mode_title\">夜间模式</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>跟随系统</item>\n        <item>关闭</item>\n        <item>开启</item>\n        <item>基于时间</item>\n        <item>基于省电模式</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">黑色夜间模式</string>\n    <string name=\"settings_file_list_animation_title\">文件列表动画</string>\n    <string name=\"settings_file_name_ellipsize_title\">显示长文件名</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>省略开头</item>\n        <item>省略中间</item>\n        <item>省略结尾</item>\n        <item>滚动</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">行为</string>\n    <string name=\"settings_default_directory_title\">默认文件夹</string>\n    <string name=\"settings_standard_directories_title\">标准文件夹</string>\n    <string name=\"settings_standard_directories_summary_empty\">未启用标准文件夹</string>\n    <string name=\"settings_bookmark_directory_screenshots\">屏幕截图</string>\n    <string name=\"settings_bookmark_directories_title\">书签文件夹</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">无书签文件夹</string>\n    <string name=\"settings_root_strategy_title\">Root 访问模式</string>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>仅普通访问</item>\n        <item>自动</item>\n        <item>仅 root 访问</item>\n    </string-array>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"other\">您有 %1$,d 个正在运行的文件操作，因此现在更改 root 访问模式可能会导致意外错误。您确定要现在更改它吗？</item>\n    </plurals>\n    <string name=\"settings_archive_file_name_encoding_title\">归档文件名编码</string>\n    <string name=\"settings_open_apk_default_action_title\">打开 Android 安装包</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>安装</item>\n        <item>查看内容</item>\n        <item>询问操作</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_title\">显示 PDF 文档缩略图</string>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">可能导致应用在旧版 Android 上不稳定</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">读取远程文件以显示缩略图</string>\n\n    <string name=\"about_title\">关于</string>\n    <string name=\"about_version_title\">版本</string>\n    <string name=\"about_github_title\">在 GitHub 上查看</string>\n    <string name=\"about_licenses_title\">许可证</string>\n    <string name=\"about_privacy_policy_title\">隐私权政策</string>\n    <string name=\"about_author_title\">作者</string>\n    <string name=\"about_author_name_title\">张海</string>\n    <string name=\"about_author_github_title\">在 GitHub 上关注</string>\n    <string name=\"about_author_twitter_title\">在 Twitter 上关注</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-zh-rTW/mime_types.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n    <string name=\"file_type_name_unknown\">檔案</string>\n    <string name=\"file_type_name_apk\">Android 套件</string>\n    <string name=\"file_type_name_archive\">%1$s 封存檔</string>\n    <string name=\"file_type_name_audio\">%1$s 音訊</string>\n    <string name=\"file_type_name_calendar\">%1$s 日曆</string>\n    <string name=\"file_type_name_certificate\">%1$s 憑證</string>\n    <string name=\"file_type_name_code\">%1$s 文件</string>\n    <string name=\"file_type_name_contact\">電子名片</string>\n    <string name=\"file_type_name_directory\">資料夾</string>\n    <string name=\"file_type_name_document\">%1$s 文件</string>\n    <string name=\"file_type_name_ebook\">%1$s 電子書</string>\n    <string name=\"file_type_name_email\">%1$s 電子郵件</string>\n    <string name=\"file_type_name_font\">%1$s 字型</string>\n    <string name=\"file_type_name_generic\">%1$s 檔案</string>\n    <string name=\"file_type_name_image\">%1$s 圖片</string>\n    <string name=\"file_type_name_pdf\">PDF 文件</string>\n    <string name=\"file_type_name_presentation\">%1$s 簡報</string>\n    <string name=\"file_type_name_spreadsheet\">%1$s 試算表</string>\n    <string name=\"file_type_name_text\">%1$s 文件</string>\n    <string name=\"file_type_name_text_plain\">純文字文件</string>\n    <string name=\"file_type_name_video\">%1$s 影片</string>\n    <string name=\"file_type_name_word\">Word 文件</string>\n    <string name=\"file_type_name_powerpoint\">PowerPoint 簡報</string>\n    <string name=\"file_type_name_excel\">Excel 試算表</string>\n    <string name=\"file_type_name_posix_character_device\">字元裝置</string>\n    <string name=\"file_type_name_posix_block_device\">區塊裝置</string>\n    <string name=\"file_type_name_posix_fifo\">管道</string>\n    <string name=\"file_type_name_posix_symbolic_link\">連結</string>\n    <string name=\"file_type_name_posix_socket\">通訊端</string>\n    <string name=\"file_type_name_posix_symbolic_link_broken\">連結（已損壞）</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-zh-rTW/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<resources>\n\n    <string name=\"app_name\">質感檔案</string>\n\n    <string name=\"close\">關閉</string>\n    <string name=\"copy\">複製</string>\n    <string name=\"cut\">剪下</string>\n    <string name=\"default_\">預設</string>\n    <string name=\"delete\">刪除</string>\n    <string name=\"discard\">捨棄</string>\n    <string name=\"edit\">編輯</string>\n    <string name=\"empty\">空</string>\n    <string name=\"empty_placeholder\">（空）</string>\n    <string name=\"error\">錯誤</string>\n    <string name=\"hide\">隱藏</string>\n    <string name=\"install\">安裝</string>\n    <string name=\"keep_editing\">繼續編輯</string>\n    <string name=\"loading\">正在載入…</string>\n    <string name=\"maybe_later\">稍後再說</string>\n    <string name=\"merge\">合併</string>\n    <string name=\"none\">無</string>\n    <string name=\"open_navigation_drawer\">開啟導覽匣</string>\n    <string name=\"paste\">貼上</string>\n    <string name=\"refresh\">更新</string>\n    <string name=\"reload\">重新載入</string>\n    <string name=\"remove\">移除</string>\n    <string name=\"rename\">重新命名</string>\n    <string name=\"replace\">取代</string>\n    <string name=\"reset\">重設</string>\n    <string name=\"retry\">重試</string>\n    <string name=\"save\">儲存</string>\n    <string name=\"search\">搜尋</string>\n    <string name=\"select\">選取</string>\n    <string name=\"select_all\">全選</string>\n    <string name=\"share\">分享</string>\n    <string name=\"show\">顯示</string>\n    <string name=\"skip\">跳過</string>\n    <string name=\"stop\">停止</string>\n    <string name=\"system_default\">系統預設</string>\n    <string name=\"unknown\">不明</string>\n    <string name=\"view\">查看</string>\n\n    <!-- android.R.string.app_not_found -->\n    <string name=\"activity_not_found\">未找到可處理此操作的程式</string>\n    <string name=\"open_settings\">開啟設定</string>\n    <string name=\"shortcut_created\">已建立捷徑</string>\n\n    <plurals name=\"size_in_bytes_format\">\n        <item quantity=\"other\">%1$,d 位元組</item>\n    </plurals>\n\n    <string name=\"all_files_access_rationale_message\">程式需要管理所有檔案的權限。請在接下來的系統設定中授予權限。</string>\n    <string name=\"storage_permission_rationale_message\">程式需要存取檔案的權限。請在接下來的系統對話框中點擊「允許」。</string>\n    <string name=\"storage_permission_permanently_denied_message\">程式需要存取檔案的權限。請在系統設定中授予「儲存空間」權限。</string>\n    <string name=\"notification_permission_rationale_message\">程式需要發布檔案作業相關通知的權限。請在接下來的系統對話框中點擊「允許」。</string>\n    <string name=\"notification_permission_permanently_denied_message\">程式需要發布檔案作業相關通知的權限。請在系統設定中授予「通知」權限。</string>\n\n    <string name=\"notification_channel_background_activity_start_name\">背景期間動作</string>\n    <string name=\"notification_channel_background_activity_start_description\">在應用程式處於背景期間採取動作</string>\n    <string name=\"notification_channel_file_job_name\">檔案作業</string>\n    <string name=\"notification_channel_file_job_description\">顯示和控制檔案作業</string>\n    <string name=\"notification_channel_ftp_server_name\">FTP 伺服器</string>\n    <string name=\"notification_channel_ftp_server_description\">顯示和控制 FTP 伺服器</string>\n\n    <string name=\"file_action_archive_password_title\">需要密碼</string>\n    <string name=\"file_action_archive_password_message_format\">“%1$s”受密碼保護。</string>\n    <string name=\"file_action_archive_password_error_empty\">密碼不能為空</string>\n\n    <plurals name=\"file_job_archive_scan_notification_title_format\">\n        <item quantity=\"other\">正在準備壓縮 %1$,d 個檔案（%2$s）</item>\n    </plurals>\n    <string name=\"file_job_archive_notification_title_one_format\">正在將「%1$s」壓縮進「%2$s」</string>\n    <plurals name=\"file_job_archive_notification_title_multiple_format\">\n        <item quantity=\"other\">正在將 %1$,d 個檔案壓縮進「%2$s」</item>\n    </plurals>\n    <plurals name=\"file_job_copy_scan_notification_title_format\">\n        <item quantity=\"other\">正在準備複製 %1$,d 個檔案（%2$s）</item>\n    </plurals>\n    <string name=\"file_job_copy_notification_title_one_format\">正在將「%1$s」複製到「%2$s」</string>\n    <plurals name=\"file_job_copy_notification_title_multiple_format\">\n        <item quantity=\"other\">正在將 %1$,d 個檔案複製到「%2$s」</item>\n    </plurals>\n    <plurals name=\"file_job_extract_scan_notification_title_format\">\n        <item quantity=\"other\">正在準備解開 %1$,d 個檔案（%2$s）</item>\n    </plurals>\n    <string name=\"file_job_extract_notification_title_one_format\">正在將「%1$s」解開到「%2$s」</string>\n    <plurals name=\"file_job_extract_notification_title_multiple_format\">\n        <item quantity=\"other\">正在將 %1$,d 個檔案解開到「%2$s」</item>\n    </plurals>\n    <plurals name=\"file_job_move_scan_notification_title_format\">\n        <item quantity=\"other\">正在準備移動 %1$,d 個檔案（%2$s）</item>\n    </plurals>\n    <string name=\"file_job_move_notification_title_one_format\">正在將「%1$s」移動到「%2$s」</string>\n    <plurals name=\"file_job_move_notification_title_multiple_format\">\n        <item quantity=\"other\">正在將 %1$,d 個檔案移動到「%2$s」</item>\n    </plurals>\n    <string name=\"file_job_transfer_size_notification_text_one_format\">%1$s / %2$s</string>\n    <string name=\"file_job_transfer_size_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <plurals name=\"file_job_delete_scan_notification_title_format\">\n        <item quantity=\"other\">正在準備刪除 %1$,d 個檔案</item>\n    </plurals>\n    <string name=\"file_job_delete_notification_title_one_format\">正在刪除「%1$s」</string>\n    <plurals name=\"file_job_delete_notification_title_multiple_format\">\n        <item quantity=\"other\">正在刪除 %1$,d 個檔案</item>\n    </plurals>\n    <plurals name=\"file_job_set_owner_scan_notification_title_format\">\n        <item quantity=\"other\">正在準備變更 %1$,d 個檔案的擁有者</item>\n    </plurals>\n    <string name=\"file_job_set_owner_notification_title_one_format\">正在變更「%1$s」的擁有者</string>\n    <plurals name=\"file_job_set_owner_notification_title_multiple_format\">\n        <item quantity=\"other\">正在變更 %1$,d 個檔案的擁有者</item>\n    </plurals>\n    <plurals name=\"file_job_set_group_scan_notification_title_format\">\n        <item quantity=\"other\">正在準備變更 %1$,d 個檔案的群組</item>\n    </plurals>\n    <string name=\"file_job_set_group_notification_title_one_format\">正在變更「%1$s」的群組</string>\n    <plurals name=\"file_job_set_group_notification_title_multiple_format\">\n        <item quantity=\"other\">正在變更 %1$,d 個檔案的群組</item>\n    </plurals>\n    <plurals name=\"file_job_set_mode_scan_notification_title_format\">\n        <item quantity=\"other\">正在準備變更 %1$,d 個檔案的模式</item>\n    </plurals>\n    <string name=\"file_job_set_mode_notification_title_one_format\">正在變更「%1$s」的模式</string>\n    <plurals name=\"file_job_set_mode_notification_title_multiple_format\">\n        <item quantity=\"other\">正在變更 %1$,d 個檔案的模式</item>\n    </plurals>\n    <plurals name=\"file_job_set_selinux_context_scan_notification_title_format\">\n        <item quantity=\"other\">正在準備變更 %1$,d 個檔案的 SELinux 上下文</item>\n    </plurals>\n    <string name=\"file_job_set_selinux_context_notification_title_one_format\">正在變更「%1$s」的 SELinux 上下文</string>\n    <plurals name=\"file_job_set_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"other\">正在變更 %1$,d 個檔案的 SELinux 上下文</item>\n    </plurals>\n    <plurals name=\"file_job_restore_selinux_context_scan_notification_title_format\">\n        <item quantity=\"other\">正在準備還原 %1$,d 個檔案的 SELinux 上下文</item>\n    </plurals>\n    <string name=\"file_job_restore_selinux_context_notification_title_one_format\">正在還原「%1$s」的 SELinux 上下文</string>\n    <plurals name=\"file_job_restore_selinux_context_notification_title_multiple_format\">\n        <item quantity=\"other\">正在還原 %1$,d 個檔案的 SELinux 上下文</item>\n    </plurals>\n    <string name=\"file_job_transfer_count_notification_text_multiple_format\">%1$,d / %2$,d</string>\n    <string name=\"file_job_write_notification_title_format\">正在寫入「%1$s」</string>\n    <string name=\"file_job_create_error_title\">建立時發生錯誤</string>\n    <string name=\"file_job_create_error_message_format\">在建立「%1$s」時發生錯誤。\\n%2$s</string>\n    <string name=\"file_job_cannot_copy_into_itself_title\">無法將資料夾複製到自身</string>\n    <string name=\"file_job_cannot_extract_into_itself_title\">無法將資料夾解開到自身</string>\n    <string name=\"file_job_cannot_move_into_itself_title\">無法將資料夾移動到自身</string>\n    <string name=\"file_job_cannot_copy_move_into_itself_message\">目標資料夾位於來源資料夾內。</string>\n    <string name=\"file_job_cannot_copy_over_itself_title\">無法將檔案複製到自身</string>\n    <string name=\"file_job_cannot_extract_over_itself_title\">無法將檔案解開到自身</string>\n    <string name=\"file_job_cannot_move_over_itself_title\">無法將檔案移動到自身</string>\n    <string name=\"file_job_cannot_copy_move_over_itself_message\">來源檔案將被目標覆蓋。</string>\n    <string name=\"file_job_replace_title_format\">取代檔案「%1$s」？</string>\n    <string name=\"file_job_replace_message_format\">在「%1$s」中存在相同名稱的另一個檔案。\\n取代將覆蓋它的內容。</string>\n    <string name=\"file_job_replace_target_name\">原始檔案</string>\n    <string name=\"file_job_replace_source_name\">取代為</string>\n    <string name=\"file_job_merge_title_format\">合併資料夾「%1$s」？</string>\n    <string name=\"file_job_merge_copy_message_format\">檔案中有檔案與正在被複製的檔案衝突時，合併將在取代前請求確認。</string>\n    <string name=\"file_job_merge_extract_message_format\">檔案中有檔案與正在被解開的檔案衝突時，合併將在取代前請求確認。</string>\n    <string name=\"file_job_merge_move_message_format\">檔案中有檔案與正在被移動的檔案衝突時，合併將在取代前請求確認。</string>\n    <string name=\"file_job_merge_target_name\">原始資料夾</string>\n    <string name=\"file_job_merge_source_name\">合併</string>\n    <string name=\"file_job_conflict_show_name\">為目標選取新的名稱</string>\n    <string name=\"file_job_conflict_name_hint\">新名稱</string>\n    <string name=\"file_job_archive_error_title_format\">壓縮「%1$s」時發生錯誤</string>\n    <string name=\"file_job_archive_error_message_format\">將檔案壓縮進「%1$s」時發生錯誤。\\n%2$s</string>\n    <string name=\"file_job_copy_error_title_format\">複製「%1$s」時發生錯誤</string>\n    <string name=\"file_job_copy_error_message_format\">將檔案複製到「%1$s」時發生錯誤。\\n%2$s</string>\n    <string name=\"file_job_extract_error_title_format\">解開「%1$s」時發生錯誤</string>\n    <string name=\"file_job_extract_error_message_format\">將檔案解開到「%1$s」時發生錯誤。\\n%2$s</string>\n    <string name=\"file_job_move_error_title_format\">移動「%1$s」時發生錯誤</string>\n    <string name=\"file_job_move_error_message_format\">將檔案移動到「%1$s」時發生錯誤。\\n%2$s</string>\n    <string name=\"file_job_delete_error_title\">刪除時發生錯誤</string>\n    <string name=\"file_job_delete_error_message_format\">在刪除「%1$s」時發生錯誤。\\n%2$s</string>\n    <string name=\"file_job_rename_error_title_format\">重新命名「%1$s」時發生錯誤</string>\n    <string name=\"file_job_rename_error_message_format\">將檔案重新命名為「%1$s」時發生錯誤。\\n%2$s</string>\n    <string name=\"file_job_set_owner_error_title_format\">變更「%1$s」的擁有者時發生錯誤</string>\n    <string name=\"file_job_set_owner_error_message_format\">將擁有者變更為「%1$s」時發生錯誤。\\n%2$s</string>\n    <string name=\"file_job_set_group_error_title_format\">變更「%1$s」的群組時發生錯誤</string>\n    <string name=\"file_job_set_group_error_message_format\">將群組變更為「%1$s」時發生錯誤。\\n%2$s</string>\n    <string name=\"file_job_set_mode_error_title_format\">變更「%1$s」的模式時發生錯誤</string>\n    <string name=\"file_job_set_mode_error_message_format\">將模式變更為「%1$s」時發生錯誤。\\n%2$s</string>\n    <string name=\"file_job_set_selinux_context_error_title_format\">變更「%1$s」的 SELinux 上下文時發生錯誤</string>\n    <string name=\"file_job_set_selinux_context_error_message_format\">將 SELinux 上下文變更為「%1$s」時發生錯誤。\\n%2$s</string>\n    <string name=\"file_job_restore_selinux_context_error_title\">還原 SELinux 上下文時發生錯誤</string>\n    <string name=\"file_job_restore_selinux_context_error_message_format\">在還原「%1$s」的 SELinux 上下文時發生錯誤。\\n%2$s</string>\n    <string name=\"file_job_write_error_title\">寫入時發生錯誤</string>\n    <string name=\"file_job_write_error_message_format\">在寫入「%1$s」時發生錯誤。\\n%2$s</string>\n    <string name=\"file_job_get_information_error_message_format\">在取得「%1$s」的資訊時發生錯誤。</string>\n    <string name=\"file_job_remount_format\">重新掛接「%1$s」為讀寫模式</string>\n    <string name=\"file_job_remount_loading_format\">正在重新掛接「%1$s」為讀寫模式…</string>\n    <string name=\"file_job_remount_success_format\">「%1$s」已被重新掛接為讀寫模式</string>\n    <string name=\"file_job_action_all\">套用此操作到所有檔案</string>\n\n    <string name=\"file_open_apk_message\">這個檔案是一個 Android 套件。您想要安裝它，還是查看它的內容？</string>\n    <string name=\"file_open_as_title\">開啟為…</string>\n    <string name=\"file_open_as_title_format\">將「%1$s」開啟為</string>\n    <string name=\"file_open_as_type_text\">文字</string>\n    <string name=\"file_open_as_type_image\">圖片</string>\n    <string name=\"file_open_as_type_audio\">音訊</string>\n    <string name=\"file_open_as_type_video\">影片</string>\n    <string name=\"file_open_as_type_directory\">資料夾</string>\n    <string name=\"file_open_as_type_any\">其他</string>\n    <string name=\"file_install_apk_from_background_title_format\">「%1$s」已可供安裝</string>\n    <string name=\"file_install_apk_from_background_text\">輕觸即可安裝</string>\n    <string name=\"file_open_from_background_title_format\">「%1$s」已可供開啟</string>\n    <string name=\"file_open_from_background_text\">輕觸即可開啟</string>\n    <string name=\"file_name_error_empty\">檔案名稱不能為空</string>\n    <string name=\"file_name_error_invalid\">無效的檔案名稱</string>\n    <string name=\"file_name_error_already_exists\">已存在同名的檔案</string>\n    <string name=\"file_replace_message_format\">取代「%1$s」？</string>\n    <string name=\"file_delete_message_file_format\">刪除「%1$s」？</string>\n    <string name=\"file_delete_message_directory_format\">刪除資料夾「%1$s」和它的內容？</string>\n    <plurals name=\"file_delete_message_multiple_files_format\">\n        <item quantity=\"other\">刪除 %1$,d 個檔案？</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_directories_format\">\n        <item quantity=\"other\">刪除 %1$,d 個資料夾和它們的內容？</item>\n    </plurals>\n    <plurals name=\"file_delete_message_multiple_mixed_format\">\n        <item quantity=\"other\">刪除 %1$,d 個檔案和資料夾？</item>\n    </plurals>\n    <string name=\"file_create_archive_title\">建立壓縮檔案</string>\n    <string name=\"file_create_archive_type_zip\">.zip</string>\n    <string name=\"file_create_archive_type_tar_xz\">.tar.xz</string>\n    <string name=\"file_create_archive_type_7z\">.7z</string>\n    <string name=\"file_create_archive_password\">密碼（選填）</string>\n    <string name=\"file_add_bookmark_success\">已加入書籤</string>\n    <string name=\"file_create_file_title\">新檔案</string>\n    <string name=\"file_create_directory_title\">新資料夾</string>\n\n    <string name=\"file_list_title\">檔案</string>\n    <plurals name=\"file_list_title_open_file\">\n        <item quantity=\"other\">選取檔案</item>\n    </plurals>\n    <string name=\"file_list_title_create_file\">儲存檔案</string>\n    <plurals name=\"file_list_title_open_directory\">\n        <item quantity=\"other\">選取資料夾</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_directory_count_format\">\n        <item quantity=\"other\">%1$,d 個資料夾</item>\n    </plurals>\n    <plurals name=\"file_list_subtitle_file_count_format\">\n        <item quantity=\"other\">%1$,d 個檔案</item>\n    </plurals>\n    <string name=\"file_list_subtitle_separator\">，</string>\n    <string name=\"file_list_path_error_empty\">路徑不能為空</string>\n    <string name=\"file_list_path_error_invalid\">無效的路徑</string>\n    <string name=\"file_list_action_view_sort\">檢視和排序</string>\n    <string name=\"file_list_action_view_list\">清單</string>\n    <string name=\"file_list_action_view_grid\">網格</string>\n    <string name=\"file_list_action_sort_by_name\">名稱</string>\n    <string name=\"file_list_action_sort_by_type\">類型</string>\n    <string name=\"file_list_action_sort_by_size\">大小</string>\n    <string name=\"file_list_action_sort_by_last_modified\">最後修改</string>\n    <string name=\"file_list_action_sort_order_ascending\">升序</string>\n    <string name=\"file_list_action_sort_directories_first\">資料夾優先</string>\n    <string name=\"file_list_action_view_sort_path_specific\">僅用於此資料夾</string>\n    <string name=\"file_list_action_new_task\">新視窗</string>\n    <string name=\"file_list_action_navigate_up\">向上</string>\n    <string name=\"file_list_action_navigate_to\">移至</string>\n    <string name=\"file_list_action_show_hidden_files\">顯示隱藏檔案</string>\n    <string name=\"file_list_action_copy_path\">複製路徑</string>\n    <string name=\"file_list_action_open_in_terminal\">在終端中開啟</string>\n    <string name=\"file_list_action_add_bookmark\">加入書籤</string>\n    <string name=\"file_list_action_create_shortcut\">建立捷徑</string>\n    <string name=\"file_list_breadcrumb_action_open_in_new_task\">在新視窗中開啟</string>\n    <string name=\"file_list_select_title_format\">%1$,d</string>\n    <string name=\"file_list_create_file_name_hint\">檔案名稱</string>\n    <string name=\"file_list_open_current_directory_format\">選取「%1$s」</string>\n    <string name=\"file_list_paste_move_title_format\">移動 %1$,d</string>\n    <string name=\"file_list_paste_copy_title_format\">複製 %1$,d</string>\n    <string name=\"file_list_paste_extract_title_format\">解開 %1$,d</string>\n    <string name=\"file_list_paste_action_extract_here\">在這裡解開</string>\n    <string name=\"file_list_empty\">無檔案</string>\n    <string name=\"file_list_action_create_file\">檔案</string>\n    <string name=\"file_list_action_create_directory\">資料夾</string>\n\n    <string name=\"file_item_description_separator\">\"    \"</string>\n    <string name=\"file_item_action_open_with\">開啟方式</string>\n    <string name=\"file_item_action_extract\">解開</string>\n    <string name=\"file_item_action_archive\">壓縮</string>\n    <string name=\"file_item_action_properties\">屬性</string>\n\n    <string name=\"file_properties_title_format\">「%1$s」屬性</string>\n    <string name=\"file_properties_basic\">基本</string>\n    <string name=\"file_properties_basic_name\">名稱</string>\n    <string name=\"file_properties_basic_type\">類型</string>\n    <string name=\"file_properties_basic_type_format\">%1$s（%2$s）</string>\n    <string name=\"file_properties_basic_type_symbolic_link_format\">到 %1$s 的連結（%2$s）</string>\n    <string name=\"file_properties_basic_symbolic_link_target\">連結目標</string>\n    <string name=\"file_properties_basic_size\">大小</string>\n    <string name=\"file_properties_basic_size_with_human_readable_format\">%1$s（%2$s）</string>\n    <string name=\"file_properties_basic_contents\">內容</string>\n    <plurals name=\"file_properties_basic_contents_format\">\n        <item quantity=\"other\">%1$,d 個項目，總計 %2$s</item>\n    </plurals>\n    <string name=\"file_properties_basic_last_modification_time\">最後修改</string>\n    <string name=\"file_properties_basic_parent_directory\">父資料夾</string>\n    <string name=\"file_properties_basic_archive_file\">封存檔</string>\n    <string name=\"file_properties_basic_archive_entry\">封存條目</string>\n    <string name=\"file_properties_basic_free_space\">可用空間</string>\n    <string name=\"file_properties_permission\">權限</string>\n    <string name=\"file_properties_permission_owner\">擁有者</string>\n    <string name=\"file_properties_permission_group\">群組</string>\n    <string name=\"file_properties_permission_principal_format\">%1$s（%2$d）</string>\n    <string name=\"file_properties_permission_set_owner_title\">變更擁有者</string>\n    <string name=\"file_properties_permission_set_group_title\">變更群組</string>\n    <string name=\"file_properties_permission_set_principal_filter_hint\">輸入名稱或 ID</string>\n    <string name=\"file_properties_permission_set_principal_system\">系統</string>\n    <string name=\"file_properties_permission_mode\">模式</string>\n    <string name=\"file_properties_permission_mode_format\">%1$s（%2$04o）</string>\n    <string name=\"file_properties_permission_set_mode_title\">變更模式</string>\n    <string name=\"file_properties_permission_set_mode_others\">其他</string>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_file\">\n        <item>讀取</item>\n        <item>寫入</item>\n        <item>執行</item>\n    </string-array>\n    <string-array name=\"file_properties_permission_set_mode_normal_mode_bits_directory\">\n        <item>讀取</item>\n        <item>寫入</item>\n        <item>搜尋</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_special\">特殊</string>\n    <string-array name=\"file_properties_permission_set_mode_special_mode_bits\">\n        <item>設定 UID</item>\n        <item>設定 GID</item>\n        <item>粘滯位</item>\n    </string-array>\n    <string name=\"file_properties_permission_set_mode_uppercase_x\">不要為包含的檔案新增「執行」</string>\n    <string name=\"file_properties_permission_selinux_context\">SELinux 上下文</string>\n    <string name=\"file_properties_permission_set_selinux_context_title\">變更 SELinux 上下文</string>\n    <string name=\"file_properties_permission_set_selinux_context_restore\">還原</string>\n    <string name=\"file_properties_permission_recursive\">套用到包含的檔案</string>\n    <string name=\"file_properties_image\">圖片</string>\n    <string name=\"file_properties_media_dimensions\">尺寸</string>\n    <string name=\"file_properties_media_dimensions_format\">%1$d \\u00d7 %2$d</string>\n    <string name=\"file_properties_media_date_time\">拍攝時間</string>\n    <string name=\"file_properties_media_coordinates\">座標</string>\n    <string name=\"file_properties_media_coordinates_format\">%1$.3f，%2$.3f</string>\n    <string name=\"file_properties_media_address\">位置</string>\n    <string name=\"file_properties_image_gps_altitude\">高度</string>\n    <string name=\"file_properties_image_gps_altitude_format\">%1$,.3f 公尺</string>\n    <string name=\"file_properties_image_equipment\">相機</string>\n    <string name=\"file_properties_image_equipment_format\">%1$s %2$s</string>\n    <string name=\"file_properties_image_f_number\">光圈</string>\n    <string name=\"file_properties_image_shutter_speed\">快門速度</string>\n    <string name=\"file_properties_image_focal_length\">焦距</string>\n    <string name=\"file_properties_image_focal_length_format\">%1$.2f 毫米</string>\n    <string name=\"file_properties_image_photographic_sensitivity\">等效 ISO</string>\n    <string name=\"file_properties_image_photographic_sensitivity_format\">ISO %1$d</string>\n    <string name=\"file_properties_image_software\">軟體</string>\n    <string name=\"file_properties_image_description\">說明</string>\n    <string name=\"file_properties_image_artist\">建立者</string>\n    <string name=\"file_properties_image_copyright\">著作權</string>\n    <string name=\"file_properties_audio\">音訊</string>\n    <string name=\"file_properties_media_title\">標題</string>\n    <string name=\"file_properties_audio_artist\">演出者</string>\n    <string name=\"file_properties_audio_album\">專輯</string>\n    <string name=\"file_properties_audio_album_artist\">專輯演出者</string>\n    <string name=\"file_properties_audio_composer\">作曲者</string>\n    <string name=\"file_properties_audio_disc_number\">光碟編號</string>\n    <string name=\"file_properties_audio_track_number\">曲目編號</string>\n    <string name=\"file_properties_audio_year\">年份</string>\n    <string name=\"file_properties_audio_genre\">內容類型</string>\n    <string name=\"file_properties_media_duration\">時間長度</string>\n    <string name=\"file_properties_media_bit_rate\">位元率</string>\n    <string name=\"file_properties_media_bit_rate_format\">%1$d kbps</string>\n    <string name=\"file_properties_audio_sample_rate\">取樣率</string>\n    <string name=\"file_properties_audio_sample_rate_format\">%1$d Hz</string>\n    <string name=\"file_properties_video\">影片</string>\n    <string name=\"file_properties_apk\">套件</string>\n    <string name=\"file_properties_apk_label\">名稱</string>\n    <string name=\"file_properties_apk_package_name\">套件名稱</string>\n    <string name=\"file_properties_apk_version\">版本</string>\n    <string name=\"file_properties_apk_version_format\">%1$s（%2$d）</string>\n    <string name=\"file_properties_apk_min_sdk_version\">最低系統版本</string>\n    <string name=\"file_properties_apk_target_sdk_version\">目標系統版本</string>\n    <string name=\"file_properites_apk_sdk_version_format\">%1$s（%2$s，%3$d）</string>\n    <string name=\"file_properties_apk_requested_permissions\">權限</string>\n    <string name=\"file_properties_apk_requested_permissions_zero\">未要求任何權限</string>\n    <plurals name=\"file_properties_apk_requested_permissions_positive_format\">\n        <item quantity=\"other\">已要求 %1$,d 項權限</item>\n    </plurals>\n    <string name=\"file_properties_apk_signature_digests\">簽名</string>\n    <string name=\"file_properties_apk_signature_digests_empty\">無有效簽名</string>\n    <string name=\"file_properties_apk_past_signature_digests\">舊簽名</string>\n    <string name=\"file_properties_checksum\">總和檢查碼</string>\n    <string name=\"file_properties_checksum_compare\">比較</string>\n    <string name=\"file_properties_checksum_compare_placeholder\">任何總和檢查碼</string>\n    <string name=\"file_properties_checksum_compare_match_format\">與 %1$s 總和檢查碼相符</string>\n    <string name=\"file_properties_checksum_compare_prefix_match_format\">與 %1$s 總和檢查碼的前綴相符</string>\n    <string name=\"file_properties_checksum_compare_no_match\">找不到相符的總和檢查碼</string>\n\n    <string name=\"storage_file_system_root_title\">根目錄</string>\n    <string name=\"storage_list_title\">儲存空間</string>\n    <string name=\"storage_list_empty\">無儲存空間</string>\n    <string name=\"storage_add_storage_title\">新增儲存空間</string>\n    <string name=\"storage_add_storage_document_tree\">外部儲存空間</string>\n    <string name=\"storage_add_storage_ftp_server\">FTP 伺服器</string>\n    <string name=\"storage_add_storage_sftp_server\">SFTP 伺服器</string>\n    <string name=\"storage_add_storage_smb_server\">SMB 伺服器</string>\n    <string name=\"storage_add_storage_webdav_server\">WebDAV 伺服器</string>\n    <string name=\"storage_edit_device_storage_title\">編輯裝置儲存空間</string>\n    <string name=\"storage_edit_device_storage_name\">名稱</string>\n    <string name=\"storage_edit_device_storage_path\">路徑</string>\n    <string name=\"storage_add_external_storage_shortcut_title\">新增外部儲存空間捷徑</string>\n    <string name=\"storage_edit_external_storage_shortcut_title\">編輯外部儲存空間捷徑</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id\">儲存空間</string>\n    <string name=\"storage_edit_external_storage_shortcut_root_id_error_empty\">輸入儲存空間</string>\n    <string name=\"storage_edit_external_storage_shortcut_path\">路徑</string>\n    <string name=\"storage_add_document_tree_title\">新增外部儲存空間</string>\n    <string name=\"storage_edit_document_tree_title\">編輯外部儲存空間</string>\n    <string name=\"storage_edit_document_tree_name\">名稱</string>\n    <string name=\"storage_edit_document_tree_uri\">URI</string>\n    <string name=\"storage_edit_document_tree_path\">路徑</string>\n    <string name=\"storage_edit_ftp_server_title_edit\">編輯 FTP 伺服器</string>\n    <string name=\"storage_edit_ftp_server_title_add\">新增 FTP 伺服器</string>\n    <string name=\"storage_edit_ftp_server_host\">主機名稱</string>\n    <string name=\"storage_edit_ftp_server_host_error_empty\">輸入主機名稱</string>\n    <string name=\"storage_edit_ftp_server_host_error_invalid\">無效的主機名稱</string>\n    <string name=\"storage_edit_ftp_server_port\">連接埠</string>\n    <string name=\"storage_edit_ftp_server_port_error_invalid\">無效的連接埠</string>\n    <string name=\"storage_edit_ftp_server_path\">路徑</string>\n    <string name=\"storage_edit_ftp_server_path_placeholder\">可留空</string>\n    <string name=\"storage_edit_ftp_server_name\">名稱</string>\n    <string name=\"storage_edit_ftp_server_name_placeholder\">使用主機名稱</string>\n    <string name=\"storage_edit_ftp_server_protocol\">通訊協定</string>\n    <string name=\"storage_edit_ftp_server_authentication_type\">驗證</string>\n    <string-array name=\"storage_edit_ftp_server_authentication_type_entries\">\n        <item>密碼</item>\n        <item>匿名</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_username\">使用者名稱</string>\n    <string name=\"storage_edit_ftp_server_username_error_empty\">輸入使用者名稱</string>\n    <string name=\"storage_edit_ftp_server_password\">密碼</string>\n    <string name=\"storage_edit_ftp_server_mode\">模式</string>\n    <string-array name=\"storage_edit_ftp_server_mode_entries\">\n        <item>主動</item>\n        <item>被動</item>\n    </string-array>\n    <string name=\"storage_edit_ftp_server_encoding\">編碼</string>\n    <string name=\"storage_edit_ftp_server_connect_and_add\">連線並新增</string>\n    <string name=\"storage_edit_ftp_server_add\">新增</string>\n    <string name=\"storage_edit_sftp_server_title_edit\">編輯 SFTP 伺服器</string>\n    <string name=\"storage_edit_sftp_server_title_add\">新增 SFTP 伺服器</string>\n    <string name=\"storage_edit_sftp_server_host\">主機名稱</string>\n    <string name=\"storage_edit_sftp_server_host_error_empty\">輸入主機名稱</string>\n    <string name=\"storage_edit_sftp_server_host_error_invalid\">無效的主機名稱</string>\n    <string name=\"storage_edit_sftp_server_port\">連接埠</string>\n    <string name=\"storage_edit_sftp_server_port_error_invalid\">無效的連接埠</string>\n    <string name=\"storage_edit_sftp_server_path\">路徑</string>\n    <string name=\"storage_edit_sftp_server_path_placeholder\">可留空</string>\n    <string name=\"storage_edit_sftp_server_name\">名稱</string>\n    <string name=\"storage_edit_sftp_server_name_placeholder\">使用主機名稱</string>\n    <string name=\"storage_edit_sftp_server_authentication_type\">驗證</string>\n    <string-array name=\"storage_edit_sftp_server_authentication_type_entries\">\n        <item>密碼</item>\n        <item>公開金鑰</item>\n    </string-array>\n    <string name=\"storage_edit_sftp_server_username\">使用者名稱</string>\n    <string name=\"storage_edit_sftp_server_username_error_empty\">輸入使用者名稱</string>\n    <string name=\"storage_edit_sftp_server_password\">密碼</string>\n    <string name=\"storage_edit_sftp_server_private_key\">私密金鑰</string>\n    <string name=\"storage_edit_sftp_server_private_key_open_file\">開啟檔案</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_empty\">輸入私密金鑰</string>\n    <string name=\"storage_edit_sftp_server_private_key_error_invalid\">無效的私密金鑰</string>\n    <string name=\"storage_edit_sftp_server_private_key_password\">私密金鑰密碼</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_placeholder\">可留空</string>\n    <string name=\"storage_edit_sftp_server_private_key_password_error_invalid\">無效的私密金鑰密碼</string>\n    <string name=\"storage_edit_sftp_server_connect_and_add\">連線並新增</string>\n    <string name=\"storage_edit_sftp_server_add\">新增</string>\n    <string name=\"storage_add_lan_smb_server_loading\">正在搜尋 SMB 伺服器…</string>\n    <string name=\"storage_add_lan_smb_server_add\">手動新增</string>\n    <string name=\"storage_edit_smb_server_title_edit\">編輯 SMB 伺服器</string>\n    <string name=\"storage_edit_smb_server_title_add\">新增 SMB 伺服器</string>\n    <string name=\"storage_edit_smb_server_host\">主機名稱</string>\n    <string name=\"storage_edit_smb_server_host_error_empty\">輸入主機名稱</string>\n    <string name=\"storage_edit_smb_server_host_error_invalid\">無效的主機名稱</string>\n    <string name=\"storage_edit_smb_server_port\">連接埠</string>\n    <string name=\"storage_edit_smb_server_port_error_invalid\">無效的連接埠</string>\n    <string name=\"storage_edit_smb_server_path\">路徑</string>\n    <string name=\"storage_edit_smb_server_path_placeholder\">可留空</string>\n    <string name=\"storage_edit_smb_server_name\">名稱</string>\n    <string name=\"storage_edit_smb_server_name_placeholder\">使用主機名稱</string>\n    <string name=\"storage_edit_smb_server_authentication_type\">驗證</string>\n    <string-array name=\"storage_edit_smb_server_authentication_type_entries\">\n        <item>密碼</item>\n        <item>來賓</item>\n        <item>匿名</item>\n    </string-array>\n    <string name=\"storage_edit_smb_server_username\">使用者名稱</string>\n    <string name=\"storage_edit_smb_server_username_error_empty\">輸入使用者名稱</string>\n    <string name=\"storage_edit_smb_server_password\">密碼</string>\n    <string name=\"storage_edit_smb_server_domain\">網域</string>\n    <string name=\"storage_edit_smb_server_connect_and_add\">連線並新增</string>\n    <string name=\"storage_edit_smb_server_add\">新增</string>\n    <string name=\"storage_edit_webdav_server_title_edit\">編輯 WebDAV 伺服器</string>\n    <string name=\"storage_edit_webdav_server_title_add\">新增 WebDAV 伺服器</string>\n    <string name=\"storage_edit_webdav_server_host\">主機名稱</string>\n    <string name=\"storage_edit_webdav_server_host_error_empty\">輸入主機名稱</string>\n    <string name=\"storage_edit_webdav_server_host_error_invalid\">無效的主機名稱</string>\n    <string name=\"storage_edit_webdav_server_port\">連接埠</string>\n    <string name=\"storage_edit_webdav_server_port_error_invalid\">無效的連接埠</string>\n    <string name=\"storage_edit_webdav_server_path\">路徑</string>\n    <string name=\"storage_edit_webdav_server_path_placeholder\">可留空</string>\n    <string name=\"storage_edit_webdav_server_name\">名稱</string>\n    <string name=\"storage_edit_webdav_server_name_placeholder\">使用主機名稱</string>\n    <string name=\"storage_edit_webdav_server_protocol\">通訊協定</string>\n    <string name=\"storage_edit_webdav_server_authentication_type\">驗證</string>\n    <string-array name=\"storage_edit_webdav_server_authentication_type_entries\">\n        <item>密碼</item>\n        <item>存取權杖</item>\n        <item>無</item>\n    </string-array>\n    <string name=\"storage_edit_webdav_server_username\">使用者名稱</string>\n    <string name=\"storage_edit_webdav_server_username_error_empty\">輸入使用者名稱</string>\n    <string name=\"storage_edit_webdav_server_password\">密碼</string>\n    <string name=\"storage_edit_webdav_server_access_token\">存取權杖</string>\n    <string name=\"storage_edit_webdav_server_access_token_error_empty\">輸入存取權杖</string>\n    <string name=\"storage_edit_webdav_server_connect_and_add\">連線並新增</string>\n    <string name=\"storage_edit_webdav_server_add\">新增</string>\n\n    <string name=\"navigation_storage_subtitle_format\">共 %2$s，剩餘 %1$s</string>\n    <string name=\"navigation_add_storage\">新增儲存空間…</string>\n    <string name=\"navigation_standard_directory_alarms\">鬧鐘</string>\n    <string name=\"navigation_standard_directory_dcim\">相片</string>\n    <string name=\"navigation_standard_directory_documents\">文件</string>\n    <string name=\"navigation_standard_directory_downloads\">下載</string>\n    <string name=\"navigation_standard_directory_movies\">電影</string>\n    <string name=\"navigation_standard_directory_music\">音樂</string>\n    <string name=\"navigation_standard_directory_notifications\">通知</string>\n    <string name=\"navigation_standard_directory_pictures\">圖片</string>\n    <string name=\"navigation_standard_directory_podcasts\">播客</string>\n    <string name=\"navigation_standard_directory_ringtones\">鈴聲</string>\n    <string name=\"navigation_standard_directory_qq\">QQ</string>\n    <string name=\"navigation_standard_directory_tim\">TIM</string>\n    <string name=\"navigation_standard_directory_wechat\">微信</string>\n    <string name=\"navigation_edit_bookmark_directory_title\">書籤資料夾</string>\n    <string name=\"navigation_edit_bookmark_directory_name\">名稱</string>\n    <string name=\"navigation_edit_bookmark_directory_path\">路徑</string>\n\n    <string name=\"archive_viewer_title\">封存檔檢視器</string>\n    <string name=\"image_viewer_title\">圖片檢視器</string>\n    <string name=\"image_viewer_subtitle_format\">%1$,d/%2$,d</string>\n    <string name=\"save_as_title\">另存為</string>\n    <string name=\"save_as_error\">儲存時發生錯誤</string>\n    <string name=\"save_as_success_format\">已將「%1$s」儲存到「%2$s」</string>\n    <string name=\"text_editor_title\">文字編輯器</string>\n    <string name=\"text_editor_title_format\">%1$s</string>\n    <string name=\"text_editor_title_changed_format\">*%1$s</string>\n    <string name=\"text_editor_save_success\">已儲存</string>\n    <string name=\"text_editor_reload_message\">您確定要重新載入嗎？對此文件未儲存的變更將丟失。</string>\n    <string name=\"text_editor_encoding\">編碼</string>\n    <string name=\"text_editor_close_message\">您確定要捨棄對此文件未儲存的變更嗎？</string>\n\n    <string name=\"ftp_server_title\">FTP 伺服器</string>\n    <string name=\"ftp_server_state_title\">狀態</string>\n    <string name=\"ftp_server_state_summary_starting\">正在啓動…</string>\n    <string name=\"ftp_server_state_summary_running\">正在執行</string>\n    <string name=\"ftp_server_state_summary_stopping\">正在停止…</string>\n    <string name=\"ftp_server_state_summary_stopped\">未啓動</string>\n    <string name=\"ftp_server_url_title\">網址</string>\n    <string name=\"ftp_server_url_summary_no_local_inet_address\">本機 IP 位址不明</string>\n    <string name=\"ftp_server_url_menu_copy_url\">複製網址</string>\n    <string name=\"ftp_server_url_menu_copy_password\">複製密碼</string>\n    <string name=\"ftp_server_add_tile_title\">加入「快速設定」選單</string>\n    <string name=\"ftp_server_add_tile_result_added\">已將「FTP 伺服器」加入「快速設定」選單</string>\n    <string name=\"ftp_server_add_tile_result_already_added\">「FTP 伺服器」已被加入「快速設定」選單</string>\n    <string name=\"ftp_server_add_tile_result_error\">將「FTP 伺服器」加入「快速設定」選單時發生錯誤</string>\n    <string name=\"ftp_server_configuration_title\">配置</string>\n    <string name=\"ftp_server_anonymous_login_title\">匿名登入</string>\n    <string name=\"ftp_server_username_title\">使用者名稱</string>\n    <string name=\"ftp_server_password_title\">密碼</string>\n    <string name=\"ftp_server_port_title\">連接埠</string>\n    <string name=\"ftp_server_home_directory_title\">根資料夾</string>\n    <string name=\"ftp_server_writable_title\">允許寫入</string>\n\n    <string name=\"settings_title\">設定</string>\n    <string name=\"settings_interface_title\">介面</string>\n    <string name=\"settings_locale_title\">語言</string>\n    <string name=\"settings_theme_color_title\">主題色</string>\n    <string name=\"settings_theme_color_summary\">應用程式中最常見的色彩</string>\n    <string name=\"settings_material_design_3_title\">質感設計 3</string>\n    <string name=\"settings_night_mode_title\">夜間模式</string>\n    <string-array name=\"settings_night_mode_entries\">\n        <item>跟隨系統</item>\n        <item>關閉</item>\n        <item>開啟</item>\n        <item>基於時間</item>\n        <item>基於省電模式</item>\n    </string-array>\n    <string name=\"settings_black_night_mode\">黑色夜間模式</string>\n    <string name=\"settings_file_list_animation_title\">檔案列表動畫</string>\n    <string name=\"settings_file_name_ellipsize_title\">顯示長檔案名稱</string>\n    <string-array name=\"settings_file_name_ellipsize_entries\">\n        <item>省略開頭</item>\n        <item>省略中間</item>\n        <item>省略結尾</item>\n        <item>滾動</item>\n    </string-array>\n    <string name=\"settings_behavior_title\">行為</string>\n    <string name=\"settings_default_directory_title\">預設資料夾</string>\n    <string name=\"settings_standard_directories_title\">標準資料夾</string>\n    <string name=\"settings_standard_directories_summary_empty\">未啟用標準資料夾</string>\n    <string name=\"settings_bookmark_directory_screenshots\">螢幕擷取畫面</string>\n    <string name=\"settings_bookmark_directories_title\">書籤資料夾</string>\n    <string name=\"settings_bookmark_directories_summary_empty\">無書籤資料夾</string>\n    <string name=\"settings_root_strategy_title\">Root 存取模式</string>\n    <string-array name=\"settings_root_strategy_entries\">\n        <item>僅普通存取</item>\n        <item>自動</item>\n        <item>僅 root 存取</item>\n    </string-array>\n    <plurals name=\"settings_root_strategy_message_format\">\n        <item quantity=\"other\">您有 %1$,d 個正在執行的檔案作業，因此現在變更 root 存取模式可能會導致未預期的錯誤。您確定要現在變更它嗎？</item>\n    </plurals>\n    <string name=\"settings_archive_file_name_encoding_title\">封存檔名稱編碼</string>\n    <string name=\"settings_open_apk_default_action_title\">開啟 Android 套件</string>\n    <string-array name=\"settings_open_apk_default_action_entries\">\n        <item>安裝</item>\n        <item>查看內容</item>\n        <item>詢問操作</item>\n    </string-array>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_title\">顯示 PDF 文件縮圖</string>\n    <string name=\"settings_show_pdf_thumbnail_pre_28_summary\">可能導致應用程式在舊版 Android 上不穩定</string>\n    <string name=\"settings_read_remote_files_for_thumbnail_title\">讀取遠端檔案以顯示縮圖</string>\n\n    <string name=\"about_title\">關於</string>\n    <string name=\"about_version_title\">版本</string>\n    <string name=\"about_github_title\">在 GitHub 上查看</string>\n    <string name=\"about_licenses_title\">許可證</string>\n    <string name=\"about_privacy_policy_title\">隱私權政策</string>\n    <string name=\"about_author_title\">作者</string>\n    <string name=\"about_author_name_title\">張海</string>\n    <string name=\"about_author_github_title\">在 GitHub 上關注</string>\n    <string name=\"about_author_twitter_title\">在 Twitter 上關注</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/xml/ftp_server.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<PreferenceScreen\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <me.zhanghai.android.files.ftpserver.FtpServerStatePreference\n        android:key=\"@string/pref_key_ftp_server_state\"\n        android:disableDependentsState=\"true\"\n        android:title=\"@string/ftp_server_state_title\" />\n\n    <me.zhanghai.android.files.ftpserver.FtpServerUrlPreference\n        android:title=\"@string/ftp_server_url_title\" />\n\n    <me.zhanghai.android.files.ftpserver.FtpServerAddTilePreference\n        android:title=\"@string/ftp_server_add_tile_title\"\n        app:isPreferenceVisible=\"@bool/pref_visible_ftp_server_add_tile\" />\n\n    <PreferenceCategory\n        android:title=\"@string/ftp_server_configuration_title\"\n        android:dependency=\"@string/pref_key_ftp_server_state\">\n\n        <SwitchPreferenceCompat\n            android:key=\"@string/pref_key_ftp_server_anonymous_login\"\n            android:title=\"@string/ftp_server_anonymous_login_title\"\n            android:defaultValue=\"@bool/pref_default_value_ftp_server_anonymous_login\"\n            android:disableDependentsState=\"true\" />\n\n        <me.zhanghai.android.files.settings.DefaultIfEmptyEditTextPreference\n            android:key=\"@string/pref_key_ftp_server_username\"\n            android:title=\"@string/ftp_server_username_title\"\n            android:defaultValue=\"@string/pref_default_value_ftp_server_username\"\n            android:dependency=\"@string/pref_key_ftp_server_anonymous_login\"\n            app:useSimpleSummaryProvider=\"true\" />\n\n        <me.zhanghai.android.files.settings.PasswordPreference\n            android:key=\"@string/pref_key_ftp_server_password\"\n            android:title=\"@string/ftp_server_password_title\"\n            android:dependency=\"@string/pref_key_ftp_server_anonymous_login\"\n            android:inputType=\"textVisiblePassword\"\n            app:useSimpleSummaryProvider=\"true\" />\n\n        <me.zhanghai.android.files.settings.NonNegativeIntegerPreference\n            android:key=\"@string/pref_key_ftp_server_port\"\n            android:title=\"@string/ftp_server_port_title\"\n            android:defaultValue=\"@integer/pref_default_value_ftp_server_port\"\n            app:useSimpleSummaryProvider=\"true\" />\n\n        <me.zhanghai.android.files.ftpserver.FtpServerHomeDirectoryPreference\n            android:key=\"@string/pref_key_ftp_server_home_directory\"\n            android:title=\"@string/ftp_server_home_directory_title\"\n            app:useSimpleSummaryProvider=\"true\" />\n\n        <SwitchPreferenceCompat\n            android:key=\"@string/pref_key_ftp_server_writable\"\n            android:title=\"@string/ftp_server_writable_title\"\n            android:defaultValue=\"@bool/pref_default_value_ftp_server_writable\" />\n    </PreferenceCategory>\n</PreferenceScreen>\n"
  },
  {
    "path": "app/src/main/res/xml/netework_security_config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2024 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<network-security-config\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:ignore=\"AcceptsUserCertificates,InsecureBaseConfiguration\">\n    <base-config cleartextTrafficPermitted=\"true\">\n        <trust-anchors>\n            <certificates src=\"system\" />\n            <certificates src=\"user\" />\n        </trust-anchors>\n    </base-config>\n</network-security-config>\n"
  },
  {
    "path": "app/src/main/res/xml/settings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<PreferenceScreen\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <PreferenceCategory android:title=\"@string/settings_interface_title\">\n\n        <me.zhanghai.android.files.settings.LocalePreference\n            android:key=\"@string/pref_key_locale\"\n            android:title=\"@string/settings_locale_title\" />\n\n        <me.zhanghai.android.files.theme.custom.ThemeColorPreference\n            android:key=\"@string/pref_key_theme_color\"\n            android:title=\"@string/settings_theme_color_title\"\n            android:defaultValue=\"@string/pref_default_value_theme_color\"\n            android:dependency=\"@string/pref_key_material_design_3\"\n            android:summary=\"@string/settings_theme_color_summary\" />\n\n        <SwitchPreferenceCompat\n            android:key=\"@string/pref_key_material_design_3\"\n            android:title=\"@string/settings_material_design_3_title\"\n            android:defaultValue=\"@bool/pref_default_value_material_design_3\"\n            android:disableDependentsState=\"true\" />\n\n        <rikka.preference.SimpleMenuPreference\n            android:key=\"@string/pref_key_night_mode\"\n            android:title=\"@string/settings_night_mode_title\"\n            android:defaultValue=\"@string/pref_default_value_night_mode\"\n            android:entries=\"@array/settings_night_mode_entries\"\n            android:entryValues=\"@array/pref_entry_values_night_mode\"\n            app:useSimpleSummaryProvider=\"true\" />\n\n        <SwitchPreferenceCompat\n            android:key=\"@string/pref_key_black_night_mode\"\n            android:title=\"@string/settings_black_night_mode\"\n            android:defaultValue=\"@bool/pref_default_value_black_night_mode\" />\n\n        <SwitchPreferenceCompat\n            android:key=\"@string/pref_key_file_list_animation\"\n            android:title=\"@string/settings_file_list_animation_title\"\n            android:defaultValue=\"@bool/pref_default_value_file_list_animation\" />\n\n        <rikka.preference.SimpleMenuPreference\n            android:key=\"@string/pref_key_file_name_ellipsize\"\n            android:title=\"@string/settings_file_name_ellipsize_title\"\n            android:defaultValue=\"@string/pref_default_value_file_name_ellipsize\"\n            android:entries=\"@array/settings_file_name_ellipsize_entries\"\n            android:entryValues=\"@array/pref_entry_values_file_name_ellipsize\"\n            app:useSimpleSummaryProvider=\"true\" />\n    </PreferenceCategory>\n\n    <PreferenceCategory android:title=\"@string/settings_behavior_title\">\n\n        <me.zhanghai.android.files.settings.DefaultDirectoryPreference\n            android:key=\"@string/pref_key_file_list_default_directory\"\n            android:title=\"@string/settings_default_directory_title\"\n            app:useSimpleSummaryProvider=\"true\" />\n\n        <me.zhanghai.android.files.settings.StoragesPreference\n            android:title=\"@string/settings_storages_title\"\n            android:summary=\"@string/settings_storages_summary_empty\" />\n\n        <me.zhanghai.android.files.settings.StandardDirectoriesPreference\n            android:title=\"@string/settings_standard_directories_title\"\n            android:summary=\"@string/settings_standard_directories_summary_empty\" />\n\n        <me.zhanghai.android.files.settings.BookmarkDirectoriesPreference\n            android:title=\"@string/settings_bookmark_directories_title\"\n            android:summary=\"@string/settings_bookmark_directories_summary_empty\" />\n\n        <me.zhanghai.android.files.settings.RootStrategyPreference\n            android:key=\"@string/pref_key_root_strategy\"\n            android:title=\"@string/settings_root_strategy_title\"\n            android:defaultValue=\"@string/pref_default_value_root_strategy\"\n            android:entries=\"@array/settings_root_strategy_entries\"\n            android:entryValues=\"@array/pref_entry_values_root_strategy\"\n            app:useSimpleSummaryProvider=\"true\" />\n\n        <me.zhanghai.android.files.settings.CharsetPreference\n            android:key=\"@string/pref_key_archive_file_name_encoding\"\n            android:title=\"@string/settings_archive_file_name_encoding_title\"\n            android:defaultValue=\"@string/pref_default_value_archive_file_name_encoding\"\n            app:useSimpleSummaryProvider=\"true\" />\n\n        <rikka.preference.SimpleMenuPreference\n            android:key=\"@string/pref_key_open_apk_default_action\"\n            android:title=\"@string/settings_open_apk_default_action_title\"\n            android:defaultValue=\"@string/pref_default_value_open_apk_default_action\"\n            android:entries=\"@array/settings_open_apk_default_action_entries\"\n            android:entryValues=\"@array/pref_entry_values_open_apk_default_action\"\n            app:useSimpleSummaryProvider=\"true\" />\n\n        <SwitchPreferenceCompat\n            android:key=\"@string/pref_key_show_pdf_thumbnail_pre_28\"\n            android:title=\"@string/settings_show_pdf_thumbnail_pre_28_title\"\n            android:defaultValue=\"@bool/pref_default_value_show_pdf_thumbnail_pre_28\"\n            android:summary=\"@string/settings_show_pdf_thumbnail_pre_28_summary\"\n            app:isPreferenceVisible=\"@bool/pref_visible_show_pdf_thumbnail_pre_28\" />\n\n        <SwitchPreferenceCompat\n            android:key=\"@string/pref_key_read_remote_files_for_thumbnail\"\n            android:title=\"@string/settings_read_remote_files_for_thumbnail_title\"\n            android:defaultValue=\"@bool/pref_default_value_read_remote_files_for_thumbnail\" />\n    </PreferenceCategory>\n</PreferenceScreen>\n"
  },
  {
    "path": "app/src/main/res/xml/shortcuts.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n  ~ Copyright (c) 2019 Hai Zhang <dreaming.in.code.zh@gmail.com>\n  ~ All Rights Reserved.\n  -->\n\n<shortcuts\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:targetApi=\"n_mr1\">\n\n    <shortcut\n        android:shortcutId=\"downloads\"\n        android:icon=\"@mipmap/downloads_shortcut_icon\"\n        android:shortcutShortLabel=\"@string/shortcut_downloads_title\">\n\n        <intent\n            android:action=\"me.zhanghai.android.files.intent.action.VIEW_DOWNLOADS\"\n            android:targetPackage=\"me.zhanghai.android.files\" />\n    </shortcut>\n\n    <shortcut\n        android:shortcutId=\"ftp_server\"\n        android:icon=\"@mipmap/ftp_server_shortcut_icon\"\n        android:shortcutShortLabel=\"@string/shortcut_ftp_server_title\">\n\n        <intent\n            android:action=\"me.zhanghai.android.files.intent.action.MANAGE_FTP_SERVER\"\n            android:targetPackage=\"me.zhanghai.android.files\" />\n    </shortcut>\n</shortcuts>\n"
  },
  {
    "path": "art/deploy-png.sh",
    "content": "#!/bin/bash\nset -e\n\nfor dpi in {m,h,xh,xxh,xxxh}dpi; do\n    cp \"launcher_icon-${dpi}.png\" \"../app/src/main/res/mipmap-${dpi}/launcher_icon.png\"\n    cp \"launcher_icon_foreground-${dpi}.png\" \"../app/src/main/res/mipmap-${dpi}/launcher_icon_foreground.png\"\ndone\n\ncp launcher_icon-play.png ../fastlane/metadata/android/en-US/images/icon.png\n\nfor shortcut in directory downloads file ftp_server; do\n    for dpi in {m,h,xh,xxh,xxxh}dpi; do\n        cp \"${shortcut}_shortcut_icon-${dpi}.png\" \"../app/src/main/res/mipmap-${dpi}/${shortcut}_shortcut_icon.png\"\n    done\ndone\n\ncp banner-xhdpi.png ../app/src/main/res/drawable-xhdpi/banner.png\ncp banner-play.png ../fastlane/metadata/android/en-US/images/tvBanner.png\n"
  },
  {
    "path": "art/generate-png.sh",
    "content": "#!/bin/bash\nset -e\n\ninkscape -o launcher_icon-mdpi.png --export-area=15:15:93:93 -w 48 -h 48 launcher_icon.svg\ninkscape -o launcher_icon-hdpi.png --export-area=15:15:93:93 -w 72 -h 72 launcher_icon.svg\ninkscape -o launcher_icon-xhdpi.png --export-area=15:15:93:93 -w 96 -h 96 launcher_icon.svg\ninkscape -o launcher_icon-xxhdpi.png --export-area=15:15:93:93 -w 144 -h 144 launcher_icon.svg\ninkscape -o launcher_icon-xxxhdpi.png --export-area=15:15:93:93 -w 192 -h 192 launcher_icon.svg\n\ncp launcher_icon.svg launcher_icon-web.svg\ninkscape --batch-process --actions='select:circle;RemoveFilter;FileSave;FileQuit' launcher_icon-web.svg\ninkscape -o launcher_icon-web.png --export-area=18:18:90:90 -w 512 -h 512 launcher_icon-web.svg\nrm launcher_icon-web.svg\n\ncp launcher_icon.svg launcher_icon_foreground.svg\ninkscape --batch-process --actions='select:circle;select:circle-edge-top;select:circle-edge-bottom;EditDelete;select:circle-clip-group;SelectionUnGroup;FileSave;FileQuit' launcher_icon_foreground.svg\ninkscape -o launcher_icon_foreground-mdpi.png -w 108 -h 108 launcher_icon_foreground.svg\ninkscape -o launcher_icon_foreground-hdpi.png -w 162 -h 162 launcher_icon_foreground.svg\ninkscape -o launcher_icon_foreground-xhdpi.png -w 216 -h 216 launcher_icon_foreground.svg\ninkscape -o launcher_icon_foreground-xxhdpi.png -w 324 -h 324 launcher_icon_foreground.svg\ninkscape -o launcher_icon_foreground-xxxhdpi.png -w 432 -h 432 launcher_icon_foreground.svg\ninkscape -o launcher_icon-play.png --export-area=18:18:90:90 -b '#1a73e8' -w 512 -h 512 launcher_icon_foreground.svg\nrm launcher_icon_foreground.svg\n\nfor shortcut in directory downloads file ftp_server; do\n    inkscape -o \"${shortcut}_shortcut_icon-mdpi.png\" --export-area=15:15:93:93 -w 48 -h 48 \"${shortcut}_shortcut_icon.svg\"\n    inkscape -o \"${shortcut}_shortcut_icon-hdpi.png\" --export-area=15:15:93:93 -w 72 -h 72 \"${shortcut}_shortcut_icon.svg\"\n    inkscape -o \"${shortcut}_shortcut_icon-xhdpi.png\" --export-area=15:15:93:93 -w 96 -h 96 \"${shortcut}_shortcut_icon.svg\"\n    inkscape -o \"${shortcut}_shortcut_icon-xxhdpi.png\" --export-area=15:15:93:93 -w 144 -h 144 \"${shortcut}_shortcut_icon.svg\"\n    inkscape -o \"${shortcut}_shortcut_icon-xxxhdpi.png\" --export-area=15:15:93:93 -w 192 -h 192 \"${shortcut}_shortcut_icon.svg\"\ndone\n\ninkscape -o banner-xhdpi.png --export-area=0:0:320:180 -w 320 -h 180 banner.svg\ninkscape -o banner-play.png --export-area=0:0:320:180 -w 1280 -h 720 banner.svg\n"
  },
  {
    "path": "build.gradle",
    "content": "/*\n * Copyright (c) 2018 Hai Zhang <dreaming.in.code.zh@gmail.com>\n * All Rights Reserved.\n */\n\n// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    ext {\n        kotlin_version = '2.1.21'\n    }\n    repositories {\n        google()\n        mavenCentral()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:8.11.1'\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\n\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n        jcenter()\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/12.txt",
    "content": "- Added shortcuts for launcher icon.\n- Added creating shortcut for files and folders.\n- Added fast scroll support in text editor.\n- Added prompting remount partition as read-write when saving in text editor.\n- Fixed inconsistency in MediaStore after file operations.\n- Other fixes and improvements.\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/14.txt",
    "content": "- Minor fixes and improvements.\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/15.txt",
    "content": "- First stable release.\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/16.txt",
    "content": "- Added quick settings tile for FTP server.\n- Added Russian translation.\n- Made Material Design 2 the default theme.\n- Fixed issue when merging folders.\n- Fixed issue where added external storage is read-only.\n- Other fixes and improvements.\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/17.txt",
    "content": "- Added German and Portuguese (Brazil) translation.\n- Fixed root access on Android 10.\n- Fixed starting FTP server from quick settings tile.\n- Fixed video playback in external storage.\n- Fixed search to be case-insensitive.\n- Other fixes and improvements.\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/18.txt",
    "content": "- Added file properties tab for image, audio, video and APK files.\n- Added showing file count and content for folders in file properties.\n- Added Spanish, Basque, French, Dutch, Portuguese (Portugal) and Vietnamese translations.\n- Changed file name sorting to use Unicode Collation Algorithm for better results.\n- Changed search to show files directly under the current folder first.\n- Source code has been rewritten in Kotlin.\n- Other fixes and improvements.\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/19.txt",
    "content": "- Added file properties tab for image, audio, video and APK files.\n- Added showing file count and content for folders in file properties.\n- Added Spanish, Basque, French, Dutch, Portuguese (Portugal) and Vietnamese translations.\n- Changed file name sorting to use Unicode Collation Algorithm for better results.\n- Changed search to show files directly under the current folder first.\n- Source code has been rewritten in Kotlin.\n- Other fixes and improvements.\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/20.txt",
    "content": "- Bug fixes and improvements.\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/21.txt",
    "content": "- The Material Design 1 theme has been deprecated and will be removed in the next major release. This reduces code complexity and maintenance burden, meanwhile allows new features including AMOLED dark theme, alternative colors for the MD2 theme and outlined icons.\n- Added Italian and Japanese translations.\n- Fixed crash when viewing large images.\n- Fixed certain issues when picking files.\n- Fixed certain issues in FTP server.\n- Other bug fixes and improvements.\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/22.txt",
    "content": "- Improved the Material Design 2 theme, adding theme color setting and optional true black night mode, and removed the deprecated Material Design 1 theme.\n- Added SMB support.\n- Added storage management.\n- Added Sui support.\n- Added viewing permission info for APK files.\n- Added thumbnail for PDF files.\n- Added setting for displaying file names.\n- Added support for dragging out the navigation drawer from anywhere on screen.\n- Fixed certain issues in FTP server.\n- Other bug fixes and improvements.\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/23.txt",
    "content": "- Added Arabic, Hungarian, Indonesian and Polish translations.\n- Fixed issues in image viewer.\n- Other bug fixes and improvements.\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/25.txt",
    "content": "- Added SFTP support.\n- Added Material You theme on Android 12.\n- Added setting an initial path for SFTP and SMB.\n- Added encoding support in text editor.\n- Added Bulgarian and Korean translations.\n- Fixed not showing files when running in work profile.\n- Fixed slow loading for a large number of files.\n- Fixed thumbnail support for SFTP and SMB.\n- Other bug fixes and improvements.\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/26.txt",
    "content": "- Added direct SD card access on Android 11+.\n- Added multi-user support for SFTP and SMB.\n- Added monochromatic app icon on Android 13.\n- Added Persian, Romanian and Turkish translations.\n- Changed app name to \"Material Files\" to avoid confusion.\n- Changed root library to libsu to support new Magisk.\n- Fixed SMB UI crash on Android 6.0.\n- Other bug fixes and improvements.\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/27.txt",
    "content": "- Added Czech and Hebrew translations.\n- Other bug fixes and improvements.\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/28.txt",
    "content": "- Added Czech and Hebrew translations.\n- Other bug fixes and improvements.\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/29.txt",
    "content": "- Added FTP client.\n- Added support for viewing archive files outside internal storage.\n- Added full Material Design 3 theme available back to Android 5.0.\n- Added app icon badge for folders named after package names.\n- Added menu item for going to a custom path.\n- Added optional private key password for SFTP.\n- Added Icelandic translation.\n- Other bug fixes and improvements.\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/30.txt",
    "content": "- Fixed crash when viewing archive files.\n- Other bug fixes and improvements.\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/31.txt",
    "content": "- Fixed issue with opening files inside archive files.\n- Other bug fixes and improvements.\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/32.txt",
    "content": "- Added grid view.\n- Added per-app language setting.\n- Added shortcut to access Android/data.\n- Added URL information to FTP server notification.\n- Added banner for Android TV.\n- Added Greek, Finnish, Lithuanian, Norwegian Bokmål and Ukrainian translations.\n- Fast scroll popup now shows text according to the current sort options.\n- Video thumbnail is now taken from 1/3 of the video.\n- Material Design 2 theme will be removed in the upcoming version 1.7.0.\n- Other bug fixes and improvements.\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/33.txt",
    "content": "- Fixed FTP client not working before Android 8.0.\n- Fixed root access not working on some OnePlus devices.\n- Fixed number of grid view columns on landscape tablet.\n- Other bug fixes and improvements.\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/34.txt",
    "content": "- Added support for reading and creating password-protected ZIP archives.\n- Added support for reading RAR5 archives.\n- Added support for video thumbnail in SMB storage.\n- Targeted Android 14 and added requesting notification permission.\n- Fixed crash after system update to Android 14.\n- Fixed image viewer not preloading adjacent images.\n- Fixed settings page crashing on some LG devices.\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/35.txt",
    "content": "- Added WebDAV support.\n- Added keyboard shortcut support.\n- Added Android TV launcher icon.\n- Added predicative back gesture support.\n- Added Catalan translation.\n- Fixed storage permission support for Android TV 11, 12 and 12L.\n- Material Design 2 theme may be removed in the upcoming version 1.8.0.\n- Other bug fixes and improvements.\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/36.txt",
    "content": "- Fixed crash upon reading SFTP and SMB files.\n- Fixed black and custom color themes.\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/37.txt",
    "content": "- Added \"Save as\" functionality.\n- Added \"Checksum\" properties tab.\n- Improved UI to allow easier shortcut into Android/data subfolders.\n- Marked user-installed certificates in system credential storage as trusted for WebDAV with self signed certificate.\n- Fixed SMB issue with guest and anonymous authentication.\n- Material Design 2 theme may be removed in the upcoming version 1.8.0.\n- Other bug fixes and improvements.\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/38.txt",
    "content": "- Fixed crash when opening archive files directly.\n- Fixed FTP server Quick Settings tile crash on Android 14.\n- Material Design 2 theme may be removed in the upcoming version 1.8.0.\n- Other bug fixes and improvements.\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/changelogs/39.txt",
    "content": "- Fixed crash when opening archive files directly.\n- Fixed FTP server Quick Settings tile crash on Android 14.\n- Material Design 2 theme may be removed in the upcoming version 1.8.0.\n- Other bug fixes and improvements.\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/full_description.txt",
    "content": "An open source Material Design file manager.\n\nFeatures:\n- Open source: Lightweight, clean and secure.\n- Material Design: Follows Material Design guidelines, with attention into details.\n- Breadcrumbs: Navigate in the filesystem with ease.\n- Root support: View and manage files with root access.\n- Archive support: View, extract and create common compressed files.\n- NAS support: View and manage files on FTP, SFTP, SMB and WebDAV servers.\n- Themes: Customizable UI colors, plus night mode with optional true black.\n- Linux-aware: Knows symbolic links, file permissions and SELinux context.\n- Robust: Uses Linux system calls under the hood, not yet another ls parser.\n- Well-implemented: Built upon the right things, including Java NIO2 File API and LiveData.\n\nhttps://github.com/zhanghai/MaterialFiles\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/short_description.txt",
    "content": "Open source Material Design file manager\n"
  },
  {
    "path": "fastlane/metadata/android/en-US/title.txt",
    "content": "Material Files\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/changelogs/12.txt",
    "content": "- 添加了启动器图标上的快捷方式。\n- 添加了为文件和文件夹创建快捷方式的支持。\n- 添加了文本编辑器的快速滚动支持。\n- 添加了文本编辑器保存时提示重写挂载分区为读写的支持。\n- 修复了文件操作后 MediaStore 未更新的问题。\n- 其他修复和改进。\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/changelogs/14.txt",
    "content": "- 少量修复和改进。\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/changelogs/15.txt",
    "content": "- 发布第一个稳定版本。\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/changelogs/16.txt",
    "content": "- 添加了 FTP 服务器的快捷设置磁贴。\n- 添加了俄语翻译。\n- 将 Material Design 2 作为了默认主题。\n- 修复了合并文件夹时的问题。\n- 修复了添加的外部存储只读的问题。\n- 其他修复和改进。\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/changelogs/17.txt",
    "content": "- 添加了德语和葡萄牙语（巴西）翻译。\n- 修复了在 Android 10 上的 root 访问问题。\n- 修复了无法从快捷设置磁贴启动 FTP 服务器的问题。\n- 修复了播放位于外部存储的视频时的问题。\n- 修复了搜索没有忽略大小写的问题。\n- 其他修复和改进。\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/changelogs/18.txt",
    "content": "- 添加了图像、音频、视频和 APK 的文件属性选项卡。\n- 添加了在文件夹属性中显示文件数量和内容。\n- 添加了西班牙语、巴斯克语、法语、荷兰语、葡萄牙语（葡萄牙）和越南语翻译。\n- 修改了文件名排序算法为 Unicode Collation Algorithm 以获得更好的结果。\n- 修改了搜索以优先显示当前文件夹下的结果。\n- 使用 Kotlin 重写了源代码。\n- 其他修复和改进。\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/changelogs/19.txt",
    "content": "- 添加了图像、音频、视频和 APK 的文件属性选项卡。\n- 添加了在文件夹属性中显示文件数量和内容。\n- 添加了西班牙语、巴斯克语、法语、荷兰语、葡萄牙语（葡萄牙）和越南语翻译。\n- 修改了文件名排序算法为 Unicode Collation Algorithm 以获得更好的结果。\n- 修改了搜索以优先显示当前文件夹下的结果。\n- 使用 Kotlin 重写了源代码。\n- 其他修复和改进。\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/changelogs/20.txt",
    "content": "- 错误修复和改进。\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/changelogs/21.txt",
    "content": "- 质感设计 1 主题已被弃用，并将在下个大版本中被移除。这有利于降低代码复杂度和维护成本，并且使得 AMOLED 黑色主题、质感设计 2 主题色选择和轮廓图标等功能成为可能。\n- 添加了意大利语和日语翻译。\n- 修复了在查看大图片时的崩溃。\n- 修复了选取文件时的多个问题。\n- 修复了 FTP 服务器的多个问题。\n- 其他错误修复和改进。\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/changelogs/22.txt",
    "content": "- 改进了质感设计 2 主题，添加了主题色设置和可选的纯黑夜间模式，并移除了已弃用的质感设计 1 主题。\n- 添加了 SMB 支持。\n- 添加了存储管理。\n- 添加了 Sui 支持。\n- 添加了查看 APK 文件的权限信息。\n- 添加了 PDF 文件的缩略图。\n- 添加了显示文件名的设置。\n- 添加了从屏幕任意位置拉出导航抽屉的支持。\n- 修复了 FTP 服务器的多个问题。\n- 其他错误修复和改进。\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/changelogs/23.txt",
    "content": "- 添加了阿拉伯语、匈牙利语、印度尼西亚语和波兰语翻译。\n- 修复了图像查看器的问题。\n- 其他错误修复和改进。\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/changelogs/25.txt",
    "content": "- 添加了 SFTP 支持。\n- 添加了在 Android 12 上的您的质感设计（Material You）主题。\n- 添加了 SFTP 和 SMB 的初始路径设置。\n- 添加了文本编辑器的编码支持。\n- 添加了保加利亚语和韩语翻译。\n- 修复了在工作资料中运行时无法显示文件的问题。\n- 修复了加载大量文件时过于缓慢的问题。\n- 修复了 SFTP 和 SMB 的缩略图支持。\n- 其他错误修复和改进。\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/changelogs/26.txt",
    "content": "- 添加了在 Android 11+ 上直接访问 SD 卡的功能。\n- 添加了 SFTP 和 SMB 的多用户支持。\n- 添加了在 Android 13 上的单色图标。\n- 添加了波斯语、罗马尼亚语和土耳其语翻译。\n- 修改了应用名为“质感文件”以防止误解。\n- 修改了 root 库为 libsu 以支持新版 Magisk。\n- 修复了在 Android 6.0 上的 SMB 界面崩溃。\n- 其他错误修复和改进。\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/changelogs/27.txt",
    "content": "- 添加了捷克语和希伯来语翻译。\n- 其他错误修复和改进。\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/changelogs/28.txt",
    "content": "- 添加了捷克语和希伯来语翻译。\n- 其他错误修复和改进。\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/changelogs/29.txt",
    "content": "- 添加了 FTP 客户端。\n- 添加了查看位于内部存储以外档案文件的支持。\n- 添加了完整的质感设计 3 主题并使其在 Android 5.0 上可用。\n- 添加了与应用包名同名文件夹的应用图标角标。\n- 添加了转到自定义路径的菜单条目。\n- 添加了可选的 SFTP 私钥密码。\n- 添加了冰岛语翻译。\n- 其他错误修复和改进。\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/changelogs/30.txt",
    "content": "- 修复了查看归档文件时的崩溃。\n- 其他错误修复和改进。\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/changelogs/31.txt",
    "content": "- 修复了打开位于归档文件内的文件时的问题。\n- 其他错误修复和改进。\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/changelogs/32.txt",
    "content": "- 添加了网格视图。\n- 添加了分应用语言设置。\n- 添加了访问 Android/data 的快捷方式。\n- 向 FTP 服务器的通知添加了 URL 信息。\n- 为 Android TV 添加了横幅。\n- 添加了希腊语、芬兰语、立陶宛语、书面挪威语和乌克兰语翻译。\n- 快速滚动的提示现在会根据排序选项来显示文字。\n- 视频缩略图现在会从视频的 1/3 处获取。\n- Material Design 2 主题将在未来的 1.7.0 版本中被移除。\n- 其他错误修复和改进。\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/changelogs/33.txt",
    "content": "- 修复了 FTP 客户端在 Android 8.0 之前无法工作的问题。\n- 修复了 root 访问在部分一加设备上无法工作的问题。\n- 修复了在平板电脑的横屏模式下网格视图列数不正确的问题。\n- 其他错误修复和改进。\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/changelogs/34.txt",
    "content": "- 添加了读取和创建受密码保护的 ZIP 归档文件的支持。\n- 添加了读取 RAR5 归档文件的支持。\n- 添加了 SMB 存储内视频缩略图的支持。\n- 适配 Android 14 并添加了通知权限的请求。\n- 修复了在系统更新到 Android 14 后崩溃的问题。\n- 修复了图片查看器不预加载相邻图片的问题。\n- 修复了设置页面在部分 LG 设备上崩溃的问题。\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/changelogs/35.txt",
    "content": "- 添加了 WebDAV 支持。\n- 添加了键盘快捷键支持。\n- 添加了 Android TV 启动器图标。\n- 添加了预测性返回手势支持。\n- 添加了加泰罗尼亚语翻译。\n- 修复了 Android TV 11, 12 和 12L 的存储权限支持.\n- Material Design 2 主题可能在未来的 1.8.0 版本中被移除。\n- 其他错误修复和改进。\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/changelogs/36.txt",
    "content": "- 修复了读取 SFTP 和 SMB 文件时的崩溃。\n- 修复了黑色和自定义颜色主题。\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/changelogs/37.txt",
    "content": "- 添加了“另存为”功能。\n- 添加了“校验和”属性标签页。\n- 改进了用户界面以便创建到 Android/data 子文件夹的快捷方式。\n- 信任了系统凭据存储中用户安装的证书以便使用依赖自签名证书的 WebDAV。\n- 修复了使用来宾和匿名认证时的 SMB 问题.\n- Material Design 2 主题可能在未来的 1.8.0 版本中被移除。\n- 其他错误修复和改进。\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/changelogs/38.txt",
    "content": "- 修复了直接打开归档文件时的崩溃。\n- 修复了 Android 14 上 FTP 服务器快捷设置磁贴的崩溃。\n- Material Design 2 主题可能在未来的 1.8.0 版本中被移除。\n- 其他错误修复和改进。\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/changelogs/39.txt",
    "content": "- 修复了直接打开归档文件时的崩溃。\n- 修复了 Android 14 上 FTP 服务器快捷设置磁贴的崩溃。\n- Material Design 2 主题可能在未来的 1.8.0 版本中被移除。\n- 其他错误修复和改进。\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/full_description.txt",
    "content": "一个开源的 Material Design 文件管理器。\n\n特性：\n- 开源：轻量、简洁并且安全。\n- Material Design：遵循 Material Design 规范，并且注重细节。\n- 面包屑导航栏：点击导航栏所显示路径中的任一文件夹即可快速访问。\n- Root 支持：使用 root 权限查看和管理文件。\n- 压缩文件支持：查看、提取和创建常见的压缩文件。\n- NAS 支持：查看和管理 FTP、SFTP、SMB 和 WebDAV 服务器上的文件。\n- 主题：可定制的界面颜色，以及可选纯黑的夜间模式。\n- Linux 友好：支持符号链接、文件权限和 SELinux 上下文。\n- 健壮性：使用 Linux 系统调用实现，而不是另一个 ls 解析器。\n- 实现良好：使用正确的方式打造，包括 Java NIO2 文件 API 和 LiveData。\n\nhttps://github.com/zhanghai/MaterialFiles\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/short_description.txt",
    "content": "开源的 Material Design 文件管理器\n"
  },
  {
    "path": "fastlane/metadata/android/zh-CN/title.txt",
    "content": "质感文件\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Thu Sep 23 01:27:10 PDT 2021\ndistributionBase=GRADLE_USER_HOME\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.13-all.zip\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n\n# 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.\norg.gradle.jvmargs=-Xmx2048m -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\n# AndroidX package structure to make it clearer which packages are bundled with the\n# Android operating system, and which are packaged with your app's APK\n# https://developer.android.com/topic/libraries/support-library/androidx-rn\nandroid.useAndroidX=true\n\n# Kotlin code style for this project: \"official\" or \"obsolete\":\nkotlin.code.style=official\n"
  },
  {
    "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  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": "mime/MimeTypeMapCompat.kt",
    "content": "private val extensionToMimeTypeMap = mapOf(\n    \"%\" to \"application/x-trash\",\n    \"123\" to \"application/vnd.lotus-1-2-3\",\n    \"1905.1\" to \"application/vnd.ieee.1905\",\n    \"1clr\" to \"application/clr\",\n    \"1km\" to \"application/vnd.1000minds.decision-model+xml\",\n    \"210\" to \"application/p21\",\n    \"3dm\" to \"text/vnd.in3d.3dml\",\n    \"3dml\" to \"text/vnd.in3d.3dml\",\n    \"3g2\" to \"video/3gpp2\",\n    \"3ga\" to \"audio/3gpp\",\n    \"3gp\" to \"video/3gpp\",\n    \"3gp2\" to \"video/3gpp2\",\n    \"3gpp\" to \"video/3gpp\",\n    \"3gpp2\" to \"video/3gpp2\",\n    \"3mf\" to \"application/vnd.ms-3mfdocument\",\n    \"3tz\" to \"application/vnd.maxar.archive.3tz+zip\",\n    \"726\" to \"audio/32kadpcm\",\n    \"7z\" to \"application/x-7z-compressed\",\n    \"AMR\" to \"audio/AMR\",\n    \"AWB\" to \"audio/AMR-WB\",\n    \"CQL\" to \"text/cql\",\n    \"ELN\" to \"application/vnd.eln+zip\",\n    \"J2C\" to \"image/j2c\",\n    \"J2K\" to \"image/j2c\",\n    \"PGB\" to \"image/vnd.globalgraphics.pgb\",\n    \"QCP\" to \"audio/EVRC-QCP\",\n    \"SAR\" to \"application/vnd.sar\",\n    \"VES\" to \"application/vnd.ves.encrypted\",\n    \"VFK\" to \"text/vnd.exchangeable\",\n    \"a\" to \"text/vnd.a\",\n    \"a2l\" to \"application/A2L\",\n    \"a52\" to \"audio/ac3\",\n    \"aa3\" to \"audio/ATRAC3\",\n    \"aac\" to \"audio/aac\",\n    \"aal\" to \"audio/ATRAC-ADVANCED-LOSSLESS\",\n    \"abc\" to \"text/vnd.abc\",\n    \"abw\" to \"application/x-abiword\",\n    \"ac\" to \"application/pkix-attr-cert\",\n    \"ac2\" to \"application/vnd.banana-accounting\",\n    \"ac3\" to \"audio/ac3\",\n    \"acc\" to \"application/vnd.americandynamics.acc\",\n    \"acn\" to \"audio/asc\",\n    \"acu\" to \"application/vnd.acucobol\",\n    \"acutc\" to \"application/vnd.acucorp\",\n    \"adt\" to \"audio/aac\",\n    \"adts\" to \"audio/aac\",\n    \"aep\" to \"application/vnd.audiograph\",\n    \"afp\" to \"application/vnd.afpc.modca\",\n    \"age\" to \"application/vnd.age\",\n    \"ahead\" to \"application/vnd.ahead.space\",\n    \"ahv\" to \"application/vnd.android.haptics.vibration+xml\",\n    \"ai\" to \"application/postscript\",\n    \"aif\" to \"audio/x-aiff\",\n    \"aifc\" to \"audio/x-aiff\",\n    \"aiff\" to \"audio/x-aiff\",\n    \"aion\" to \"application/vnd.veritone.aion+json\",\n    \"ait\" to \"application/vnd.dvb.ait\",\n    \"alc\" to \"chemical/x-alchemy\",\n    \"ami\" to \"application/vnd.amiga.ami\",\n    \"aml\" to \"application/AML\",\n    \"amlx\" to \"application/automationml-amlx+zip\",\n    \"amr\" to \"audio/amr\",\n    \"anx\" to \"application/annodex\",\n    \"apex\" to \"application/vnd.apexlang\",\n    \"apexlang\" to \"application/vnd.apexlang\",\n    \"apk\" to \"application/vnd.android.package-archive\",\n    \"apkg\" to \"application/vnd.anki\",\n    \"apng\" to \"image/apng\",\n    \"appcache\" to \"text/cache-manifest\",\n    \"apr\" to \"application/vnd.lotus-approach\",\n    \"apxml\" to \"application/auth-policy+xml\",\n    \"arrow\" to \"application/vnd.apache.arrow.file\",\n    \"arrows\" to \"application/vnd.apache.arrow.stream\",\n    \"art\" to \"image/x-jg\",\n    \"artisan\" to \"application/vnd.artisan+json\",\n    \"arw\" to \"image/x-sony-arw\",\n    \"asc\" to \"application/pgp-keys\",\n    \"ascii\" to \"text/vnd.ascii-art\",\n    \"asf\" to \"application/vnd.ms-asf\",\n    \"asice\" to \"application/vnd.etsi.asic-e+zip\",\n    \"asics\" to \"application/vnd.etsi.asic-s+zip\",\n    \"asn\" to \"chemical/x-ncbi-asn1-spec\",\n    \"aso\" to \"chemical/x-ncbi-asn1-binary\",\n    \"ass\" to \"audio/aac\",\n    \"at3\" to \"audio/ATRAC3\",\n    \"atc\" to \"application/vnd.acucorp\",\n    \"atf\" to \"application/ATF\",\n    \"atfx\" to \"application/ATFX\",\n    \"atom\" to \"application/atom+xml\",\n    \"atomcat\" to \"application/atomcat+xml\",\n    \"atomdeleted\" to \"application/atomdeleted+xml\",\n    \"atomsrv\" to \"application/atomserv+xml\",\n    \"atomsvc\" to \"application/atomsvc+xml\",\n    \"atx\" to \"audio/ATRAC-X\",\n    \"atxml\" to \"application/ATXML\",\n    \"au\" to \"audio/basic\",\n    \"auc\" to \"application/tamp-apex-update-confirm\",\n    \"avci\" to \"image/avci\",\n    \"avcs\" to \"image/avcs\",\n    \"avi\" to \"video/avi\",\n    \"avif\" to \"image/avif\",\n    \"awb\" to \"audio/AMR-WB\",\n    \"axa\" to \"audio/annodex\",\n    \"axv\" to \"video/annodex\",\n    \"azf\" to \"application/vnd.airzip.filesecure.azf\",\n    \"azs\" to \"application/vnd.airzip.filesecure.azs\",\n    \"azv\" to \"image/vnd.airzip.accelerator.azv\",\n    \"azw3\" to \"application/vnd.amazon.mobi8-ebook\",\n    \"b\" to \"chemical/x-molconn-Z\",\n    \"b16\" to \"image/vnd.pco.b16\",\n    \"bak\" to \"application/x-trash\",\n    \"bar\" to \"application/vnd.qualcomm.brew-app-res\",\n    \"bary\" to \"model/vnd.bary\",\n    \"bat\" to \"application/x-msdos-program\",\n    \"bcpio\" to \"application/x-bcpio\",\n    \"bdm\" to \"application/vnd.syncml.dm+wbxml\",\n    \"bed\" to \"application/vnd.realvnc.bed\",\n    \"bh2\" to \"application/vnd.fujitsu.oasysprs\",\n    \"bib\" to \"text/x-bibtex\",\n    \"bik\" to \"video/vnd.radgamettools.bink\",\n    \"bin\" to \"application/octet-stream\",\n    \"bk2\" to \"video/vnd.radgamettools.bink\",\n    \"bkm\" to \"application/vnd.nervana\",\n    \"bmed\" to \"multipart/vnd.bint.med-plus\",\n    \"bmi\" to \"application/vnd.bmi\",\n    \"bmml\" to \"application/vnd.balsamiq.bmml+xml\",\n    \"bmp\" to \"image/x-ms-bmp\",\n    \"bmpr\" to \"application/vnd.balsamiq.bmpr\",\n    \"boo\" to \"text/x-boo\",\n    \"book\" to \"application/x-maker\",\n    \"box\" to \"application/vnd.previewsystems.box\",\n    \"bpd\" to \"application/vnd.hbci\",\n    \"brf\" to \"text/plain\",\n    \"bsd\" to \"chemical/x-crossfire\",\n    \"bsp\" to \"model/vnd.valve.source.compiled-map\",\n    \"btf\" to \"image/prs.btif\",\n    \"btif\" to \"image/prs.btif\",\n    \"c\" to \"text/x-csrc\",\n    \"c++\" to \"text/x-c++src\",\n    \"c11amc\" to \"application/vnd.cluetrust.cartomobile-config\",\n    \"c11amz\" to \"application/vnd.cluetrust.cartomobile-config-pkg\",\n    \"c3d\" to \"chemical/x-chem3d\",\n    \"c3ex\" to \"application/cccex\",\n    \"c4d\" to \"application/vnd.clonk.c4group\",\n    \"c4f\" to \"application/vnd.clonk.c4group\",\n    \"c4g\" to \"application/vnd.clonk.c4group\",\n    \"c4p\" to \"application/vnd.clonk.c4group\",\n    \"c4u\" to \"application/vnd.clonk.c4group\",\n    \"c9r\" to \"application/vnd.cryptomator.encrypted\",\n    \"c9s\" to \"application/vnd.cryptomator.encrypted\",\n    \"cab\" to \"application/vnd.ms-cab-compressed\",\n    \"cac\" to \"chemical/x-cache\",\n    \"cache\" to \"chemical/x-cache\",\n    \"cap\" to \"application/vnd.tcpdump.pcap\",\n    \"car\" to \"application/vnd.ipld.car\",\n    \"carjson\" to \"application/vnd.eu.kasparian.car+json\",\n    \"cascii\" to \"chemical/x-cactvs-binary\",\n    \"cat\" to \"application/vnd.ms-pki.seccat\",\n    \"cbin\" to \"chemical/x-cactvs-binary\",\n    \"cbor\" to \"application/cbor\",\n    \"cbr\" to \"application/vnd.comicbook-rar\",\n    \"cbz\" to \"application/vnd.comicbook+zip\",\n    \"cc\" to \"text/x-c++src\",\n    \"ccc\" to \"text/vnd.net2phone.commcenter.command\",\n    \"ccmp\" to \"application/ccmp+xml\",\n    \"ccxml\" to \"application/ccxml+xml\",\n    \"cda\" to \"application/x-cdf\",\n    \"cdbcmsg\" to \"application/vnd.contact.cmsg\",\n    \"cdf\" to \"application/x-cdf\",\n    \"cdfx\" to \"application/CDFX+XML\",\n    \"cdkey\" to \"application/vnd.mediastation.cdkey\",\n    \"cdmia\" to \"application/cdmi-capability\",\n    \"cdmic\" to \"application/cdmi-container\",\n    \"cdmid\" to \"application/cdmi-domain\",\n    \"cdmio\" to \"application/cdmi-object\",\n    \"cdmiq\" to \"application/cdmi-queue\",\n    \"cdr\" to \"image/x-coreldraw\",\n    \"cdt\" to \"image/x-coreldrawtemplate\",\n    \"cdx\" to \"chemical/x-cdx\",\n    \"cdxml\" to \"application/vnd.chemdraw+xml\",\n    \"cdy\" to \"application/vnd.cinderella\",\n    \"cea\" to \"application/CEA\",\n    \"cef\" to \"chemical/x-cxf\",\n    \"cellml\" to \"application/cellml+xml\",\n    \"cer\" to \"application/pkix-cert\",\n    \"cgm\" to \"image/cgm\",\n    \"chm\" to \"chemical/x-chemdraw\",\n    \"chrt\" to \"application/vnd.kde.kchart\",\n    \"cif\" to \"chemical/x-cif\",\n    \"cii\" to \"application/vnd.anser-web-certificate-issue-initiation\",\n    \"cil\" to \"application/vnd.ms-artgalry\",\n    \"cl\" to \"application/simple-filter+xml\",\n    \"cla\" to \"application/vnd.claymore\",\n    \"class\" to \"application/java-vm\",\n    \"cld\" to \"model/vnd.cld\",\n    \"clkk\" to \"application/vnd.crick.clicker.keyboard\",\n    \"clkp\" to \"application/vnd.crick.clicker.palette\",\n    \"clkt\" to \"application/vnd.crick.clicker.template\",\n    \"clkw\" to \"application/vnd.crick.clicker.wordbank\",\n    \"clkx\" to \"application/vnd.crick.clicker\",\n    \"cls\" to \"text/x-tex\",\n    \"clue\" to \"application/clue_info+xml\",\n    \"cmc\" to \"application/vnd.cosmocaller\",\n    \"cmdf\" to \"chemical/x-cmdf\",\n    \"cml\" to \"chemical/x-cml\",\n    \"cmp\" to \"application/vnd.yellowriver-custom-menu\",\n    \"cmsc\" to \"application/cms\",\n    \"cnd\" to \"text/jcr-cnd\",\n    \"cod\" to \"application/vnd.rim.cod\",\n    \"coffee\" to \"application/vnd.coffeescript\",\n    \"com\" to \"application/x-msdos-program\",\n    \"copyright\" to \"text/vnd.debian.copyright\",\n    \"coswid\" to \"application/swid+cbor\",\n    \"cpa\" to \"chemical/x-compass\",\n    \"cpio\" to \"application/x-cpio\",\n    \"cpkg\" to \"application/vnd.xmpie.cpkg\",\n    \"cpl\" to \"application/cpl+xml\",\n    \"cpp\" to \"text/x-c++src\",\n    \"cpt\" to \"image/x-corelphotopaint\",\n    \"cr2\" to \"image/x-canon-cr2\",\n    \"crl\" to \"application/pkix-crl\",\n    \"crt\" to \"application/x-x509-ca-cert\",\n    \"crtr\" to \"application/vnd.multiad.creator\",\n    \"crw\" to \"image/x-canon-crw\",\n    \"cryptomator\" to \"application/vnd.cryptomator.vault\",\n    \"cryptonote\" to \"application/vnd.rig.cryptonote\",\n    \"csd\" to \"audio/csound\",\n    \"csf\" to \"chemical/x-cache-csf\",\n    \"csh\" to \"text/x-csh\",\n    \"csl\" to \"application/vnd.citationstyles.style+xml\",\n    \"csm\" to \"chemical/x-csml\",\n    \"csml\" to \"chemical/x-csml\",\n    \"csp\" to \"application/vnd.commonspace\",\n    \"csrattrs\" to \"application/csrattrs\",\n    \"css\" to \"text/css\",\n    \"cst\" to \"application/vnd.commonspace\",\n    \"csv\" to \"text/comma-separated-values\",\n    \"csvs\" to \"text/csv-schema\",\n    \"ctab\" to \"chemical/x-cactvs-binary\",\n    \"ctx\" to \"chemical/x-ctx\",\n    \"cu\" to \"application/cu-seeme\",\n    \"cub\" to \"chemical/x-gaussian-cube\",\n    \"cuc\" to \"application/tamp-community-update-confirm\",\n    \"cur\" to \"image/ico\",\n    \"curl\" to \"text/vnd.curl\",\n    \"cw\" to \"application/prs.cww\",\n    \"cwl\" to \"application/cwl\",\n    \"cwl.json\" to \"application/cwl+json\",\n    \"cww\" to \"application/prs.cww\",\n    \"cxf\" to \"chemical/x-cxf\",\n    \"cxx\" to \"text/x-c++src\",\n    \"d\" to \"text/x-dsrc\",\n    \"dae\" to \"model/vnd.collada+xml\",\n    \"daf\" to \"application/vnd.Mobius.DAF\",\n    \"dart\" to \"application/vnd.dart\",\n    \"dataless\" to \"application/vnd.fdsn.seed\",\n    \"davmount\" to \"application/davmount+xml\",\n    \"dbf\" to \"application/vnd.dbf\",\n    \"dcd\" to \"application/DCD\",\n    \"dcm\" to \"application/dicom\",\n    \"dcr\" to \"application/x-director\",\n    \"dd2\" to \"application/vnd.oma.dd2+xml\",\n    \"ddd\" to \"application/vnd.fujixerox.ddd\",\n    \"ddeb\" to \"application/vnd.debian.binary-package\",\n    \"ddf\" to \"application/vnd.syncml.dmddf+xml\",\n    \"deb\" to \"application/vnd.debian.binary-package\",\n    \"deploy\" to \"application/octet-stream\",\n    \"der\" to \"application/x-x509-ca-cert\",\n    \"dfac\" to \"application/vnd.dreamfactory\",\n    \"dfxp\" to \"application/ttml+xml\",\n    \"dif\" to \"video/dv\",\n    \"diff\" to \"text/plain\",\n    \"dii\" to \"application/DII\",\n    \"dim\" to \"application/vnd.fastcopy-disk-image\",\n    \"dir\" to \"application/x-director\",\n    \"dis\" to \"application/vnd.Mobius.DIS\",\n    \"dist\" to \"application/vnd.apple.installer+xml\",\n    \"distz\" to \"application/vnd.apple.installer+xml\",\n    \"dit\" to \"application/DIT\",\n    \"dive\" to \"application/vnd.patentdive\",\n    \"djv\" to \"image/vnd.djvu\",\n    \"djvu\" to \"image/vnd.djvu\",\n    \"dl\" to \"application/vnd.datalog\",\n    \"dll\" to \"application/x-msdos-program\",\n    \"dls\" to \"audio/dls\",\n    \"dmg\" to \"application/x-apple-diskimage\",\n    \"dmp\" to \"application/vnd.tcpdump.pcap\",\n    \"dms\" to \"text/vnd.DMClientScript\",\n    \"dna\" to \"application/vnd.dna\",\n    \"dng\" to \"image/x-adobe-dng\",\n    \"doc\" to \"application/msword\",\n    \"docjson\" to \"application/vnd.document+json\",\n    \"docm\" to \"application/vnd.ms-word.document.macroEnabled.12\",\n    \"docx\" to \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n    \"dor\" to \"model/vnd.gdl\",\n    \"dot\" to \"text/vnd.graphviz\",\n    \"dotm\" to \"application/vnd.ms-word.template.macroEnabled.12\",\n    \"dotx\" to \"application/vnd.openxmlformats-officedocument.wordprocessingml.template\",\n    \"dp\" to \"application/vnd.osgi.dp\",\n    \"dpg\" to \"application/vnd.dpgraph\",\n    \"dpgraph\" to \"application/vnd.dpgraph\",\n    \"dpkg\" to \"application/vnd.xmpie.dpkg\",\n    \"dpx\" to \"image/dpx\",\n    \"drle\" to \"image/dicom-rle\",\n    \"dsc\" to \"text/prs.lines.tag\",\n    \"dsm\" to \"application/vnd.desmume.movie\",\n    \"dssc\" to \"application/dssc+der\",\n    \"dtd\" to \"application/xml-dtd\",\n    \"dts\" to \"audio/vnd.dts\",\n    \"dtshd\" to \"audio/vnd.dts.hd\",\n    \"dv\" to \"video/dv\",\n    \"dvb\" to \"video/vnd.dvb.file\",\n    \"dvc\" to \"application/dvcs\",\n    \"dvi\" to \"application/x-dvi\",\n    \"dwd\" to \"application/atsc-dwd+xml\",\n    \"dwf\" to \"model/vnd.dwf\",\n    \"dwg\" to \"image/vnd.dwg\",\n    \"dx\" to \"chemical/x-jcamp-dx\",\n    \"dxf\" to \"image/vnd.dxf\",\n    \"dxp\" to \"application/vnd.spotfire.dxp\",\n    \"dxr\" to \"application/x-director\",\n    \"dzr\" to \"application/vnd.dzr\",\n    \"ebuild\" to \"application/vnd.gentoo.ebuild\",\n    \"ecelp4800\" to \"audio/vnd.nuera.ecelp4800\",\n    \"ecelp7470\" to \"audio/vnd.nuera.ecelp7470\",\n    \"ecelp9600\" to \"audio/vnd.nuera.ecelp9600\",\n    \"ecig\" to \"application/vnd.evolv.ecig.settings\",\n    \"ecigprofile\" to \"application/vnd.evolv.ecig.profile\",\n    \"ecigtheme\" to \"application/vnd.evolv.ecig.theme\",\n    \"eclass\" to \"application/vnd.gentoo.eclass\",\n    \"edm\" to \"application/vnd.novadigm.EDM\",\n    \"edx\" to \"application/vnd.novadigm.EDX\",\n    \"efi\" to \"application/efi\",\n    \"efif\" to \"application/vnd.picsel\",\n    \"ei6\" to \"application/vnd.pg.osasli\",\n    \"emb\" to \"chemical/x-embl-dl-nucleotide\",\n    \"embl\" to \"chemical/x-embl-dl-nucleotide\",\n    \"emf\" to \"image/emf\",\n    \"eml\" to \"message/rfc822\",\n    \"emm\" to \"application/vnd.ibm.electronic-media\",\n    \"emma\" to \"application/emma+xml\",\n    \"emotionml\" to \"application/emotionml+xml\",\n    \"ent\" to \"application/xml-external-parsed-entity\",\n    \"entity\" to \"application/vnd.nervana\",\n    \"enw\" to \"audio/EVRCNW\",\n    \"eol\" to \"audio/vnd.digital-winds\",\n    \"eot\" to \"application/vnd.ms-fontobject\",\n    \"ep\" to \"application/vnd.bluetooth.ep.oob\",\n    \"eps\" to \"application/postscript\",\n    \"eps2\" to \"application/postscript\",\n    \"eps3\" to \"application/postscript\",\n    \"epsf\" to \"application/postscript\",\n    \"epsi\" to \"application/postscript\",\n    \"epub\" to \"application/epub+zip\",\n    \"erf\" to \"image/x-epson-erf\",\n    \"es\" to \"text/javascript\",\n    \"es3\" to \"application/vnd.eszigno3+xml\",\n    \"esa\" to \"application/vnd.osgi.subsystem\",\n    \"esf\" to \"application/vnd.epson.esf\",\n    \"espass\" to \"application/vnd.espass-espass+zip\",\n    \"et3\" to \"application/vnd.eszigno3+xml\",\n    \"etx\" to \"text/x-setext\",\n    \"evb\" to \"audio/EVRCB\",\n    \"evc\" to \"audio/EVRC\",\n    \"evw\" to \"audio/EVRCWB\",\n    \"exe\" to \"application/x-msdos-program\",\n    \"exi\" to \"application/exi\",\n    \"exp\" to \"application/express\",\n    \"exr\" to \"image/aces\",\n    \"ext\" to \"application/vnd.novadigm.EXT\",\n    \"ez\" to \"application/andrew-inset\",\n    \"ez2\" to \"application/vnd.ezpix-album\",\n    \"ez3\" to \"application/vnd.ezpix-package\",\n    \"f4a\" to \"audio/mp4\",\n    \"f4b\" to \"audio/mp4\",\n    \"f4p\" to \"audio/mp4\",\n    \"f4v\" to \"video/mp4\",\n    \"fb\" to \"application/x-maker\",\n    \"fbdoc\" to \"application/x-maker\",\n    \"fbs\" to \"image/vnd.fastbidsheet\",\n    \"fcdt\" to \"application/vnd.adobe.formscentral.fcdt\",\n    \"fch\" to \"chemical/x-gaussian-checkpoint\",\n    \"fchk\" to \"chemical/x-gaussian-checkpoint\",\n    \"fcs\" to \"application/vnd.isac.fcs\",\n    \"fdf\" to \"application/fdf\",\n    \"fdt\" to \"application/fdt+xml\",\n    \"fe_launch\" to \"application/vnd.denovo.fcselayout-link\",\n    \"fg5\" to \"application/vnd.fujitsu.oasysgp\",\n    \"fig\" to \"application/x-xfig\",\n    \"finf\" to \"application/fastinfoset\",\n    \"fit\" to \"image/fits\",\n    \"fits\" to \"image/fits\",\n    \"fl\" to \"application/x-android-drm-fl\",\n    \"fla\" to \"application/vnd.dtg.local.flash\",\n    \"flac\" to \"audio/flac\",\n    \"flb\" to \"application/vnd.ficlab.flb+zip\",\n    \"fli\" to \"video/fli\",\n    \"flo\" to \"application/vnd.micrografx.flo\",\n    \"flt\" to \"text/vnd.ficlab.flt\",\n    \"flv\" to \"video/x-flv\",\n    \"flw\" to \"application/vnd.kde.kivio\",\n    \"flx\" to \"text/vnd.fmi.flexstor\",\n    \"fly\" to \"text/vnd.fly\",\n    \"fm\" to \"application/x-maker\",\n    \"fo\" to \"application/vnd.software602.filler.form+xml\",\n    \"fpx\" to \"image/vnd.fpx\",\n    \"frame\" to \"application/x-maker\",\n    \"frm\" to \"application/x-maker\",\n    \"fsc\" to \"application/vnd.fsc.weblaunch\",\n    \"fst\" to \"image/vnd.fst\",\n    \"ftc\" to \"application/vnd.fluxtime.clip\",\n    \"fti\" to \"application/vnd.anser-web-funds-transfer-initiation\",\n    \"fts\" to \"image/fits\",\n    \"fvt\" to \"video/vnd.fvt\",\n    \"fxp\" to \"application/vnd.adobe.fxp\",\n    \"fxpl\" to \"application/vnd.adobe.fxp\",\n    \"fzs\" to \"application/vnd.fuzzysheet\",\n    \"g2w\" to \"application/vnd.geoplan\",\n    \"g3w\" to \"application/vnd.geospace\",\n    \"gac\" to \"application/vnd.groove-account\",\n    \"gal\" to \"chemical/x-gaussian-log\",\n    \"gam\" to \"chemical/x-gamess-input\",\n    \"gamin\" to \"chemical/x-gamess-input\",\n    \"gan\" to \"application/x-ganttproject\",\n    \"gau\" to \"chemical/x-gaussian-input\",\n    \"gbr\" to \"application/rpki-ghostbusters\",\n    \"gcd\" to \"text/x-pcs-gcd\",\n    \"gcf\" to \"application/x-graphing-calculator\",\n    \"gcg\" to \"chemical/x-gcg8-sequence\",\n    \"gdl\" to \"model/vnd.gdl\",\n    \"gdz\" to \"application/vnd.familysearch.gedcom+zip\",\n    \"ged\" to \"text/vnd.familysearch.gedcom\",\n    \"gen\" to \"chemical/x-genbank\",\n    \"genozip\" to \"application/vnd.genozip\",\n    \"geo\" to \"application/vnd.dynageo\",\n    \"geojson\" to \"application/geo+json\",\n    \"gex\" to \"application/vnd.geometry-explorer\",\n    \"gf\" to \"application/x-tex-gf\",\n    \"gff3\" to \"text/gff3\",\n    \"ggb\" to \"application/vnd.geogebra.file\",\n    \"ggs\" to \"application/vnd.geogebra.slides\",\n    \"ggt\" to \"application/vnd.geogebra.tool\",\n    \"ghf\" to \"application/vnd.groove-help\",\n    \"gif\" to \"image/gif\",\n    \"gim\" to \"application/vnd.groove-identity-message\",\n    \"gjc\" to \"chemical/x-gaussian-input\",\n    \"gjf\" to \"chemical/x-gaussian-input\",\n    \"gl\" to \"video/gl\",\n    \"glb\" to \"model/gltf-binary\",\n    \"glbin\" to \"application/gltf-buffer\",\n    \"glbuf\" to \"application/gltf-buffer\",\n    \"gltf\" to \"model/gltf+json\",\n    \"gml\" to \"application/gml+xml\",\n    \"gnumeric\" to \"application/x-gnumeric\",\n    \"gph\" to \"application/vnd.FloGraphIt\",\n    \"gpkg\" to \"application/geopackage+sqlite3\",\n    \"gpkg.tar\" to \"application/vnd.gentoo.gpkg\",\n    \"gpt\" to \"chemical/x-mopac-graph\",\n    \"gqf\" to \"application/vnd.grafeq\",\n    \"gqs\" to \"application/vnd.grafeq\",\n    \"gram\" to \"application/srgs\",\n    \"grd\" to \"application/vnd.gentics.grd+json\",\n    \"gre\" to \"application/vnd.geometry-explorer\",\n    \"grv\" to \"application/vnd.groove-injector\",\n    \"grxml\" to \"application/srgs+xml\",\n    \"gsf\" to \"application/x-font\",\n    \"gsheet\" to \"application/urc-grpsheet+xml\",\n    \"gsm\" to \"audio/x-gsm\",\n    \"gtar\" to \"application/x-gtar\",\n    \"gtm\" to \"application/vnd.groove-tool-message\",\n    \"gtw\" to \"model/vnd.gtw\",\n    \"gv\" to \"text/vnd.graphviz\",\n    \"gxt\" to \"application/vnd.geonext\",\n    \"gz\" to \"application/gzip\",\n    \"h\" to \"text/x-chdr\",\n    \"h++\" to \"text/x-c++hdr\",\n    \"hal\" to \"application/vnd.hal+xml\",\n    \"hans\" to \"text/vnd.hans\",\n    \"hbc\" to \"application/vnd.hbci\",\n    \"hbci\" to \"application/vnd.hbci\",\n    \"hdf\" to \"application/x-hdf\",\n    \"hdr\" to \"image/vnd.radiance\",\n    \"hdt\" to \"application/vnd.hdt\",\n    \"heic\" to \"image/heic\",\n    \"heics\" to \"image/heic-sequence\",\n    \"heif\" to \"image/heif\",\n    \"heifs\" to \"image/heif-sequence\",\n    \"hej2\" to \"image/hej2k\",\n    \"held\" to \"application/atsc-held+xml\",\n    \"hgl\" to \"text/vnd.hgl\",\n    \"hh\" to \"text/x-c++hdr\",\n    \"hif\" to \"image/heif\",\n    \"hin\" to \"chemical/x-hin\",\n    \"hpgl\" to \"application/vnd.hp-HPGL\",\n    \"hpi\" to \"application/vnd.hp-hpid\",\n    \"hpid\" to \"application/vnd.hp-hpid\",\n    \"hpp\" to \"text/x-c++hdr\",\n    \"hps\" to \"application/vnd.hp-hps\",\n    \"hpub\" to \"application/prs.hpub+zip\",\n    \"hqx\" to \"application/mac-binhex40\",\n    \"hs\" to \"text/x-haskell\",\n    \"hsj2\" to \"image/hsj2\",\n    \"hsl\" to \"application/vnd.hsl\",\n    \"hta\" to \"application/hta\",\n    \"htc\" to \"text/x-component\",\n    \"htke\" to \"application/vnd.kenameaapp\",\n    \"htm\" to \"text/html\",\n    \"html\" to \"text/html\",\n    \"hvd\" to \"application/vnd.yamaha.hv-dic\",\n    \"hvp\" to \"application/vnd.yamaha.hv-voice\",\n    \"hvs\" to \"application/vnd.yamaha.hv-script\",\n    \"hwp\" to \"application/x-hwp\",\n    \"hxx\" to \"text/x-c++hdr\",\n    \"i2g\" to \"application/vnd.intergeo\",\n    \"ic0\" to \"application/vnd.commerce-battelle\",\n    \"ic1\" to \"application/vnd.commerce-battelle\",\n    \"ic2\" to \"application/vnd.commerce-battelle\",\n    \"ic3\" to \"application/vnd.commerce-battelle\",\n    \"ic4\" to \"application/vnd.commerce-battelle\",\n    \"ic5\" to \"application/vnd.commerce-battelle\",\n    \"ic6\" to \"application/vnd.commerce-battelle\",\n    \"ic7\" to \"application/vnd.commerce-battelle\",\n    \"ic8\" to \"application/vnd.commerce-battelle\",\n    \"ica\" to \"application/x-ica\",\n    \"icc\" to \"application/vnd.iccprofile\",\n    \"icd\" to \"application/vnd.commerce-battelle\",\n    \"icf\" to \"application/vnd.commerce-battelle\",\n    \"icm\" to \"application/vnd.iccprofile\",\n    \"ico\" to \"image/x-icon\",\n    \"ics\" to \"text/calendar\",\n    \"ief\" to \"image/ief\",\n    \"ifb\" to \"text/calendar\",\n    \"ifc\" to \"application/p21\",\n    \"ifm\" to \"application/vnd.shana.informed.formdata\",\n    \"iges\" to \"model/iges\",\n    \"igl\" to \"application/vnd.igloader\",\n    \"igm\" to \"application/vnd.insors.igm\",\n    \"ign\" to \"application/vnd.coreos.ignition+json\",\n    \"ignition\" to \"application/vnd.coreos.ignition+json\",\n    \"igs\" to \"model/iges\",\n    \"igx\" to \"application/vnd.micrografx.igx\",\n    \"iif\" to \"application/vnd.shana.informed.interchange\",\n    \"iii\" to \"application/x-iphone\",\n    \"imf\" to \"application/vnd.imagemeter.folder+zip\",\n    \"imgcal\" to \"application/vnd.3lightssoftware.imagescal\",\n    \"imi\" to \"application/vnd.imagemeter.image+zip\",\n    \"imp\" to \"application/vnd.accpac.simply.imp\",\n    \"ims\" to \"application/vnd.ms-ims\",\n    \"imscc\" to \"application/vnd.ims.imsccv1p1\",\n    \"imy\" to \"audio/imelody\",\n    \"info\" to \"application/x-info\",\n    \"ink\" to \"application/inkml+xml\",\n    \"inkml\" to \"application/inkml+xml\",\n    \"inp\" to \"chemical/x-gamess-input\",\n    \"ins\" to \"application/x-internet-signup\",\n    \"iota\" to \"application/vnd.astraea-software.iota\",\n    \"ipfix\" to \"application/ipfix\",\n    \"ipk\" to \"application/vnd.shana.informed.package\",\n    \"ipns-record\" to \"application/vnd.ipfs.ipns-record\",\n    \"irm\" to \"application/vnd.ibm.rights-management\",\n    \"irp\" to \"application/vnd.irepository.package+xml\",\n    \"ism\" to \"model/vnd.gdl\",\n    \"iso\" to \"application/x-iso9660-image\",\n    \"isp\" to \"application/x-internet-signup\",\n    \"ist\" to \"chemical/x-isostar\",\n    \"istc\" to \"application/vnd.veryant.thin\",\n    \"istr\" to \"chemical/x-isostar\",\n    \"isws\" to \"application/vnd.veryant.thin\",\n    \"itp\" to \"application/vnd.shana.informed.formtemplate\",\n    \"its\" to \"application/its+xml\",\n    \"ivp\" to \"application/vnd.immervision-ivp\",\n    \"ivu\" to \"application/vnd.immervision-ivu\",\n    \"j2c\" to \"image/j2c\",\n    \"j2k\" to \"image/j2c\",\n    \"jad\" to \"text/vnd.sun.j2me.app-descriptor\",\n    \"jam\" to \"application/vnd.jam\",\n    \"jar\" to \"application/java-archive\",\n    \"java\" to \"text/x-java\",\n    \"jdx\" to \"chemical/x-jcamp-dx\",\n    \"jfif\" to \"image/jpeg\",\n    \"jhc\" to \"image/jphc\",\n    \"jisp\" to \"application/vnd.jisp\",\n    \"jls\" to \"image/jls\",\n    \"jlt\" to \"application/vnd.hp-jlyt\",\n    \"jmz\" to \"application/x-jmol\",\n    \"jng\" to \"image/x-jng\",\n    \"jnlp\" to \"application/x-java-jnlp-file\",\n    \"joda\" to \"application/vnd.joost.joda-archive\",\n    \"jp2\" to \"image/jp2\",\n    \"jpe\" to \"image/jpeg\",\n    \"jpeg\" to \"image/jpeg\",\n    \"jpf\" to \"image/jpx\",\n    \"jpg\" to \"image/jpeg\",\n    \"jpg2\" to \"image/jp2\",\n    \"jpgm\" to \"image/jpm\",\n    \"jph\" to \"image/jph\",\n    \"jphc\" to \"image/jphc\",\n    \"jpm\" to \"image/jpm\",\n    \"jpx\" to \"image/jpx\",\n    \"jrd\" to \"application/jrd+json\",\n    \"js\" to \"text/javascript\",\n    \"json\" to \"application/json\",\n    \"json-patch\" to \"application/json-patch+json\",\n    \"jsonld\" to \"application/ld+json\",\n    \"jsontd\" to \"application/td+json\",\n    \"jsontm\" to \"application/tm+json\",\n    \"jt\" to \"model/JT\",\n    \"jtd\" to \"text/vnd.esmertec.theme-descriptor\",\n    \"jxl\" to \"image/jxl\",\n    \"jxr\" to \"image/jxr\",\n    \"jxra\" to \"image/jxrA\",\n    \"jxrs\" to \"image/jxrS\",\n    \"jxs\" to \"image/jxs\",\n    \"jxsc\" to \"image/jxsc\",\n    \"jxsi\" to \"image/jxsi\",\n    \"jxss\" to \"image/jxss\",\n    \"karbon\" to \"application/vnd.kde.karbon\",\n    \"kcm\" to \"application/vnd.nervana\",\n    \"key\" to \"application/pgp-keys\",\n    \"keynote\" to \"application/vnd.apple.keynote\",\n    \"kfo\" to \"application/vnd.kde.kformula\",\n    \"kia\" to \"application/vnd.kidspiration\",\n    \"kil\" to \"application/x-killustrator\",\n    \"kin\" to \"chemical/x-kinemage\",\n    \"kml\" to \"application/vnd.google-earth.kml+xml\",\n    \"kmz\" to \"application/vnd.google-earth.kmz\",\n    \"kne\" to \"application/vnd.Kinar\",\n    \"knp\" to \"application/vnd.Kinar\",\n    \"kom\" to \"application/vnd.hbci\",\n    \"kon\" to \"application/vnd.kde.kontour\",\n    \"koz\" to \"audio/vnd.audiokoz\",\n    \"kpr\" to \"application/vnd.kde.kpresenter\",\n    \"kpt\" to \"application/vnd.kde.kpresenter\",\n    \"ksp\" to \"application/vnd.kde.kspread\",\n    \"ktr\" to \"application/vnd.kahootz\",\n    \"ktx\" to \"image/ktx\",\n    \"ktx2\" to \"image/ktx2\",\n    \"ktz\" to \"application/vnd.kahootz\",\n    \"kwd\" to \"application/vnd.kde.kword\",\n    \"kwt\" to \"application/vnd.kde.kword\",\n    \"l16\" to \"audio/L16\",\n    \"las\" to \"application/vnd.las\",\n    \"lasjson\" to \"application/vnd.las.las+json\",\n    \"lasxml\" to \"application/vnd.las.las+xml\",\n    \"latex\" to \"application/x-latex\",\n    \"lbc\" to \"audio/iLBC\",\n    \"lbd\" to \"application/vnd.llamagraphics.life-balance.desktop\",\n    \"lbe\" to \"application/vnd.llamagraphics.life-balance.exchange+xml\",\n    \"lca\" to \"application/vnd.logipipe.circuit+zip\",\n    \"lcs\" to \"application/vnd.logipipe.circuit+zip\",\n    \"le\" to \"application/vnd.bluetooth.le.oob\",\n    \"les\" to \"application/vnd.hhe.lesson-player\",\n    \"lgr\" to \"application/lgr+xml\",\n    \"lha\" to \"application/x-lha\",\n    \"lhs\" to \"text/x-literate-haskell\",\n    \"lhzd\" to \"application/vnd.belightsoft.lhzd+zip\",\n    \"lhzl\" to \"application/vnd.belightsoft.lhzl+zip\",\n    \"lin\" to \"application/bbolin\",\n    \"line\" to \"application/vnd.nebumind.line\",\n    \"link66\" to \"application/vnd.route66.link66+xml\",\n    \"list3820\" to \"application/vnd.afpc.modca\",\n    \"listafp\" to \"application/vnd.afpc.modca\",\n    \"lmp\" to \"model/vnd.gdl\",\n    \"loas\" to \"audio/usac\",\n    \"loom\" to \"application/vnd.loom\",\n    \"lostsyncxml\" to \"application/lostsync+xml\",\n    \"lostxml\" to \"application/lost+xml\",\n    \"lpf\" to \"application/lpf+zip\",\n    \"lrc\" to \"application/lrc\",\n    \"lrm\" to \"application/vnd.ms-lrm\",\n    \"lsf\" to \"video/x-la-asf\",\n    \"lsx\" to \"video/x-la-asf\",\n    \"ltx\" to \"text/x-tex\",\n    \"lvp\" to \"audio/vnd.lucent.voice\",\n    \"lwp\" to \"application/vnd.lotus-wordpro\",\n    \"lxf\" to \"application/LXF\",\n    \"ly\" to \"text/x-lilypond\",\n    \"lyx\" to \"application/x-lyx\",\n    \"lzh\" to \"application/x-lzh\",\n    \"lzx\" to \"application/x-lzx\",\n    \"m\" to \"application/vnd.wolfram.mathematica.package\",\n    \"m1v\" to \"video/mpeg\",\n    \"m21\" to \"application/mp21\",\n    \"m2t\" to \"video/mpeg\",\n    \"m2ts\" to \"video/mp2t\",\n    \"m2v\" to \"video/mpeg\",\n    \"m3g\" to \"application/m3g\",\n    \"m3u\" to \"audio/x-mpegurl\",\n    \"m3u8\" to \"audio/x-mpegurl\",\n    \"m4a\" to \"audio/mpeg\",\n    \"m4b\" to \"audio/mp4\",\n    \"m4p\" to \"audio/mp4\",\n    \"m4r\" to \"audio/mpeg\",\n    \"m4s\" to \"video/iso.segment\",\n    \"m4u\" to \"video/vnd.mpegurl\",\n    \"m4v\" to \"video/mp4\",\n    \"ma\" to \"application/mathematica\",\n    \"mads\" to \"application/mads+xml\",\n    \"maei\" to \"application/mmt-aei+xml\",\n    \"mag\" to \"application/vnd.ecowin.chart\",\n    \"mail\" to \"message/rfc822\",\n    \"maker\" to \"application/x-maker\",\n    \"man\" to \"application/x-troff-man\",\n    \"manifest\" to \"text/cache-manifest\",\n    \"markdown\" to \"text/markdown\",\n    \"mb\" to \"application/mathematica\",\n    \"mbk\" to \"application/vnd.Mobius.MBK\",\n    \"mbox\" to \"application/mbox\",\n    \"mbsdf\" to \"application/vnd.mdl-mbsdf\",\n    \"mc1\" to \"application/vnd.medcalcdata\",\n    \"mc2\" to \"text/vnd.senx.warpscript\",\n    \"mcd\" to \"application/vnd.mcd\",\n    \"mcif\" to \"chemical/x-mmcif\",\n    \"mcm\" to \"chemical/x-macmolecule\",\n    \"md\" to \"text/markdown\",\n    \"mdb\" to \"application/msaccess\",\n    \"mdc\" to \"application/vnd.marlin.drm.mdcf\",\n    \"mdi\" to \"image/vnd.ms-modi\",\n    \"mdl\" to \"application/vnd.mdl\",\n    \"me\" to \"application/x-troff-me\",\n    \"mesh\" to \"model/mesh\",\n    \"meta4\" to \"application/metalink4+xml\",\n    \"mets\" to \"application/mets+xml\",\n    \"mf4\" to \"application/MF4\",\n    \"mfm\" to \"application/vnd.mfmp\",\n    \"mft\" to \"application/rpki-manifest\",\n    \"mgp\" to \"application/vnd.osgeo.mapguide.package\",\n    \"mgz\" to \"application/vnd.proteus.magazine\",\n    \"mhas\" to \"audio/mhas\",\n    \"mid\" to \"audio/sp-midi\",\n    \"mif\" to \"application/vnd.mif\",\n    \"miz\" to \"text/mizar\",\n    \"mj2\" to \"video/mj2\",\n    \"mjp2\" to \"video/mj2\",\n    \"mjs\" to \"text/javascript\",\n    \"mka\" to \"audio/x-matroska\",\n    \"mkv\" to \"video/x-matroska\",\n    \"ml2\" to \"application/vnd.sybyl.mol2\",\n    \"mlp\" to \"audio/vnd.dolby.mlp\",\n    \"mm\" to \"application/x-freemind\",\n    \"mmd\" to \"application/vnd.chipnuts.karaoke-mmd\",\n    \"mmdb\" to \"application/vnd.maxmind.maxmind-db\",\n    \"mmf\" to \"application/vnd.smaf\",\n    \"mml\" to \"application/mathml+xml\",\n    \"mmod\" to \"chemical/x-macromodel-input\",\n    \"mmr\" to \"image/vnd.fujixerox.edmics-mmr\",\n    \"mng\" to \"video/x-mng\",\n    \"mobi\" to \"application/x-mobipocket-ebook\",\n    \"moc\" to \"text/x-moc\",\n    \"mod\" to \"application/xml-dtd\",\n    \"model-inter\" to \"application/vnd.vd-study\",\n    \"modl\" to \"application/vnd.modl\",\n    \"mods\" to \"application/mods+xml\",\n    \"mol\" to \"chemical/x-mdl-molfile\",\n    \"mol2\" to \"application/vnd.sybyl.mol2\",\n    \"moml\" to \"model/vnd.moml+xml\",\n    \"moo\" to \"chemical/x-mopac-out\",\n    \"mop\" to \"chemical/x-mopac-input\",\n    \"mopcrt\" to \"chemical/x-mopac-input\",\n    \"mov\" to \"video/quicktime\",\n    \"movie\" to \"video/x-sgi-movie\",\n    \"mp1\" to \"audio/mpeg\",\n    \"mp1v\" to \"video/mpeg\",\n    \"mp2\" to \"audio/mpeg\",\n    \"mp21\" to \"application/mp21\",\n    \"mp2v\" to \"video/mpeg\",\n    \"mp3\" to \"audio/mpeg\",\n    \"mp4\" to \"video/mp4\",\n    \"mp4v\" to \"video/mp4\",\n    \"mpa\" to \"audio/mpeg\",\n    \"mpc\" to \"chemical/x-mopac-input\",\n    \"mpd\" to \"application/dash+xml\",\n    \"mpdd\" to \"application/dashdelta\",\n    \"mpe\" to \"video/mpeg\",\n    \"mpeg\" to \"video/mpeg\",\n    \"mpeg1\" to \"video/mpeg\",\n    \"mpeg2\" to \"video/mpeg\",\n    \"mpeg4\" to \"video/mp4\",\n    \"mpega\" to \"audio/mpeg\",\n    \"mpf\" to \"text/vnd.ms-mediapackage\",\n    \"mpg\" to \"video/mpeg\",\n    \"mpg4\" to \"video/mp4\",\n    \"mpga\" to \"audio/mpeg\",\n    \"mph\" to \"application/x-comsol\",\n    \"mpkg\" to \"application/vnd.apple.installer+xml\",\n    \"mpm\" to \"application/vnd.blueice.multipass\",\n    \"mpn\" to \"application/vnd.mophun.application\",\n    \"mpp\" to \"application/vnd.ms-project\",\n    \"mpt\" to \"application/vnd.ms-project\",\n    \"mpv\" to \"video/x-matroska\",\n    \"mpv1\" to \"video/mpeg\",\n    \"mpv2\" to \"video/mpeg\",\n    \"mpw\" to \"application/vnd.exstream-empower+zip\",\n    \"mpy\" to \"application/vnd.ibm.MiniPay\",\n    \"mqy\" to \"application/vnd.Mobius.MQY\",\n    \"mrc\" to \"application/marc\",\n    \"mrcx\" to \"application/marcxml+xml\",\n    \"ms\" to \"application/x-troff-ms\",\n    \"msa\" to \"application/vnd.msa-disk-image\",\n    \"msd\" to \"application/vnd.fdsn.mseed\",\n    \"mseed\" to \"application/vnd.fdsn.mseed\",\n    \"mseq\" to \"application/vnd.mseq\",\n    \"msf\" to \"application/vnd.epson.msf\",\n    \"msh\" to \"model/mesh\",\n    \"msi\" to \"application/x-msi\",\n    \"msl\" to \"application/vnd.Mobius.MSL\",\n    \"msm\" to \"model/vnd.gdl\",\n    \"msp\" to \"application/octet-stream\",\n    \"msty\" to \"application/vnd.muvee.style\",\n    \"msu\" to \"application/octet-stream\",\n    \"mtl\" to \"model/mtl\",\n    \"mts\" to \"video/mp2t\",\n    \"multitrack\" to \"audio/vnd.presonus.multitrack\",\n    \"mus\" to \"application/vnd.musician\",\n    \"musd\" to \"application/mmt-usd+xml\",\n    \"mvb\" to \"chemical/x-mopac-vib\",\n    \"mvt\" to \"application/vnd.mapbox-vector-tile\",\n    \"mwc\" to \"application/vnd.dpgraph\",\n    \"mwf\" to \"application/vnd.MFER\",\n    \"mxf\" to \"application/mxf\",\n    \"mxi\" to \"application/vnd.vd-study\",\n    \"mxl\" to \"application/vnd.recordare.musicxml\",\n    \"mxmf\" to \"audio/mobile-xmf\",\n    \"mxml\" to \"application/xv+xml\",\n    \"mxs\" to \"application/vnd.triscape.mxs\",\n    \"mxu\" to \"video/vnd.mpegurl\",\n    \"n3\" to \"text/n3\",\n    \"nb\" to \"application/vnd.wolfram.mathematica\",\n    \"nbp\" to \"application/vnd.wolfram.player\",\n    \"nc\" to \"application/x-netcdf\",\n    \"ndc\" to \"application/vnd.osa.netdeploy\",\n    \"ndl\" to \"application/vnd.lotus-notes\",\n    \"nds\" to \"application/vnd.nintendo.nitro.rom\",\n    \"nebul\" to \"application/vnd.nebumind.line\",\n    \"nef\" to \"image/x-nikon-nef\",\n    \"ngdat\" to \"application/vnd.nokia.n-gage.data\",\n    \"nim\" to \"video/vnd.nokia.interleaved-multimedia\",\n    \"nimn\" to \"application/vnd.nimn\",\n    \"nitf\" to \"application/vnd.nitf\",\n    \"nlu\" to \"application/vnd.neurolanguage.nlu\",\n    \"nml\" to \"application/vnd.enliven\",\n    \"nnd\" to \"application/vnd.noblenet-directory\",\n    \"nns\" to \"application/vnd.noblenet-sealer\",\n    \"nnw\" to \"application/vnd.noblenet-web\",\n    \"notebook\" to \"application/vnd.smart.notebook\",\n    \"nq\" to \"application/n-quads\",\n    \"nrw\" to \"image/x-nikon-nrw\",\n    \"ns2\" to \"application/vnd.lotus-notes\",\n    \"ns3\" to \"application/vnd.lotus-notes\",\n    \"ns4\" to \"application/vnd.lotus-notes\",\n    \"nsf\" to \"application/vnd.lotus-notes\",\n    \"nsg\" to \"application/vnd.lotus-notes\",\n    \"nsh\" to \"application/vnd.lotus-notes\",\n    \"nt\" to \"application/n-triples\",\n    \"ntf\" to \"application/vnd.lotus-notes\",\n    \"numbers\" to \"application/vnd.apple.numbers\",\n    \"nwc\" to \"application/x-nwc\",\n    \"o\" to \"application/x-object\",\n    \"oa2\" to \"application/vnd.fujitsu.oasys2\",\n    \"oa3\" to \"application/vnd.fujitsu.oasys3\",\n    \"oas\" to \"application/vnd.fujitsu.oasys\",\n    \"ob\" to \"application/vnd.1ob\",\n    \"obg\" to \"application/vnd.openblox.game-binary\",\n    \"obgx\" to \"application/vnd.openblox.game+xml\",\n    \"obj\" to \"model/obj\",\n    \"oda\" to \"application/ODA\",\n    \"odb\" to \"application/vnd.oasis.opendocument.base\",\n    \"odc\" to \"application/vnd.oasis.opendocument.chart\",\n    \"odd\" to \"application/tei+xml\",\n    \"odf\" to \"application/vnd.oasis.opendocument.formula\",\n    \"odg\" to \"application/vnd.oasis.opendocument.graphics\",\n    \"odi\" to \"application/vnd.oasis.opendocument.image\",\n    \"odm\" to \"application/vnd.oasis.opendocument.text-master\",\n    \"odp\" to \"application/vnd.oasis.opendocument.presentation\",\n    \"ods\" to \"application/vnd.oasis.opendocument.spreadsheet\",\n    \"odt\" to \"application/vnd.oasis.opendocument.text\",\n    \"odx\" to \"application/ODX\",\n    \"oeb\" to \"application/vnd.openeye.oeb\",\n    \"oga\" to \"audio/ogg\",\n    \"ogex\" to \"model/vnd.opengex\",\n    \"ogg\" to \"audio/ogg\",\n    \"ogv\" to \"video/ogg\",\n    \"ogx\" to \"application/ogg\",\n    \"old\" to \"application/x-trash\",\n    \"omg\" to \"audio/ATRAC3\",\n    \"one\" to \"application/onenote\",\n    \"onepkg\" to \"application/onenote\",\n    \"onetmp\" to \"application/onenote\",\n    \"onetoc2\" to \"application/onenote\",\n    \"opf\" to \"application/oebps-package+xml\",\n    \"oprc\" to \"application/vnd.palm\",\n    \"opus\" to \"audio/ogg\",\n    \"or2\" to \"application/vnd.lotus-organizer\",\n    \"or3\" to \"application/vnd.lotus-organizer\",\n    \"orc\" to \"audio/csound\",\n    \"orf\" to \"image/x-olympus-orf\",\n    \"org\" to \"application/vnd.lotus-organizer\",\n    \"orq\" to \"application/ocsp-request\",\n    \"ors\" to \"application/ocsp-response\",\n    \"osf\" to \"application/vnd.yamaha.openscoreformat\",\n    \"osm\" to \"application/vnd.openstreetmap.data+xml\",\n    \"ota\" to \"application/vnd.android.ota\",\n    \"otc\" to \"application/vnd.oasis.opendocument.chart-template\",\n    \"otf\" to \"font/otf\",\n    \"otg\" to \"application/vnd.oasis.opendocument.graphics-template\",\n    \"oth\" to \"application/vnd.oasis.opendocument.text-web\",\n    \"oti\" to \"application/vnd.oasis.opendocument.image-template\",\n    \"otm\" to \"application/vnd.oasis.opendocument.text-master-template\",\n    \"otp\" to \"application/vnd.oasis.opendocument.presentation-template\",\n    \"ots\" to \"application/vnd.oasis.opendocument.spreadsheet-template\",\n    \"ott\" to \"application/vnd.oasis.opendocument.text-template\",\n    \"ovl\" to \"application/vnd.afpc.modca-overlay\",\n    \"oxlicg\" to \"application/vnd.oxli.countgraph\",\n    \"oxps\" to \"application/oxps\",\n    \"oxt\" to \"application/vnd.openofficeorg.extension\",\n    \"oza\" to \"application/x-oz-application\",\n    \"p\" to \"text/x-pascal\",\n    \"p10\" to \"application/pkcs10\",\n    \"p12\" to \"application/x-pkcs12\",\n    \"p21\" to \"application/p21\",\n    \"p2p\" to \"application/vnd.wfa.p2p\",\n    \"p7c\" to \"application/pkcs7-mime\",\n    \"p7m\" to \"application/pkcs7-mime\",\n    \"p7r\" to \"application/x-pkcs7-certreqresp\",\n    \"p7s\" to \"application/pkcs7-signature\",\n    \"p7z\" to \"application/pkcs7-mime\",\n    \"p8\" to \"application/pkcs8\",\n    \"p8e\" to \"application/pkcs8-encrypted\",\n    \"pac\" to \"application/x-ns-proxy-autoconfig\",\n    \"package\" to \"application/vnd.autopackage\",\n    \"pages\" to \"application/vnd.apple.pages\",\n    \"pas\" to \"text/x-pascal\",\n    \"pat\" to \"image/x-coreldrawpattern\",\n    \"patch\" to \"text/x-diff\",\n    \"paw\" to \"application/vnd.pawaafile\",\n    \"pbd\" to \"application/vnd.powerbuilder6\",\n    \"pbm\" to \"image/x-portable-bitmap\",\n    \"pcap\" to \"application/vnd.tcpdump.pcap\",\n    \"pcf\" to \"application/x-font\",\n    \"pcf.Z\" to \"application/x-font-pcf\",\n    \"pcl\" to \"application/vnd.hp-PCL\",\n    \"pcx\" to \"image/vnd.zbrush.pcx\",\n    \"pdb\" to \"chemical/x-pdb\",\n    \"pdf\" to \"application/pdf\",\n    \"pdx\" to \"application/PDX\",\n    \"pef\" to \"image/x-pentax-pef\",\n    \"pem\" to \"application/x-pem-file\",\n    \"pfa\" to \"application/x-font\",\n    \"pfb\" to \"application/x-font\",\n    \"pfr\" to \"application/font-tdpfr\",\n    \"pfx\" to \"application/x-pkcs12\",\n    \"pgb\" to \"image/vnd.globalgraphics.pgb\",\n    \"pgm\" to \"image/x-portable-graymap\",\n    \"pgn\" to \"application/vnd.chess-pgn\",\n    \"pgp\" to \"application/pgp-signature\",\n    \"phps\" to \"text/text\",\n    \"pil\" to \"application/vnd.piaccess.application-licence\",\n    \"pk\" to \"application/x-tex-pk\",\n    \"pkd\" to \"application/vnd.hbci\",\n    \"pkg\" to \"application/vnd.apple.installer+xml\",\n    \"pki\" to \"application/pkixcmp\",\n    \"pkipath\" to \"application/pkix-pkipath\",\n    \"pkpass\" to \"application/vnd.apple.pkpass\",\n    \"pkpasses\" to \"application/vnd.apple.pkpasses\",\n    \"pl\" to \"text/x-perl\",\n    \"plb\" to \"application/vnd.3gpp.pic-bw-large\",\n    \"plc\" to \"application/vnd.Mobius.PLC\",\n    \"plf\" to \"application/vnd.pocketlearn\",\n    \"plj\" to \"audio/vnd.everad.plj\",\n    \"plp\" to \"application/vnd.panoply\",\n    \"pls\" to \"audio/x-scpls\",\n    \"pm\" to \"text/x-perl\",\n    \"pml\" to \"application/vnd.ctc-posml\",\n    \"png\" to \"image/png\",\n    \"pnm\" to \"image/x-portable-anymap\",\n    \"po\" to \"text/plain\",\n    \"portpkg\" to \"application/vnd.macports.portpkg\",\n    \"pot\" to \"application/vnd.ms-powerpoint\",\n    \"potm\" to \"application/vnd.ms-powerpoint.template.macroEnabled.12\",\n    \"potx\" to \"application/vnd.openxmlformats-officedocument.presentationml.template\",\n    \"ppam\" to \"application/vnd.ms-powerpoint.addin.macroEnabled.12\",\n    \"ppd\" to \"application/vnd.cups-ppd\",\n    \"ppkg\" to \"application/vnd.xmpie.ppkg\",\n    \"ppm\" to \"image/x-portable-pixmap\",\n    \"pps\" to \"application/vnd.ms-powerpoint\",\n    \"ppsm\" to \"application/vnd.ms-powerpoint.slideshow.macroEnabled.12\",\n    \"ppsx\" to \"application/vnd.openxmlformats-officedocument.presentationml.slideshow\",\n    \"ppt\" to \"application/vnd.ms-powerpoint\",\n    \"pptm\" to \"application/vnd.ms-powerpoint.presentation.macroEnabled.12\",\n    \"ppttc\" to \"application/vnd.think-cell.ppttc+json\",\n    \"pptx\" to \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n    \"pqa\" to \"application/vnd.palm\",\n    \"prc\" to \"application/x-mobipocket-ebook\",\n    \"pre\" to \"application/vnd.lotus-freelance\",\n    \"preminet\" to \"application/vnd.preminet\",\n    \"prf\" to \"application/pics-rules\",\n    \"provn\" to \"text/provenance-notation\",\n    \"provx\" to \"application/provenance+xml\",\n    \"prt\" to \"chemical/x-ncbi-asn1-ascii\",\n    \"prz\" to \"application/vnd.lotus-freelance\",\n    \"ps\" to \"application/postscript\",\n    \"psb\" to \"application/vnd.3gpp.pic-bw-small\",\n    \"psd\" to \"image/x-photoshop\",\n    \"pseg3820\" to \"application/vnd.afpc.modca\",\n    \"psfs\" to \"application/vnd.psfs\",\n    \"psg\" to \"application/vnd.afpc.modca-pagesegment\",\n    \"psid\" to \"audio/prs.sid\",\n    \"pskcxml\" to \"application/pskc+xml\",\n    \"pt\" to \"application/vnd.snesdev-page-table\",\n    \"pti\" to \"image/prs.pti\",\n    \"ptid\" to \"application/vnd.pvi.ptid1\",\n    \"ptrom\" to \"application/vnd.snesdev-page-table\",\n    \"pub\" to \"application/vnd.exstream-package\",\n    \"pvb\" to \"application/vnd.3gpp.pic-bw-var\",\n    \"pwn\" to \"application/vnd.3M.Post-it-Notes\",\n    \"py\" to \"text/x-python\",\n    \"pya\" to \"audio/vnd.ms-playready.media.pya\",\n    \"pyc\" to \"application/x-python-code\",\n    \"pyo\" to \"application/x-python-code\",\n    \"pyox\" to \"model/vnd.pytha.pyox\",\n    \"pyv\" to \"video/vnd.ms-playready.media.pyv\",\n    \"qam\" to \"application/vnd.epson.quickanime\",\n    \"qbo\" to \"application/vnd.intu.qbo\",\n    \"qca\" to \"application/vnd.ericsson.quickcall\",\n    \"qcall\" to \"application/vnd.ericsson.quickcall\",\n    \"qcp\" to \"audio/EVRC-QCP\",\n    \"qfx\" to \"application/vnd.intu.qfx\",\n    \"qgs\" to \"application/x-qgis\",\n    \"qps\" to \"application/vnd.publishare-delta-tree\",\n    \"qt\" to \"video/quicktime\",\n    \"qtl\" to \"application/x-quicktimeplayer\",\n    \"quiz\" to \"application/vnd.quobject-quoxdocument\",\n    \"quox\" to \"application/vnd.quobject-quoxdocument\",\n    \"qvd\" to \"application/vnd.theqvd\",\n    \"qwd\" to \"application/vnd.Quark.QuarkXPress\",\n    \"qwt\" to \"application/vnd.Quark.QuarkXPress\",\n    \"qxb\" to \"application/vnd.Quark.QuarkXPress\",\n    \"qxd\" to \"application/vnd.Quark.QuarkXPress\",\n    \"qxl\" to \"application/vnd.Quark.QuarkXPress\",\n    \"qxt\" to \"application/vnd.Quark.QuarkXPress\",\n    \"ra\" to \"audio/x-pn-realaudio\",\n    \"raf\" to \"image/x-fuji-raf\",\n    \"ram\" to \"audio/x-pn-realaudio\",\n    \"rapd\" to \"application/route-apd+xml\",\n    \"rar\" to \"application/vnd.rar\",\n    \"ras\" to \"image/x-cmu-raster\",\n    \"rb\" to \"application/x-ruby\",\n    \"rcprofile\" to \"application/vnd.ipunplugged.rcprofile\",\n    \"rct\" to \"application/prs.nprend\",\n    \"rd\" to \"chemical/x-mdl-rdfile\",\n    \"rdf\" to \"application/rdf+xml\",\n    \"rdf-crypt\" to \"application/prs.rdf-xml-crypt\",\n    \"rdp\" to \"application/x-rdp\",\n    \"rdz\" to \"application/vnd.data-vision.rdz\",\n    \"relo\" to \"application/p2p-overlay+xml\",\n    \"reload\" to \"application/vnd.resilient.logic\",\n    \"rep\" to \"application/vnd.businessobjects\",\n    \"request\" to \"application/vnd.nervana\",\n    \"rfcxml\" to \"application/rfc+xml\",\n    \"rgb\" to \"image/x-rgb\",\n    \"rgbe\" to \"image/vnd.radiance\",\n    \"rif\" to \"application/reginfo+xml\",\n    \"rip\" to \"audio/vnd.rip\",\n    \"rl\" to \"application/resource-lists+xml\",\n    \"rlc\" to \"image/vnd.fujixerox.edmics-rlc\",\n    \"rld\" to \"application/resource-lists-diff+xml\",\n    \"rlm\" to \"application/vnd.resilient.logic\",\n    \"rm\" to \"audio/x-pn-realaudio\",\n    \"rms\" to \"application/vnd.jcp.javame.midlet-rms\",\n    \"rnc\" to \"application/relax-ng-compact-syntax\",\n    \"rnd\" to \"application/prs.nprend\",\n    \"roa\" to \"application/rpki-roa\",\n    \"roff\" to \"text/troff\",\n    \"ros\" to \"chemical/x-rosdal\",\n    \"rp9\" to \"application/vnd.cloanto.rp9\",\n    \"rpm\" to \"application/x-redhat-package-manager\",\n    \"rpss\" to \"application/vnd.nokia.radio-presets\",\n    \"rpst\" to \"application/vnd.nokia.radio-preset\",\n    \"rq\" to \"application/sparql-query\",\n    \"rs\" to \"application/rls-services+xml\",\n    \"rsat\" to \"application/atsc-rsat+xml\",\n    \"rsheet\" to \"application/urc-ressheet+xml\",\n    \"rsm\" to \"model/vnd.gdl\",\n    \"rss\" to \"application/rss+xml\",\n    \"rst\" to \"text/prs.fallenstein.rst\",\n    \"rtf\" to \"text/rtf\",\n    \"rtttl\" to \"audio/midi\",\n    \"rtx\" to \"audio/midi\",\n    \"rusd\" to \"application/route-usd+xml\",\n    \"rw2\" to \"image/x-panasonic-rw2\",\n    \"rxn\" to \"chemical/x-mdl-rxnfile\",\n    \"rxt\" to \"application/vnd.medicalholodeck.recordxr\",\n    \"s11\" to \"video/vnd.sealed.mpeg1\",\n    \"s14\" to \"video/vnd.sealed.mpeg4\",\n    \"s1a\" to \"application/vnd.sealedmedia.softseal.pdf\",\n    \"s1e\" to \"application/vnd.sealed.xls\",\n    \"s1g\" to \"image/vnd.sealedmedia.softseal.gif\",\n    \"s1h\" to \"application/vnd.sealedmedia.softseal.html\",\n    \"s1j\" to \"image/vnd.sealedmedia.softseal.jpg\",\n    \"s1m\" to \"audio/vnd.sealedmedia.softseal.mpeg\",\n    \"s1n\" to \"image/vnd.sealed.png\",\n    \"s1p\" to \"application/vnd.sealed.ppt\",\n    \"s1q\" to \"video/vnd.sealedmedia.softseal.mov\",\n    \"s1w\" to \"application/vnd.sealed.doc\",\n    \"s3df\" to \"application/vnd.sealed.3df\",\n    \"sac\" to \"application/tamp-sequence-adjust-confirm\",\n    \"saf\" to \"application/vnd.yamaha.smaf-audio\",\n    \"sam\" to \"application/vnd.lotus-wordpro\",\n    \"sarif\" to \"application/sarif+json\",\n    \"sarif-external-properties\" to \"application/sarif-external-properties+json\",\n    \"sarif-external-properties.json\" to \"application/sarif-external-properties+json\",\n    \"sarif.json\" to \"application/sarif+json\",\n    \"sc\" to \"application/vnd.ibm.secure-container\",\n    \"scala\" to \"text/x-scala\",\n    \"scd\" to \"application/vnd.scribus\",\n    \"sce\" to \"application/x-scilab\",\n    \"sci\" to \"application/x-scilab\",\n    \"scim\" to \"application/scim+json\",\n    \"scl\" to \"application/vnd.sycle+xml\",\n    \"scld\" to \"application/vnd.doremir.scorecloud-binary-document\",\n    \"scm\" to \"application/vnd.lotus-screencam\",\n    \"sco\" to \"audio/csound\",\n    \"scq\" to \"application/scvp-cv-request\",\n    \"scr\" to \"application/x-silverlight\",\n    \"scs\" to \"application/scvp-cv-response\",\n    \"scsf\" to \"application/vnd.sealed.csf\",\n    \"sd\" to \"chemical/x-mdl-sdfile\",\n    \"sd2\" to \"audio/x-sd2\",\n    \"sda\" to \"application/vnd.stardivision.draw\",\n    \"sdc\" to \"application/vnd.stardivision.calc\",\n    \"sdd\" to \"application/vnd.stardivision.impress\",\n    \"sdf\" to \"chemical/x-mdl-sdfile\",\n    \"sdkd\" to \"application/vnd.solent.sdkm+xml\",\n    \"sdkm\" to \"application/vnd.solent.sdkm+xml\",\n    \"sdo\" to \"application/vnd.sealed.doc\",\n    \"sdoc\" to \"application/vnd.sealed.doc\",\n    \"sdp\" to \"application/vnd.stardivision.impress\",\n    \"sds\" to \"application/vnd.stardivision.chart\",\n    \"sdw\" to \"application/vnd.stardivision.writer\",\n    \"see\" to \"application/vnd.seemail\",\n    \"seed\" to \"application/vnd.fdsn.seed\",\n    \"sem\" to \"application/vnd.sealed.eml\",\n    \"sema\" to \"application/vnd.sema\",\n    \"semd\" to \"application/vnd.semd\",\n    \"semf\" to \"application/vnd.semf\",\n    \"seml\" to \"application/vnd.sealed.eml\",\n    \"senml\" to \"application/senml+json\",\n    \"senml-etchc\" to \"application/senml-etch+cbor\",\n    \"senml-etchj\" to \"application/senml-etch+json\",\n    \"senmlc\" to \"application/senml+cbor\",\n    \"senmle\" to \"application/senml-exi\",\n    \"senmlx\" to \"application/senml+xml\",\n    \"sensml\" to \"application/sensml+json\",\n    \"sensmlc\" to \"application/sensml+cbor\",\n    \"sensmle\" to \"application/sensml-exi\",\n    \"sensmlx\" to \"application/sensml+xml\",\n    \"ser\" to \"application/java-serialized-object\",\n    \"sfc\" to \"application/vnd.nintendo.snes.rom\",\n    \"sfd\" to \"application/vnd.font-fontforge-sfd\",\n    \"sfd-hdstx\" to \"application/vnd.hydrostatix.sof-data\",\n    \"sfs\" to \"application/vnd.spotfire.sfs\",\n    \"sfv\" to \"text/x-sfv\",\n    \"sgf\" to \"application/x-go-sgf\",\n    \"sgi\" to \"image/vnd.sealedmedia.softseal.gif\",\n    \"sgif\" to \"image/vnd.sealedmedia.softseal.gif\",\n    \"sgl\" to \"application/vnd.stardivision.writer-global\",\n    \"sgm\" to \"text/SGML\",\n    \"sgml\" to \"text/SGML\",\n    \"sh\" to \"text/x-sh\",\n    \"shaclc\" to \"text/shaclc\",\n    \"shar\" to \"application/x-shar\",\n    \"shc\" to \"text/shaclc\",\n    \"shex\" to \"text/shex\",\n    \"shf\" to \"application/shf+xml\",\n    \"shp\" to \"application/x-qgis\",\n    \"shtml\" to \"text/html\",\n    \"shx\" to \"application/x-qgis\",\n    \"si\" to \"text/vnd.wap.si\",\n    \"sic\" to \"application/vnd.wap.sic\",\n    \"sid\" to \"audio/prs.sid\",\n    \"sieve\" to \"application/sieve\",\n    \"sig\" to \"application/pgp-signature\",\n    \"sik\" to \"application/x-trash\",\n    \"silo\" to \"model/mesh\",\n    \"sipa\" to \"application/vnd.smintio.portals.archive\",\n    \"sis\" to \"application/vnd.symbian.install\",\n    \"sit\" to \"application/x-stuffit\",\n    \"sitx\" to \"application/x-stuffit\",\n    \"siv\" to \"application/sieve\",\n    \"sjp\" to \"image/vnd.sealedmedia.softseal.jpg\",\n    \"sjpg\" to \"image/vnd.sealedmedia.softseal.jpg\",\n    \"skd\" to \"application/vnd.koan\",\n    \"skm\" to \"application/vnd.koan\",\n    \"skp\" to \"application/vnd.koan\",\n    \"skt\" to \"application/vnd.koan\",\n    \"sl\" to \"text/vnd.wap.sl\",\n    \"sla\" to \"application/vnd.scribus\",\n    \"slaz\" to \"application/vnd.scribus\",\n    \"slc\" to \"application/vnd.wap.slc\",\n    \"sldm\" to \"application/vnd.ms-powerpoint.slide.macroEnabled.12\",\n    \"sldx\" to \"application/vnd.openxmlformats-officedocument.presentationml.slide\",\n    \"sls\" to \"application/route-s-tsid+xml\",\n    \"slt\" to \"application/vnd.epson.salt\",\n    \"sm\" to \"application/vnd.stepmania.stepchart\",\n    \"smc\" to \"application/vnd.nintendo.snes.rom\",\n    \"smf\" to \"audio/sp-midi\",\n    \"smh\" to \"application/vnd.sealed.mht\",\n    \"smht\" to \"application/vnd.sealed.mht\",\n    \"smi\" to \"application/smil+xml\",\n    \"smil\" to \"application/smil+xml\",\n    \"smk\" to \"video/vnd.radgamettools.smacker\",\n    \"sml\" to \"application/smil+xml\",\n    \"smo\" to \"video/vnd.sealedmedia.softseal.mov\",\n    \"smov\" to \"video/vnd.sealedmedia.softseal.mov\",\n    \"smp\" to \"audio/vnd.sealedmedia.softseal.mpeg\",\n    \"smp3\" to \"audio/vnd.sealedmedia.softseal.mpeg\",\n    \"smpg\" to \"video/vnd.sealed.mpeg1\",\n    \"sms\" to \"application/vnd.3gpp2.sms\",\n    \"smv\" to \"audio/SMV\",\n    \"smzip\" to \"application/vnd.stepmania.package\",\n    \"snd\" to \"audio/basic\",\n    \"soa\" to \"text/dns\",\n    \"soc\" to \"application/sgml-open-catalog\",\n    \"sofa\" to \"audio/sofa\",\n    \"sos\" to \"text/vnd.sosi\",\n    \"spc\" to \"chemical/x-galactic-spc\",\n    \"spd\" to \"application/vnd.sealedmedia.softseal.pdf\",\n    \"spdf\" to \"application/vnd.sealedmedia.softseal.pdf\",\n    \"spdx\" to \"text/spdx\",\n    \"spdx.json\" to \"application/spdx+json\",\n    \"spf\" to \"application/vnd.yamaha.smaf-phrase\",\n    \"spl\" to \"application/futuresplash\",\n    \"spn\" to \"image/vnd.sealed.png\",\n    \"spng\" to \"image/vnd.sealed.png\",\n    \"spo\" to \"text/vnd.in3d.spot\",\n    \"spot\" to \"text/vnd.in3d.spot\",\n    \"spp\" to \"application/scvp-vp-response\",\n    \"sppt\" to \"application/vnd.sealed.ppt\",\n    \"spq\" to \"application/scvp-vp-request\",\n    \"spx\" to \"audio/ogg\",\n    \"sql\" to \"application/sql\",\n    \"sqlite\" to \"application/vnd.sqlite3\",\n    \"sqlite3\" to \"application/vnd.sqlite3\",\n    \"sr\" to \"application/vnd.sigrok.session\",\n    \"src\" to \"application/x-wais-source\",\n    \"srt\" to \"application/x-subrip\",\n    \"sru\" to \"application/sru+xml\",\n    \"srw\" to \"image/x-samsung-srw\",\n    \"srx\" to \"application/sparql-results+xml\",\n    \"sse\" to \"application/vnd.kodak-descriptor\",\n    \"ssf\" to \"application/vnd.epson.ssf\",\n    \"ssml\" to \"application/ssml+xml\",\n    \"ssv\" to \"application/vnd.shade-save-file\",\n    \"ssvc\" to \"application/vnd.crypto-shade-file\",\n    \"ssw\" to \"video/vnd.sealed.swf\",\n    \"sswf\" to \"video/vnd.sealed.swf\",\n    \"st\" to \"application/vnd.sailingtracker.track\",\n    \"stc\" to \"application/vnd.sun.xml.calc.template\",\n    \"std\" to \"application/vnd.sun.xml.draw.template\",\n    \"step\" to \"model/step\",\n    \"stf\" to \"application/vnd.wt.stf\",\n    \"sti\" to \"application/vnd.sun.xml.impress.template\",\n    \"stif\" to \"application/vnd.sealed.tiff\",\n    \"stix\" to \"application/stix+json\",\n    \"stk\" to \"application/hyperstudio\",\n    \"stl\" to \"application/vnd.ms-pki.stl\",\n    \"stml\" to \"application/vnd.sealedmedia.softseal.html\",\n    \"stp\" to \"model/step\",\n    \"stpnc\" to \"application/p21\",\n    \"stpx\" to \"model/step+xml\",\n    \"stpxz\" to \"model/step-xml+zip\",\n    \"stpz\" to \"model/step+zip\",\n    \"str\" to \"application/vnd.pg.format\",\n    \"study-inter\" to \"application/vnd.vd-study\",\n    \"stw\" to \"application/vnd.sun.xml.writer.template\",\n    \"sty\" to \"text/x-tex\",\n    \"sus\" to \"application/vnd.sus-calendar\",\n    \"susp\" to \"application/vnd.sus-calendar\",\n    \"sv4cpio\" to \"application/x-sv4cpio\",\n    \"sv4crc\" to \"application/x-sv4crc\",\n    \"svc\" to \"application/vnd.dvb.service\",\n    \"svg\" to \"image/svg+xml\",\n    \"svgz\" to \"image/svg+xml\",\n    \"sw\" to \"chemical/x-swissprot\",\n    \"swf\" to \"application/vnd.adobe.flash.movie\",\n    \"swi\" to \"application/vnd.aristanetworks.swi\",\n    \"swidtag\" to \"application/swid+xml\",\n    \"sxc\" to \"application/vnd.sun.xml.calc\",\n    \"sxd\" to \"application/vnd.sun.xml.draw\",\n    \"sxg\" to \"application/vnd.sun.xml.writer.global\",\n    \"sxi\" to \"application/vnd.sun.xml.impress\",\n    \"sxl\" to \"application/vnd.sealed.xls\",\n    \"sxls\" to \"application/vnd.sealed.xls\",\n    \"sxm\" to \"application/vnd.sun.xml.math\",\n    \"sxw\" to \"application/vnd.sun.xml.writer\",\n    \"sy2\" to \"application/vnd.sybyl.mol2\",\n    \"syft.json\" to \"application/vnd.syft+json\",\n    \"t\" to \"text/troff\",\n    \"tag\" to \"text/prs.lines.tag\",\n    \"taglet\" to \"application/vnd.mynfc\",\n    \"tam\" to \"application/vnd.onepager\",\n    \"tamp\" to \"application/vnd.onepagertamp\",\n    \"tamx\" to \"application/vnd.onepagertamx\",\n    \"tao\" to \"application/vnd.tao.intent-module-archive\",\n    \"tap\" to \"image/vnd.tencent.tap\",\n    \"tar\" to \"application/x-tar\",\n    \"tat\" to \"application/vnd.onepagertat\",\n    \"tatp\" to \"application/vnd.onepagertatp\",\n    \"tatx\" to \"application/vnd.onepagertatx\",\n    \"tau\" to \"application/tamp-apex-update\",\n    \"taz\" to \"application/x-gtar-compressed\",\n    \"tcap\" to \"application/vnd.3gpp2.tcap\",\n    \"tcl\" to \"text/x-tcl\",\n    \"tcu\" to \"application/tamp-community-update\",\n    \"td\" to \"application/urc-targetdesc+xml\",\n    \"teacher\" to \"application/vnd.smart.teacher\",\n    \"tei\" to \"application/tei+xml\",\n    \"teiCorpus\" to \"application/tei+xml\",\n    \"ter\" to \"application/tamp-error\",\n    \"tex\" to \"text/x-tex\",\n    \"texi\" to \"application/x-texinfo\",\n    \"texinfo\" to \"application/x-texinfo\",\n    \"text\" to \"text/plain\",\n    \"tfi\" to \"application/thraud+xml\",\n    \"tfx\" to \"image/tiff-fx\",\n    \"tgf\" to \"chemical/x-mdl-tgf\",\n    \"tgz\" to \"application/x-gtar-compressed\",\n    \"thmx\" to \"application/vnd.ms-officetheme\",\n    \"tif\" to \"image/tiff\",\n    \"tiff\" to \"image/tiff\",\n    \"tk\" to \"text/x-tcl\",\n    \"tlclient\" to \"application/vnd.cendio.thinlinc.clientconf\",\n    \"tm\" to \"text/texmacs\",\n    \"tm.json\" to \"application/tm+json\",\n    \"tm.jsonld\" to \"application/tm+json\",\n    \"tmo\" to \"application/vnd.tmobile-livetv\",\n    \"tnef\" to \"application/vnd.ms-tnef\",\n    \"tnf\" to \"application/vnd.ms-tnef\",\n    \"torrent\" to \"application/x-bittorrent\",\n    \"tpl\" to \"application/vnd.groove-tool-template\",\n    \"tpt\" to \"application/vnd.trid.tpt\",\n    \"tr\" to \"text/troff\",\n    \"tra\" to \"application/vnd.trueapp\",\n    \"tree\" to \"application/vnd.rainstor.data\",\n    \"trig\" to \"application/trig\",\n    \"ts\" to \"video/mp2ts\",\n    \"tsa\" to \"application/tamp-sequence-adjust\",\n    \"tsd\" to \"application/timestamped-data\",\n    \"tsp\" to \"application/dsptype\",\n    \"tsq\" to \"application/timestamp-query\",\n    \"tsr\" to \"application/timestamp-reply\",\n    \"tst\" to \"application/vnd.etsi.timestamp-token\",\n    \"tsv\" to \"text/tab-separated-values\",\n    \"ttc\" to \"font/collection\",\n    \"ttf\" to \"font/ttf\",\n    \"ttl\" to \"text/turtle\",\n    \"ttml\" to \"application/ttml+xml\",\n    \"tuc\" to \"application/tamp-update-confirm\",\n    \"tur\" to \"application/tamp-update\",\n    \"twd\" to \"application/vnd.SimTech-MindMapper\",\n    \"twds\" to \"application/vnd.SimTech-MindMapper\",\n    \"txd\" to \"application/vnd.genomatix.tuxedo\",\n    \"txf\" to \"application/vnd.Mobius.TXF\",\n    \"txt\" to \"text/plain\",\n    \"u3d\" to \"model/u3d\",\n    \"u8dsn\" to \"message/global-delivery-status\",\n    \"u8hdr\" to \"message/global-headers\",\n    \"u8mdn\" to \"message/global-disposition-notification\",\n    \"u8msg\" to \"message/global\",\n    \"udeb\" to \"application/vnd.debian.binary-package\",\n    \"ufd\" to \"application/vnd.ufdl\",\n    \"ufdl\" to \"application/vnd.ufdl\",\n    \"uis\" to \"application/urc-uisocketdesc+xml\",\n    \"umj\" to \"application/vnd.umajin\",\n    \"unityweb\" to \"application/vnd.unity\",\n    \"uo\" to \"application/vnd.uoml+xml\",\n    \"uoml\" to \"application/vnd.uoml+xml\",\n    \"upa\" to \"application/vnd.hbci\",\n    \"uri\" to \"text/uri-list\",\n    \"urim\" to \"application/vnd.uri-map\",\n    \"urimap\" to \"application/vnd.uri-map\",\n    \"uris\" to \"text/uri-list\",\n    \"usda\" to \"model/vnd.usda\",\n    \"usdz\" to \"model/vnd.usdz+zip\",\n    \"ustar\" to \"application/x-ustar\",\n    \"utz\" to \"application/vnd.uiq.theme\",\n    \"uva\" to \"audio/vnd.dece.audio\",\n    \"uvd\" to \"application/vnd.dece.data\",\n    \"uvf\" to \"application/vnd.dece.data\",\n    \"uvg\" to \"image/vnd.dece.graphic\",\n    \"uvh\" to \"video/vnd.dece.hd\",\n    \"uvi\" to \"image/vnd.dece.graphic\",\n    \"uvm\" to \"video/vnd.dece.mobile\",\n    \"uvp\" to \"video/vnd.dece.pd\",\n    \"uvs\" to \"video/vnd.dece.sd\",\n    \"uvt\" to \"application/vnd.dece.ttml+xml\",\n    \"uvu\" to \"video/vnd.dece.mp4\",\n    \"uvv\" to \"video/vnd.dece.video\",\n    \"uvva\" to \"audio/vnd.dece.audio\",\n    \"uvvd\" to \"application/vnd.dece.data\",\n    \"uvvf\" to \"application/vnd.dece.data\",\n    \"uvvg\" to \"image/vnd.dece.graphic\",\n    \"uvvh\" to \"video/vnd.dece.hd\",\n    \"uvvi\" to \"image/vnd.dece.graphic\",\n    \"uvvm\" to \"video/vnd.dece.mobile\",\n    \"uvvp\" to \"video/vnd.dece.pd\",\n    \"uvvs\" to \"video/vnd.dece.sd\",\n    \"uvvt\" to \"application/vnd.dece.ttml+xml\",\n    \"uvvu\" to \"video/vnd.dece.mp4\",\n    \"uvvv\" to \"video/vnd.dece.video\",\n    \"uvvx\" to \"application/vnd.dece.unspecified\",\n    \"uvvz\" to \"application/vnd.dece.zip\",\n    \"uvx\" to \"application/vnd.dece.unspecified\",\n    \"uvz\" to \"application/vnd.dece.zip\",\n    \"val\" to \"chemical/x-ncbi-asn1-binary\",\n    \"vbk\" to \"audio/vnd.nortel.vbk\",\n    \"vbox\" to \"application/vnd.previewsystems.box\",\n    \"vcard\" to \"text/vcard\",\n    \"vcd\" to \"application/x-cdlink\",\n    \"vcf\" to \"text/x-vcard\",\n    \"vcg\" to \"application/vnd.groove-vcard\",\n    \"vcj\" to \"application/voucher-cms+json\",\n    \"vcs\" to \"text/x-vcalendar\",\n    \"vcx\" to \"application/vnd.vcx\",\n    \"vds\" to \"model/vnd.sap.vds\",\n    \"vew\" to \"application/vnd.lotus-approach\",\n    \"vfr\" to \"application/vnd.tml\",\n    \"viaframe\" to \"application/vnd.tml\",\n    \"vis\" to \"application/vnd.visionary\",\n    \"viv\" to \"video/vnd.vivo\",\n    \"vmd\" to \"chemical/x-vmd\",\n    \"vms\" to \"chemical/x-vamas-iso14976\",\n    \"vmt\" to \"application/vnd.valve.source.material\",\n    \"vor\" to \"application/vnd.stardivision.writer\",\n    \"vpm\" to \"multipart/voice-message\",\n    \"vrm\" to \"model/vrml\",\n    \"vrml\" to \"model/vrml\",\n    \"vsc\" to \"application/vnd.vidsoft.vidconference\",\n    \"vsd\" to \"application/vnd.visio\",\n    \"vsf\" to \"application/vnd.vsf\",\n    \"vss\" to \"application/vnd.visio\",\n    \"vst\" to \"application/vnd.visio\",\n    \"vsw\" to \"application/vnd.visio\",\n    \"vtf\" to \"image/vnd.valve.source.texture\",\n    \"vtnstd\" to \"application/vnd.veritone.aion+json\",\n    \"vtt\" to \"text/vtt\",\n    \"vtu\" to \"model/vnd.vtu\",\n    \"vwx\" to \"application/vnd.vectorworks\",\n    \"vxml\" to \"application/voicexml+xml\",\n    \"wad\" to \"application/x-doom\",\n    \"wadl\" to \"application/vnd.sun.wadl+xml\",\n    \"wafl\" to \"application/vnd.wasmflow.wafl\",\n    \"wasm\" to \"application/wasm\",\n    \"wav\" to \"audio/x-wav\",\n    \"wax\" to \"audio/x-ms-wax\",\n    \"wbmp\" to \"image/vnd.wap.wbmp\",\n    \"wbs\" to \"application/vnd.criticaltools.wbs+xml\",\n    \"wbxml\" to \"application/vnd.wap.wbxml\",\n    \"wcm\" to \"application/vnd.ms-works\",\n    \"wdb\" to \"application/vnd.ms-works\",\n    \"webarchive\" to \"application/x-webarchive\",\n    \"webarchivexml\" to \"application/x-webarchive-xml\",\n    \"webm\" to \"video/webm\",\n    \"webmanifest\" to \"application/manifest+json\",\n    \"webp\" to \"image/webp\",\n    \"wg\" to \"application/vnd.pmi.widget\",\n    \"wgsl\" to \"text/wgsl\",\n    \"wgt\" to \"application/widget\",\n    \"wif\" to \"application/watcherinfo+xml\",\n    \"win\" to \"model/vnd.gdl\",\n    \"wk\" to \"application/x-123\",\n    \"wk1\" to \"application/vnd.lotus-1-2-3\",\n    \"wk3\" to \"application/vnd.lotus-1-2-3\",\n    \"wk4\" to \"application/vnd.lotus-1-2-3\",\n    \"wks\" to \"application/vnd.ms-works\",\n    \"wlnk\" to \"application/link-format\",\n    \"wm\" to \"video/x-ms-wm\",\n    \"wma\" to \"audio/x-ms-wma\",\n    \"wmc\" to \"application/vnd.wmc\",\n    \"wmd\" to \"application/x-ms-wmd\",\n    \"wmf\" to \"image/wmf\",\n    \"wml\" to \"text/vnd.wap.wml\",\n    \"wmlc\" to \"application/vnd.wap.wmlc\",\n    \"wmls\" to \"text/vnd.wap.wmlscript\",\n    \"wmlsc\" to \"application/vnd.wap.wmlscriptc\",\n    \"wmv\" to \"video/x-ms-wmv\",\n    \"wmx\" to \"video/x-ms-wmx\",\n    \"wmz\" to \"application/x-ms-wmz\",\n    \"woff\" to \"font/woff\",\n    \"woff2\" to \"font/woff2\",\n    \"wpd\" to \"application/vnd.wordperfect\",\n    \"wpl\" to \"application/vnd.ms-wpl\",\n    \"wps\" to \"application/vnd.ms-works\",\n    \"wqd\" to \"application/vnd.wqd\",\n    \"wrf\" to \"video/x-webex\",\n    \"wrl\" to \"model/vrml\",\n    \"wsc\" to \"application/vnd.wfa.wsc\",\n    \"wsdl\" to \"application/wsdl+xml\",\n    \"wspolicy\" to \"application/wspolicy+xml\",\n    \"wtb\" to \"application/vnd.webturbo\",\n    \"wv\" to \"application/vnd.wv.csp+wbxml\",\n    \"wvx\" to \"video/x-ms-wvx\",\n    \"wz\" to \"application/x-wingz\",\n    \"x3d\" to \"model/x3d+xml\",\n    \"x3db\" to \"model/x3d+fastinfoset\",\n    \"x3dv\" to \"model/x3d-vrml\",\n    \"x3dvz\" to \"model/x3d-vrml\",\n    \"x3dz\" to \"model/x3d+xml\",\n    \"x_b\" to \"model/vnd.parasolid.transmit.binary\",\n    \"x_t\" to \"model/vnd.parasolid.transmit.text\",\n    \"xar\" to \"application/vnd.xara\",\n    \"xav\" to \"application/xcap-att+xml\",\n    \"xbd\" to \"application/vnd.fujixerox.docuworks.binder\",\n    \"xbm\" to \"image/x-xbitmap\",\n    \"xca\" to \"application/xcap-caps+xml\",\n    \"xcf\" to \"image/x-xcf\",\n    \"xcos\" to \"application/x-scilab-xcos\",\n    \"xcs\" to \"application/calendar+xml\",\n    \"xct\" to \"application/vnd.fujixerox.docuworks.container\",\n    \"xdd\" to \"application/bacnet-xdd+zip\",\n    \"xdf\" to \"application/xcap-diff+xml\",\n    \"xdm\" to \"application/vnd.syncml.dm+xml\",\n    \"xdp\" to \"application/vnd.adobe.xdp+xml\",\n    \"xdssc\" to \"application/dssc+xml\",\n    \"xdw\" to \"application/vnd.fujixerox.docuworks\",\n    \"xel\" to \"application/xcap-el+xml\",\n    \"xer\" to \"application/xcap-error+xml\",\n    \"xfd\" to \"application/vnd.xfdl\",\n    \"xfdf\" to \"application/xfdf\",\n    \"xfdl\" to \"application/vnd.xfdl\",\n    \"xhe\" to \"audio/usac\",\n    \"xht\" to \"application/xhtml+xml\",\n    \"xhtm\" to \"application/xhtml+xml\",\n    \"xhtml\" to \"application/xhtml+xml\",\n    \"xhvml\" to \"application/xv+xml\",\n    \"xif\" to \"image/vnd.xiff\",\n    \"xla\" to \"application/vnd.ms-excel\",\n    \"xlam\" to \"application/vnd.ms-excel.addin.macroEnabled.12\",\n    \"xlc\" to \"application/vnd.ms-excel\",\n    \"xlf\" to \"application/xliff+xml\",\n    \"xlim\" to \"application/vnd.xmpie.xlim\",\n    \"xlm\" to \"application/vnd.ms-excel\",\n    \"xls\" to \"application/vnd.ms-excel\",\n    \"xlsb\" to \"application/vnd.ms-excel.sheet.binary.macroEnabled.12\",\n    \"xlsm\" to \"application/vnd.ms-excel.sheet.macroEnabled.12\",\n    \"xlsx\" to \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n    \"xlt\" to \"application/vnd.ms-excel\",\n    \"xltm\" to \"application/vnd.ms-excel.template.macroEnabled.12\",\n    \"xltx\" to \"application/vnd.openxmlformats-officedocument.spreadsheetml.template\",\n    \"xlw\" to \"application/vnd.ms-excel\",\n    \"xmf\" to \"audio/midi\",\n    \"xml\" to \"text/xml\",\n    \"xmls\" to \"application/dskpp+xml\",\n    \"xmt_bin\" to \"model/vnd.parasolid.transmit.binary\",\n    \"xmt_txt\" to \"model/vnd.parasolid.transmit.text\",\n    \"xns\" to \"application/xcap-ns+xml\",\n    \"xo\" to \"application/vnd.olpc-sugar\",\n    \"xodp\" to \"application/vnd.collabio.xodocuments.presentation\",\n    \"xods\" to \"application/vnd.collabio.xodocuments.spreadsheet\",\n    \"xodt\" to \"application/vnd.collabio.xodocuments.document\",\n    \"xop\" to \"application/xop+xml\",\n    \"xotp\" to \"application/vnd.collabio.xodocuments.presentation-template\",\n    \"xots\" to \"application/vnd.collabio.xodocuments.spreadsheet-template\",\n    \"xott\" to \"application/vnd.collabio.xodocuments.document-template\",\n    \"xpak\" to \"application/vnd.gentoo.xpak\",\n    \"xpi\" to \"application/x-xpinstall\",\n    \"xpm\" to \"image/x-xpixmap\",\n    \"xpr\" to \"application/vnd.is-xpr\",\n    \"xps\" to \"application/vnd.ms-xpsdocument\",\n    \"xpw\" to \"application/vnd.intercon.formnet\",\n    \"xpx\" to \"application/vnd.intercon.formnet\",\n    \"xsf\" to \"application/prs.xsf+xml\",\n    \"xsl\" to \"application/xslt+xml\",\n    \"xslt\" to \"application/xslt+xml\",\n    \"xsm\" to \"application/vnd.syncml+xml\",\n    \"xspf\" to \"application/xspf+xml\",\n    \"xtel\" to \"chemical/x-xtel\",\n    \"xul\" to \"application/vnd.mozilla.xul+xml\",\n    \"xvm\" to \"application/xv+xml\",\n    \"xvml\" to \"application/xv+xml\",\n    \"xwd\" to \"image/x-xwindowdump\",\n    \"xyz\" to \"chemical/x-xyz\",\n    \"xyze\" to \"image/vnd.radiance\",\n    \"xz\" to \"application/x-xz\",\n    \"yaml\" to \"application/yaml\",\n    \"yang\" to \"application/yang\",\n    \"yin\" to \"application/yin+xml\",\n    \"yme\" to \"application/vnd.yaoweme\",\n    \"yml\" to \"application/yaml\",\n    \"yt\" to \"video/vnd.youtube.yt\",\n    \"zaz\" to \"application/vnd.zzazz.deck+xml\",\n    \"zfc\" to \"application/vnd.filmit.zfc\",\n    \"zfo\" to \"application/vnd.software602.filler.form-xml-zip\",\n    \"zip\" to \"application/zip\",\n    \"zir\" to \"application/vnd.zul\",\n    \"zirz\" to \"application/vnd.zul\",\n    \"zmm\" to \"application/vnd.HandHeld-Entertainment+xml\",\n    \"zmt\" to \"chemical/x-mopac-input\",\n    \"zone\" to \"text/dns\",\n    \"zst\" to \"application/zstd\",\n    \"~\" to \"application/x-trash\",\n)\n"
  },
  {
    "path": "mime/android.extensions",
    "content": "% application/x-trash\n123 application/vnd.lotus-1-2-3\n1905.1 application/vnd.ieee.1905\n1clr application/clr\n1km application/vnd.1000minds.decision-model+xml\n210 application/p21\n3dm text/vnd.in3d.3dml\n3dml text/vnd.in3d.3dml\n3g2 video/3gpp2\n3ga audio/3gpp\n3gp video/3gpp\n3gp2 video/3gpp2\n3gpp video/3gpp\n3gpp2 video/3gpp2\n3mf application/vnd.ms-3mfdocument\n3tz application/vnd.maxar.archive.3tz+zip\n726 audio/32kadpcm\n7z application/x-7z-compressed\nAMR audio/AMR\nAWB audio/AMR-WB\nCQL text/cql\nELN application/vnd.eln+zip\nJ2C image/j2c\nJ2K image/j2c\nPGB image/vnd.globalgraphics.pgb\nQCP audio/EVRC-QCP\nSAR application/vnd.sar\nVES application/vnd.ves.encrypted\nVFK text/vnd.exchangeable\na text/vnd.a\na2l application/A2L\na52 audio/ac3\naa3 audio/ATRAC3\naac audio/aac\naal audio/ATRAC-ADVANCED-LOSSLESS\nabc text/vnd.abc\nabw application/x-abiword\nac application/pkix-attr-cert\nac2 application/vnd.banana-accounting\nac3 audio/ac3\nacc application/vnd.americandynamics.acc\nacn audio/asc\nacu application/vnd.acucobol\nacutc application/vnd.acucorp\nadt audio/aac\nadts audio/aac\naep application/vnd.audiograph\nafp application/vnd.afpc.modca\nage application/vnd.age\nahead application/vnd.ahead.space\nahv application/vnd.android.haptics.vibration+xml\nai application/postscript\naif audio/x-aiff\naifc audio/x-aiff\naiff audio/x-aiff\naion application/vnd.veritone.aion+json\nait application/vnd.dvb.ait\nalc chemical/x-alchemy\nami application/vnd.amiga.ami\naml application/AML\namlx application/automationml-amlx+zip\namr audio/amr\nanx application/annodex\napex application/vnd.apexlang\napexlang application/vnd.apexlang\napk application/vnd.android.package-archive\napkg application/vnd.anki\napng image/apng\nappcache text/cache-manifest\napr application/vnd.lotus-approach\napxml application/auth-policy+xml\narrow application/vnd.apache.arrow.file\narrows application/vnd.apache.arrow.stream\nart image/x-jg\nartisan application/vnd.artisan+json\narw image/x-sony-arw\nasc application/pgp-keys\nascii text/vnd.ascii-art\nasf application/vnd.ms-asf\nasice application/vnd.etsi.asic-e+zip\nasics application/vnd.etsi.asic-s+zip\nasn chemical/x-ncbi-asn1-spec\naso chemical/x-ncbi-asn1-binary\nass audio/aac\nat3 audio/ATRAC3\natc application/vnd.acucorp\natf application/ATF\natfx application/ATFX\natom application/atom+xml\natomcat application/atomcat+xml\natomdeleted application/atomdeleted+xml\natomsrv application/atomserv+xml\natomsvc application/atomsvc+xml\natx audio/ATRAC-X\natxml application/ATXML\nau audio/basic\nauc application/tamp-apex-update-confirm\navci image/avci\navcs image/avcs\navi video/avi\navif image/avif\nawb audio/AMR-WB\naxa audio/annodex\naxv video/annodex\nazf application/vnd.airzip.filesecure.azf\nazs application/vnd.airzip.filesecure.azs\nazv image/vnd.airzip.accelerator.azv\nazw3 application/vnd.amazon.mobi8-ebook\nb chemical/x-molconn-Z\nb16 image/vnd.pco.b16\nbak application/x-trash\nbar application/vnd.qualcomm.brew-app-res\nbary model/vnd.bary\nbat application/x-msdos-program\nbcpio application/x-bcpio\nbdm application/vnd.syncml.dm+wbxml\nbed application/vnd.realvnc.bed\nbh2 application/vnd.fujitsu.oasysprs\nbib text/x-bibtex\nbik video/vnd.radgamettools.bink\nbin application/octet-stream\nbk2 video/vnd.radgamettools.bink\nbkm application/vnd.nervana\nbmed multipart/vnd.bint.med-plus\nbmi application/vnd.bmi\nbmml application/vnd.balsamiq.bmml+xml\nbmp image/x-ms-bmp\nbmpr application/vnd.balsamiq.bmpr\nboo text/x-boo\nbook application/x-maker\nbox application/vnd.previewsystems.box\nbpd application/vnd.hbci\nbrf text/plain\nbsd chemical/x-crossfire\nbsp model/vnd.valve.source.compiled-map\nbtf image/prs.btif\nbtif image/prs.btif\nc text/x-csrc\nc++ text/x-c++src\nc11amc application/vnd.cluetrust.cartomobile-config\nc11amz application/vnd.cluetrust.cartomobile-config-pkg\nc3d chemical/x-chem3d\nc3ex application/cccex\nc4d application/vnd.clonk.c4group\nc4f application/vnd.clonk.c4group\nc4g application/vnd.clonk.c4group\nc4p application/vnd.clonk.c4group\nc4u application/vnd.clonk.c4group\nc9r application/vnd.cryptomator.encrypted\nc9s application/vnd.cryptomator.encrypted\ncab application/vnd.ms-cab-compressed\ncac chemical/x-cache\ncache chemical/x-cache\ncap application/vnd.tcpdump.pcap\ncar application/vnd.ipld.car\ncarjson application/vnd.eu.kasparian.car+json\ncascii chemical/x-cactvs-binary\ncat application/vnd.ms-pki.seccat\ncbin chemical/x-cactvs-binary\ncbor application/cbor\ncbr application/vnd.comicbook-rar\ncbz application/vnd.comicbook+zip\ncc text/x-c++src\nccc text/vnd.net2phone.commcenter.command\nccmp application/ccmp+xml\nccxml application/ccxml+xml\ncda application/x-cdf\ncdbcmsg application/vnd.contact.cmsg\ncdf application/x-cdf\ncdfx application/CDFX+XML\ncdkey application/vnd.mediastation.cdkey\ncdmia application/cdmi-capability\ncdmic application/cdmi-container\ncdmid application/cdmi-domain\ncdmio application/cdmi-object\ncdmiq application/cdmi-queue\ncdr image/x-coreldraw\ncdt image/x-coreldrawtemplate\ncdx chemical/x-cdx\ncdxml application/vnd.chemdraw+xml\ncdy application/vnd.cinderella\ncea application/CEA\ncef chemical/x-cxf\ncellml application/cellml+xml\ncer application/pkix-cert\ncgm image/cgm\nchm chemical/x-chemdraw\nchrt application/vnd.kde.kchart\ncif chemical/x-cif\ncii application/vnd.anser-web-certificate-issue-initiation\ncil application/vnd.ms-artgalry\ncl application/simple-filter+xml\ncla application/vnd.claymore\nclass application/java-vm\ncld model/vnd.cld\nclkk application/vnd.crick.clicker.keyboard\nclkp application/vnd.crick.clicker.palette\nclkt application/vnd.crick.clicker.template\nclkw application/vnd.crick.clicker.wordbank\nclkx application/vnd.crick.clicker\ncls text/x-tex\nclue application/clue_info+xml\ncmc application/vnd.cosmocaller\ncmdf chemical/x-cmdf\ncml chemical/x-cml\ncmp application/vnd.yellowriver-custom-menu\ncmsc application/cms\ncnd text/jcr-cnd\ncod application/vnd.rim.cod\ncoffee application/vnd.coffeescript\ncom application/x-msdos-program\ncopyright text/vnd.debian.copyright\ncoswid application/swid+cbor\ncpa chemical/x-compass\ncpio application/x-cpio\ncpkg application/vnd.xmpie.cpkg\ncpl application/cpl+xml\ncpp text/x-c++src\ncpt image/x-corelphotopaint\ncr2 image/x-canon-cr2\ncrl application/pkix-crl\ncrt application/x-x509-ca-cert\ncrtr application/vnd.multiad.creator\ncrw image/x-canon-crw\ncryptomator application/vnd.cryptomator.vault\ncryptonote application/vnd.rig.cryptonote\ncsd audio/csound\ncsf chemical/x-cache-csf\ncsh text/x-csh\ncsl application/vnd.citationstyles.style+xml\ncsm chemical/x-csml\ncsml chemical/x-csml\ncsp application/vnd.commonspace\ncsrattrs application/csrattrs\ncss text/css\ncst application/vnd.commonspace\ncsv text/comma-separated-values\ncsvs text/csv-schema\nctab chemical/x-cactvs-binary\nctx chemical/x-ctx\ncu application/cu-seeme\ncub chemical/x-gaussian-cube\ncuc application/tamp-community-update-confirm\ncur image/ico\ncurl text/vnd.curl\ncw application/prs.cww\ncwl application/cwl\ncwl.json application/cwl+json\ncww application/prs.cww\ncxf chemical/x-cxf\ncxx text/x-c++src\nd text/x-dsrc\ndae model/vnd.collada+xml\ndaf application/vnd.Mobius.DAF\ndart application/vnd.dart\ndataless application/vnd.fdsn.seed\ndavmount application/davmount+xml\ndbf application/vnd.dbf\ndcd application/DCD\ndcm application/dicom\ndcr application/x-director\ndd2 application/vnd.oma.dd2+xml\nddd application/vnd.fujixerox.ddd\nddeb application/vnd.debian.binary-package\nddf application/vnd.syncml.dmddf+xml\ndeb application/vnd.debian.binary-package\ndeploy application/octet-stream\nder application/x-x509-ca-cert\ndfac application/vnd.dreamfactory\ndfxp application/ttml+xml\ndif video/dv\ndiff text/plain\ndii application/DII\ndim application/vnd.fastcopy-disk-image\ndir application/x-director\ndis application/vnd.Mobius.DIS\ndist application/vnd.apple.installer+xml\ndistz application/vnd.apple.installer+xml\ndit application/DIT\ndive application/vnd.patentdive\ndjv image/vnd.djvu\ndjvu image/vnd.djvu\ndl application/vnd.datalog\ndll application/x-msdos-program\ndls audio/dls\ndmg application/x-apple-diskimage\ndmp application/vnd.tcpdump.pcap\ndms text/vnd.DMClientScript\ndna application/vnd.dna\ndng image/x-adobe-dng\ndoc application/msword\ndocjson application/vnd.document+json\ndocm application/vnd.ms-word.document.macroEnabled.12\ndocx application/vnd.openxmlformats-officedocument.wordprocessingml.document\ndor model/vnd.gdl\ndot text/vnd.graphviz\ndotm application/vnd.ms-word.template.macroEnabled.12\ndotx application/vnd.openxmlformats-officedocument.wordprocessingml.template\ndp application/vnd.osgi.dp\ndpg application/vnd.dpgraph\ndpgraph application/vnd.dpgraph\ndpkg application/vnd.xmpie.dpkg\ndpx image/dpx\ndrle image/dicom-rle\ndsc text/prs.lines.tag\ndsm application/vnd.desmume.movie\ndssc application/dssc+der\ndtd application/xml-dtd\ndts audio/vnd.dts\ndtshd audio/vnd.dts.hd\ndv video/dv\ndvb video/vnd.dvb.file\ndvc application/dvcs\ndvi application/x-dvi\ndwd application/atsc-dwd+xml\ndwf model/vnd.dwf\ndwg image/vnd.dwg\ndx chemical/x-jcamp-dx\ndxf image/vnd.dxf\ndxp application/vnd.spotfire.dxp\ndxr application/x-director\ndzr application/vnd.dzr\nebuild application/vnd.gentoo.ebuild\necelp4800 audio/vnd.nuera.ecelp4800\necelp7470 audio/vnd.nuera.ecelp7470\necelp9600 audio/vnd.nuera.ecelp9600\necig application/vnd.evolv.ecig.settings\necigprofile application/vnd.evolv.ecig.profile\necigtheme application/vnd.evolv.ecig.theme\neclass application/vnd.gentoo.eclass\nedm application/vnd.novadigm.EDM\nedx application/vnd.novadigm.EDX\nefi application/efi\nefif application/vnd.picsel\nei6 application/vnd.pg.osasli\nemb chemical/x-embl-dl-nucleotide\nembl chemical/x-embl-dl-nucleotide\nemf image/emf\neml message/rfc822\nemm application/vnd.ibm.electronic-media\nemma application/emma+xml\nemotionml application/emotionml+xml\nent application/xml-external-parsed-entity\nentity application/vnd.nervana\nenw audio/EVRCNW\neol audio/vnd.digital-winds\neot application/vnd.ms-fontobject\nep application/vnd.bluetooth.ep.oob\neps application/postscript\neps2 application/postscript\neps3 application/postscript\nepsf application/postscript\nepsi application/postscript\nepub application/epub+zip\nerf image/x-epson-erf\nes text/javascript\nes3 application/vnd.eszigno3+xml\nesa application/vnd.osgi.subsystem\nesf application/vnd.epson.esf\nespass application/vnd.espass-espass+zip\net3 application/vnd.eszigno3+xml\netx text/x-setext\nevb audio/EVRCB\nevc audio/EVRC\nevw audio/EVRCWB\nexe application/x-msdos-program\nexi application/exi\nexp application/express\nexr image/aces\next application/vnd.novadigm.EXT\nez application/andrew-inset\nez2 application/vnd.ezpix-album\nez3 application/vnd.ezpix-package\nf4a audio/mp4\nf4b audio/mp4\nf4p audio/mp4\nf4v video/mp4\nfb application/x-maker\nfbdoc application/x-maker\nfbs image/vnd.fastbidsheet\nfcdt application/vnd.adobe.formscentral.fcdt\nfch chemical/x-gaussian-checkpoint\nfchk chemical/x-gaussian-checkpoint\nfcs application/vnd.isac.fcs\nfdf application/fdf\nfdt application/fdt+xml\nfe_launch application/vnd.denovo.fcselayout-link\nfg5 application/vnd.fujitsu.oasysgp\nfig application/x-xfig\nfinf application/fastinfoset\nfit image/fits\nfits image/fits\nfl application/x-android-drm-fl\nfla application/vnd.dtg.local.flash\nflac audio/flac\nflb application/vnd.ficlab.flb+zip\nfli video/fli\nflo application/vnd.micrografx.flo\nflt text/vnd.ficlab.flt\nflv video/x-flv\nflw application/vnd.kde.kivio\nflx text/vnd.fmi.flexstor\nfly text/vnd.fly\nfm application/x-maker\nfo application/vnd.software602.filler.form+xml\nfpx image/vnd.fpx\nframe application/x-maker\nfrm application/x-maker\nfsc application/vnd.fsc.weblaunch\nfst image/vnd.fst\nftc application/vnd.fluxtime.clip\nfti application/vnd.anser-web-funds-transfer-initiation\nfts image/fits\nfvt video/vnd.fvt\nfxp application/vnd.adobe.fxp\nfxpl application/vnd.adobe.fxp\nfzs application/vnd.fuzzysheet\ng2w application/vnd.geoplan\ng3w application/vnd.geospace\ngac application/vnd.groove-account\ngal chemical/x-gaussian-log\ngam chemical/x-gamess-input\ngamin chemical/x-gamess-input\ngan application/x-ganttproject\ngau chemical/x-gaussian-input\ngbr application/rpki-ghostbusters\ngcd text/x-pcs-gcd\ngcf application/x-graphing-calculator\ngcg chemical/x-gcg8-sequence\ngdl model/vnd.gdl\ngdz application/vnd.familysearch.gedcom+zip\nged text/vnd.familysearch.gedcom\ngen chemical/x-genbank\ngenozip application/vnd.genozip\ngeo application/vnd.dynageo\ngeojson application/geo+json\ngex application/vnd.geometry-explorer\ngf application/x-tex-gf\ngff3 text/gff3\nggb application/vnd.geogebra.file\nggs application/vnd.geogebra.slides\nggt application/vnd.geogebra.tool\nghf application/vnd.groove-help\ngif image/gif\ngim application/vnd.groove-identity-message\ngjc chemical/x-gaussian-input\ngjf chemical/x-gaussian-input\ngl video/gl\nglb model/gltf-binary\nglbin application/gltf-buffer\nglbuf application/gltf-buffer\ngltf model/gltf+json\ngml application/gml+xml\ngnumeric application/x-gnumeric\ngph application/vnd.FloGraphIt\ngpkg application/geopackage+sqlite3\ngpkg.tar application/vnd.gentoo.gpkg\ngpt chemical/x-mopac-graph\ngqf application/vnd.grafeq\ngqs application/vnd.grafeq\ngram application/srgs\ngrd application/vnd.gentics.grd+json\ngre application/vnd.geometry-explorer\ngrv application/vnd.groove-injector\ngrxml application/srgs+xml\ngsf application/x-font\ngsheet application/urc-grpsheet+xml\ngsm audio/x-gsm\ngtar application/x-gtar\ngtm application/vnd.groove-tool-message\ngtw model/vnd.gtw\ngv text/vnd.graphviz\ngxt application/vnd.geonext\ngz application/gzip\nh text/x-chdr\nh++ text/x-c++hdr\nhal application/vnd.hal+xml\nhans text/vnd.hans\nhbc application/vnd.hbci\nhbci application/vnd.hbci\nhdf application/x-hdf\nhdr image/vnd.radiance\nhdt application/vnd.hdt\nheic image/heic\nheics image/heic-sequence\nheif image/heif\nheifs image/heif-sequence\nhej2 image/hej2k\nheld application/atsc-held+xml\nhgl text/vnd.hgl\nhh text/x-c++hdr\nhif image/heif\nhin chemical/x-hin\nhpgl application/vnd.hp-HPGL\nhpi application/vnd.hp-hpid\nhpid application/vnd.hp-hpid\nhpp text/x-c++hdr\nhps application/vnd.hp-hps\nhpub application/prs.hpub+zip\nhqx application/mac-binhex40\nhs text/x-haskell\nhsj2 image/hsj2\nhsl application/vnd.hsl\nhta application/hta\nhtc text/x-component\nhtke application/vnd.kenameaapp\nhtm text/html\nhtml text/html\nhvd application/vnd.yamaha.hv-dic\nhvp application/vnd.yamaha.hv-voice\nhvs application/vnd.yamaha.hv-script\nhwp application/x-hwp\nhxx text/x-c++hdr\ni2g application/vnd.intergeo\nic0 application/vnd.commerce-battelle\nic1 application/vnd.commerce-battelle\nic2 application/vnd.commerce-battelle\nic3 application/vnd.commerce-battelle\nic4 application/vnd.commerce-battelle\nic5 application/vnd.commerce-battelle\nic6 application/vnd.commerce-battelle\nic7 application/vnd.commerce-battelle\nic8 application/vnd.commerce-battelle\nica application/x-ica\nicc application/vnd.iccprofile\nicd application/vnd.commerce-battelle\nicf application/vnd.commerce-battelle\nicm application/vnd.iccprofile\nico image/x-icon\nics text/calendar\nief image/ief\nifb text/calendar\nifc application/p21\nifm application/vnd.shana.informed.formdata\niges model/iges\nigl application/vnd.igloader\nigm application/vnd.insors.igm\nign application/vnd.coreos.ignition+json\nignition application/vnd.coreos.ignition+json\nigs model/iges\nigx application/vnd.micrografx.igx\niif application/vnd.shana.informed.interchange\niii application/x-iphone\nimf application/vnd.imagemeter.folder+zip\nimgcal application/vnd.3lightssoftware.imagescal\nimi application/vnd.imagemeter.image+zip\nimp application/vnd.accpac.simply.imp\nims application/vnd.ms-ims\nimscc application/vnd.ims.imsccv1p1\nimy audio/imelody\ninfo application/x-info\nink application/inkml+xml\ninkml application/inkml+xml\ninp chemical/x-gamess-input\nins application/x-internet-signup\niota application/vnd.astraea-software.iota\nipfix application/ipfix\nipk application/vnd.shana.informed.package\nipns-record application/vnd.ipfs.ipns-record\nirm application/vnd.ibm.rights-management\nirp application/vnd.irepository.package+xml\nism model/vnd.gdl\niso application/x-iso9660-image\nisp application/x-internet-signup\nist chemical/x-isostar\nistc application/vnd.veryant.thin\nistr chemical/x-isostar\nisws application/vnd.veryant.thin\nitp application/vnd.shana.informed.formtemplate\nits application/its+xml\nivp application/vnd.immervision-ivp\nivu application/vnd.immervision-ivu\nj2c image/j2c\nj2k image/j2c\njad text/vnd.sun.j2me.app-descriptor\njam application/vnd.jam\njar application/java-archive\njava text/x-java\njdx chemical/x-jcamp-dx\njfif image/jpeg\njhc image/jphc\njisp application/vnd.jisp\njls image/jls\njlt application/vnd.hp-jlyt\njmz application/x-jmol\njng image/x-jng\njnlp application/x-java-jnlp-file\njoda application/vnd.joost.joda-archive\njp2 image/jp2\njpe image/jpeg\njpeg image/jpeg\njpf image/jpx\njpg image/jpeg\njpg2 image/jp2\njpgm image/jpm\njph image/jph\njphc image/jphc\njpm image/jpm\njpx image/jpx\njrd application/jrd+json\njs text/javascript\njson application/json\njson-patch application/json-patch+json\njsonld application/ld+json\njsontd application/td+json\njsontm application/tm+json\njt model/JT\njtd text/vnd.esmertec.theme-descriptor\njxl image/jxl\njxr image/jxr\njxra image/jxrA\njxrs image/jxrS\njxs image/jxs\njxsc image/jxsc\njxsi image/jxsi\njxss image/jxss\nkarbon application/vnd.kde.karbon\nkcm application/vnd.nervana\nkey application/pgp-keys\nkeynote application/vnd.apple.keynote\nkfo application/vnd.kde.kformula\nkia application/vnd.kidspiration\nkil application/x-killustrator\nkin chemical/x-kinemage\nkml application/vnd.google-earth.kml+xml\nkmz application/vnd.google-earth.kmz\nkne application/vnd.Kinar\nknp application/vnd.Kinar\nkom application/vnd.hbci\nkon application/vnd.kde.kontour\nkoz audio/vnd.audiokoz\nkpr application/vnd.kde.kpresenter\nkpt application/vnd.kde.kpresenter\nksp application/vnd.kde.kspread\nktr application/vnd.kahootz\nktx image/ktx\nktx2 image/ktx2\nktz application/vnd.kahootz\nkwd application/vnd.kde.kword\nkwt application/vnd.kde.kword\nl16 audio/L16\nlas application/vnd.las\nlasjson application/vnd.las.las+json\nlasxml application/vnd.las.las+xml\nlatex application/x-latex\nlbc audio/iLBC\nlbd application/vnd.llamagraphics.life-balance.desktop\nlbe application/vnd.llamagraphics.life-balance.exchange+xml\nlca application/vnd.logipipe.circuit+zip\nlcs application/vnd.logipipe.circuit+zip\nle application/vnd.bluetooth.le.oob\nles application/vnd.hhe.lesson-player\nlgr application/lgr+xml\nlha application/x-lha\nlhs text/x-literate-haskell\nlhzd application/vnd.belightsoft.lhzd+zip\nlhzl application/vnd.belightsoft.lhzl+zip\nlin application/bbolin\nline application/vnd.nebumind.line\nlink66 application/vnd.route66.link66+xml\nlist3820 application/vnd.afpc.modca\nlistafp application/vnd.afpc.modca\nlmp model/vnd.gdl\nloas audio/usac\nloom application/vnd.loom\nlostsyncxml application/lostsync+xml\nlostxml application/lost+xml\nlpf application/lpf+zip\nlrc application/lrc\nlrm application/vnd.ms-lrm\nlsf video/x-la-asf\nlsx video/x-la-asf\nltx text/x-tex\nlvp audio/vnd.lucent.voice\nlwp application/vnd.lotus-wordpro\nlxf application/LXF\nly text/x-lilypond\nlyx application/x-lyx\nlzh application/x-lzh\nlzx application/x-lzx\nm application/vnd.wolfram.mathematica.package\nm1v video/mpeg\nm21 application/mp21\nm2t video/mpeg\nm2ts video/mp2t\nm2v video/mpeg\nm3g application/m3g\nm3u audio/x-mpegurl\nm3u8 audio/x-mpegurl\nm4a audio/mpeg\nm4b audio/mp4\nm4p audio/mp4\nm4r audio/mpeg\nm4s video/iso.segment\nm4u video/vnd.mpegurl\nm4v video/mp4\nma application/mathematica\nmads application/mads+xml\nmaei application/mmt-aei+xml\nmag application/vnd.ecowin.chart\nmail message/rfc822\nmaker application/x-maker\nman application/x-troff-man\nmanifest text/cache-manifest\nmarkdown text/markdown\nmb application/mathematica\nmbk application/vnd.Mobius.MBK\nmbox application/mbox\nmbsdf application/vnd.mdl-mbsdf\nmc1 application/vnd.medcalcdata\nmc2 text/vnd.senx.warpscript\nmcd application/vnd.mcd\nmcif chemical/x-mmcif\nmcm chemical/x-macmolecule\nmd text/markdown\nmdb application/msaccess\nmdc application/vnd.marlin.drm.mdcf\nmdi image/vnd.ms-modi\nmdl application/vnd.mdl\nme application/x-troff-me\nmesh model/mesh\nmeta4 application/metalink4+xml\nmets application/mets+xml\nmf4 application/MF4\nmfm application/vnd.mfmp\nmft application/rpki-manifest\nmgp application/vnd.osgeo.mapguide.package\nmgz application/vnd.proteus.magazine\nmhas audio/mhas\nmid audio/sp-midi\nmif application/vnd.mif\nmiz text/mizar\nmj2 video/mj2\nmjp2 video/mj2\nmjs text/javascript\nmka audio/x-matroska\nmkv video/x-matroska\nml2 application/vnd.sybyl.mol2\nmlp audio/vnd.dolby.mlp\nmm application/x-freemind\nmmd application/vnd.chipnuts.karaoke-mmd\nmmdb application/vnd.maxmind.maxmind-db\nmmf application/vnd.smaf\nmml application/mathml+xml\nmmod chemical/x-macromodel-input\nmmr image/vnd.fujixerox.edmics-mmr\nmng video/x-mng\nmobi application/x-mobipocket-ebook\nmoc text/x-moc\nmod application/xml-dtd\nmodel-inter application/vnd.vd-study\nmodl application/vnd.modl\nmods application/mods+xml\nmol chemical/x-mdl-molfile\nmol2 application/vnd.sybyl.mol2\nmoml model/vnd.moml+xml\nmoo chemical/x-mopac-out\nmop chemical/x-mopac-input\nmopcrt chemical/x-mopac-input\nmov video/quicktime\nmovie video/x-sgi-movie\nmp1 audio/mpeg\nmp1v video/mpeg\nmp2 audio/mpeg\nmp21 application/mp21\nmp2v video/mpeg\nmp3 audio/mpeg\nmp4 video/mp4\nmp4v video/mp4\nmpa audio/mpeg\nmpc chemical/x-mopac-input\nmpd application/dash+xml\nmpdd application/dashdelta\nmpe video/mpeg\nmpeg video/mpeg\nmpeg1 video/mpeg\nmpeg2 video/mpeg\nmpeg4 video/mp4\nmpega audio/mpeg\nmpf text/vnd.ms-mediapackage\nmpg video/mpeg\nmpg4 video/mp4\nmpga audio/mpeg\nmph application/x-comsol\nmpkg application/vnd.apple.installer+xml\nmpm application/vnd.blueice.multipass\nmpn application/vnd.mophun.application\nmpp application/vnd.ms-project\nmpt application/vnd.ms-project\nmpv video/x-matroska\nmpv1 video/mpeg\nmpv2 video/mpeg\nmpw application/vnd.exstream-empower+zip\nmpy application/vnd.ibm.MiniPay\nmqy application/vnd.Mobius.MQY\nmrc application/marc\nmrcx application/marcxml+xml\nms application/x-troff-ms\nmsa application/vnd.msa-disk-image\nmsd application/vnd.fdsn.mseed\nmseed application/vnd.fdsn.mseed\nmseq application/vnd.mseq\nmsf application/vnd.epson.msf\nmsh model/mesh\nmsi application/x-msi\nmsl application/vnd.Mobius.MSL\nmsm model/vnd.gdl\nmsp application/octet-stream\nmsty application/vnd.muvee.style\nmsu application/octet-stream\nmtl model/mtl\nmts video/mp2t\nmultitrack audio/vnd.presonus.multitrack\nmus application/vnd.musician\nmusd application/mmt-usd+xml\nmvb chemical/x-mopac-vib\nmvt application/vnd.mapbox-vector-tile\nmwc application/vnd.dpgraph\nmwf application/vnd.MFER\nmxf application/mxf\nmxi application/vnd.vd-study\nmxl application/vnd.recordare.musicxml\nmxmf audio/mobile-xmf\nmxml application/xv+xml\nmxs application/vnd.triscape.mxs\nmxu video/vnd.mpegurl\nn3 text/n3\nnb application/vnd.wolfram.mathematica\nnbp application/vnd.wolfram.player\nnc application/x-netcdf\nndc application/vnd.osa.netdeploy\nndl application/vnd.lotus-notes\nnds application/vnd.nintendo.nitro.rom\nnebul application/vnd.nebumind.line\nnef image/x-nikon-nef\nngdat application/vnd.nokia.n-gage.data\nnim video/vnd.nokia.interleaved-multimedia\nnimn application/vnd.nimn\nnitf application/vnd.nitf\nnlu application/vnd.neurolanguage.nlu\nnml application/vnd.enliven\nnnd application/vnd.noblenet-directory\nnns application/vnd.noblenet-sealer\nnnw application/vnd.noblenet-web\nnotebook application/vnd.smart.notebook\nnq application/n-quads\nnrw image/x-nikon-nrw\nns2 application/vnd.lotus-notes\nns3 application/vnd.lotus-notes\nns4 application/vnd.lotus-notes\nnsf application/vnd.lotus-notes\nnsg application/vnd.lotus-notes\nnsh application/vnd.lotus-notes\nnt application/n-triples\nntf application/vnd.lotus-notes\nnumbers application/vnd.apple.numbers\nnwc application/x-nwc\no application/x-object\noa2 application/vnd.fujitsu.oasys2\noa3 application/vnd.fujitsu.oasys3\noas application/vnd.fujitsu.oasys\nob application/vnd.1ob\nobg application/vnd.openblox.game-binary\nobgx application/vnd.openblox.game+xml\nobj model/obj\noda application/ODA\nodb application/vnd.oasis.opendocument.base\nodc application/vnd.oasis.opendocument.chart\nodd application/tei+xml\nodf application/vnd.oasis.opendocument.formula\nodg application/vnd.oasis.opendocument.graphics\nodi application/vnd.oasis.opendocument.image\nodm application/vnd.oasis.opendocument.text-master\nodp application/vnd.oasis.opendocument.presentation\nods application/vnd.oasis.opendocument.spreadsheet\nodt application/vnd.oasis.opendocument.text\nodx application/ODX\noeb application/vnd.openeye.oeb\noga audio/ogg\nogex model/vnd.opengex\nogg audio/ogg\nogv video/ogg\nogx application/ogg\nold application/x-trash\nomg audio/ATRAC3\none application/onenote\nonepkg application/onenote\nonetmp application/onenote\nonetoc2 application/onenote\nopf application/oebps-package+xml\noprc application/vnd.palm\nopus audio/ogg\nor2 application/vnd.lotus-organizer\nor3 application/vnd.lotus-organizer\norc audio/csound\norf image/x-olympus-orf\norg application/vnd.lotus-organizer\norq application/ocsp-request\nors application/ocsp-response\nosf application/vnd.yamaha.openscoreformat\nosm application/vnd.openstreetmap.data+xml\nota application/vnd.android.ota\notc application/vnd.oasis.opendocument.chart-template\notf font/otf\notg application/vnd.oasis.opendocument.graphics-template\noth application/vnd.oasis.opendocument.text-web\noti application/vnd.oasis.opendocument.image-template\notm application/vnd.oasis.opendocument.text-master-template\notp application/vnd.oasis.opendocument.presentation-template\nots application/vnd.oasis.opendocument.spreadsheet-template\nott application/vnd.oasis.opendocument.text-template\novl application/vnd.afpc.modca-overlay\noxlicg application/vnd.oxli.countgraph\noxps application/oxps\noxt application/vnd.openofficeorg.extension\noza application/x-oz-application\np text/x-pascal\np10 application/pkcs10\np12 application/x-pkcs12\np21 application/p21\np2p application/vnd.wfa.p2p\np7c application/pkcs7-mime\np7m application/pkcs7-mime\np7r application/x-pkcs7-certreqresp\np7s application/pkcs7-signature\np7z application/pkcs7-mime\np8 application/pkcs8\np8e application/pkcs8-encrypted\npac application/x-ns-proxy-autoconfig\npackage application/vnd.autopackage\npages application/vnd.apple.pages\npas text/x-pascal\npat image/x-coreldrawpattern\npatch text/x-diff\npaw application/vnd.pawaafile\npbd application/vnd.powerbuilder6\npbm image/x-portable-bitmap\npcap application/vnd.tcpdump.pcap\npcf application/x-font\npcf.Z application/x-font-pcf\npcl application/vnd.hp-PCL\npcx image/vnd.zbrush.pcx\npdb chemical/x-pdb\npdf application/pdf\npdx application/PDX\npef image/x-pentax-pef\npem application/x-pem-file\npfa application/x-font\npfb application/x-font\npfr application/font-tdpfr\npfx application/x-pkcs12\npgb image/vnd.globalgraphics.pgb\npgm image/x-portable-graymap\npgn application/vnd.chess-pgn\npgp application/pgp-signature\nphps text/text\npil application/vnd.piaccess.application-licence\npk application/x-tex-pk\npkd application/vnd.hbci\npkg application/vnd.apple.installer+xml\npki application/pkixcmp\npkipath application/pkix-pkipath\npkpass application/vnd.apple.pkpass\npkpasses application/vnd.apple.pkpasses\npl text/x-perl\nplb application/vnd.3gpp.pic-bw-large\nplc application/vnd.Mobius.PLC\nplf application/vnd.pocketlearn\nplj audio/vnd.everad.plj\nplp application/vnd.panoply\npls audio/x-scpls\npm text/x-perl\npml application/vnd.ctc-posml\npng image/png\npnm image/x-portable-anymap\npo text/plain\nportpkg application/vnd.macports.portpkg\npot application/vnd.ms-powerpoint\npotm application/vnd.ms-powerpoint.template.macroEnabled.12\npotx application/vnd.openxmlformats-officedocument.presentationml.template\nppam application/vnd.ms-powerpoint.addin.macroEnabled.12\nppd application/vnd.cups-ppd\nppkg application/vnd.xmpie.ppkg\nppm image/x-portable-pixmap\npps application/vnd.ms-powerpoint\nppsm application/vnd.ms-powerpoint.slideshow.macroEnabled.12\nppsx application/vnd.openxmlformats-officedocument.presentationml.slideshow\nppt application/vnd.ms-powerpoint\npptm application/vnd.ms-powerpoint.presentation.macroEnabled.12\nppttc application/vnd.think-cell.ppttc+json\npptx application/vnd.openxmlformats-officedocument.presentationml.presentation\npqa application/vnd.palm\nprc application/x-mobipocket-ebook\npre application/vnd.lotus-freelance\npreminet application/vnd.preminet\nprf application/pics-rules\nprovn text/provenance-notation\nprovx application/provenance+xml\nprt chemical/x-ncbi-asn1-ascii\nprz application/vnd.lotus-freelance\nps application/postscript\npsb application/vnd.3gpp.pic-bw-small\npsd image/x-photoshop\npseg3820 application/vnd.afpc.modca\npsfs application/vnd.psfs\npsg application/vnd.afpc.modca-pagesegment\npsid audio/prs.sid\npskcxml application/pskc+xml\npt application/vnd.snesdev-page-table\npti image/prs.pti\nptid application/vnd.pvi.ptid1\nptrom application/vnd.snesdev-page-table\npub application/vnd.exstream-package\npvb application/vnd.3gpp.pic-bw-var\npwn application/vnd.3M.Post-it-Notes\npy text/x-python\npya audio/vnd.ms-playready.media.pya\npyc application/x-python-code\npyo application/x-python-code\npyox model/vnd.pytha.pyox\npyv video/vnd.ms-playready.media.pyv\nqam application/vnd.epson.quickanime\nqbo application/vnd.intu.qbo\nqca application/vnd.ericsson.quickcall\nqcall application/vnd.ericsson.quickcall\nqcp audio/EVRC-QCP\nqfx application/vnd.intu.qfx\nqgs application/x-qgis\nqps application/vnd.publishare-delta-tree\nqt video/quicktime\nqtl application/x-quicktimeplayer\nquiz application/vnd.quobject-quoxdocument\nquox application/vnd.quobject-quoxdocument\nqvd application/vnd.theqvd\nqwd application/vnd.Quark.QuarkXPress\nqwt application/vnd.Quark.QuarkXPress\nqxb application/vnd.Quark.QuarkXPress\nqxd application/vnd.Quark.QuarkXPress\nqxl application/vnd.Quark.QuarkXPress\nqxt application/vnd.Quark.QuarkXPress\nra audio/x-pn-realaudio\nraf image/x-fuji-raf\nram audio/x-pn-realaudio\nrapd application/route-apd+xml\nrar application/vnd.rar\nras image/x-cmu-raster\nrb application/x-ruby\nrcprofile application/vnd.ipunplugged.rcprofile\nrct application/prs.nprend\nrd chemical/x-mdl-rdfile\nrdf application/rdf+xml\nrdf-crypt application/prs.rdf-xml-crypt\nrdp application/x-rdp\nrdz application/vnd.data-vision.rdz\nrelo application/p2p-overlay+xml\nreload application/vnd.resilient.logic\nrep application/vnd.businessobjects\nrequest application/vnd.nervana\nrfcxml application/rfc+xml\nrgb image/x-rgb\nrgbe image/vnd.radiance\nrif application/reginfo+xml\nrip audio/vnd.rip\nrl application/resource-lists+xml\nrlc image/vnd.fujixerox.edmics-rlc\nrld application/resource-lists-diff+xml\nrlm application/vnd.resilient.logic\nrm audio/x-pn-realaudio\nrms application/vnd.jcp.javame.midlet-rms\nrnc application/relax-ng-compact-syntax\nrnd application/prs.nprend\nroa application/rpki-roa\nroff text/troff\nros chemical/x-rosdal\nrp9 application/vnd.cloanto.rp9\nrpm application/x-redhat-package-manager\nrpss application/vnd.nokia.radio-presets\nrpst application/vnd.nokia.radio-preset\nrq application/sparql-query\nrs application/rls-services+xml\nrsat application/atsc-rsat+xml\nrsheet application/urc-ressheet+xml\nrsm model/vnd.gdl\nrss application/rss+xml\nrst text/prs.fallenstein.rst\nrtf text/rtf\nrtttl audio/midi\nrtx audio/midi\nrusd application/route-usd+xml\nrw2 image/x-panasonic-rw2\nrxn chemical/x-mdl-rxnfile\nrxt application/vnd.medicalholodeck.recordxr\ns11 video/vnd.sealed.mpeg1\ns14 video/vnd.sealed.mpeg4\ns1a application/vnd.sealedmedia.softseal.pdf\ns1e application/vnd.sealed.xls\ns1g image/vnd.sealedmedia.softseal.gif\ns1h application/vnd.sealedmedia.softseal.html\ns1j image/vnd.sealedmedia.softseal.jpg\ns1m audio/vnd.sealedmedia.softseal.mpeg\ns1n image/vnd.sealed.png\ns1p application/vnd.sealed.ppt\ns1q video/vnd.sealedmedia.softseal.mov\ns1w application/vnd.sealed.doc\ns3df application/vnd.sealed.3df\nsac application/tamp-sequence-adjust-confirm\nsaf application/vnd.yamaha.smaf-audio\nsam application/vnd.lotus-wordpro\nsarif application/sarif+json\nsarif-external-properties application/sarif-external-properties+json\nsarif-external-properties.json application/sarif-external-properties+json\nsarif.json application/sarif+json\nsc application/vnd.ibm.secure-container\nscala text/x-scala\nscd application/vnd.scribus\nsce application/x-scilab\nsci application/x-scilab\nscim application/scim+json\nscl application/vnd.sycle+xml\nscld application/vnd.doremir.scorecloud-binary-document\nscm application/vnd.lotus-screencam\nsco audio/csound\nscq application/scvp-cv-request\nscr application/x-silverlight\nscs application/scvp-cv-response\nscsf application/vnd.sealed.csf\nsd chemical/x-mdl-sdfile\nsd2 audio/x-sd2\nsda application/vnd.stardivision.draw\nsdc application/vnd.stardivision.calc\nsdd application/vnd.stardivision.impress\nsdf chemical/x-mdl-sdfile\nsdkd application/vnd.solent.sdkm+xml\nsdkm application/vnd.solent.sdkm+xml\nsdo application/vnd.sealed.doc\nsdoc application/vnd.sealed.doc\nsdp application/vnd.stardivision.impress\nsds application/vnd.stardivision.chart\nsdw application/vnd.stardivision.writer\nsee application/vnd.seemail\nseed application/vnd.fdsn.seed\nsem application/vnd.sealed.eml\nsema application/vnd.sema\nsemd application/vnd.semd\nsemf application/vnd.semf\nseml application/vnd.sealed.eml\nsenml application/senml+json\nsenml-etchc application/senml-etch+cbor\nsenml-etchj application/senml-etch+json\nsenmlc application/senml+cbor\nsenmle application/senml-exi\nsenmlx application/senml+xml\nsensml application/sensml+json\nsensmlc application/sensml+cbor\nsensmle application/sensml-exi\nsensmlx application/sensml+xml\nser application/java-serialized-object\nsfc application/vnd.nintendo.snes.rom\nsfd application/vnd.font-fontforge-sfd\nsfd-hdstx application/vnd.hydrostatix.sof-data\nsfs application/vnd.spotfire.sfs\nsfv text/x-sfv\nsgf application/x-go-sgf\nsgi image/vnd.sealedmedia.softseal.gif\nsgif image/vnd.sealedmedia.softseal.gif\nsgl application/vnd.stardivision.writer-global\nsgm text/SGML\nsgml text/SGML\nsh text/x-sh\nshaclc text/shaclc\nshar application/x-shar\nshc text/shaclc\nshex text/shex\nshf application/shf+xml\nshp application/x-qgis\nshtml text/html\nshx application/x-qgis\nsi text/vnd.wap.si\nsic application/vnd.wap.sic\nsid audio/prs.sid\nsieve application/sieve\nsig application/pgp-signature\nsik application/x-trash\nsilo model/mesh\nsipa application/vnd.smintio.portals.archive\nsis application/vnd.symbian.install\nsit application/x-stuffit\nsitx application/x-stuffit\nsiv application/sieve\nsjp image/vnd.sealedmedia.softseal.jpg\nsjpg image/vnd.sealedmedia.softseal.jpg\nskd application/vnd.koan\nskm application/vnd.koan\nskp application/vnd.koan\nskt application/vnd.koan\nsl text/vnd.wap.sl\nsla application/vnd.scribus\nslaz application/vnd.scribus\nslc application/vnd.wap.slc\nsldm application/vnd.ms-powerpoint.slide.macroEnabled.12\nsldx application/vnd.openxmlformats-officedocument.presentationml.slide\nsls application/route-s-tsid+xml\nslt application/vnd.epson.salt\nsm application/vnd.stepmania.stepchart\nsmc application/vnd.nintendo.snes.rom\nsmf audio/sp-midi\nsmh application/vnd.sealed.mht\nsmht application/vnd.sealed.mht\nsmi application/smil+xml\nsmil application/smil+xml\nsmk video/vnd.radgamettools.smacker\nsml application/smil+xml\nsmo video/vnd.sealedmedia.softseal.mov\nsmov video/vnd.sealedmedia.softseal.mov\nsmp audio/vnd.sealedmedia.softseal.mpeg\nsmp3 audio/vnd.sealedmedia.softseal.mpeg\nsmpg video/vnd.sealed.mpeg1\nsms application/vnd.3gpp2.sms\nsmv audio/SMV\nsmzip application/vnd.stepmania.package\nsnd audio/basic\nsoa text/dns\nsoc application/sgml-open-catalog\nsofa audio/sofa\nsos text/vnd.sosi\nspc chemical/x-galactic-spc\nspd application/vnd.sealedmedia.softseal.pdf\nspdf application/vnd.sealedmedia.softseal.pdf\nspdx text/spdx\nspdx.json application/spdx+json\nspf application/vnd.yamaha.smaf-phrase\nspl application/futuresplash\nspn image/vnd.sealed.png\nspng image/vnd.sealed.png\nspo text/vnd.in3d.spot\nspot text/vnd.in3d.spot\nspp application/scvp-vp-response\nsppt application/vnd.sealed.ppt\nspq application/scvp-vp-request\nspx audio/ogg\nsql application/sql\nsqlite application/vnd.sqlite3\nsqlite3 application/vnd.sqlite3\nsr application/vnd.sigrok.session\nsrc application/x-wais-source\nsrt application/x-subrip\nsru application/sru+xml\nsrw image/x-samsung-srw\nsrx application/sparql-results+xml\nsse application/vnd.kodak-descriptor\nssf application/vnd.epson.ssf\nssml application/ssml+xml\nssv application/vnd.shade-save-file\nssvc application/vnd.crypto-shade-file\nssw video/vnd.sealed.swf\nsswf video/vnd.sealed.swf\nst application/vnd.sailingtracker.track\nstc application/vnd.sun.xml.calc.template\nstd application/vnd.sun.xml.draw.template\nstep model/step\nstf application/vnd.wt.stf\nsti application/vnd.sun.xml.impress.template\nstif application/vnd.sealed.tiff\nstix application/stix+json\nstk application/hyperstudio\nstl application/vnd.ms-pki.stl\nstml application/vnd.sealedmedia.softseal.html\nstp model/step\nstpnc application/p21\nstpx model/step+xml\nstpxz model/step-xml+zip\nstpz model/step+zip\nstr application/vnd.pg.format\nstudy-inter application/vnd.vd-study\nstw application/vnd.sun.xml.writer.template\nsty text/x-tex\nsus application/vnd.sus-calendar\nsusp application/vnd.sus-calendar\nsv4cpio application/x-sv4cpio\nsv4crc application/x-sv4crc\nsvc application/vnd.dvb.service\nsvg image/svg+xml\nsvgz image/svg+xml\nsw chemical/x-swissprot\nswf application/vnd.adobe.flash.movie\nswi application/vnd.aristanetworks.swi\nswidtag application/swid+xml\nsxc application/vnd.sun.xml.calc\nsxd application/vnd.sun.xml.draw\nsxg application/vnd.sun.xml.writer.global\nsxi application/vnd.sun.xml.impress\nsxl application/vnd.sealed.xls\nsxls application/vnd.sealed.xls\nsxm application/vnd.sun.xml.math\nsxw application/vnd.sun.xml.writer\nsy2 application/vnd.sybyl.mol2\nsyft.json application/vnd.syft+json\nt text/troff\ntag text/prs.lines.tag\ntaglet application/vnd.mynfc\ntam application/vnd.onepager\ntamp application/vnd.onepagertamp\ntamx application/vnd.onepagertamx\ntao application/vnd.tao.intent-module-archive\ntap image/vnd.tencent.tap\ntar application/x-tar\ntat application/vnd.onepagertat\ntatp application/vnd.onepagertatp\ntatx application/vnd.onepagertatx\ntau application/tamp-apex-update\ntaz application/x-gtar-compressed\ntcap application/vnd.3gpp2.tcap\ntcl text/x-tcl\ntcu application/tamp-community-update\ntd application/urc-targetdesc+xml\nteacher application/vnd.smart.teacher\ntei application/tei+xml\nteiCorpus application/tei+xml\nter application/tamp-error\ntex text/x-tex\ntexi application/x-texinfo\ntexinfo application/x-texinfo\ntext text/plain\ntfi application/thraud+xml\ntfx image/tiff-fx\ntgf chemical/x-mdl-tgf\ntgz application/x-gtar-compressed\nthmx application/vnd.ms-officetheme\ntif image/tiff\ntiff image/tiff\ntk text/x-tcl\ntlclient application/vnd.cendio.thinlinc.clientconf\ntm text/texmacs\ntm.json application/tm+json\ntm.jsonld application/tm+json\ntmo application/vnd.tmobile-livetv\ntnef application/vnd.ms-tnef\ntnf application/vnd.ms-tnef\ntorrent application/x-bittorrent\ntpl application/vnd.groove-tool-template\ntpt application/vnd.trid.tpt\ntr text/troff\ntra application/vnd.trueapp\ntree application/vnd.rainstor.data\ntrig application/trig\nts video/mp2ts\ntsa application/tamp-sequence-adjust\ntsd application/timestamped-data\ntsp application/dsptype\ntsq application/timestamp-query\ntsr application/timestamp-reply\ntst application/vnd.etsi.timestamp-token\ntsv text/tab-separated-values\nttc font/collection\nttf font/ttf\nttl text/turtle\nttml application/ttml+xml\ntuc application/tamp-update-confirm\ntur application/tamp-update\ntwd application/vnd.SimTech-MindMapper\ntwds application/vnd.SimTech-MindMapper\ntxd application/vnd.genomatix.tuxedo\ntxf application/vnd.Mobius.TXF\ntxt text/plain\nu3d model/u3d\nu8dsn message/global-delivery-status\nu8hdr message/global-headers\nu8mdn message/global-disposition-notification\nu8msg message/global\nudeb application/vnd.debian.binary-package\nufd application/vnd.ufdl\nufdl application/vnd.ufdl\nuis application/urc-uisocketdesc+xml\numj application/vnd.umajin\nunityweb application/vnd.unity\nuo application/vnd.uoml+xml\nuoml application/vnd.uoml+xml\nupa application/vnd.hbci\nuri text/uri-list\nurim application/vnd.uri-map\nurimap application/vnd.uri-map\nuris text/uri-list\nusda model/vnd.usda\nusdz model/vnd.usdz+zip\nustar application/x-ustar\nutz application/vnd.uiq.theme\nuva audio/vnd.dece.audio\nuvd application/vnd.dece.data\nuvf application/vnd.dece.data\nuvg image/vnd.dece.graphic\nuvh video/vnd.dece.hd\nuvi image/vnd.dece.graphic\nuvm video/vnd.dece.mobile\nuvp video/vnd.dece.pd\nuvs video/vnd.dece.sd\nuvt application/vnd.dece.ttml+xml\nuvu video/vnd.dece.mp4\nuvv video/vnd.dece.video\nuvva audio/vnd.dece.audio\nuvvd application/vnd.dece.data\nuvvf application/vnd.dece.data\nuvvg image/vnd.dece.graphic\nuvvh video/vnd.dece.hd\nuvvi image/vnd.dece.graphic\nuvvm video/vnd.dece.mobile\nuvvp video/vnd.dece.pd\nuvvs video/vnd.dece.sd\nuvvt application/vnd.dece.ttml+xml\nuvvu video/vnd.dece.mp4\nuvvv video/vnd.dece.video\nuvvx application/vnd.dece.unspecified\nuvvz application/vnd.dece.zip\nuvx application/vnd.dece.unspecified\nuvz application/vnd.dece.zip\nval chemical/x-ncbi-asn1-binary\nvbk audio/vnd.nortel.vbk\nvbox application/vnd.previewsystems.box\nvcard text/vcard\nvcd application/x-cdlink\nvcf text/x-vcard\nvcg application/vnd.groove-vcard\nvcj application/voucher-cms+json\nvcs text/x-vcalendar\nvcx application/vnd.vcx\nvds model/vnd.sap.vds\nvew application/vnd.lotus-approach\nvfr application/vnd.tml\nviaframe application/vnd.tml\nvis application/vnd.visionary\nviv video/vnd.vivo\nvmd chemical/x-vmd\nvms chemical/x-vamas-iso14976\nvmt application/vnd.valve.source.material\nvor application/vnd.stardivision.writer\nvpm multipart/voice-message\nvrm model/vrml\nvrml model/vrml\nvsc application/vnd.vidsoft.vidconference\nvsd application/vnd.visio\nvsf application/vnd.vsf\nvss application/vnd.visio\nvst application/vnd.visio\nvsw application/vnd.visio\nvtf image/vnd.valve.source.texture\nvtnstd application/vnd.veritone.aion+json\nvtt text/vtt\nvtu model/vnd.vtu\nvwx application/vnd.vectorworks\nvxml application/voicexml+xml\nwad application/x-doom\nwadl application/vnd.sun.wadl+xml\nwafl application/vnd.wasmflow.wafl\nwasm application/wasm\nwav audio/x-wav\nwax audio/x-ms-wax\nwbmp image/vnd.wap.wbmp\nwbs application/vnd.criticaltools.wbs+xml\nwbxml application/vnd.wap.wbxml\nwcm application/vnd.ms-works\nwdb application/vnd.ms-works\nwebarchive application/x-webarchive\nwebarchivexml application/x-webarchive-xml\nwebm video/webm\nwebmanifest application/manifest+json\nwebp image/webp\nwg application/vnd.pmi.widget\nwgsl text/wgsl\nwgt application/widget\nwif application/watcherinfo+xml\nwin model/vnd.gdl\nwk application/x-123\nwk1 application/vnd.lotus-1-2-3\nwk3 application/vnd.lotus-1-2-3\nwk4 application/vnd.lotus-1-2-3\nwks application/vnd.ms-works\nwlnk application/link-format\nwm video/x-ms-wm\nwma audio/x-ms-wma\nwmc application/vnd.wmc\nwmd application/x-ms-wmd\nwmf image/wmf\nwml text/vnd.wap.wml\nwmlc application/vnd.wap.wmlc\nwmls text/vnd.wap.wmlscript\nwmlsc application/vnd.wap.wmlscriptc\nwmv video/x-ms-wmv\nwmx video/x-ms-wmx\nwmz application/x-ms-wmz\nwoff font/woff\nwoff2 font/woff2\nwpd application/vnd.wordperfect\nwpl application/vnd.ms-wpl\nwps application/vnd.ms-works\nwqd application/vnd.wqd\nwrf video/x-webex\nwrl model/vrml\nwsc application/vnd.wfa.wsc\nwsdl application/wsdl+xml\nwspolicy application/wspolicy+xml\nwtb application/vnd.webturbo\nwv application/vnd.wv.csp+wbxml\nwvx video/x-ms-wvx\nwz application/x-wingz\nx3d model/x3d+xml\nx3db model/x3d+fastinfoset\nx3dv model/x3d-vrml\nx3dvz model/x3d-vrml\nx3dz model/x3d+xml\nx_b model/vnd.parasolid.transmit.binary\nx_t model/vnd.parasolid.transmit.text\nxar application/vnd.xara\nxav application/xcap-att+xml\nxbd application/vnd.fujixerox.docuworks.binder\nxbm image/x-xbitmap\nxca application/xcap-caps+xml\nxcf image/x-xcf\nxcos application/x-scilab-xcos\nxcs application/calendar+xml\nxct application/vnd.fujixerox.docuworks.container\nxdd application/bacnet-xdd+zip\nxdf application/xcap-diff+xml\nxdm application/vnd.syncml.dm+xml\nxdp application/vnd.adobe.xdp+xml\nxdssc application/dssc+xml\nxdw application/vnd.fujixerox.docuworks\nxel application/xcap-el+xml\nxer application/xcap-error+xml\nxfd application/vnd.xfdl\nxfdf application/xfdf\nxfdl application/vnd.xfdl\nxhe audio/usac\nxht application/xhtml+xml\nxhtm application/xhtml+xml\nxhtml application/xhtml+xml\nxhvml application/xv+xml\nxif image/vnd.xiff\nxla application/vnd.ms-excel\nxlam application/vnd.ms-excel.addin.macroEnabled.12\nxlc application/vnd.ms-excel\nxlf application/xliff+xml\nxlim application/vnd.xmpie.xlim\nxlm application/vnd.ms-excel\nxls application/vnd.ms-excel\nxlsb application/vnd.ms-excel.sheet.binary.macroEnabled.12\nxlsm application/vnd.ms-excel.sheet.macroEnabled.12\nxlsx application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\nxlt application/vnd.ms-excel\nxltm application/vnd.ms-excel.template.macroEnabled.12\nxltx application/vnd.openxmlformats-officedocument.spreadsheetml.template\nxlw application/vnd.ms-excel\nxmf audio/midi\nxml text/xml\nxmls application/dskpp+xml\nxmt_bin model/vnd.parasolid.transmit.binary\nxmt_txt model/vnd.parasolid.transmit.text\nxns application/xcap-ns+xml\nxo application/vnd.olpc-sugar\nxodp application/vnd.collabio.xodocuments.presentation\nxods application/vnd.collabio.xodocuments.spreadsheet\nxodt application/vnd.collabio.xodocuments.document\nxop application/xop+xml\nxotp application/vnd.collabio.xodocuments.presentation-template\nxots application/vnd.collabio.xodocuments.spreadsheet-template\nxott application/vnd.collabio.xodocuments.document-template\nxpak application/vnd.gentoo.xpak\nxpi application/x-xpinstall\nxpm image/x-xpixmap\nxpr application/vnd.is-xpr\nxps application/vnd.ms-xpsdocument\nxpw application/vnd.intercon.formnet\nxpx application/vnd.intercon.formnet\nxsf application/prs.xsf+xml\nxsl application/xslt+xml\nxslt application/xslt+xml\nxsm application/vnd.syncml+xml\nxspf application/xspf+xml\nxtel chemical/x-xtel\nxul application/vnd.mozilla.xul+xml\nxvm application/xv+xml\nxvml application/xv+xml\nxwd image/x-xwindowdump\nxyz chemical/x-xyz\nxyze image/vnd.radiance\nxz application/x-xz\nyaml application/yaml\nyang application/yang\nyin application/yin+xml\nyme application/vnd.yaoweme\nyml application/yaml\nyt video/vnd.youtube.yt\nzaz application/vnd.zzazz.deck+xml\nzfc application/vnd.filmit.zfc\nzfo application/vnd.software602.filler.form-xml-zip\nzip application/zip\nzir application/vnd.zul\nzirz application/vnd.zul\nzmm application/vnd.HandHeld-Entertainment+xml\nzmt chemical/x-mopac-input\nzone text/dns\nzst application/zstd\n~ application/x-trash\n"
  },
  {
    "path": "mime/generate-code.sh",
    "content": "#!/bin/bash\nset -e\n\nawk '\nBEGIN {\n    print \"private val extensionToMimeTypeMap = mapOf(\"\n}\n{\n    print \"    \\\"\" $1 \"\\\" to \\\"\" $2 \"\\\",\"\n}\nEND {\n    print \")\"\n}\n' 'android.extensions' \\\n    >MimeTypeMapCompat.kt\n"
  },
  {
    "path": "mime/generate-extensions.sh",
    "content": "#!/bin/bash\nset -e\n\nBRANCH=main\n\ndebian_mime_types=$(mktemp)\nandroid_mime_types=$(mktemp)\ntrap \"rm ${debian_mime_types} ${android_mime_types}\" EXIT\n\ncurl \"https://android.googlesource.com/platform/external/mime-support/+/${BRANCH}/mime.types?format=TEXT\" \\\n    | base64 -d \\\n    | sed -nr '/^#/! s/^(\\S+)\\s+(.+)$/\\1 \\2/p' \\\n    >\"${debian_mime_types}\"\n\ncurl \"https://android.googlesource.com/platform/frameworks/base/+/${BRANCH}/mime/java-res/android.mime.types?format=TEXT\" \\\n    | base64 -d \\\n    | sed -nr '/^#/! s/^(\\S+)\\s+(.+)$/\\1 \\2/p' \\\n    >\"${android_mime_types}\"\n\n# See also https://android.googlesource.com/platform/external/mime-support/+/master/mime.types\n# See also https://android.googlesource.com/platform/frameworks/base/+/master/mime/java-res/android.mime.types\n# See also https://android.googlesource.com/platform/frameworks/base/+/master/mime/java/android/content/type/DefaultMimeMapFactory.java\n# See also https://android.googlesource.com/platform/libcore/+/master/luni/src/main/java/libcore/content/type/MimeMap.java\nawk '\nBEGIN {\n    extensions_size = 0\n}\n{\n    mime_type = $1\n    if (mime_type ~ /^?/) {\n        mime_type = substr(mime_type, 2)\n    }\n    for (i = 2; i <= NF; ++i) {\n        extension = $i\n        if (extension ~ /^?/) {\n            extension = substr(extension, 2)\n            if (extension in mime_types) {\n                continue\n            }\n        }\n        if (!(extension in mime_types)) {\n            ++extensions_size\n            extensions[extensions_size] = extension\n        }\n        mime_types[extension] = mime_type\n    }\n}\nEND {\n    for (i = 1; i <= extensions_size; ++i) {\n        extension = extensions[i]\n        print extension \" \" mime_types[extension]\n    }\n}\n' \"${debian_mime_types}\" \"${android_mime_types}\" \\\n    | LC_ALL=C sort \\\n    >android.extensions\n"
  },
  {
    "path": "settings.gradle",
    "content": "include ':app'\n"
  },
  {
    "path": "signing.gradle",
    "content": "android {\n    signingConfigs {\n        release {\n            def properties\n            def propertiesFile = rootProject.file('signing.properties')\n            if (propertiesFile.exists()) {\n                properties = new Properties()\n                properties.load(propertiesFile.newDataInputStream())\n            }\n            def getString = { String propertyName, String environmentName, String prompt ->\n                return properties?.get(propertyName) ?: System.getenv(environmentName) ?:\n                        System.console()?.readLine('\\n' + prompt + ': ')\n            }\n\n            storeFile getString('storeFile', 'STORE_FILE', 'Store file')?.with(rootProject.&file)\n            storePassword getString('storePassword', 'STORE_PASSWORD', 'Store password')\n            keyAlias getString('keyAlias', 'KEY_ALIAS', 'Key alias')\n            keyPassword getString('keyPassword', 'KEY_PASSWORD', 'Key password')\n        }\n    }\n}\n"
  },
  {
    "path": "signing.properties.example",
    "content": "storeFile=\nstorePassword=\nkeyAlias=\nkeyPassword=\n"
  },
  {
    "path": "utils/generate-custom-themes.sh",
    "content": "#!/bin/bash\nset -e\n\nCUSTOM_COLORS=(\n    color_primary\n    material_red\n    material_pink\n    material_purple\n    material_deep_purple\n    material_indigo\n    material_blue\n    material_light_blue\n    material_cyan\n    material_teal\n    material_green\n    material_light_green\n    material_lime\n    material_yellow\n    material_amber\n    material_orange\n    material_deep_orange\n    material_brown\n    material_grey\n    material_blue_grey\n)\nCUSTOM_COLORS_LIGHT=(\n    color_primary_light\n    material_red_500\n    material_pink_500\n    material_purple_500\n    material_deep_purple_500\n    material_indigo_500\n    material_blue_500\n    material_light_blue_500\n    material_cyan_500\n    material_teal_500\n    material_green_500\n    material_light_green_500\n    material_lime_500\n    material_yellow_500\n    material_amber_500\n    material_orange_500\n    material_deep_orange_500\n    material_brown_500\n    material_grey_500\n    material_blue_grey_500\n)\nCUSTOM_COLORS_DARK=(\n    color_primary_dark\n    material_red_300\n    material_pink_300\n    material_purple_300\n    material_deep_purple_300\n    material_indigo_300\n    material_blue_300\n    material_light_blue_300\n    material_cyan_300\n    material_teal_300\n    material_green_300\n    material_light_green_300\n    material_lime_300\n    material_yellow_300\n    material_amber_300\n    material_orange_300\n    material_deep_orange_300\n    material_brown_300\n    material_grey_300\n    material_blue_grey_300\n)\nCUSTOM_COLORS_XML='../app/src/main/res/values/colors_custom.xml'\nCUSTOM_COLORS_NIGHT_XML='../app/src/main/res/values-night/colors_custom.xml'\nTHEME_SUFFIXES=(\n    ''\n    .Translucent\n    .Immersive\n)\nCUSTOM_THEMES_XML='../app/src/main/res/values/themes_custom.xml'\nCUSTOM_THEMES_NIGHT_XML='../app/src/main/res/values-night/themes_custom.xml'\nCUSTOM_THEME_COLOR_KT='../app/src/main/java/me/zhanghai/android/files/theme/custom/ThemeColor.kt'\n\ncat >\"${CUSTOM_COLORS_XML}\" <<EOF\n<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<resources>\nEOF\nfor color_index in \"${!CUSTOM_COLORS[@]}\"; do\n    color=\"${CUSTOM_COLORS[color_index]}\"\n    if [[ \"${color}\" == 'color_primary' ]]; then\n        continue\n    fi\n    light_color=\"${CUSTOM_COLORS_LIGHT[color_index]}\"\n    cat >>\"${CUSTOM_COLORS_XML}\" <<EOF\n    <color name=\"${color}\">@color/${light_color}</color>\nEOF\ndone\ncat >>\"${CUSTOM_COLORS_XML}\" <<EOF\n</resources>\nEOF\n\ncat >\"${CUSTOM_COLORS_NIGHT_XML}\" <<EOF\n<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<resources>\nEOF\nfor color_index in \"${!CUSTOM_COLORS[@]}\"; do\n    color=\"${CUSTOM_COLORS[color_index]}\"\n    if [[ \"${color}\" == 'color_primary' ]]; then\n        continue\n    fi\n    dark_color=\"${CUSTOM_COLORS_DARK[color_index]}\"\n    cat >>\"${CUSTOM_COLORS_NIGHT_XML}\" <<EOF\n    <color name=\"${color}\">@color/${dark_color}</color>\nEOF\ndone\ncat >>\"${CUSTOM_COLORS_NIGHT_XML}\" <<EOF\n</resources>\nEOF\n\ncat >\"${CUSTOM_THEMES_XML}\" <<EOF\n<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<resources>\n\nEOF\nfor color_index in \"${!CUSTOM_COLORS[@]}\"; do\n    for theme_suffix in \"${THEME_SUFFIXES[@]}\"; do\n        color=\"${CUSTOM_COLORS[color_index]}\"\n        if [[ \"${theme_suffix}\" == '.Immersive' ]]; then\n            primary_color=\"${CUSTOM_COLORS_DARK[color_index]}\"\n        else\n            primary_color=\"${color}\"\n        fi\n        cat >>\"${CUSTOM_THEMES_XML}\" <<EOF\n    <style name=\"Theme.MaterialFiles${theme_suffix}.${color}\">\n        <item name=\"colorPrimary\">@color/${primary_color}</item>\n    </style>\nEOF\n    done\ndone\ncat >>\"${CUSTOM_THEMES_XML}\" <<EOF\n\nEOF\nfor color_index in \"${!CUSTOM_COLORS[@]}\"; do\n    for theme_suffix in \"${THEME_SUFFIXES[@]}\"; do\n        color=\"${CUSTOM_COLORS[color_index]}\"\n        cat >>\"${CUSTOM_THEMES_XML}\" <<EOF\n    <style name=\"Theme.MaterialFiles${theme_suffix}.${color}.Black\" />\nEOF\n    done\ndone\ncat >>\"${CUSTOM_THEMES_XML}\" <<EOF\n</resources>\nEOF\n\ncat >\"${CUSTOM_THEMES_NIGHT_XML}\" <<EOF\n<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<resources>\n\nEOF\nfor color_index in \"${!CUSTOM_COLORS[@]}\"; do\n    for theme_suffix in \"${THEME_SUFFIXES[@]}\"; do\n        color=\"${CUSTOM_COLORS[color_index]}\"\n        cat >>\"${CUSTOM_THEMES_NIGHT_XML}\" <<EOF\n    <style name=\"Theme.MaterialFiles${theme_suffix}.${color}.Black\">\n        <item name=\"colorPrimaryDark\">@color/color_primary_dark_black</item>\n        <item name=\"colorSurface\">@android:color/black</item>\n    </style>\nEOF\n    done\ndone\ncat >>\"${CUSTOM_THEMES_NIGHT_XML}\" <<EOF\n</resources>\nEOF\n\ncat >\"${CUSTOM_THEME_COLOR_KT}\" <<EOF\npackage me.zhanghai.android.files.theme.custom\n\nimport androidx.annotation.ColorRes\nimport me.zhanghai.android.files.R\n\nenum class ThemeColor(@ColorRes val resourceId: Int) {\nEOF\nfor color_index in \"${!CUSTOM_COLORS[@]}\"; do\n    color=\"${CUSTOM_COLORS[color_index]}\"\n    if [[ \"${color_index}\" -lt \"$((\"${#CUSTOM_COLORS[@]}\" - 1))\" ]]; then\n        separator=',';\n    else\n        separator=';';\n    fi\n    cat >>\"${CUSTOM_THEME_COLOR_KT}\" <<EOF\n    ${color^^}(R.color.${color})${separator}\nEOF\ndone\ncat >>\"${CUSTOM_THEME_COLOR_KT}\" <<EOF\n}\nEOF\n"
  },
  {
    "path": "utils/import-translations.sh",
    "content": "#!/bin/bash\nset -e\n\nLOCALES=(\n    ar\n    bg\n    ca\n    cs\n    de\n    el\n    es\n    eu\n    fa\n    fi\n    fr\n    hu\n    in\n    is\n    it\n    iw\n    ja\n    ko\n    lt\n    nb\n    nl\n    pl\n    pt-rBR\n    pt-rPT\n    ro\n    ru\n    tr\n    uk\n    vi\n)\n\nfor zip_file in \"$@\"; do\n    resource=\"$(basename \"${zip_file}\" .zip | sed -E 's/zhanghai_materialfiles_(.*)xml/\\1/')\"\n    for locale in \"${LOCALES[@]}\"; do\n        values_directory=\"../app/src/main/res/values-${locale}\"\n        mkdir -p \"${values_directory}\"\n        transifex_locale=\"$(echo \"${locale}\" | sed -e 's/-r/_/' -e 's/iw/he/' -e 's/ji/yi/' -e 's/in/id/')\"\n        unzip -p \"${zip_file}\" \"${resource}xml_${transifex_locale}.xml\" | perl -p0e 's/\\n    (<\\/resources>)/\\1/' >\"${values_directory}/${resource}.xml\"\n    done\ndone\n"
  }
]