[
  {
    "path": ".gitignore",
    "content": ".gradle\n/local.properties\n/.idea/workspace.xml\n/.idea/libraries\n.DS_Store\n/build\n/*.iml\n*.iml"
  },
  {
    "path": "COPYING",
    "content": "  GNU AFFERO GENERAL PUBLIC LICENSE\n                       Version 3, 19 November 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU Affero General Public License is a free, copyleft license for\nsoftware and other kinds of works, specifically designed to ensure\ncooperation with the community in the case of network server software.\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,\nour General Public Licenses are 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.\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  Developers that use our General Public Licenses protect your rights\nwith two steps: (1) assert copyright on the software, and (2) offer\nyou this License which gives you legal permission to copy, distribute\nand/or modify the software.\n\n  A secondary benefit of defending all users' freedom is that\nimprovements made in alternate versions of the program, if they\nreceive widespread use, become available for other developers to\nincorporate.  Many developers of free software are heartened and\nencouraged by the resulting cooperation.  However, in the case of\nsoftware used on network servers, this result may fail to come about.\nThe GNU General Public License permits making a modified version and\nletting the public access it on a server without ever releasing its\nsource code to the public.\n\n  The GNU Affero General Public License is designed specifically to\nensure that, in such cases, the modified source code becomes available\nto the community.  It requires the operator of a network server to\nprovide the source code of the modified version running there to the\nusers of that server.  Therefore, public use of a modified version, on\na publicly accessible server, gives the public access to the source\ncode of the modified version.\n\n  An older license, called the Affero General Public License and\npublished by Affero, was designed to accomplish similar goals.  This is\na different license, not a version of the Affero GPL, but Affero has\nreleased a new version of the Affero GPL which permits relicensing under\nthis license.\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 Affero 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. Remote Network Interaction; Use with the GNU General Public License.\n\n  Notwithstanding any other provision of this License, if you modify the\nProgram, your modified version must prominently offer all users\ninteracting with it remotely through a computer network (if your version\nsupports such interaction) an opportunity to receive the Corresponding\nSource of your version by providing access to the Corresponding Source\nfrom a network server at no charge, through some standard or customary\nmeans of facilitating copying of software.  This Corresponding Source\nshall include the Corresponding Source for any work covered by version 3\nof the GNU General Public License that is incorporated pursuant to the\nfollowing paragraph.\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 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 work with which it is combined will remain governed by version\n3 of the GNU General Public License.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU Affero General Public License from time to time.  Such new versions\nwill be 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 Affero 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 Affero 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 Affero 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 Affero 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 Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If your software can interact with users remotely through a computer\nnetwork, you should also make sure that it provides a way for users to\nget its source.  For example, if your program is a web application, its\ninterface could display a \"Source\" link that leads users to an archive\nof the code.  There are many ways you could offer source, and different\nsolutions will be better for different programs; see section 13 for the\nspecific requirements.\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 AGPL, see\n<http://www.gnu.org/licenses/>.\n"
  },
  {
    "path": "LICENSE",
    "content": "GNU AFFERO GENERAL PUBLIC LICENSE\n                       Version 3, 19 November 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU Affero General Public License is a free, copyleft license for\nsoftware and other kinds of works, specifically designed to ensure\ncooperation with the community in the case of network server software.\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,\nour General Public Licenses are 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.\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  Developers that use our General Public Licenses protect your rights\nwith two steps: (1) assert copyright on the software, and (2) offer\nyou this License which gives you legal permission to copy, distribute\nand/or modify the software.\n\n  A secondary benefit of defending all users' freedom is that\nimprovements made in alternate versions of the program, if they\nreceive widespread use, become available for other developers to\nincorporate.  Many developers of free software are heartened and\nencouraged by the resulting cooperation.  However, in the case of\nsoftware used on network servers, this result may fail to come about.\nThe GNU General Public License permits making a modified version and\nletting the public access it on a server without ever releasing its\nsource code to the public.\n\n  The GNU Affero General Public License is designed specifically to\nensure that, in such cases, the modified source code becomes available\nto the community.  It requires the operator of a network server to\nprovide the source code of the modified version running there to the\nusers of that server.  Therefore, public use of a modified version, on\na publicly accessible server, gives the public access to the source\ncode of the modified version.\n\n  An older license, called the Affero General Public License and\npublished by Affero, was designed to accomplish similar goals.  This is\na different license, not a version of the Affero GPL, but Affero has\nreleased a new version of the Affero GPL which permits relicensing under\nthis license.\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 Affero 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. Remote Network Interaction; Use with the GNU General Public License.\n\n  Notwithstanding any other provision of this License, if you modify the\nProgram, your modified version must prominently offer all users\ninteracting with it remotely through a computer network (if your version\nsupports such interaction) an opportunity to receive the Corresponding\nSource of your version by providing access to the Corresponding Source\nfrom a network server at no charge, through some standard or customary\nmeans of facilitating copying of software.  This Corresponding Source\nshall include the Corresponding Source for any work covered by version 3\nof the GNU General Public License that is incorporated pursuant to the\nfollowing paragraph.\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 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 work with which it is combined will remain governed by version\n3 of the GNU General Public License.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU Affero General Public License from time to time.  Such new versions\nwill be 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 Affero 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 Affero 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 Affero 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 Affero General Public License as published\n    by 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 Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If your software can interact with users remotely through a computer\nnetwork, you should also make sure that it provides a way for users to\nget its source.  For example, if your program is a web application, its\ninterface could display a \"Source\" link that leads users to an archive\nof the code.  There are many ways you could offer source, and different\nsolutions will be better for different programs; see section 13 for the\nspecific requirements.\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 AGPL, see\n<http://www.gnu.org/licenses/>.\n\n"
  },
  {
    "path": "README.md",
    "content": "# MuPDF for Android \n\n## Introduction\nThis project is intended to offer an easy integration of MuPDF library (http://www.mupdf.com) on Android, avoiding the building process and adapted to the last version of Android Studio and Gradle as of February 2015 (commit 262a4717a9997c89cac275d24ce6d605ca06284f from http://git.ghostscript.com/mupdf.git)\n\nWe also added some features:\n\n* You can add custom Bitmaps to each page.\n* You can use the MuPDFActivity as a Fragment (MuPDFFragment), that allows you to include it in your own activity as any other layout.\n* You can add an interface listener to the page of the pdf, so you can listen when the user taps, double taps or long press any coordinate of the pdf.\n\nThis version is still on development.\n\n## Installation guide\n\n1. Make sure you have installed the newest NDK from https://developer.android.com/tools/sdk/ndk/index.html#Installing (version 9+ required)\n"
  },
  {
    "path": "build.gradle",
    "content": "repositories {\n    google()\n    mavenCentral()\n    jcenter()\n    maven { url \"https://repositorio.viavansi.com/repo/\" }\n}\n\nbuildscript {\n    repositories {\n        google()\n        mavenCentral()\n        jcenter()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:3.4.2'\n    }\n}\n\napply plugin: 'com.android.library'\napply plugin: 'maven-publish'\n\ndef muPdfversionCode = 22\ndef muPdfversionName = '1.2.24.1'\n\nandroid {\n    compileSdkVersion 28\n\n    lintOptions {\n        abortOnError false\n    }\n    defaultConfig {\n        minSdkVersion 19\n        targetSdkVersion 31\n        versionCode muPdfversionCode\n        versionName muPdfversionName\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\nconfigurations {\n    deployerJars\n}\n\ndependencies {\n    deployerJars 'org.apache.maven.wagon:wagon-http:2.2'\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n    implementation 'androidx.appcompat:appcompat:1.0.0'\n}\n\n// For Maven Repository submitting : Execute task: UploadArchives. Ex: > gradle uploadArchives\n// Define repoUsername, repoPassword and repoUrl on \"gradle.properties\" file in order to use this task.\n//uploadArchives {\n//    repositories.mavenDeployer {\n//        configuration = configurations.deployerJars\n//        repository(url: repoUrl) {\n//            authentication(userName: repoUsername, password: repoPassword)\n//        }\n//        pom.version = muPdfversionName\n//        pom.artifactId = \"mupdf-android\"\n//        pom.groupId = \"com.viafirma\"\n//    }\n//}\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Settings specified in this file will override any Gradle settings\n# configured through the IDE.\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.\n# Default value: -Xmx10248m -XX:MaxPermSize=256m\n# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8\n\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\nrepoUrl=http://repositorio.viavansi.com/artifactory/libs-releases-local\nrepoUsername=<username>\nrepoPassword=<password>\nandroid.useDeprecatedNdk=true"
  },
  {
    "path": "proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /Applications/android-sdk-macosx/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\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"
  },
  {
    "path": "src/androidTest/java/com/artifex/viafirma/mupdf/ApplicationTest.java",
    "content": "package com.artifex.viafirma.mupdf;\n\nimport android.app.Application;\nimport android.test.ApplicationTestCase;\n\n/**\n * <a href=\"http://d.android.com/tools/testing/testing_android.html\">Testing Fundamentals</a>\n */\npublic class ApplicationTest extends ApplicationTestCase<Application> {\n    public ApplicationTest() {\n        super(Application.class);\n    }\n}"
  },
  {
    "path": "src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.artifex.mupdfdemo\"\n    android:installLocation=\"auto\">\n\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <supports-screens\n        android:smallScreens=\"true\"\n        android:normalScreens=\"true\"\n        android:largeScreens=\"true\"\n        android:anyDensity=\"true\" />\n\n    <application\n        android:label=\"@string/mupdf_title\"\n        android:hardwareAccelerated=\"true\">\n\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/Annotation.java",
    "content": "package com.artifex.mupdfdemo;\n\nimport android.graphics.RectF;\n\npublic class Annotation extends RectF {\n\tenum Type {\n\t\tTEXT, LINK, FREETEXT, LINE, SQUARE, CIRCLE, POLYGON, POLYLINE, HIGHLIGHT,\n\t\tUNDERLINE, SQUIGGLY, STRIKEOUT, STAMP, CARET, INK, POPUP, FILEATTACHMENT,\n\t\tSOUND, MOVIE, WIDGET, SCREEN, PRINTERMARK, TRAPNET, WATERMARK, A3D, UNKNOWN\n\t}\n\n\tpublic final Type type;\n\n\tpublic Annotation(float x0, float y0, float x1, float y1, int _type) {\n\t\tsuper(x0, y0, x1, y1);\n\t\ttype = _type == -1 ? Type.UNKNOWN : Type.values()[_type];\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/ArrayDeque.java",
    "content": "/*\n * Written by Josh Bloch of Google Inc. and released to the public domain,\n * as explained at http://creativecommons.org/publicdomain/zero/1.0/.\n */\n\npackage com.artifex.mupdfdemo;\n\nimport java.util.AbstractCollection;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.ConcurrentModificationException;\nimport java.util.Iterator;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.NoSuchElementException;\nimport java.util.Queue;\nimport java.util.Stack;\n\n// BEGIN android-note\n// removed link to collections framework docs\n// END android-note\n\n/**\n * Resizable-array implementation of the {@link Deque} interface.  Array\n * deques have no capacity restrictions; they grow as necessary to support\n * usage.  They are not thread-safe; in the absence of external\n * synchronization, they do not support concurrent access by multiple threads.\n * Null elements are prohibited.  This class is likely to be faster than\n * {@link java.util.Stack} when used as a stack, and faster than {@link java.util.LinkedList}\n * when used as a queue.\n *\n * <p>Most <tt>ArrayDeque</tt> operations run in amortized constant time.\n * Exceptions include {@link #remove(Object) remove}, {@link\n * #removeFirstOccurrence removeFirstOccurrence}, {@link #removeLastOccurrence\n * removeLastOccurrence}, {@link #contains contains}, {@link #iterator\n * iterator.remove()}, and the bulk operations, all of which run in linear\n * time.\n *\n * <p>The iterators returned by this class's <tt>iterator</tt> method are\n * <i>fail-fast</i>: If the deque is modified at any time after the iterator\n * is created, in any way except through the iterator's own <tt>remove</tt>\n * method, the iterator will generally throw a {@link\n * java.util.ConcurrentModificationException}.  Thus, in the face of concurrent\n * modification, the iterator fails quickly and cleanly, rather than risking\n * arbitrary, non-deterministic behavior at an undetermined time in the\n * future.\n *\n * <p>Note that the fail-fast behavior of an iterator cannot be guaranteed\n * as it is, generally speaking, impossible to make any hard guarantees in the\n * presence of unsynchronized concurrent modification.  Fail-fast iterators\n * throw <tt>ConcurrentModificationException</tt> on a best-effort basis.\n * Therefore, it would be wrong to write a program that depended on this\n * exception for its correctness: <i>the fail-fast behavior of iterators\n * should be used only to detect bugs.</i>\n *\n * <p>This class and its iterator implement all of the\n * <em>optional</em> methods of the {@link java.util.Collection} and {@link\n * java.util.Iterator} interfaces.\n *\n * @author  Josh Bloch and Doug Lea\n * @since   1.6\n * @param <E> the type of elements held in this collection\n */\npublic class ArrayDeque<E> extends AbstractCollection<E>\n                           implements Deque<E>, Cloneable, java.io.Serializable\n{\n    /**\n     * The array in which the elements of the deque are stored.\n     * The capacity of the deque is the length of this array, which is\n     * always a power of two. The array is never allowed to become\n     * full, except transiently within an addX method where it is\n     * resized (see doubleCapacity) immediately upon becoming full,\n     * thus avoiding head and tail wrapping around to equal each\n     * other.  We also guarantee that all array cells not holding\n     * deque elements are always null.\n     */\n    private transient Object[] elements;\n\n    /**\n     * The index of the element at the head of the deque (which is the\n     * element that would be removed by remove() or pop()); or an\n     * arbitrary number equal to tail if the deque is empty.\n     */\n    private transient int head;\n\n    /**\n     * The index at which the next element would be added to the tail\n     * of the deque (via addLast(E), add(E), or push(E)).\n     */\n    private transient int tail;\n\n    /**\n     * The minimum capacity that we'll use for a newly created deque.\n     * Must be a power of 2.\n     */\n    private static final int MIN_INITIAL_CAPACITY = 8;\n\n    // ******  Array allocation and resizing utilities ******\n\n    /**\n     * Allocate empty array to hold the given number of elements.\n     *\n     * @param numElements  the number of elements to hold\n     */\n    private void allocateElements(int numElements) {\n        int initialCapacity = MIN_INITIAL_CAPACITY;\n        // Find the best power of two to hold elements.\n        // Tests \"<=\" because arrays aren't kept full.\n        if (numElements >= initialCapacity) {\n            initialCapacity = numElements;\n            initialCapacity |= (initialCapacity >>>  1);\n            initialCapacity |= (initialCapacity >>>  2);\n            initialCapacity |= (initialCapacity >>>  4);\n            initialCapacity |= (initialCapacity >>>  8);\n            initialCapacity |= (initialCapacity >>> 16);\n            initialCapacity++;\n\n            if (initialCapacity < 0)   // Too many elements, must back off\n                initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements\n        }\n        elements = new Object[initialCapacity];\n    }\n\n    /**\n     * Double the capacity of this deque.  Call only when full, i.e.,\n     * when head and tail have wrapped around to become equal.\n     */\n    private void doubleCapacity() {\n        // assert head == tail;\n        int p = head;\n        int n = elements.length;\n        int r = n - p; // number of elements to the right of p\n        int newCapacity = n << 1;\n        if (newCapacity < 0)\n            throw new IllegalStateException(\"Sorry, deque too big\");\n        Object[] a = new Object[newCapacity];\n        System.arraycopy(elements, p, a, 0, r);\n        System.arraycopy(elements, 0, a, r, p);\n        elements = a;\n        head = 0;\n        tail = n;\n    }\n\n    /**\n     * Copies the elements from our element array into the specified array,\n     * in order (from first to last element in the deque).  It is assumed\n     * that the array is large enough to hold all elements in the deque.\n     *\n     * @return its argument\n     */\n    private <T> T[] copyElements(T[] a) {\n        if (head < tail) {\n            System.arraycopy(elements, head, a, 0, size());\n        } else if (head > tail) {\n            int headPortionLen = elements.length - head;\n            System.arraycopy(elements, head, a, 0, headPortionLen);\n            System.arraycopy(elements, 0, a, headPortionLen, tail);\n        }\n        return a;\n    }\n\n    /**\n     * Constructs an empty array deque with an initial capacity\n     * sufficient to hold 16 elements.\n     */\n    public ArrayDeque() {\n        elements = new Object[16];\n    }\n\n    /**\n     * Constructs an empty array deque with an initial capacity\n     * sufficient to hold the specified number of elements.\n     *\n     * @param numElements  lower bound on initial capacity of the deque\n     */\n    public ArrayDeque(int numElements) {\n        allocateElements(numElements);\n    }\n\n    /**\n     * Constructs a deque containing the elements of the specified\n     * collection, in the order they are returned by the collection's\n     * iterator.  (The first element returned by the collection's\n     * iterator becomes the first element, or <i>front</i> of the\n     * deque.)\n     *\n     * @param c the collection whose elements are to be placed into the deque\n     * @throws NullPointerException if the specified collection is null\n     */\n    public ArrayDeque(Collection<? extends E> c) {\n        allocateElements(c.size());\n        addAll(c);\n    }\n\n    // The main insertion and extraction methods are addFirst,\n    // addLast, pollFirst, pollLast. The other methods are defined in\n    // terms of these.\n\n    /**\n     * Inserts the specified element at the front of this deque.\n     *\n     * @param e the element to add\n     * @throws NullPointerException if the specified element is null\n     */\n    public void addFirst(E e) {\n        if (e == null)\n            throw new NullPointerException(\"e == null\");\n        elements[head = (head - 1) & (elements.length - 1)] = e;\n        if (head == tail)\n            doubleCapacity();\n    }\n\n    /**\n     * Inserts the specified element at the end of this deque.\n     *\n     * <p>This method is equivalent to {@link #add}.\n     *\n     * @param e the element to add\n     * @throws NullPointerException if the specified element is null\n     */\n    public void addLast(E e) {\n        if (e == null)\n            throw new NullPointerException(\"e == null\");\n        elements[tail] = e;\n        if ( (tail = (tail + 1) & (elements.length - 1)) == head)\n            doubleCapacity();\n    }\n\n    /**\n     * Inserts the specified element at the front of this deque.\n     *\n     * @param e the element to add\n     * @return <tt>true</tt> (as specified by {@link Deque#offerFirst})\n     * @throws NullPointerException if the specified element is null\n     */\n    public boolean offerFirst(E e) {\n        addFirst(e);\n        return true;\n    }\n\n    /**\n     * Inserts the specified element at the end of this deque.\n     *\n     * @param e the element to add\n     * @return <tt>true</tt> (as specified by {@link Deque#offerLast})\n     * @throws NullPointerException if the specified element is null\n     */\n    public boolean offerLast(E e) {\n        addLast(e);\n        return true;\n    }\n\n    /**\n     * @throws java.util.NoSuchElementException {@inheritDoc}\n     */\n    public E removeFirst() {\n        E x = pollFirst();\n        if (x == null)\n            throw new NoSuchElementException();\n        return x;\n    }\n\n    /**\n     * @throws java.util.NoSuchElementException {@inheritDoc}\n     */\n    public E removeLast() {\n        E x = pollLast();\n        if (x == null)\n            throw new NoSuchElementException();\n        return x;\n    }\n\n    public E pollFirst() {\n        int h = head;\n        @SuppressWarnings(\"unchecked\") E result = (E) elements[h];\n        // Element is null if deque empty\n        if (result == null)\n            return null;\n        elements[h] = null;     // Must null out slot\n        head = (h + 1) & (elements.length - 1);\n        return result;\n    }\n\n    public E pollLast() {\n        int t = (tail - 1) & (elements.length - 1);\n        @SuppressWarnings(\"unchecked\") E result = (E) elements[t];\n        if (result == null)\n            return null;\n        elements[t] = null;\n        tail = t;\n        return result;\n    }\n\n    /**\n     * @throws java.util.NoSuchElementException {@inheritDoc}\n     */\n    public E getFirst() {\n        @SuppressWarnings(\"unchecked\") E result = (E) elements[head];\n        if (result == null)\n            throw new NoSuchElementException();\n        return result;\n    }\n\n    /**\n     * @throws java.util.NoSuchElementException {@inheritDoc}\n     */\n    public E getLast() {\n        @SuppressWarnings(\"unchecked\")\n        E result = (E) elements[(tail - 1) & (elements.length - 1)];\n        if (result == null)\n            throw new NoSuchElementException();\n        return result;\n    }\n\n    public E peekFirst() {\n        @SuppressWarnings(\"unchecked\") E result = (E) elements[head];\n        // elements[head] is null if deque empty\n        return result;\n    }\n\n    public E peekLast() {\n        @SuppressWarnings(\"unchecked\")\n        E result = (E) elements[(tail - 1) & (elements.length - 1)];\n        return result;\n    }\n\n    /**\n     * Removes the first occurrence of the specified element in this\n     * deque (when traversing the deque from head to tail).\n     * If the deque does not contain the element, it is unchanged.\n     * More formally, removes the first element <tt>e</tt> such that\n     * <tt>o.equals(e)</tt> (if such an element exists).\n     * Returns <tt>true</tt> if this deque contained the specified element\n     * (or equivalently, if this deque changed as a result of the call).\n     *\n     * @param o element to be removed from this deque, if present\n     * @return <tt>true</tt> if the deque contained the specified element\n     */\n    public boolean removeFirstOccurrence(Object o) {\n        if (o == null)\n            return false;\n        int mask = elements.length - 1;\n        int i = head;\n        Object x;\n        while ( (x = elements[i]) != null) {\n            if (o.equals(x)) {\n                delete(i);\n                return true;\n            }\n            i = (i + 1) & mask;\n        }\n        return false;\n    }\n\n    /**\n     * Removes the last occurrence of the specified element in this\n     * deque (when traversing the deque from head to tail).\n     * If the deque does not contain the element, it is unchanged.\n     * More formally, removes the last element <tt>e</tt> such that\n     * <tt>o.equals(e)</tt> (if such an element exists).\n     * Returns <tt>true</tt> if this deque contained the specified element\n     * (or equivalently, if this deque changed as a result of the call).\n     *\n     * @param o element to be removed from this deque, if present\n     * @return <tt>true</tt> if the deque contained the specified element\n     */\n    public boolean removeLastOccurrence(Object o) {\n        if (o == null)\n            return false;\n        int mask = elements.length - 1;\n        int i = (tail - 1) & mask;\n        Object x;\n        while ( (x = elements[i]) != null) {\n            if (o.equals(x)) {\n                delete(i);\n                return true;\n            }\n            i = (i - 1) & mask;\n        }\n        return false;\n    }\n\n    // *** Queue methods ***\n\n    /**\n     * Inserts the specified element at the end of this deque.\n     *\n     * <p>This method is equivalent to {@link #addLast}.\n     *\n     * @param e the element to add\n     * @return <tt>true</tt> (as specified by {@link java.util.Collection#add})\n     * @throws NullPointerException if the specified element is null\n     */\n    public boolean add(E e) {\n        addLast(e);\n        return true;\n    }\n\n    /**\n     * Inserts the specified element at the end of this deque.\n     *\n     * <p>This method is equivalent to {@link #offerLast}.\n     *\n     * @param e the element to add\n     * @return <tt>true</tt> (as specified by {@link java.util.Queue#offer})\n     * @throws NullPointerException if the specified element is null\n     */\n    public boolean offer(E e) {\n        return offerLast(e);\n    }\n\n    /**\n     * Retrieves and removes the head of the queue represented by this deque.\n     *\n     * This method differs from {@link #poll poll} only in that it throws an\n     * exception if this deque is empty.\n     *\n     * <p>This method is equivalent to {@link #removeFirst}.\n     *\n     * @return the head of the queue represented by this deque\n     * @throws java.util.NoSuchElementException {@inheritDoc}\n     */\n    public E remove() {\n        return removeFirst();\n    }\n\n    /**\n     * Retrieves and removes the head of the queue represented by this deque\n     * (in other words, the first element of this deque), or returns\n     * <tt>null</tt> if this deque is empty.\n     *\n     * <p>This method is equivalent to {@link #pollFirst}.\n     *\n     * @return the head of the queue represented by this deque, or\n     *         <tt>null</tt> if this deque is empty\n     */\n    public E poll() {\n        return pollFirst();\n    }\n\n    /**\n     * Retrieves, but does not remove, the head of the queue represented by\n     * this deque.  This method differs from {@link #peek peek} only in\n     * that it throws an exception if this deque is empty.\n     *\n     * <p>This method is equivalent to {@link #getFirst}.\n     *\n     * @return the head of the queue represented by this deque\n     * @throws java.util.NoSuchElementException {@inheritDoc}\n     */\n    public E element() {\n        return getFirst();\n    }\n\n    /**\n     * Retrieves, but does not remove, the head of the queue represented by\n     * this deque, or returns <tt>null</tt> if this deque is empty.\n     *\n     * <p>This method is equivalent to {@link #peekFirst}.\n     *\n     * @return the head of the queue represented by this deque, or\n     *         <tt>null</tt> if this deque is empty\n     */\n    public E peek() {\n        return peekFirst();\n    }\n\n    // *** Stack methods ***\n\n    /**\n     * Pushes an element onto the stack represented by this deque.  In other\n     * words, inserts the element at the front of this deque.\n     *\n     * <p>This method is equivalent to {@link #addFirst}.\n     *\n     * @param e the element to push\n     * @throws NullPointerException if the specified element is null\n     */\n    public void push(E e) {\n        addFirst(e);\n    }\n\n    /**\n     * Pops an element from the stack represented by this deque.  In other\n     * words, removes and returns the first element of this deque.\n     *\n     * <p>This method is equivalent to {@link #removeFirst()}.\n     *\n     * @return the element at the front of this deque (which is the top\n     *         of the stack represented by this deque)\n     * @throws java.util.NoSuchElementException {@inheritDoc}\n     */\n    public E pop() {\n        return removeFirst();\n    }\n\n    private void checkInvariants() {\n        // assert elements[tail] == null;\n        // assert head == tail ? elements[head] == null :\n        //     (elements[head] != null &&\n        //      elements[(tail - 1) & (elements.length - 1)] != null);\n        // assert elements[(head - 1) & (elements.length - 1)] == null;\n    }\n\n    /**\n     * Removes the element at the specified position in the elements array,\n     * adjusting head and tail as necessary.  This can result in motion of\n     * elements backwards or forwards in the array.\n     *\n     * <p>This method is called delete rather than remove to emphasize\n     * that its semantics differ from those of {@link java.util.List#remove(int)}.\n     *\n     * @return true if elements moved backwards\n     */\n    private boolean delete(int i) {\n        //checkInvariants();\n        final Object[] elements = this.elements;\n        final int mask = elements.length - 1;\n        final int h = head;\n        final int t = tail;\n        final int front = (i - h) & mask;\n        final int back  = (t - i) & mask;\n\n        // Invariant: head <= i < tail mod circularity\n        if (front >= ((t - h) & mask))\n            throw new ConcurrentModificationException();\n\n        // Optimize for least element motion\n        if (front < back) {\n            if (h <= i) {\n                System.arraycopy(elements, h, elements, h + 1, front);\n            } else { // Wrap around\n                System.arraycopy(elements, 0, elements, 1, i);\n                elements[0] = elements[mask];\n                System.arraycopy(elements, h, elements, h + 1, mask - h);\n            }\n            elements[h] = null;\n            head = (h + 1) & mask;\n            return false;\n        } else {\n            if (i < t) { // Copy the null tail as well\n                System.arraycopy(elements, i + 1, elements, i, back);\n                tail = t - 1;\n            } else { // Wrap around\n                System.arraycopy(elements, i + 1, elements, i, mask - i);\n                elements[mask] = elements[0];\n                System.arraycopy(elements, 1, elements, 0, t);\n                tail = (t - 1) & mask;\n            }\n            return true;\n        }\n    }\n\n    // *** Collection Methods ***\n\n    /**\n     * Returns the number of elements in this deque.\n     *\n     * @return the number of elements in this deque\n     */\n    public int size() {\n        return (tail - head) & (elements.length - 1);\n    }\n\n    /**\n     * Returns <tt>true</tt> if this deque contains no elements.\n     *\n     * @return <tt>true</tt> if this deque contains no elements\n     */\n    public boolean isEmpty() {\n        return head == tail;\n    }\n\n    /**\n     * Returns an iterator over the elements in this deque.  The elements\n     * will be ordered from first (head) to last (tail).  This is the same\n     * order that elements would be dequeued (via successive calls to\n     * {@link #remove} or popped (via successive calls to {@link #pop}).\n     *\n     * @return an iterator over the elements in this deque\n     */\n    public Iterator<E> iterator() {\n        return new DeqIterator();\n    }\n\n    public Iterator<E> descendingIterator() {\n        return new DescendingIterator();\n    }\n\n    private class DeqIterator implements Iterator<E> {\n        /**\n         * Index of element to be returned by subsequent call to next.\n         */\n        private int cursor = head;\n\n        /**\n         * Tail recorded at construction (also in remove), to stop\n         * iterator and also to check for comodification.\n         */\n        private int fence = tail;\n\n        /**\n         * Index of element returned by most recent call to next.\n         * Reset to -1 if element is deleted by a call to remove.\n         */\n        private int lastRet = -1;\n\n        public boolean hasNext() {\n            return cursor != fence;\n        }\n\n        public E next() {\n            if (cursor == fence)\n                throw new NoSuchElementException();\n            @SuppressWarnings(\"unchecked\") E result = (E) elements[cursor];\n            // This check doesn't catch all possible comodifications,\n            // but does catch the ones that corrupt traversal\n            if (tail != fence || result == null)\n                throw new ConcurrentModificationException();\n            lastRet = cursor;\n            cursor = (cursor + 1) & (elements.length - 1);\n            return result;\n        }\n\n        public void remove() {\n            if (lastRet < 0)\n                throw new IllegalStateException();\n            if (delete(lastRet)) { // if left-shifted, undo increment in next()\n                cursor = (cursor - 1) & (elements.length - 1);\n                fence = tail;\n            }\n            lastRet = -1;\n        }\n    }\n\n    private class DescendingIterator implements Iterator<E> {\n        /*\n         * This class is nearly a mirror-image of DeqIterator, using\n         * tail instead of head for initial cursor, and head instead of\n         * tail for fence.\n         */\n        private int cursor = tail;\n        private int fence = head;\n        private int lastRet = -1;\n\n        public boolean hasNext() {\n            return cursor != fence;\n        }\n\n        public E next() {\n            if (cursor == fence)\n                throw new NoSuchElementException();\n            cursor = (cursor - 1) & (elements.length - 1);\n            @SuppressWarnings(\"unchecked\") E result = (E) elements[cursor];\n            if (head != fence || result == null)\n                throw new ConcurrentModificationException();\n            lastRet = cursor;\n            return result;\n        }\n\n        public void remove() {\n            if (lastRet < 0)\n                throw new IllegalStateException();\n            if (!delete(lastRet)) {\n                cursor = (cursor + 1) & (elements.length - 1);\n                fence = head;\n            }\n            lastRet = -1;\n        }\n    }\n\n    /**\n     * Returns <tt>true</tt> if this deque contains the specified element.\n     * More formally, returns <tt>true</tt> if and only if this deque contains\n     * at least one element <tt>e</tt> such that <tt>o.equals(e)</tt>.\n     *\n     * @param o object to be checked for containment in this deque\n     * @return <tt>true</tt> if this deque contains the specified element\n     */\n    public boolean contains(Object o) {\n        if (o == null)\n            return false;\n        int mask = elements.length - 1;\n        int i = head;\n        Object x;\n        while ( (x = elements[i]) != null) {\n            if (o.equals(x))\n                return true;\n            i = (i + 1) & mask;\n        }\n        return false;\n    }\n\n    /**\n     * Removes a single instance of the specified element from this deque.\n     * If the deque does not contain the element, it is unchanged.\n     * More formally, removes the first element <tt>e</tt> such that\n     * <tt>o.equals(e)</tt> (if such an element exists).\n     * Returns <tt>true</tt> if this deque contained the specified element\n     * (or equivalently, if this deque changed as a result of the call).\n     *\n     * <p>This method is equivalent to {@link #removeFirstOccurrence}.\n     *\n     * @param o element to be removed from this deque, if present\n     * @return <tt>true</tt> if this deque contained the specified element\n     */\n    public boolean remove(Object o) {\n        return removeFirstOccurrence(o);\n    }\n\n    /**\n     * Removes all of the elements from this deque.\n     * The deque will be empty after this call returns.\n     */\n    public void clear() {\n        int h = head;\n        int t = tail;\n        if (h != t) { // clear all cells\n            head = tail = 0;\n            int i = h;\n            int mask = elements.length - 1;\n            do {\n                elements[i] = null;\n                i = (i + 1) & mask;\n            } while (i != t);\n        }\n    }\n\n    /**\n     * Returns an array containing all of the elements in this deque\n     * in proper sequence (from first to last element).\n     *\n     * <p>The returned array will be \"safe\" in that no references to it are\n     * maintained by this deque.  (In other words, this method must allocate\n     * a new array).  The caller is thus free to modify the returned array.\n     *\n     * <p>This method acts as bridge between array-based and collection-based\n     * APIs.\n     *\n     * @return an array containing all of the elements in this deque\n     */\n    public Object[] toArray() {\n        return copyElements(new Object[size()]);\n    }\n\n    /**\n     * Returns an array containing all of the elements in this deque in\n     * proper sequence (from first to last element); the runtime type of the\n     * returned array is that of the specified array.  If the deque fits in\n     * the specified array, it is returned therein.  Otherwise, a new array\n     * is allocated with the runtime type of the specified array and the\n     * size of this deque.\n     *\n     * <p>If this deque fits in the specified array with room to spare\n     * (i.e., the array has more elements than this deque), the element in\n     * the array immediately following the end of the deque is set to\n     * <tt>null</tt>.\n     *\n     * <p>Like the {@link #toArray()} method, this method acts as bridge between\n     * array-based and collection-based APIs.  Further, this method allows\n     * precise control over the runtime type of the output array, and may,\n     * under certain circumstances, be used to save allocation costs.\n     *\n     * <p>Suppose <tt>x</tt> is a deque known to contain only strings.\n     * The following code can be used to dump the deque into a newly\n     * allocated array of <tt>String</tt>:\n     *\n     *  <pre> {@code String[] y = x.toArray(new String[0]);}</pre>\n     *\n     * Note that <tt>toArray(new Object[0])</tt> is identical in function to\n     * <tt>toArray()</tt>.\n     *\n     * @param a the array into which the elements of the deque are to\n     *          be stored, if it is big enough; otherwise, a new array of the\n     *          same runtime type is allocated for this purpose\n     * @return an array containing all of the elements in this deque\n     * @throws ArrayStoreException if the runtime type of the specified array\n     *         is not a supertype of the runtime type of every element in\n     *         this deque\n     * @throws NullPointerException if the specified array is null\n     */\n    @SuppressWarnings(\"unchecked\")\n    public <T> T[] toArray(T[] a) {\n        int size = size();\n        if (a.length < size)\n            a = (T[])java.lang.reflect.Array.newInstance(\n                    a.getClass().getComponentType(), size);\n        copyElements(a);\n        if (a.length > size)\n            a[size] = null;\n        return a;\n    }\n\n    // *** Object methods ***\n\n    /**\n     * Returns a copy of this deque.\n     *\n     * @return a copy of this deque\n     */\n    public ArrayDeque<E> clone() {\n        try {\n            @SuppressWarnings(\"unchecked\")\n            ArrayDeque<E> result = (ArrayDeque<E>) super.clone();\n            result.elements = Arrays.copyOf(elements, elements.length);\n            return result;\n\n        } catch (CloneNotSupportedException e) {\n            throw new AssertionError();\n        }\n    }\n\n    /**\n     * Appease the serialization gods.\n     */\n    private static final long serialVersionUID = 2340985798034038923L;\n\n    /**\n     * Serialize this deque.\n     *\n     * @serialData The current size (<tt>int</tt>) of the deque,\n     * followed by all of its elements (each an object reference) in\n     * first-to-last order.\n     */\n    private void writeObject(java.io.ObjectOutputStream s)\n            throws java.io.IOException {\n        s.defaultWriteObject();\n\n        // Write out size\n        s.writeInt(size());\n\n        // Write out elements in order.\n        int mask = elements.length - 1;\n        for (int i = head; i != tail; i = (i + 1) & mask)\n            s.writeObject(elements[i]);\n    }\n\n    /**\n     * Deserialize this deque.\n     */\n    private void readObject(java.io.ObjectInputStream s)\n            throws java.io.IOException, ClassNotFoundException {\n        s.defaultReadObject();\n\n        // Read in size and allocate array\n        int size = s.readInt();\n        allocateElements(size);\n        head = 0;\n        tail = size;\n\n        // Read in all elements in the proper order.\n        for (int i = 0; i < size; i++)\n            elements[i] = s.readObject();\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/AsyncTask.java",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.artifex.mupdfdemo;\n\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.CancellationException;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Executor;\nimport java.util.concurrent.FutureTask;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport android.os.Process;\nimport android.os.Handler;\nimport android.os.Message;\n\n/**\n * <p>AsyncTask enables proper and easy use of the UI thread. This class allows to\n * perform background operations and publish results on the UI thread without\n * having to manipulate threads and/or handlers.</p>\n *\n * <p>AsyncTask is designed to be a helper class around {@link Thread} and {@link android.os.Handler}\n * and does not constitute a generic threading framework. AsyncTasks should ideally be\n * used for short operations (a few seconds at the most.) If you need to keep threads\n * running for long periods of time, it is highly recommended you use the various APIs\n * provided by the <code>java.util.concurrent</code> pacakge such as {@link java.util.concurrent.Executor},\n * {@link java.util.concurrent.ThreadPoolExecutor} and {@link java.util.concurrent.FutureTask}.</p>\n *\n * <p>An asynchronous task is defined by a computation that runs on a background thread and\n * whose result is published on the UI thread. An asynchronous task is defined by 3 generic\n * types, called <code>Params</code>, <code>Progress</code> and <code>Result</code>,\n * and 4 steps, called <code>onPreExecute</code>, <code>doInBackground</code>,\n * <code>onProgressUpdate</code> and <code>onPostExecute</code>.</p>\n *\n * <div class=\"special reference\">\n * <h3>Developer Guides</h3>\n * <p>For more information about using tasks and threads, read the\n * <a href=\"{@docRoot}guide/topics/fundamentals/processes-and-threads.html\">Processes and\n * Threads</a> developer guide.</p>\n * </div>\n *\n * <h2>Usage</h2>\n * <p>AsyncTask must be subclassed to be used. The subclass will override at least\n * one method ({@link #doInBackground}), and most often will override a\n * second one ({@link #onPostExecute}.)</p>\n *\n * <p>Here is an example of subclassing:</p>\n * <pre class=\"prettyprint\">\n * private class DownloadFilesTask extends AsyncTask&lt;URL, Integer, Long&gt; {\n *     protected Long doInBackground(URL... urls) {\n *         int count = urls.length;\n *         long totalSize = 0;\n *         for (int i = 0; i < count; i++) {\n *             totalSize += Downloader.downloadFile(urls[i]);\n *             publishProgress((int) ((i / (float) count) * 100));\n *             // Escape early if cancel() is called\n *             if (isCancelled()) break;\n *         }\n *         return totalSize;\n *     }\n *\n *     protected void onProgressUpdate(Integer... progress) {\n *         setProgressPercent(progress[0]);\n *     }\n *\n *     protected void onPostExecute(Long result) {\n *         showDialog(\"Downloaded \" + result + \" bytes\");\n *     }\n * }\n * </pre>\n *\n * <p>Once created, a task is executed very simply:</p>\n * <pre class=\"prettyprint\">\n * new DownloadFilesTask().execute(url1, url2, url3);\n * </pre>\n *\n * <h2>AsyncTask's generic types</h2>\n * <p>The three types used by an asynchronous task are the following:</p>\n * <ol>\n *     <li><code>Params</code>, the type of the parameters sent to the task upon\n *     execution.</li>\n *     <li><code>Progress</code>, the type of the progress units published during\n *     the background computation.</li>\n *     <li><code>Result</code>, the type of the result of the background\n *     computation.</li>\n * </ol>\n * <p>Not all types are always used by an asynchronous task. To mark a type as unused,\n * simply use the type {@link Void}:</p>\n * <pre>\n * private class MyTask extends AsyncTask&lt;Void, Void, Void&gt; { ... }\n * </pre>\n *\n * <h2>The 4 steps</h2>\n * <p>When an asynchronous task is executed, the task goes through 4 steps:</p>\n * <ol>\n *     <li>{@link #onPreExecute()}, invoked on the UI thread before the task\n *     is executed. This step is normally used to setup the task, for instance by\n *     showing a progress bar in the user interface.</li>\n *     <li>{@link #doInBackground}, invoked on the background thread\n *     immediately after {@link #onPreExecute()} finishes executing. This step is used\n *     to perform background computation that can take a long time. The parameters\n *     of the asynchronous task are passed to this step. The result of the computation must\n *     be returned by this step and will be passed back to the last step. This step\n *     can also use {@link #publishProgress} to publish one or more units\n *     of progress. These values are published on the UI thread, in the\n *     {@link #onProgressUpdate} step.</li>\n *     <li>{@link #onProgressUpdate}, invoked on the UI thread after a\n *     call to {@link #publishProgress}. The timing of the execution is\n *     undefined. This method is used to display any form of progress in the user\n *     interface while the background computation is still executing. For instance,\n *     it can be used to animate a progress bar or show logs in a text field.</li>\n *     <li>{@link #onPostExecute}, invoked on the UI thread after the background\n *     computation finishes. The result of the background computation is passed to\n *     this step as a parameter.</li>\n * </ol>\n *\n * <h2>Cancelling a task</h2>\n * <p>A task can be cancelled at any time by invoking {@link #cancel(boolean)}. Invoking\n * this method will cause subsequent calls to {@link #isCancelled()} to return true.\n * After invoking this method, {@link #onCancelled(Object)}, instead of\n * {@link #onPostExecute(Object)} will be invoked after {@link #doInBackground(Object[])}\n * returns. To ensure that a task is cancelled as quickly as possible, you should always\n * check the return value of {@link #isCancelled()} periodically from\n * {@link #doInBackground(Object[])}, if possible (inside a loop for instance.)</p>\n *\n * <h2>Threading rules</h2>\n * <p>There are a few threading rules that must be followed for this class to\n * work properly:</p>\n * <ul>\n *     <li>The AsyncTask class must be loaded on the UI thread. This is done\n *     automatically as of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}.</li>\n *     <li>The task instance must be created on the UI thread.</li>\n *     <li>{@link #execute} must be invoked on the UI thread.</li>\n *     <li>Do not call {@link #onPreExecute()}, {@link #onPostExecute},\n *     {@link #doInBackground}, {@link #onProgressUpdate} manually.</li>\n *     <li>The task can be executed only once (an exception will be thrown if\n *     a second execution is attempted.)</li>\n * </ul>\n *\n * <h2>Memory observability</h2>\n * <p>AsyncTask guarantees that all callback calls are synchronized in such a way that the following\n * operations are safe without explicit synchronizations.</p>\n * <ul>\n *     <li>Set member fields in the constructor or {@link #onPreExecute}, and refer to them\n *     in {@link #doInBackground}.\n *     <li>Set member fields in {@link #doInBackground}, and refer to them in\n *     {@link #onProgressUpdate} and {@link #onPostExecute}.\n * </ul>\n *\n * <h2>Order of execution</h2>\n * <p>When first introduced, AsyncTasks were executed serially on a single background\n * thread. Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed\n * to a pool of threads allowing multiple tasks to operate in parallel. Starting with\n * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are executed on a single\n * thread to avoid common application errors caused by parallel execution.</p>\n * <p>If you truly want parallel execution, you can invoke\n * {@link #executeOnExecutor(java.util.concurrent.Executor, Object[])} with\n * {@link #THREAD_POOL_EXECUTOR}.</p>\n */\npublic abstract class AsyncTask<Params, Progress, Result> {\n    private static final String LOG_TAG = \"AsyncTask\";\n\n    private static final int CORE_POOL_SIZE = 5;\n    private static final int MAXIMUM_POOL_SIZE = 128;\n    private static final int KEEP_ALIVE = 1;\n\n    private static final ThreadFactory sThreadFactory = new ThreadFactory() {\n        private final AtomicInteger mCount = new AtomicInteger(1);\n\n        public Thread newThread(Runnable r) {\n            return new Thread(r, \"AsyncTask #\" + mCount.getAndIncrement());\n        }\n    };\n\n    private static final BlockingQueue<Runnable> sPoolWorkQueue =\n            new LinkedBlockingQueue<Runnable>(10);\n\n    /**\n     * An {@link java.util.concurrent.Executor} that can be used to execute tasks in parallel.\n     */\n    public static final Executor THREAD_POOL_EXECUTOR\n            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,\n                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);\n\n    /**\n     * An {@link java.util.concurrent.Executor} that executes tasks one at a time in serial\n     * order.  This serialization is global to a particular process.\n     */\n    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();\n\n    private static final int MESSAGE_POST_RESULT = 0x1;\n    private static final int MESSAGE_POST_PROGRESS = 0x2;\n\n    private static final InternalHandler sHandler = new InternalHandler();\n\n    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;\n    private final WorkerRunnable<Params, Result> mWorker;\n    private final FutureTask<Result> mFuture;\n\n    private volatile Status mStatus = Status.PENDING;\n\n    private final AtomicBoolean mCancelled = new AtomicBoolean();\n    private final AtomicBoolean mTaskInvoked = new AtomicBoolean();\n\n    private static class SerialExecutor implements Executor {\n        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();\n        Runnable mActive;\n\n        public synchronized void execute(final Runnable r) {\n            mTasks.offer(new Runnable() {\n                public void run() {\n                    try {\n                        r.run();\n                    } finally {\n                        scheduleNext();\n                    }\n                }\n            });\n            if (mActive == null) {\n                scheduleNext();\n            }\n        }\n\n        protected synchronized void scheduleNext() {\n            if ((mActive = mTasks.poll()) != null) {\n                THREAD_POOL_EXECUTOR.execute(mActive);\n            }\n        }\n    }\n\n    /**\n     * Indicates the current status of the task. Each status will be set only once\n     * during the lifetime of a task.\n     */\n    public enum Status {\n        /**\n         * Indicates that the task has not been executed yet.\n         */\n        PENDING,\n        /**\n         * Indicates that the task is running.\n         */\n        RUNNING,\n        /**\n         * Indicates that {@link com.artifex.mupdfdemo.AsyncTask#onPostExecute} has finished.\n         */\n        FINISHED,\n    }\n\n    /** @hide Used to force static handler to be created. */\n    public static void init() {\n        sHandler.getLooper();\n    }\n\n    /** @hide */\n    public static void setDefaultExecutor(Executor exec) {\n        sDefaultExecutor = exec;\n    }\n\n    /**\n     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.\n     */\n    public AsyncTask() {\n        mWorker = new WorkerRunnable<Params, Result>() {\n            public Result call() throws Exception {\n                mTaskInvoked.set(true);\n\n                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);\n                //noinspection unchecked\n                return postResult(doInBackground(mParams));\n            }\n        };\n\n        mFuture = new FutureTask<Result>(mWorker) {\n            @Override\n            protected void done() {\n                try {\n                    postResultIfNotInvoked(get());\n                } catch (InterruptedException e) {\n                    android.util.Log.w(LOG_TAG, e);\n                } catch (ExecutionException e) {\n                    throw new RuntimeException(\"An error occured while executing doInBackground()\",\n                            e.getCause());\n                } catch (CancellationException e) {\n                    postResultIfNotInvoked(null);\n                }\n            }\n        };\n    }\n\n    private void postResultIfNotInvoked(Result result) {\n        final boolean wasTaskInvoked = mTaskInvoked.get();\n        if (!wasTaskInvoked) {\n            postResult(result);\n        }\n    }\n\n    private Result postResult(Result result) {\n        @SuppressWarnings(\"unchecked\")\n        Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,\n                new AsyncTaskResult<Result>(this, result));\n        message.sendToTarget();\n        return result;\n    }\n\n    /**\n     * Returns the current status of this task.\n     *\n     * @return The current status.\n     */\n    public final Status getStatus() {\n        return mStatus;\n    }\n\n    /**\n     * Override this method to perform a computation on a background thread. The\n     * specified parameters are the parameters passed to {@link #execute}\n     * by the caller of this task.\n     *\n     * This method can call {@link #publishProgress} to publish updates\n     * on the UI thread.\n     *\n     * @param params The parameters of the task.\n     *\n     * @return A result, defined by the subclass of this task.\n     *\n     * @see #onPreExecute()\n     * @see #onPostExecute\n     * @see #publishProgress\n     */\n    protected abstract Result doInBackground(Params... params);\n\n    /**\n     * Runs on the UI thread before {@link #doInBackground}.\n     *\n     * @see #onPostExecute\n     * @see #doInBackground\n     */\n    protected void onPreExecute() {\n    }\n\n    /**\n     * <p>Runs on the UI thread after {@link #doInBackground}. The\n     * specified result is the value returned by {@link #doInBackground}.</p>\n     *\n     * <p>This method won't be invoked if the task was cancelled.</p>\n     *\n     * @param result The result of the operation computed by {@link #doInBackground}.\n     *\n     * @see #onPreExecute\n     * @see #doInBackground\n     * @see #onCancelled(Object)\n     */\n    @SuppressWarnings({\"UnusedDeclaration\"})\n    protected void onPostExecute(Result result) {\n    }\n\n    /**\n     * Runs on the UI thread after {@link #publishProgress} is invoked.\n     * The specified values are the values passed to {@link #publishProgress}.\n     *\n     * @param values The values indicating progress.\n     *\n     * @see #publishProgress\n     * @see #doInBackground\n     */\n    @SuppressWarnings({\"UnusedDeclaration\"})\n    protected void onProgressUpdate(Progress... values) {\n    }\n\n    /**\n     * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and\n     * {@link #doInBackground(Object[])} has finished.</p>\n     *\n     * <p>The default implementation simply invokes {@link #onCancelled()} and\n     * ignores the result. If you write your own implementation, do not call\n     * <code>super.onCancelled(result)</code>.</p>\n     *\n     * @param result The result, if any, computed in\n     *               {@link #doInBackground(Object[])}, can be null\n     *\n     * @see #cancel(boolean)\n     * @see #isCancelled()\n     */\n    @SuppressWarnings({\"UnusedParameters\"})\n    protected void onCancelled(Result result) {\n        onCancelled();\n    }\n\n    /**\n     * <p>Applications should preferably override {@link #onCancelled(Object)}.\n     * This method is invoked by the default implementation of\n     * {@link #onCancelled(Object)}.</p>\n     *\n     * <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and\n     * {@link #doInBackground(Object[])} has finished.</p>\n     *\n     * @see #onCancelled(Object)\n     * @see #cancel(boolean)\n     * @see #isCancelled()\n     */\n    protected void onCancelled() {\n    }\n\n    /**\n     * Returns <tt>true</tt> if this task was cancelled before it completed\n     * normally. If you are calling {@link #cancel(boolean)} on the task,\n     * the value returned by this method should be checked periodically from\n     * {@link #doInBackground(Object[])} to end the task as soon as possible.\n     *\n     * @return <tt>true</tt> if task was cancelled before it completed\n     *\n     * @see #cancel(boolean)\n     */\n    public final boolean isCancelled() {\n        return mCancelled.get();\n    }\n\n    /**\n     * <p>Attempts to cancel execution of this task.  This attempt will\n     * fail if the task has already completed, already been cancelled,\n     * or could not be cancelled for some other reason. If successful,\n     * and this task has not started when <tt>cancel</tt> is called,\n     * this task should never run. If the task has already started,\n     * then the <tt>mayInterruptIfRunning</tt> parameter determines\n     * whether the thread executing this task should be interrupted in\n     * an attempt to stop the task.</p>\n     *\n     * <p>Calling this method will result in {@link #onCancelled(Object)} being\n     * invoked on the UI thread after {@link #doInBackground(Object[])}\n     * returns. Calling this method guarantees that {@link #onPostExecute(Object)}\n     * is never invoked. After invoking this method, you should check the\n     * value returned by {@link #isCancelled()} periodically from\n     * {@link #doInBackground(Object[])} to finish the task as early as\n     * possible.</p>\n     *\n     * @param mayInterruptIfRunning <tt>true</tt> if the thread executing this\n     *        task should be interrupted; otherwise, in-progress tasks are allowed\n     *        to complete.\n     *\n     * @return <tt>false</tt> if the task could not be cancelled,\n     *         typically because it has already completed normally;\n     *         <tt>true</tt> otherwise\n     *\n     * @see #isCancelled()\n     * @see #onCancelled(Object)\n     */\n    public final boolean cancel(boolean mayInterruptIfRunning) {\n        mCancelled.set(true);\n        return mFuture.cancel(mayInterruptIfRunning);\n    }\n\n    /**\n     * Waits if necessary for the computation to complete, and then\n     * retrieves its result.\n     *\n     * @return The computed result.\n     *\n     * @throws java.util.concurrent.CancellationException If the computation was cancelled.\n     * @throws java.util.concurrent.ExecutionException If the computation threw an exception.\n     * @throws InterruptedException If the current thread was interrupted\n     *         while waiting.\n     */\n    public final Result get() throws InterruptedException, ExecutionException {\n        return mFuture.get();\n    }\n\n    /**\n     * Waits if necessary for at most the given time for the computation\n     * to complete, and then retrieves its result.\n     *\n     * @param timeout Time to wait before cancelling the operation.\n     * @param unit The time unit for the timeout.\n     *\n     * @return The computed result.\n     *\n     * @throws java.util.concurrent.CancellationException If the computation was cancelled.\n     * @throws java.util.concurrent.ExecutionException If the computation threw an exception.\n     * @throws InterruptedException If the current thread was interrupted\n     *         while waiting.\n     * @throws java.util.concurrent.TimeoutException If the wait timed out.\n     */\n    public final Result get(long timeout, TimeUnit unit) throws InterruptedException,\n            ExecutionException, TimeoutException {\n        return mFuture.get(timeout, unit);\n    }\n\n    /**\n     * Executes the task with the specified parameters. The task returns\n     * itself (this) so that the caller can keep a reference to it.\n     *\n     * <p>Note: this function schedules the task on a queue for a single background\n     * thread or pool of threads depending on the platform version.  When first\n     * introduced, AsyncTasks were executed serially on a single background thread.\n     * Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed\n     * to a pool of threads allowing multiple tasks to operate in parallel. Starting\n     * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are back to being\n     * executed on a single thread to avoid common application errors caused\n     * by parallel execution.  If you truly want parallel execution, you can use\n     * the {@link #executeOnExecutor} version of this method\n     * with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings\n     * on its use.\n     *\n     * <p>This method must be invoked on the UI thread.\n     *\n     * @param params The parameters of the task.\n     *\n     * @return This instance of AsyncTask.\n     *\n     * @throws IllegalStateException If {@link #getStatus()} returns either\n     *         {@link com.artifex.mupdfdemo.AsyncTask.Status#RUNNING} or {@link com.artifex.mupdfdemo.AsyncTask.Status#FINISHED}.\n     *\n     * @see #executeOnExecutor(java.util.concurrent.Executor, Object[])\n     * @see #execute(Runnable)\n     */\n    public final AsyncTask<Params, Progress, Result> execute(Params... params) {\n        return executeOnExecutor(sDefaultExecutor, params);\n    }\n\n    /**\n     * Executes the task with the specified parameters. The task returns\n     * itself (this) so that the caller can keep a reference to it.\n     *\n     * <p>This method is typically used with {@link #THREAD_POOL_EXECUTOR} to\n     * allow multiple tasks to run in parallel on a pool of threads managed by\n     * AsyncTask, however you can also use your own {@link java.util.concurrent.Executor} for custom\n     * behavior.\n     *\n     * <p><em>Warning:</em> Allowing multiple tasks to run in parallel from\n     * a thread pool is generally <em>not</em> what one wants, because the order\n     * of their operation is not defined.  For example, if these tasks are used\n     * to modify any state in common (such as writing a file due to a button click),\n     * there are no guarantees on the order of the modifications.\n     * Without careful work it is possible in rare cases for the newer version\n     * of the data to be over-written by an older one, leading to obscure data\n     * loss and stability issues.  Such changes are best\n     * executed in serial; to guarantee such work is serialized regardless of\n     * platform version you can use this function with {@link #SERIAL_EXECUTOR}.\n     *\n     * <p>This method must be invoked on the UI thread.\n     *\n     * @param exec The executor to use.  {@link #THREAD_POOL_EXECUTOR} is available as a\n     *              convenient process-wide thread pool for tasks that are loosely coupled.\n     * @param params The parameters of the task.\n     *\n     * @return This instance of AsyncTask.\n     *\n     * @throws IllegalStateException If {@link #getStatus()} returns either\n     *         {@link com.artifex.mupdfdemo.AsyncTask.Status#RUNNING} or {@link com.artifex.mupdfdemo.AsyncTask.Status#FINISHED}.\n     *\n     * @see #execute(Object[])\n     */\n    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,\n            Params... params) {\n        if (mStatus != Status.PENDING) {\n            switch (mStatus) {\n                case RUNNING:\n                    throw new IllegalStateException(\"Cannot execute task:\"\n                            + \" the task is already running.\");\n                case FINISHED:\n                    throw new IllegalStateException(\"Cannot execute task:\"\n                            + \" the task has already been executed \"\n                            + \"(a task can be executed only once)\");\n            }\n        }\n\n        mStatus = Status.RUNNING;\n\n        onPreExecute();\n\n        mWorker.mParams = params;\n        exec.execute(mFuture);\n\n        return this;\n    }\n\n    /**\n     * Convenience version of {@link #execute(Object...)} for use with\n     * a simple Runnable object. See {@link #execute(Object[])} for more\n     * information on the order of execution.\n     *\n     * @see #execute(Object[])\n     * @see #executeOnExecutor(java.util.concurrent.Executor, Object[])\n     */\n    public static void execute(Runnable runnable) {\n        sDefaultExecutor.execute(runnable);\n    }\n\n    /**\n     * This method can be invoked from {@link #doInBackground} to\n     * publish updates on the UI thread while the background computation is\n     * still running. Each call to this method will trigger the execution of\n     * {@link #onProgressUpdate} on the UI thread.\n     *\n     * {@link #onProgressUpdate} will note be called if the task has been\n     * canceled.\n     *\n     * @param values The progress values to update the UI with.\n     *\n     * @see #onProgressUpdate\n     * @see #doInBackground\n     */\n    protected final void publishProgress(Progress... values) {\n        if (!isCancelled()) {\n            sHandler.obtainMessage(MESSAGE_POST_PROGRESS,\n                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();\n        }\n    }\n\n    private void finish(Result result) {\n        if (isCancelled()) {\n            onCancelled(result);\n        } else {\n            onPostExecute(result);\n        }\n        mStatus = Status.FINISHED;\n    }\n\n    private static class InternalHandler extends Handler {\n        @SuppressWarnings({\"unchecked\", \"RawUseOfParameterizedType\"})\n        @Override\n        public void handleMessage(Message msg) {\n            AsyncTaskResult result = (AsyncTaskResult) msg.obj;\n            switch (msg.what) {\n                case MESSAGE_POST_RESULT:\n                    // There is only one result\n                    result.mTask.finish(result.mData[0]);\n                    break;\n                case MESSAGE_POST_PROGRESS:\n                    result.mTask.onProgressUpdate(result.mData);\n                    break;\n            }\n        }\n    }\n\n    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {\n        Params[] mParams;\n    }\n\n    @SuppressWarnings({\"RawUseOfParameterizedType\"})\n    private static class AsyncTaskResult<Data> {\n        final AsyncTask mTask;\n        final Data[] mData;\n\n        AsyncTaskResult(AsyncTask task, Data... data) {\n            mTask = task;\n            mData = data;\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/CancellableAsyncTask.java",
    "content": "package com.artifex.mupdfdemo;\n\nimport java.util.concurrent.CancellationException;\nimport java.util.concurrent.ExecutionException;\n\n// Ideally this would be a subclass of AsyncTask, however the cancel() method is final, and cannot\n// be overridden. I felt that having two different, but similar cancel methods was a bad idea.\npublic class CancellableAsyncTask<Params, Result>\n{\n\tprivate final AsyncTask<Params, Void, Result> asyncTask;\n\tprivate final CancellableTaskDefinition<Params, Result> ourTask;\n\n\tpublic void onPreExecute()\n\t{\n        \n\t}\n\n\tpublic void onPostExecute(Result result)\n\t{\n\n\t}\n\n\tpublic CancellableAsyncTask(final CancellableTaskDefinition<Params, Result> task)\n\t{\n\t\tif (task == null)\n\t\t\t\tthrow new IllegalArgumentException();\n\n\t\tthis.ourTask = task;\n\t\tasyncTask = new AsyncTask<Params, Void, Result>()\n\t\t\t\t{\n\t\t\t\t\t@Override\n\t\t\t\t\tprotected Result doInBackground(Params... params)\n\t\t\t\t\t{\n\t\t\t\t\t\treturn task.doInBackground(params);\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tprotected void onPreExecute()\n\t\t\t\t\t{\n\t\t\t\t\t\tCancellableAsyncTask.this.onPreExecute();\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tprotected void onPostExecute(Result result)\n\t\t\t\t\t{\n\t\t\t\t\t\tCancellableAsyncTask.this.onPostExecute(result);\n\t\t\t\t\t\ttask.doCleanup();\n\t\t\t\t\t}\n\t\t\t\t};\n\t}\n\n\tpublic void cancelAndWait()\n\t{\n\t\tthis.asyncTask.cancel(true);\n\t\tourTask.doCancel();\n\n\t\ttry\n\t\t{\n\t\t\tthis.asyncTask.get();\n\t\t}\n\t\tcatch (InterruptedException e)\n\t\t{\n\t\t}\n\t\tcatch (ExecutionException e)\n\t\t{\n\t\t}\n\t\tcatch (CancellationException e)\n\t\t{\n\t\t}\n\n\t\tourTask.doCleanup();\n\t}\n\n\tpublic void execute(Params ... params)\n\t{\n\t\tasyncTask.execute(params);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/CancellableTaskDefinition.java",
    "content": "package com.artifex.mupdfdemo;\n\npublic interface CancellableTaskDefinition <Params, Result>\n{\n\tpublic Result doInBackground(Params... params);\n\tpublic void doCancel();\n\tpublic void doCleanup();\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/ChoosePDFActivity.java",
    "content": "package com.artifex.mupdfdemo;\n\nimport java.io.File;\nimport java.io.FileFilter;\nimport java.util.Arrays;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport android.app.AlertDialog;\nimport android.app.ListActivity;\nimport android.content.DialogInterface;\nimport android.content.DialogInterface.OnClickListener;\nimport android.content.Intent;\nimport android.content.res.Resources;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.os.Environment;\nimport android.os.FileObserver;\nimport android.os.Handler;\nimport android.view.View;\nimport android.widget.ListView;\n\nenum Purpose {\n\tPickPDF,\n\tPickKeyFile\n}\n\npublic class ChoosePDFActivity extends ListActivity {\n\tstatic public final String PICK_KEY_FILE = \"com.artifex.mupdfdemo.PICK_KEY_FILE\";\n\tstatic private File  mDirectory;\n\tstatic private Map<String, Integer> mPositions = new HashMap<String, Integer>();\n\tprivate File         mParent;\n\tprivate File []      mDirs;\n\tprivate File []      mFiles;\n\tprivate Handler\t     mHandler;\n\tprivate Runnable     mUpdateFiles;\n\tprivate ChoosePDFAdapter adapter;\n\tprivate Purpose      mPurpose;\n\n\t@Override\n\tprotected void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\n\t\tmPurpose = PICK_KEY_FILE.equals(getIntent().getAction()) ? Purpose.PickKeyFile : Purpose.PickPDF;\n\n\n\t\tString storageState = Environment.getExternalStorageState();\n\n\t\tif (!Environment.MEDIA_MOUNTED.equals(storageState)\n\t\t\t\t&& !Environment.MEDIA_MOUNTED_READ_ONLY.equals(storageState))\n\t\t{\n\t\t\tAlertDialog.Builder builder = new AlertDialog.Builder(this);\n\t\t\tbuilder.setTitle(R.string.no_media_warning);\n\t\t\tbuilder.setMessage(R.string.no_media_hint);\n\t\t\tAlertDialog alert = builder.create();\n\t\t\talert.setButton(AlertDialog.BUTTON_POSITIVE,getString(R.string.dismiss),\n\t\t\t\t\tnew OnClickListener() {\n\t\t\t\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\t\t\t\tfinish();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\talert.show();\n\t\t\treturn;\n\t\t}\n\n\t\tif (mDirectory == null)\n\t\t\tmDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);\n\n\t\t// Create a list adapter...\n\t\tadapter = new ChoosePDFAdapter(getLayoutInflater());\n\t\tsetListAdapter(adapter);\n\n\t\t// ...that is updated dynamically when files are scanned\n\t\tmHandler = new Handler();\n\t\tmUpdateFiles = new Runnable() {\n\t\t\tpublic void run() {\n\t\t\t\tResources res = getResources();\n\t\t\t\tString appName = res.getString(R.string.mupdf_title);\n\t\t\t\tString version = res.getString(R.string.version);\n\t\t\t\tString title = res.getString(R.string.picker_title_App_Ver_Dir);\n\t\t\t\tsetTitle(String.format(title, appName, version, mDirectory));\n\n\t\t\t\tmParent = mDirectory.getParentFile();\n\n\t\t\t\tmDirs = mDirectory.listFiles(new FileFilter() {\n\n\t\t\t\t\tpublic boolean accept(File file) {\n\t\t\t\t\t\treturn file.isDirectory();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tif (mDirs == null)\n\t\t\t\t\tmDirs = new File[0];\n\n\t\t\t\tmFiles = mDirectory.listFiles(new FileFilter() {\n\n\t\t\t\t\tpublic boolean accept(File file) {\n\t\t\t\t\t\tif (file.isDirectory())\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\tString fname = file.getName().toLowerCase();\n\t\t\t\t\t\tswitch (mPurpose) {\n\t\t\t\t\t\tcase PickPDF:\n\t\t\t\t\t\t\tif (fname.endsWith(\".pdf\"))\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\tif (fname.endsWith(\".xps\"))\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\tif (fname.endsWith(\".cbz\"))\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\tif (fname.endsWith(\".png\"))\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\tif (fname.endsWith(\".jpe\"))\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\tif (fname.endsWith(\".jpeg\"))\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\tif (fname.endsWith(\".jpg\"))\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\tif (fname.endsWith(\".jfif\"))\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\tif (fname.endsWith(\".jfif-tbnl\"))\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\tif (fname.endsWith(\".tif\"))\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\tif (fname.endsWith(\".tiff\"))\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\tcase PickKeyFile:\n\t\t\t\t\t\t\tif (fname.endsWith(\".pfx\"))\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tif (mFiles == null)\n\t\t\t\t\tmFiles = new File[0];\n\n\t\t\t\tArrays.sort(mFiles, new Comparator<File>() {\n\t\t\t\t\tpublic int compare(File arg0, File arg1) {\n\t\t\t\t\t\treturn arg0.getName().compareToIgnoreCase(arg1.getName());\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tArrays.sort(mDirs, new Comparator<File>() {\n\t\t\t\t\tpublic int compare(File arg0, File arg1) {\n\t\t\t\t\t\treturn arg0.getName().compareToIgnoreCase(arg1.getName());\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tadapter.clear();\n\t\t\t\tif (mParent != null)\n\t\t\t\t\tadapter.add(new ChoosePDFItem(ChoosePDFItem.Type.PARENT, getString(R.string.parent_directory)));\n\t\t\t\tfor (File f : mDirs)\n\t\t\t\t\tadapter.add(new ChoosePDFItem(ChoosePDFItem.Type.DIR, f.getName()));\n\t\t\t\tfor (File f : mFiles)\n\t\t\t\t\tadapter.add(new ChoosePDFItem(ChoosePDFItem.Type.DOC, f.getName()));\n\n\t\t\t\tlastPosition();\n\t\t\t}\n\t\t};\n\n\t\t// Start initial file scan...\n\t\tmHandler.post(mUpdateFiles);\n\n\t\t// ...and observe the directory and scan files upon changes.\n\t\tFileObserver observer = new FileObserver(mDirectory.getPath(), FileObserver.CREATE | FileObserver.DELETE) {\n\t\t\tpublic void onEvent(int event, String path) {\n\t\t\t\tmHandler.post(mUpdateFiles);\n\t\t\t}\n\t\t};\n\t\tobserver.startWatching();\n\t}\n\n\tprivate void lastPosition() {\n\t\tString p = mDirectory.getAbsolutePath();\n\t\tif (mPositions.containsKey(p))\n\t\t\tgetListView().setSelection(mPositions.get(p));\n\t}\n\n\t@Override\n\tprotected void onListItemClick(ListView l, View v, int position, long id) {\n\t\tsuper.onListItemClick(l, v, position, id);\n\n\t\tmPositions.put(mDirectory.getAbsolutePath(), getListView().getFirstVisiblePosition());\n\n\t\tif (position < (mParent == null ? 0 : 1)) {\n\t\t\tmDirectory = mParent;\n\t\t\tmHandler.post(mUpdateFiles);\n\t\t\treturn;\n\t\t}\n\n\t\tposition -= (mParent == null ? 0 : 1);\n\n\t\tif (position < mDirs.length) {\n\t\t\tmDirectory = mDirs[position];\n\t\t\tmHandler.post(mUpdateFiles);\n\t\t\treturn;\n\t\t}\n\n\t\tposition -= mDirs.length;\n\n\t\tUri uri = Uri.parse(mFiles[position].getAbsolutePath());\n\t\tIntent intent = new Intent(this,MuPDFActivity.class);\n\t\tintent.setAction(Intent.ACTION_VIEW);\n\t\tintent.setData(uri);\n\t\tswitch (mPurpose) {\n\t\tcase PickPDF:\n\t\t\t// Start an activity to display the PDF file\n\t\t\tstartActivity(intent);\n\t\t\tbreak;\n\t\tcase PickKeyFile:\n\t\t\t// Return the uri to the caller\n\t\t\tsetResult(RESULT_OK, intent);\n\t\t\tfinish();\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t@Override\n\tprotected void onPause() {\n\t\tsuper.onPause();\n\t\tif (mDirectory != null)\n\t\t\tmPositions.put(mDirectory.getAbsolutePath(), getListView().getFirstVisiblePosition());\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/ChoosePDFAdapter.java",
    "content": "package com.artifex.mupdfdemo;\n\nimport java.util.LinkedList;\n\nimport android.graphics.Color;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.BaseAdapter;\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\npublic class ChoosePDFAdapter extends BaseAdapter {\n\tprivate final LinkedList<ChoosePDFItem> mItems;\n\tprivate final LayoutInflater mInflater;\n\n\tpublic ChoosePDFAdapter(LayoutInflater inflater) {\n\t\tmInflater = inflater;\n\t\tmItems = new LinkedList<ChoosePDFItem>();\n\t}\n\n\tpublic void clear() {\n\t\tmItems.clear();\n\t}\n\n\tpublic void add(ChoosePDFItem item) {\n\t\tmItems.add(item);\n\t\tnotifyDataSetChanged();\n\t}\n\n\tpublic int getCount() {\n\t\treturn mItems.size();\n\t}\n\n\tpublic Object getItem(int i) {\n\t\treturn null;\n\t}\n\n\tpublic long getItemId(int arg0) {\n\t\treturn 0;\n\t}\n\n\tprivate int iconForType(ChoosePDFItem.Type type) {\n\t\tswitch (type) {\n\t\tcase PARENT: return R.drawable.ic_arrow_up;\n\t\tcase DIR: return R.drawable.ic_dir;\n\t\tcase DOC: return R.drawable.ic_doc;\n\t\tdefault: return 0;\n\t\t}\n\t}\n\n\tpublic View getView(int position, View convertView, ViewGroup parent) {\n\t\tView v;\n\t\tif (convertView == null) {\n\t\t\tv = mInflater.inflate(R.layout.picker_entry, null);\n\t\t} else {\n\t\t\tv = convertView;\n\t\t}\n\t\tChoosePDFItem item = mItems.get(position);\n\t\t((TextView)v.findViewById(R.id.name)).setText(item.name);\n\t\t((ImageView)v.findViewById(R.id.icon)).setImageResource(iconForType(item.type));\n\t\t((ImageView)v.findViewById(R.id.icon)).setColorFilter(Color.argb(255, 0, 0, 0));\n\t\treturn v;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/ChoosePDFItem.java",
    "content": "package com.artifex.mupdfdemo;\n\npublic class ChoosePDFItem {\n\tenum Type {\n\t\tPARENT, DIR, DOC\n\t}\n\n\tfinal public Type type;\n\tfinal public String name;\n\n\tpublic ChoosePDFItem (Type t, String n) {\n\t\ttype = t;\n\t\tname = n;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/Deque.java",
    "content": "/*\n * Written by Doug Lea and Josh Bloch with assistance from members of\n * JCP JSR-166 Expert Group and released to the public domain, as explained\n * at http://creativecommons.org/publicdomain/zero/1.0/\n */\n\npackage com.artifex.mupdfdemo;\n\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.NoSuchElementException;\nimport java.util.Queue;\nimport java.util.Stack;\n\n// BEGIN android-note\n// removed link to collections framework docs\n// END android-note\n\n/**\n * A linear collection that supports element insertion and removal at\n * both ends.  The name <i>deque</i> is short for \"double ended queue\"\n * and is usually pronounced \"deck\".  Most <tt>Deque</tt>\n * implementations place no fixed limits on the number of elements\n * they may contain, but this interface supports capacity-restricted\n * deques as well as those with no fixed size limit.\n *\n * <p>This interface defines methods to access the elements at both\n * ends of the deque.  Methods are provided to insert, remove, and\n * examine the element.  Each of these methods exists in two forms:\n * one throws an exception if the operation fails, the other returns a\n * special value (either <tt>null</tt> or <tt>false</tt>, depending on\n * the operation).  The latter form of the insert operation is\n * designed specifically for use with capacity-restricted\n * <tt>Deque</tt> implementations; in most implementations, insert\n * operations cannot fail.\n *\n * <p>The twelve methods described above are summarized in the\n * following table:\n *\n * <p>\n * <table BORDER CELLPADDING=3 CELLSPACING=1>\n *  <tr>\n *    <td></td>\n *    <td ALIGN=CENTER COLSPAN = 2> <b>First Element (Head)</b></td>\n *    <td ALIGN=CENTER COLSPAN = 2> <b>Last Element (Tail)</b></td>\n *  </tr>\n *  <tr>\n *    <td></td>\n *    <td ALIGN=CENTER><em>Throws exception</em></td>\n *    <td ALIGN=CENTER><em>Special value</em></td>\n *    <td ALIGN=CENTER><em>Throws exception</em></td>\n *    <td ALIGN=CENTER><em>Special value</em></td>\n *  </tr>\n *  <tr>\n *    <td><b>Insert</b></td>\n *    <td>{@link #addFirst addFirst(e)}</td>\n *    <td>{@link #offerFirst offerFirst(e)}</td>\n *    <td>{@link #addLast addLast(e)}</td>\n *    <td>{@link #offerLast offerLast(e)}</td>\n *  </tr>\n *  <tr>\n *    <td><b>Remove</b></td>\n *    <td>{@link #removeFirst removeFirst()}</td>\n *    <td>{@link #pollFirst pollFirst()}</td>\n *    <td>{@link #removeLast removeLast()}</td>\n *    <td>{@link #pollLast pollLast()}</td>\n *  </tr>\n *  <tr>\n *    <td><b>Examine</b></td>\n *    <td>{@link #getFirst getFirst()}</td>\n *    <td>{@link #peekFirst peekFirst()}</td>\n *    <td>{@link #getLast getLast()}</td>\n *    <td>{@link #peekLast peekLast()}</td>\n *  </tr>\n * </table>\n *\n * <p>This interface extends the {@link java.util.Queue} interface.  When a deque is\n * used as a queue, FIFO (First-In-First-Out) behavior results.  Elements are\n * added at the end of the deque and removed from the beginning.  The methods\n * inherited from the <tt>Queue</tt> interface are precisely equivalent to\n * <tt>Deque</tt> methods as indicated in the following table:\n *\n * <p>\n * <table BORDER CELLPADDING=3 CELLSPACING=1>\n *  <tr>\n *    <td ALIGN=CENTER> <b><tt>Queue</tt> Method</b></td>\n *    <td ALIGN=CENTER> <b>Equivalent <tt>Deque</tt> Method</b></td>\n *  </tr>\n *  <tr>\n *    <td>{@link java.util.Queue#add add(e)}</td>\n *    <td>{@link #addLast addLast(e)}</td>\n *  </tr>\n *  <tr>\n *    <td>{@link java.util.Queue#offer offer(e)}</td>\n *    <td>{@link #offerLast offerLast(e)}</td>\n *  </tr>\n *  <tr>\n *    <td>{@link java.util.Queue#remove remove()}</td>\n *    <td>{@link #removeFirst removeFirst()}</td>\n *  </tr>\n *  <tr>\n *    <td>{@link java.util.Queue#poll poll()}</td>\n *    <td>{@link #pollFirst pollFirst()}</td>\n *  </tr>\n *  <tr>\n *    <td>{@link java.util.Queue#element element()}</td>\n *    <td>{@link #getFirst getFirst()}</td>\n *  </tr>\n *  <tr>\n *    <td>{@link java.util.Queue#peek peek()}</td>\n *    <td>{@link #peek peekFirst()}</td>\n *  </tr>\n * </table>\n *\n * <p>Deques can also be used as LIFO (Last-In-First-Out) stacks.  This\n * interface should be used in preference to the legacy {@link java.util.Stack} class.\n * When a deque is used as a stack, elements are pushed and popped from the\n * beginning of the deque.  Stack methods are precisely equivalent to\n * <tt>Deque</tt> methods as indicated in the table below:\n *\n * <p>\n * <table BORDER CELLPADDING=3 CELLSPACING=1>\n *  <tr>\n *    <td ALIGN=CENTER> <b>Stack Method</b></td>\n *    <td ALIGN=CENTER> <b>Equivalent <tt>Deque</tt> Method</b></td>\n *  </tr>\n *  <tr>\n *    <td>{@link #push push(e)}</td>\n *    <td>{@link #addFirst addFirst(e)}</td>\n *  </tr>\n *  <tr>\n *    <td>{@link #pop pop()}</td>\n *    <td>{@link #removeFirst removeFirst()}</td>\n *  </tr>\n *  <tr>\n *    <td>{@link #peek peek()}</td>\n *    <td>{@link #peekFirst peekFirst()}</td>\n *  </tr>\n * </table>\n *\n * <p>Note that the {@link #peek peek} method works equally well when\n * a deque is used as a queue or a stack; in either case, elements are\n * drawn from the beginning of the deque.\n *\n * <p>This interface provides two methods to remove interior\n * elements, {@link #removeFirstOccurrence removeFirstOccurrence} and\n * {@link #removeLastOccurrence removeLastOccurrence}.\n *\n * <p>Unlike the {@link java.util.List} interface, this interface does not\n * provide support for indexed access to elements.\n *\n * <p>While <tt>Deque</tt> implementations are not strictly required\n * to prohibit the insertion of null elements, they are strongly\n * encouraged to do so.  Users of any <tt>Deque</tt> implementations\n * that do allow null elements are strongly encouraged <i>not</i> to\n * take advantage of the ability to insert nulls.  This is so because\n * <tt>null</tt> is used as a special return value by various methods\n * to indicated that the deque is empty.\n *\n * <p><tt>Deque</tt> implementations generally do not define\n * element-based versions of the <tt>equals</tt> and <tt>hashCode</tt>\n * methods, but instead inherit the identity-based versions from class\n * <tt>Object</tt>.\n *\n * @author Doug Lea\n * @author Josh Bloch\n * @since  1.6\n * @param <E> the type of elements held in this collection\n */\n\npublic interface Deque<E> extends Queue<E> {\n    /**\n     * Inserts the specified element at the front of this deque if it is\n     * possible to do so immediately without violating capacity restrictions.\n     * When using a capacity-restricted deque, it is generally preferable to\n     * use method {@link #offerFirst}.\n     *\n     * @param e the element to add\n     * @throws IllegalStateException if the element cannot be added at this\n     *         time due to capacity restrictions\n     * @throws ClassCastException if the class of the specified element\n     *         prevents it from being added to this deque\n     * @throws NullPointerException if the specified element is null and this\n     *         deque does not permit null elements\n     * @throws IllegalArgumentException if some property of the specified\n     *         element prevents it from being added to this deque\n     */\n    void addFirst(E e);\n\n    /**\n     * Inserts the specified element at the end of this deque if it is\n     * possible to do so immediately without violating capacity restrictions.\n     * When using a capacity-restricted deque, it is generally preferable to\n     * use method {@link #offerLast}.\n     *\n     * <p>This method is equivalent to {@link #add}.\n     *\n     * @param e the element to add\n     * @throws IllegalStateException if the element cannot be added at this\n     *         time due to capacity restrictions\n     * @throws ClassCastException if the class of the specified element\n     *         prevents it from being added to this deque\n     * @throws NullPointerException if the specified element is null and this\n     *         deque does not permit null elements\n     * @throws IllegalArgumentException if some property of the specified\n     *         element prevents it from being added to this deque\n     */\n    void addLast(E e);\n\n    /**\n     * Inserts the specified element at the front of this deque unless it would\n     * violate capacity restrictions.  When using a capacity-restricted deque,\n     * this method is generally preferable to the {@link #addFirst} method,\n     * which can fail to insert an element only by throwing an exception.\n     *\n     * @param e the element to add\n     * @return <tt>true</tt> if the element was added to this deque, else\n     *         <tt>false</tt>\n     * @throws ClassCastException if the class of the specified element\n     *         prevents it from being added to this deque\n     * @throws NullPointerException if the specified element is null and this\n     *         deque does not permit null elements\n     * @throws IllegalArgumentException if some property of the specified\n     *         element prevents it from being added to this deque\n     */\n    boolean offerFirst(E e);\n\n    /**\n     * Inserts the specified element at the end of this deque unless it would\n     * violate capacity restrictions.  When using a capacity-restricted deque,\n     * this method is generally preferable to the {@link #addLast} method,\n     * which can fail to insert an element only by throwing an exception.\n     *\n     * @param e the element to add\n     * @return <tt>true</tt> if the element was added to this deque, else\n     *         <tt>false</tt>\n     * @throws ClassCastException if the class of the specified element\n     *         prevents it from being added to this deque\n     * @throws NullPointerException if the specified element is null and this\n     *         deque does not permit null elements\n     * @throws IllegalArgumentException if some property of the specified\n     *         element prevents it from being added to this deque\n     */\n    boolean offerLast(E e);\n\n    /**\n     * Retrieves and removes the first element of this deque.  This method\n     * differs from {@link #pollFirst pollFirst} only in that it throws an\n     * exception if this deque is empty.\n     *\n     * @return the head of this deque\n     * @throws java.util.NoSuchElementException if this deque is empty\n     */\n    E removeFirst();\n\n    /**\n     * Retrieves and removes the last element of this deque.  This method\n     * differs from {@link #pollLast pollLast} only in that it throws an\n     * exception if this deque is empty.\n     *\n     * @return the tail of this deque\n     * @throws java.util.NoSuchElementException if this deque is empty\n     */\n    E removeLast();\n\n    /**\n     * Retrieves and removes the first element of this deque,\n     * or returns <tt>null</tt> if this deque is empty.\n     *\n     * @return the head of this deque, or <tt>null</tt> if this deque is empty\n     */\n    E pollFirst();\n\n    /**\n     * Retrieves and removes the last element of this deque,\n     * or returns <tt>null</tt> if this deque is empty.\n     *\n     * @return the tail of this deque, or <tt>null</tt> if this deque is empty\n     */\n    E pollLast();\n\n    /**\n     * Retrieves, but does not remove, the first element of this deque.\n     *\n     * This method differs from {@link #peekFirst peekFirst} only in that it\n     * throws an exception if this deque is empty.\n     *\n     * @return the head of this deque\n     * @throws java.util.NoSuchElementException if this deque is empty\n     */\n    E getFirst();\n\n    /**\n     * Retrieves, but does not remove, the last element of this deque.\n     * This method differs from {@link #peekLast peekLast} only in that it\n     * throws an exception if this deque is empty.\n     *\n     * @return the tail of this deque\n     * @throws java.util.NoSuchElementException if this deque is empty\n     */\n    E getLast();\n\n    /**\n     * Retrieves, but does not remove, the first element of this deque,\n     * or returns <tt>null</tt> if this deque is empty.\n     *\n     * @return the head of this deque, or <tt>null</tt> if this deque is empty\n     */\n    E peekFirst();\n\n    /**\n     * Retrieves, but does not remove, the last element of this deque,\n     * or returns <tt>null</tt> if this deque is empty.\n     *\n     * @return the tail of this deque, or <tt>null</tt> if this deque is empty\n     */\n    E peekLast();\n\n    /**\n     * Removes the first occurrence of the specified element from this deque.\n     * If the deque does not contain the element, it is unchanged.\n     * More formally, removes the first element <tt>e</tt> such that\n     * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>\n     * (if such an element exists).\n     * Returns <tt>true</tt> if this deque contained the specified element\n     * (or equivalently, if this deque changed as a result of the call).\n     *\n     * @param o element to be removed from this deque, if present\n     * @return <tt>true</tt> if an element was removed as a result of this call\n     * @throws ClassCastException if the class of the specified element\n     *         is incompatible with this deque (optional)\n     * @throws NullPointerException if the specified element is null and this\n     *         deque does not permit null elements (optional)\n     */\n    boolean removeFirstOccurrence(Object o);\n\n    /**\n     * Removes the last occurrence of the specified element from this deque.\n     * If the deque does not contain the element, it is unchanged.\n     * More formally, removes the last element <tt>e</tt> such that\n     * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>\n     * (if such an element exists).\n     * Returns <tt>true</tt> if this deque contained the specified element\n     * (or equivalently, if this deque changed as a result of the call).\n     *\n     * @param o element to be removed from this deque, if present\n     * @return <tt>true</tt> if an element was removed as a result of this call\n     * @throws ClassCastException if the class of the specified element\n     *         is incompatible with this deque (optional)\n     * @throws NullPointerException if the specified element is null and this\n     *         deque does not permit null elements (optional)\n     */\n    boolean removeLastOccurrence(Object o);\n\n    // *** Queue methods ***\n\n    /**\n     * Inserts the specified element into the queue represented by this deque\n     * (in other words, at the tail of this deque) if it is possible to do so\n     * immediately without violating capacity restrictions, returning\n     * <tt>true</tt> upon success and throwing an\n     * <tt>IllegalStateException</tt> if no space is currently available.\n     * When using a capacity-restricted deque, it is generally preferable to\n     * use {@link #offer(Object) offer}.\n     *\n     * <p>This method is equivalent to {@link #addLast}.\n     *\n     * @param e the element to add\n     * @return <tt>true</tt> (as specified by {@link java.util.Collection#add})\n     * @throws IllegalStateException if the element cannot be added at this\n     *         time due to capacity restrictions\n     * @throws ClassCastException if the class of the specified element\n     *         prevents it from being added to this deque\n     * @throws NullPointerException if the specified element is null and this\n     *         deque does not permit null elements\n     * @throws IllegalArgumentException if some property of the specified\n     *         element prevents it from being added to this deque\n     */\n    boolean add(E e);\n\n    /**\n     * Inserts the specified element into the queue represented by this deque\n     * (in other words, at the tail of this deque) if it is possible to do so\n     * immediately without violating capacity restrictions, returning\n     * <tt>true</tt> upon success and <tt>false</tt> if no space is currently\n     * available.  When using a capacity-restricted deque, this method is\n     * generally preferable to the {@link #add} method, which can fail to\n     * insert an element only by throwing an exception.\n     *\n     * <p>This method is equivalent to {@link #offerLast}.\n     *\n     * @param e the element to add\n     * @return <tt>true</tt> if the element was added to this deque, else\n     *         <tt>false</tt>\n     * @throws ClassCastException if the class of the specified element\n     *         prevents it from being added to this deque\n     * @throws NullPointerException if the specified element is null and this\n     *         deque does not permit null elements\n     * @throws IllegalArgumentException if some property of the specified\n     *         element prevents it from being added to this deque\n     */\n    boolean offer(E e);\n\n    /**\n     * Retrieves and removes the head of the queue represented by this deque\n     * (in other words, the first element of this deque).\n     * This method differs from {@link #poll poll} only in that it throws an\n     * exception if this deque is empty.\n     *\n     * <p>This method is equivalent to {@link #removeFirst()}.\n     *\n     * @return the head of the queue represented by this deque\n     * @throws java.util.NoSuchElementException if this deque is empty\n     */\n    E remove();\n\n    /**\n     * Retrieves and removes the head of the queue represented by this deque\n     * (in other words, the first element of this deque), or returns\n     * <tt>null</tt> if this deque is empty.\n     *\n     * <p>This method is equivalent to {@link #pollFirst()}.\n     *\n     * @return the first element of this deque, or <tt>null</tt> if\n     *         this deque is empty\n     */\n    E poll();\n\n    /**\n     * Retrieves, but does not remove, the head of the queue represented by\n     * this deque (in other words, the first element of this deque).\n     * This method differs from {@link #peek peek} only in that it throws an\n     * exception if this deque is empty.\n     *\n     * <p>This method is equivalent to {@link #getFirst()}.\n     *\n     * @return the head of the queue represented by this deque\n     * @throws java.util.NoSuchElementException if this deque is empty\n     */\n    E element();\n\n    /**\n     * Retrieves, but does not remove, the head of the queue represented by\n     * this deque (in other words, the first element of this deque), or\n     * returns <tt>null</tt> if this deque is empty.\n     *\n     * <p>This method is equivalent to {@link #peekFirst()}.\n     *\n     * @return the head of the queue represented by this deque, or\n     *         <tt>null</tt> if this deque is empty\n     */\n    E peek();\n\n\n    // *** Stack methods ***\n\n    /**\n     * Pushes an element onto the stack represented by this deque (in other\n     * words, at the head of this deque) if it is possible to do so\n     * immediately without violating capacity restrictions, returning\n     * <tt>true</tt> upon success and throwing an\n     * <tt>IllegalStateException</tt> if no space is currently available.\n     *\n     * <p>This method is equivalent to {@link #addFirst}.\n     *\n     * @param e the element to push\n     * @throws IllegalStateException if the element cannot be added at this\n     *         time due to capacity restrictions\n     * @throws ClassCastException if the class of the specified element\n     *         prevents it from being added to this deque\n     * @throws NullPointerException if the specified element is null and this\n     *         deque does not permit null elements\n     * @throws IllegalArgumentException if some property of the specified\n     *         element prevents it from being added to this deque\n     */\n    void push(E e);\n\n    /**\n     * Pops an element from the stack represented by this deque.  In other\n     * words, removes and returns the first element of this deque.\n     *\n     * <p>This method is equivalent to {@link #removeFirst()}.\n     *\n     * @return the element at the front of this deque (which is the top\n     *         of the stack represented by this deque)\n     * @throws java.util.NoSuchElementException if this deque is empty\n     */\n    E pop();\n\n\n    // *** Collection methods ***\n\n    /**\n     * Removes the first occurrence of the specified element from this deque.\n     * If the deque does not contain the element, it is unchanged.\n     * More formally, removes the first element <tt>e</tt> such that\n     * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>\n     * (if such an element exists).\n     * Returns <tt>true</tt> if this deque contained the specified element\n     * (or equivalently, if this deque changed as a result of the call).\n     *\n     * <p>This method is equivalent to {@link #removeFirstOccurrence}.\n     *\n     * @param o element to be removed from this deque, if present\n     * @return <tt>true</tt> if an element was removed as a result of this call\n     * @throws ClassCastException if the class of the specified element\n     *         is incompatible with this deque (optional)\n     * @throws NullPointerException if the specified element is null and this\n     *         deque does not permit null elements (optional)\n     */\n    boolean remove(Object o);\n\n    /**\n     * Returns <tt>true</tt> if this deque contains the specified element.\n     * More formally, returns <tt>true</tt> if and only if this deque contains\n     * at least one element <tt>e</tt> such that\n     * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>.\n     *\n     * @param o element whose presence in this deque is to be tested\n     * @return <tt>true</tt> if this deque contains the specified element\n     * @throws ClassCastException if the type of the specified element\n     *         is incompatible with this deque (optional)\n     * @throws NullPointerException if the specified element is null and this\n     *         deque does not permit null elements (optional)\n     */\n    boolean contains(Object o);\n\n    /**\n     * Returns the number of elements in this deque.\n     *\n     * @return the number of elements in this deque\n     */\n    public int size();\n\n    /**\n     * Returns an iterator over the elements in this deque in proper sequence.\n     * The elements will be returned in order from first (head) to last (tail).\n     *\n     * @return an iterator over the elements in this deque in proper sequence\n     */\n    Iterator<E> iterator();\n\n    /**\n     * Returns an iterator over the elements in this deque in reverse\n     * sequential order.  The elements will be returned in order from\n     * last (tail) to first (head).\n     *\n     * @return an iterator over the elements in this deque in reverse\n     * sequence\n     */\n    Iterator<E> descendingIterator();\n\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/FilePicker.java",
    "content": "package com.artifex.mupdfdemo;\n\nimport android.net.Uri;\n\npublic abstract class FilePicker {\n\tpublic interface FilePickerSupport {\n\t\tvoid performPickFor(FilePicker picker);\n\t}\n\n\tprivate final FilePickerSupport support;\n\n\tFilePicker(FilePickerSupport _support) {\n\t\tsupport = _support;\n\t}\n\n\tvoid pick() {\n\t\tsupport.performPickFor(this);\n\t}\n\n\tabstract void onPick(Uri uri);\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/LinkInfo.java",
    "content": "package com.artifex.mupdfdemo;\n\nimport android.graphics.RectF;\n\npublic class LinkInfo {\n\tfinal public RectF rect;\n\n\tpublic LinkInfo(float l, float t, float r, float b) {\n\t\trect = new RectF(l, t, r, b);\n\t}\n\n\tpublic void acceptVisitor(LinkInfoVisitor visitor) {\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/LinkInfoExternal.java",
    "content": "package com.artifex.mupdfdemo;\n\npublic class LinkInfoExternal extends LinkInfo {\n\tfinal public String url;\n\n\tpublic LinkInfoExternal(float l, float t, float r, float b, String u) {\n\t\tsuper(l, t, r, b);\n\t\turl = u;\n\t}\n\n\tpublic void acceptVisitor(LinkInfoVisitor visitor) {\n\t\tvisitor.visitExternal(this);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/LinkInfoInternal.java",
    "content": "package com.artifex.mupdfdemo;\n\npublic class LinkInfoInternal extends LinkInfo {\n\tfinal public int pageNumber;\n\n\tpublic LinkInfoInternal(float l, float t, float r, float b, int p) {\n\t\tsuper(l, t, r, b);\n\t\tpageNumber = p;\n\t}\n\n\tpublic void acceptVisitor(LinkInfoVisitor visitor) {\n\t\tvisitor.visitInternal(this);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/LinkInfoRemote.java",
    "content": "package com.artifex.mupdfdemo;\n\npublic class LinkInfoRemote extends LinkInfo {\n\tfinal public String fileSpec;\n\tfinal public int pageNumber;\n\tfinal public boolean newWindow;\n\n\tpublic LinkInfoRemote(float l, float t, float r, float b, String f, int p, boolean n) {\n\t\tsuper(l, t, r, b);\n\t\tfileSpec = f;\n\t\tpageNumber = p;\n\t\tnewWindow = n;\n\t}\n\n\tpublic void acceptVisitor(LinkInfoVisitor visitor) {\n\t\tvisitor.visitRemote(this);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/LinkInfoVisitor.java",
    "content": "package com.artifex.mupdfdemo;\n\nabstract public class LinkInfoVisitor {\n\tpublic abstract void visitInternal(LinkInfoInternal li);\n\tpublic abstract void visitExternal(LinkInfoExternal li);\n\tpublic abstract void visitRemote(LinkInfoRemote li);\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/MuPDFActivity.java",
    "content": "package com.artifex.mupdfdemo;\n\nimport java.io.InputStream;\nimport java.util.concurrent.Executor;\n\nimport com.artifex.mupdfdemo.ReaderView.ViewMapper;\n\nimport android.app.Activity;\nimport android.app.AlertDialog;\nimport android.content.Context;\nimport android.content.DialogInterface;\nimport android.content.DialogInterface.OnCancelListener;\nimport android.content.Intent;\nimport android.content.SharedPreferences;\nimport android.content.res.Resources;\nimport android.database.Cursor;\nimport android.graphics.Color;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.text.Editable;\nimport android.text.TextWatcher;\nimport android.text.method.PasswordTransformationMethod;\nimport android.view.KeyEvent;\nimport android.view.Menu;\nimport android.view.View;\nimport android.view.animation.Animation;\nimport android.view.animation.TranslateAnimation;\nimport android.view.inputmethod.EditorInfo;\nimport android.view.inputmethod.InputMethodManager;\nimport android.widget.EditText;\nimport android.widget.ImageButton;\nimport android.widget.RelativeLayout;\nimport android.widget.SeekBar;\nimport android.widget.TextView;\nimport android.widget.ViewAnimator;\n\nclass ThreadPerTaskExecutor implements Executor {\n    public void execute(Runnable r) {\n        new Thread(r).start();\n    }\n}\n\npublic class MuPDFActivity extends Activity implements FilePicker.FilePickerSupport\n{\n\t/* The core rendering instance */\n\tenum TopBarMode {Main, Search, Annot, Delete, More, Accept};\n\tenum AcceptMode {Highlight, Underline, StrikeOut, Ink, CopyText};\n\n\tprivate final int    OUTLINE_REQUEST=0;\n\tprivate final int    PRINT_REQUEST=1;\n\tprivate final int    FILEPICK_REQUEST=2;\n\tprivate MuPDFCore    core;\n\tprivate String       mFileName;\n\tprivate MuPDFReaderView mDocView;\n\tprivate View         mButtonsView;\n\tprivate boolean      mButtonsVisible;\n\tprivate EditText     mPasswordView;\n\tprivate TextView     mFilenameView;\n\tprivate SeekBar      mPageSlider;\n\tprivate int          mPageSliderRes;\n\tprivate TextView     mPageNumberView;\n\tprivate TextView     mInfoView;\n\tprivate ImageButton  mSearchButton;\n\tprivate ImageButton  mReflowButton;\n\tprivate ImageButton  mOutlineButton;\n\tprivate ImageButton\tmMoreButton;\n\tprivate TextView     mAnnotTypeText;\n\tprivate ImageButton mAnnotButton;\n\tprivate ViewAnimator mTopBarSwitcher;\n\tprivate ImageButton  mLinkButton;\n\tprivate TopBarMode   mTopBarMode = TopBarMode.Main;\n\tprivate AcceptMode   mAcceptMode;\n\tprivate ImageButton  mSearchBack;\n\tprivate ImageButton  mSearchFwd;\n\tprivate EditText     mSearchText;\n\tprivate SearchTask   mSearchTask;\n\tprivate AlertDialog.Builder mAlertBuilder;\n\tprivate boolean    mLinkHighlight = false;\n\tprivate final Handler mHandler = new Handler();\n\tprivate boolean mAlertsActive= false;\n\tprivate boolean mReflow = false;\n\tprivate AsyncTask<Void,Void,MuPDFAlert> mAlertTask;\n\tprivate AlertDialog mAlertDialog;\n\tprivate FilePicker mFilePicker;\n\n\tpublic void createAlertWaiter() {\n\t\tmAlertsActive = true;\n\t\t// All mupdf library calls are performed on asynchronous tasks to avoid stalling\n\t\t// the UI. Some calls can lead to javascript-invoked requests to display an\n\t\t// alert dialog and collect a reply from the user. The task has to be blocked\n\t\t// until the user's reply is received. This method creates an asynchronous task,\n\t\t// the purpose of which is to wait of these requests and produce the dialog\n\t\t// in response, while leaving the core blocked. When the dialog receives the\n\t\t// user's response, it is sent to the core via replyToAlert, unblocking it.\n\t\t// Another alert-waiting task is then created to pick up the next alert.\n\t\tif (mAlertTask != null) {\n\t\t\tmAlertTask.cancel(true);\n\t\t\tmAlertTask = null;\n\t\t}\n\t\tif (mAlertDialog != null) {\n\t\t\tmAlertDialog.cancel();\n\t\t\tmAlertDialog = null;\n\t\t}\n\t\tmAlertTask = new AsyncTask<Void,Void,MuPDFAlert>() {\n\n\t\t\t@Override\n\t\t\tprotected MuPDFAlert doInBackground(Void... arg0) {\n\t\t\t\tif (!mAlertsActive)\n\t\t\t\t\treturn null;\n\n\t\t\t\treturn core.waitForAlert();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected void onPostExecute(final MuPDFAlert result) {\n\t\t\t\t// core.waitForAlert may return null when shutting down\n\t\t\t\tif (result == null)\n\t\t\t\t\treturn;\n\t\t\t\tfinal MuPDFAlert.ButtonPressed pressed[] = new MuPDFAlert.ButtonPressed[3];\n\t\t\t\tfor(int i = 0; i < 3; i++)\n\t\t\t\t\tpressed[i] = MuPDFAlert.ButtonPressed.None;\n\t\t\t\tDialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {\n\t\t\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\t\t\tmAlertDialog = null;\n\t\t\t\t\t\tif (mAlertsActive) {\n\t\t\t\t\t\t\tint index = 0;\n\t\t\t\t\t\t\tswitch (which) {\n\t\t\t\t\t\t\tcase AlertDialog.BUTTON1: index=0; break;\n\t\t\t\t\t\t\tcase AlertDialog.BUTTON2: index=1; break;\n\t\t\t\t\t\t\tcase AlertDialog.BUTTON3: index=2; break;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tresult.buttonPressed = pressed[index];\n\t\t\t\t\t\t\t// Send the user's response to the core, so that it can\n\t\t\t\t\t\t\t// continue processing.\n\t\t\t\t\t\t\tcore.replyToAlert(result);\n\t\t\t\t\t\t\t// Create another alert-waiter to pick up the next alert.\n\t\t\t\t\t\t\tcreateAlertWaiter();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tmAlertDialog = mAlertBuilder.create();\n\t\t\t\tmAlertDialog.setTitle(result.title);\n\t\t\t\tmAlertDialog.setMessage(result.message);\n\t\t\t\tswitch (result.iconType)\n\t\t\t\t{\n\t\t\t\tcase Error:\n\t\t\t\t\tbreak;\n\t\t\t\tcase Warning:\n\t\t\t\t\tbreak;\n\t\t\t\tcase Question:\n\t\t\t\t\tbreak;\n\t\t\t\tcase Status:\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tswitch (result.buttonGroupType)\n\t\t\t\t{\n\t\t\t\tcase OkCancel:\n\t\t\t\t\tmAlertDialog.setButton(AlertDialog.BUTTON2, getString(R.string.cancel), listener);\n\t\t\t\t\tpressed[1] = MuPDFAlert.ButtonPressed.Cancel;\n\t\t\t\tcase Ok:\n\t\t\t\t\tmAlertDialog.setButton(AlertDialog.BUTTON1, getString(R.string.okay), listener);\n\t\t\t\t\tpressed[0] = MuPDFAlert.ButtonPressed.Ok;\n\t\t\t\t\tbreak;\n\t\t\t\tcase YesNoCancel:\n\t\t\t\t\tmAlertDialog.setButton(AlertDialog.BUTTON3, getString(R.string.cancel), listener);\n\t\t\t\t\tpressed[2] = MuPDFAlert.ButtonPressed.Cancel;\n\t\t\t\tcase YesNo:\n\t\t\t\t\tmAlertDialog.setButton(AlertDialog.BUTTON1, getString(R.string.yes), listener);\n\t\t\t\t\tpressed[0] = MuPDFAlert.ButtonPressed.Yes;\n\t\t\t\t\tmAlertDialog.setButton(AlertDialog.BUTTON2, getString(R.string.no), listener);\n\t\t\t\t\tpressed[1] = MuPDFAlert.ButtonPressed.No;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tmAlertDialog.setOnCancelListener(new OnCancelListener() {\n\t\t\t\t\tpublic void onCancel(DialogInterface dialog) {\n\t\t\t\t\t\tmAlertDialog = null;\n\t\t\t\t\t\tif (mAlertsActive) {\n\t\t\t\t\t\t\tresult.buttonPressed = MuPDFAlert.ButtonPressed.None;\n\t\t\t\t\t\t\tcore.replyToAlert(result);\n\t\t\t\t\t\t\tcreateAlertWaiter();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tmAlertDialog.show();\n\t\t\t}\n\t\t};\n\n\t\tmAlertTask.executeOnExecutor(new ThreadPerTaskExecutor());\n\t}\n\n\tpublic void destroyAlertWaiter() {\n\t\tmAlertsActive = false;\n\t\tif (mAlertDialog != null) {\n\t\t\tmAlertDialog.cancel();\n\t\t\tmAlertDialog = null;\n\t\t}\n\t\tif (mAlertTask != null) {\n\t\t\tmAlertTask.cancel(true);\n\t\t\tmAlertTask = null;\n\t\t}\n\t}\n\n\tprivate MuPDFCore openFile(String path)\n\t{\n\t\tint lastSlashPos = path.lastIndexOf('/');\n\t\tmFileName = new String(lastSlashPos == -1\n\t\t\t\t\t? path\n\t\t\t\t\t: path.substring(lastSlashPos+1));\n\t\tSystem.out.println(\"Trying to open \"+path);\n\t\ttry\n\t\t{\n\t\t\tcore = new MuPDFCore(this, path);\n\t\t\t// New file: drop the old outline data\n\t\t\tOutlineActivityData.set(null);\n\t\t}\n\t\tcatch (Exception e)\n\t\t{\n\t\t\tSystem.out.println(e);\n\t\t\treturn null;\n\t\t}\n\t\treturn core;\n\t}\n\n\tprivate MuPDFCore openBuffer(byte buffer[], String magic)\n\t{\n\t\tSystem.out.println(\"Trying to open byte buffer\");\n\t\ttry\n\t\t{\n\t\t\tcore = new MuPDFCore(this, buffer, magic);\n\t\t\t// New file: drop the old outline data\n\t\t\tOutlineActivityData.set(null);\n\t\t}\n\t\tcatch (Exception e)\n\t\t{\n\t\t\tSystem.out.println(e);\n\t\t\treturn null;\n\t\t}\n\t\treturn core;\n\t}\n\n\t/** Called when the activity is first created. */\n\t@Override\n\tpublic void onCreate(Bundle savedInstanceState)\n\t{\n\t\tsuper.onCreate(savedInstanceState);\n\n\t\tmAlertBuilder = new AlertDialog.Builder(this);\n\n\t\tif (core == null) {\n\t\t\tcore = (MuPDFCore)getLastNonConfigurationInstance();\n\n\t\t\tif (savedInstanceState != null && savedInstanceState.containsKey(\"FileName\")) {\n\t\t\t\tmFileName = savedInstanceState.getString(\"FileName\");\n\t\t\t}\n\t\t}\n\t\tif (core == null) {\n\t\t\tIntent intent = getIntent();\n\t\t\tbyte buffer[] = null;\n\t\t\tif (Intent.ACTION_VIEW.equals(intent.getAction())) {\n\t\t\t\tUri uri = intent.getData();\n\t\t\t\tSystem.out.println(\"URI to open is: \" + uri);\n\t\t\t\tif (uri.toString().startsWith(\"content://\")) {\n\t\t\t\t\tString reason = null;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tInputStream is = getContentResolver().openInputStream(uri);\n\t\t\t\t\t\tint len = is.available();\n\t\t\t\t\t\tbuffer = new byte[len];\n\t\t\t\t\t\tis.read(buffer, 0, len);\n\t\t\t\t\t\tis.close();\n\t\t\t\t\t}\n\t\t\t\t\tcatch (OutOfMemoryError e) {\n\t\t\t\t\t\tSystem.out.println(\"Out of memory during buffer reading\");\n\t\t\t\t\t\treason = e.toString();\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Exception e) {\n\t\t\t\t\t\tSystem.out.println(\"Exception reading from stream: \" + e);\n\n\t\t\t\t\t\t// Handle view requests from the Transformer Prime's file manager\n\t\t\t\t\t\t// Hopefully other file managers will use this same scheme, if not\n\t\t\t\t\t\t// using explicit paths.\n\t\t\t\t\t\t// I'm hoping that this case below is no longer needed...but it's\n\t\t\t\t\t\t// hard to test as the file manager seems to have changed in 4.x.\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tCursor cursor = getContentResolver().query(uri, new String[]{\"_data\"}, null, null, null);\n\t\t\t\t\t\t\tif (cursor.moveToFirst()) {\n\t\t\t\t\t\t\t\tString str = cursor.getString(0);\n\t\t\t\t\t\t\t\tif (str == null) {\n\t\t\t\t\t\t\t\t\treason = \"Couldn't parse data in intent\";\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\turi = Uri.parse(str);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (Exception e2) {\n\t\t\t\t\t\t\tSystem.out.println(\"Exception in Transformer Prime file manager code: \" + e2);\n\t\t\t\t\t\t\treason = e2.toString();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (reason != null) {\n\t\t\t\t\t\tbuffer = null;\n\t\t\t\t\t\tResources res = getResources();\n\t\t\t\t\t\tAlertDialog alert = mAlertBuilder.create();\n\t\t\t\t\t\tsetTitle(String.format(res.getString(R.string.cannot_open_document_Reason), reason));\n\t\t\t\t\t\talert.setButton(AlertDialog.BUTTON_POSITIVE, getString(R.string.dismiss),\n\t\t\t\t\t\t\t\tnew DialogInterface.OnClickListener() {\n\t\t\t\t\t\t\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\t\t\t\t\t\t\tfinish();\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\talert.show();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (buffer != null) {\n\t\t\t\t\tcore = openBuffer(buffer, intent.getType());\n\t\t\t\t} else {\n\t\t\t\t\tcore = openFile(Uri.decode(uri.getEncodedPath()));\n\t\t\t\t}\n\t\t\t\tSearchTaskResult.set(null);\n\t\t\t}\n\t\t\tif (core != null && core.needsPassword()) {\n\t\t\t\trequestPassword(savedInstanceState);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (core != null && core.countPages() == 0)\n\t\t\t{\n\t\t\t\tcore = null;\n\t\t\t}\n\t\t}\n\t\tif (core == null)\n\t\t{\n\t\t\tAlertDialog alert = mAlertBuilder.create();\n\t\t\talert.setTitle(R.string.cannot_open_document);\n\t\t\talert.setButton(AlertDialog.BUTTON_POSITIVE, getString(R.string.dismiss),\n\t\t\t\t\tnew DialogInterface.OnClickListener() {\n\t\t\t\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\t\t\t\tfinish();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\talert.setOnCancelListener(new OnCancelListener() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void onCancel(DialogInterface dialog) {\n\t\t\t\t\tfinish();\n\t\t\t\t}\n\t\t\t});\n\t\t\talert.show();\n\t\t\treturn;\n\t\t}\n\n\t\tcreateUI(savedInstanceState);\n\t}\n\n\tpublic void requestPassword(final Bundle savedInstanceState) {\n\t\tmPasswordView = new EditText(this);\n\t\tmPasswordView.setInputType(EditorInfo.TYPE_TEXT_VARIATION_PASSWORD);\n\t\tmPasswordView.setTransformationMethod(new PasswordTransformationMethod());\n\n\t\tAlertDialog alert = mAlertBuilder.create();\n\t\talert.setTitle(R.string.enter_password);\n\t\talert.setView(mPasswordView);\n\t\talert.setButton(AlertDialog.BUTTON_POSITIVE, getString(R.string.okay),\n\t\t\t\tnew DialogInterface.OnClickListener() {\n\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\tif (core.authenticatePassword(mPasswordView.getText().toString())) {\n\t\t\t\t\tcreateUI(savedInstanceState);\n\t\t\t\t} else {\n\t\t\t\t\trequestPassword(savedInstanceState);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\talert.setButton(AlertDialog.BUTTON_NEGATIVE, getString(R.string.cancel),\n\t\t\t\tnew DialogInterface.OnClickListener() {\n\n\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\tfinish();\n\t\t\t}\n\t\t});\n\t\talert.show();\n\t}\n\n\tpublic void createUI(Bundle savedInstanceState) {\n\t\tif (core == null)\n\t\t\treturn;\n\n\t\t// Now create the UI.\n\t\t// First create the document view\n\t\tmDocView = new MuPDFReaderView(this) {\n\t\t\t@Override\n\t\t\tprotected void onMoveToChild(int i) {\n\t\t\t\tif (core == null)\n\t\t\t\t\treturn;\n\t\t\t\tmPageNumberView.setText(String.format(\"%d / %d\", i + 1,\n\t\t\t\t\t\tcore.countPages()));\n\t\t\t\tmPageSlider.setMax((core.countPages() - 1) * mPageSliderRes);\n\t\t\t\tmPageSlider.setProgress(i * mPageSliderRes);\n\t\t\t\tsuper.onMoveToChild(i);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected void onTapMainDocArea() {\n\t\t\t\tif (!mButtonsVisible) {\n\t\t\t\t\tshowButtons();\n\t\t\t\t} else {\n\t\t\t\t\tif (mTopBarMode == TopBarMode.Main)\n\t\t\t\t\t\thideButtons();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected void onDocMotion() {\n\t\t\t\thideButtons();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected void onHit(Hit item) {\n\t\t\t\tswitch (mTopBarMode) {\n\t\t\t\tcase Annot:\n\t\t\t\t\tif (item == Hit.Annotation) {\n\t\t\t\t\t\tshowButtons();\n\t\t\t\t\t\tmTopBarMode = TopBarMode.Delete;\n\t\t\t\t\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase Delete:\n\t\t\t\t\tmTopBarMode = TopBarMode.Annot;\n\t\t\t\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t\t\t\t// fall through\n\t\t\t\tdefault:\n\t\t\t\t\t// Not in annotation editing mode, but the pageview will\n\t\t\t\t\t// still select and highlight hit annotations, so\n\t\t\t\t\t// deselect just in case.\n\t\t\t\t\tMuPDFView pageView = (MuPDFView) mDocView.getDisplayedView();\n\t\t\t\t\tif (pageView != null)\n\t\t\t\t\t\tpageView.deselectAnnotation();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t\tmDocView.setAdapter(new MuPDFPageAdapter(this, this, core));\n\n\t\tmSearchTask = new SearchTask(this, core) {\n\t\t\t@Override\n\t\t\tprotected void onTextFound(SearchTaskResult result) {\n\t\t\t\tSearchTaskResult.set(result);\n\t\t\t\t// Ask the ReaderView to move to the resulting page\n\t\t\t\tmDocView.setDisplayedViewIndex(result.pageNumber);\n\t\t\t\t// Make the ReaderView act on the change to SearchTaskResult\n\t\t\t\t// via overridden onChildSetup method.\n\t\t\t\tmDocView.resetupChildren();\n\t\t\t}\n\t\t};\n\n\t\t// Make the buttons overlay, and store all its\n\t\t// controls in variables\n\t\tmakeButtonsView();\n\n\t\t// Set up the page slider\n\t\tint smax = Math.max(core.countPages()-1,1);\n\t\tmPageSliderRes = ((10 + smax - 1)/smax) * 2;\n\n\t\t// Set the file-name text\n\t\tmFilenameView.setText(mFileName);\n\n\t\t// Activate the seekbar\n\t\tmPageSlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {\n\t\t\tpublic void onStopTrackingTouch(SeekBar seekBar) {\n\t\t\t\tmDocView.setDisplayedViewIndex((seekBar.getProgress()+mPageSliderRes/2)/mPageSliderRes);\n\t\t\t}\n\n\t\t\tpublic void onStartTrackingTouch(SeekBar seekBar) {}\n\n\t\t\tpublic void onProgressChanged(SeekBar seekBar, int progress,\n\t\t\t\t\tboolean fromUser) {\n\t\t\t\tupdatePageNumView((progress+mPageSliderRes/2)/mPageSliderRes);\n\t\t\t}\n\t\t});\n\n\t\t// Activate the search-preparing button\n\t\tmSearchButton.setOnClickListener(new View.OnClickListener() {\n\t\t\tpublic void onClick(View v) {\n\t\t\t\tsearchModeOn();\n\t\t\t}\n\t\t});\n\n\t\t// Activate the reflow button\n\t\tmReflowButton.setOnClickListener(new View.OnClickListener() {\n\t\t\tpublic void onClick(View v) {\n\t\t\t\ttoggleReflow();\n\t\t\t}\n\t\t});\n\n\t\tif (core.fileFormat().startsWith(\"PDF\") && core.isUnencryptedPDF() && !core.wasOpenedFromBuffer())\n\t\t{\n\t\t\tmAnnotButton.setOnClickListener(new View.OnClickListener() {\n\t\t\t\tpublic void onClick(View v) {\n\t\t\t\t\tmTopBarMode = TopBarMode.Annot;\n\t\t\t\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmAnnotButton.setVisibility(View.GONE);\n\t\t}\n\n\t\t// Search invoking buttons are disabled while there is no text specified\n\t\tmSearchBack.setEnabled(false);\n\t\tmSearchFwd.setEnabled(false);\n\t\tmSearchBack.setColorFilter(Color.argb(255, 128, 128, 128));\n\t\tmSearchFwd.setColorFilter(Color.argb(255, 128, 128, 128));\n\n\t\t// React to interaction with the text widget\n\t\tmSearchText.addTextChangedListener(new TextWatcher() {\n\n\t\t\tpublic void afterTextChanged(Editable s) {\n\t\t\t\tboolean haveText = s.toString().length() > 0;\n\t\t\t\tsetButtonEnabled(mSearchBack, haveText);\n\t\t\t\tsetButtonEnabled(mSearchFwd, haveText);\n\n\t\t\t\t// Remove any previous search results\n\t\t\t\tif (SearchTaskResult.get() != null && !mSearchText.getText().toString().equals(SearchTaskResult.get().txt)) {\n\t\t\t\t\tSearchTaskResult.set(null);\n\t\t\t\t\tmDocView.resetupChildren();\n\t\t\t\t}\n\t\t\t}\n\t\t\tpublic void beforeTextChanged(CharSequence s, int start, int count,\n\t\t\t\t\tint after) {}\n\t\t\tpublic void onTextChanged(CharSequence s, int start, int before,\n\t\t\t\t\tint count) {}\n\t\t});\n\n\t\t//React to Done button on keyboard\n\t\tmSearchText.setOnEditorActionListener(new TextView.OnEditorActionListener() {\n\t\t\tpublic boolean onEditorAction(TextView v, int actionId, KeyEvent event) {\n\t\t\t\tif (actionId == EditorInfo.IME_ACTION_DONE)\n\t\t\t\t\tsearch(1);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t});\n\n\t\tmSearchText.setOnKeyListener(new View.OnKeyListener() {\n\t\t\tpublic boolean onKey(View v, int keyCode, KeyEvent event) {\n\t\t\t\tif (event.getAction() == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER)\n\t\t\t\t\tsearch(1);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t});\n\n\t\t// Activate search invoking buttons\n\t\tmSearchBack.setOnClickListener(new View.OnClickListener() {\n\t\t\tpublic void onClick(View v) {\n\t\t\t\tsearch(-1);\n\t\t\t}\n\t\t});\n\t\tmSearchFwd.setOnClickListener(new View.OnClickListener() {\n\t\t\tpublic void onClick(View v) {\n\t\t\t\tsearch(1);\n\t\t\t}\n\t\t});\n\n\t\tmLinkButton.setOnClickListener(new View.OnClickListener() {\n\t\t\tpublic void onClick(View v) {\n\t\t\t\tsetLinkHighlight(!mLinkHighlight);\n\t\t\t}\n\t\t});\n\n\t\tif (core.hasOutline()) {\n\t\t\tmOutlineButton.setOnClickListener(new View.OnClickListener() {\n\t\t\t\tpublic void onClick(View v) {\n\t\t\t\t\tOutlineItem outline[] = core.getOutline();\n\t\t\t\t\tif (outline != null) {\n\t\t\t\t\t\tOutlineActivityData.get().items = outline;\n\t\t\t\t\t\tIntent intent = new Intent(MuPDFActivity.this, OutlineActivity.class);\n\t\t\t\t\t\tstartActivityForResult(intent, OUTLINE_REQUEST);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t} else {\n\t\t\tmOutlineButton.setVisibility(View.GONE);\n\t\t}\n\n\t\t// Reenstate last state if it was recorded\n\t\tSharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);\n\t\tmDocView.setDisplayedViewIndex(prefs.getInt(\"page\"+mFileName, 0));\n\n\t\tif (savedInstanceState == null || !savedInstanceState.getBoolean(\"ButtonsHidden\", false))\n\t\t\tshowButtons();\n\n\t\tif(savedInstanceState != null && savedInstanceState.getBoolean(\"SearchMode\", false))\n\t\t\tsearchModeOn();\n\n\t\tif(savedInstanceState != null && savedInstanceState.getBoolean(\"ReflowMode\", false))\n\t\t\treflowModeSet(true);\n\n\t\t// Stick the document view and the buttons overlay into a parent view\n\t\tRelativeLayout layout = new RelativeLayout(this);\n\t\tlayout.addView(mDocView);\n\t\tlayout.addView(mButtonsView);\n\t\tsetContentView(layout);\n\t}\n\n\t@Override\n\tprotected void onActivityResult(int requestCode, int resultCode, Intent data) {\n\t\tswitch (requestCode) {\n\t\tcase OUTLINE_REQUEST:\n\t\t\tif (resultCode >= 0)\n\t\t\t\tmDocView.setDisplayedViewIndex(resultCode);\n\t\t\tbreak;\n\t\tcase PRINT_REQUEST:\n\t\t\tif (resultCode == RESULT_CANCELED)\n\t\t\t\tshowInfo(getString(R.string.print_failed));\n\t\t\tbreak;\n\t\tcase FILEPICK_REQUEST:\n\t\t\tif (mFilePicker != null && resultCode == RESULT_OK)\n\t\t\t\tmFilePicker.onPick(data.getData());\n\t\t}\n\t\tsuper.onActivityResult(requestCode, resultCode, data);\n\t}\n\n\tpublic Object onRetainNonConfigurationInstance()\n\t{\n\t\tMuPDFCore mycore = core;\n\t\tcore = null;\n\t\treturn mycore;\n\t}\n\n\tprivate void reflowModeSet(boolean reflow)\n\t{\n\t\tmReflow = reflow;\n\t\tmDocView.setAdapter(mReflow ? new MuPDFReflowAdapter(this, core) : new MuPDFPageAdapter(this, this, core));\n\t\tmReflowButton.setColorFilter(mReflow ? Color.argb(0xFF, 172, 114, 37) : Color.argb(0xFF, 255, 255, 255));\n\t\tsetButtonEnabled(mAnnotButton, !reflow);\n\t\tsetButtonEnabled(mSearchButton, !reflow);\n\t\tif (reflow) setLinkHighlight(false);\n\t\tsetButtonEnabled(mLinkButton, !reflow);\n\t\tsetButtonEnabled(mMoreButton, !reflow);\n\t\tmDocView.refresh(mReflow);\n\t}\n\n\tprivate void toggleReflow() {\n\t\treflowModeSet(!mReflow);\n\t\tshowInfo(mReflow ? getString(R.string.entering_reflow_mode) : getString(R.string.leaving_reflow_mode));\n\t}\n\n\t@Override\n\tprotected void onSaveInstanceState(Bundle outState) {\n\t\tsuper.onSaveInstanceState(outState);\n\n\t\tif (mFileName != null && mDocView != null) {\n\t\t\toutState.putString(\"FileName\", mFileName);\n\n\t\t\t// Store current page in the prefs against the file name,\n\t\t\t// so that we can pick it up each time the file is loaded\n\t\t\t// Other info is needed only for screen-orientation change,\n\t\t\t// so it can go in the bundle\n\t\t\tSharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);\n\t\t\tSharedPreferences.Editor edit = prefs.edit();\n\t\t\tedit.putInt(\"page\"+mFileName, mDocView.getDisplayedViewIndex());\n\t\t\tedit.commit();\n\t\t}\n\n\t\tif (!mButtonsVisible)\n\t\t\toutState.putBoolean(\"ButtonsHidden\", true);\n\n\t\tif (mTopBarMode == TopBarMode.Search)\n\t\t\toutState.putBoolean(\"SearchMode\", true);\n\n\t\tif (mReflow)\n\t\t\toutState.putBoolean(\"ReflowMode\", true);\n\t}\n\n\t@Override\n\tprotected void onPause() {\n\t\tsuper.onPause();\n\n\t\tif (mSearchTask != null)\n\t\t\tmSearchTask.stop();\n\n\t\tif (mFileName != null && mDocView != null) {\n\t\t\tSharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);\n\t\t\tSharedPreferences.Editor edit = prefs.edit();\n\t\t\tedit.putInt(\"page\"+mFileName, mDocView.getDisplayedViewIndex());\n\t\t\tedit.commit();\n\t\t}\n\t}\n\n\tpublic void onDestroy()\n\t{\n\t\tif (mDocView != null) {\n\t\t\tmDocView.applyToChildren(new ViewMapper() {\n\t\t\t\tvoid applyToView(View view) {\n\t\t\t\t\t((MuPDFView)view).releaseBitmaps();\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\tif (core != null)\n\t\t\tcore.onDestroy();\n\t\tif (mAlertTask != null) {\n\t\t\tmAlertTask.cancel(true);\n\t\t\tmAlertTask = null;\n\t\t}\n\t\tcore = null;\n\t\tsuper.onDestroy();\n\t}\n\n\tprivate void setButtonEnabled(ImageButton button, boolean enabled) {\n\t\tbutton.setEnabled(enabled);\n\t\tbutton.setColorFilter(enabled ? Color.argb(255, 255, 255, 255):Color.argb(255, 128, 128, 128));\n\t}\n\n\tprivate void setLinkHighlight(boolean highlight) {\n\t\tmLinkHighlight = highlight;\n\t\t// LINK_COLOR tint\n\t\tmLinkButton.setColorFilter(highlight ? Color.argb(0xFF, 172, 114, 37) : Color.argb(0xFF, 255, 255, 255));\n\t\t// Inform pages of the change.\n\t\tmDocView.setLinksEnabled(highlight);\n\t}\n\n\tprivate void showButtons() {\n\t\tif (core == null)\n\t\t\treturn;\n\t\tif (!mButtonsVisible) {\n\t\t\tmButtonsVisible = true;\n\t\t\t// Update page number text and slider\n\t\t\tint index = mDocView.getDisplayedViewIndex();\n\t\t\tupdatePageNumView(index);\n\t\t\tmPageSlider.setMax((core.countPages()-1)*mPageSliderRes);\n\t\t\tmPageSlider.setProgress(index*mPageSliderRes);\n\t\t\tif (mTopBarMode == TopBarMode.Search) {\n\t\t\t\tmSearchText.requestFocus();\n\t\t\t\tshowKeyboard();\n\t\t\t}\n\n\t\t\tAnimation anim = new TranslateAnimation(0, 0, -mTopBarSwitcher.getHeight(), 0);\n\t\t\tanim.setDuration(200);\n\t\t\tanim.setAnimationListener(new Animation.AnimationListener() {\n\t\t\t\tpublic void onAnimationStart(Animation animation) {\n\t\t\t\t\tmTopBarSwitcher.setVisibility(View.VISIBLE);\n\t\t\t\t}\n\t\t\t\tpublic void onAnimationRepeat(Animation animation) {}\n\t\t\t\tpublic void onAnimationEnd(Animation animation) {}\n\t\t\t});\n\t\t\tmTopBarSwitcher.startAnimation(anim);\n\n\t\t\tanim = new TranslateAnimation(0, 0, mPageSlider.getHeight(), 0);\n\t\t\tanim.setDuration(200);\n\t\t\tanim.setAnimationListener(new Animation.AnimationListener() {\n\t\t\t\tpublic void onAnimationStart(Animation animation) {\n\t\t\t\t\tmPageSlider.setVisibility(View.VISIBLE);\n\t\t\t\t}\n\t\t\t\tpublic void onAnimationRepeat(Animation animation) {}\n\t\t\t\tpublic void onAnimationEnd(Animation animation) {\n\t\t\t\t\tmPageNumberView.setVisibility(View.VISIBLE);\n\t\t\t\t}\n\t\t\t});\n\t\t\tmPageSlider.startAnimation(anim);\n\t\t}\n\t}\n\n\tprivate void hideButtons() {\n\t\tif (mButtonsVisible) {\n\t\t\tmButtonsVisible = false;\n\t\t\thideKeyboard();\n\n\t\t\tAnimation anim = new TranslateAnimation(0, 0, 0, -mTopBarSwitcher.getHeight());\n\t\t\tanim.setDuration(200);\n\t\t\tanim.setAnimationListener(new Animation.AnimationListener() {\n\t\t\t\tpublic void onAnimationStart(Animation animation) {}\n\t\t\t\tpublic void onAnimationRepeat(Animation animation) {}\n\t\t\t\tpublic void onAnimationEnd(Animation animation) {\n\t\t\t\t\tmTopBarSwitcher.setVisibility(View.INVISIBLE);\n\t\t\t\t}\n\t\t\t});\n\t\t\tmTopBarSwitcher.startAnimation(anim);\n\n\t\t\tanim = new TranslateAnimation(0, 0, 0, mPageSlider.getHeight());\n\t\t\tanim.setDuration(200);\n\t\t\tanim.setAnimationListener(new Animation.AnimationListener() {\n\t\t\t\tpublic void onAnimationStart(Animation animation) {\n\t\t\t\t\tmPageNumberView.setVisibility(View.INVISIBLE);\n\t\t\t\t}\n\t\t\t\tpublic void onAnimationRepeat(Animation animation) {}\n\t\t\t\tpublic void onAnimationEnd(Animation animation) {\n\t\t\t\t\tmPageSlider.setVisibility(View.INVISIBLE);\n\t\t\t\t}\n\t\t\t});\n\t\t\tmPageSlider.startAnimation(anim);\n\t\t}\n\t}\n\n\tprivate void searchModeOn() {\n\t\tif (mTopBarMode != TopBarMode.Search) {\n\t\t\tmTopBarMode = TopBarMode.Search;\n\t\t\t//Focus on EditTextWidget\n\t\t\tmSearchText.requestFocus();\n\t\t\tshowKeyboard();\n\t\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t\t}\n\t}\n\n\tprivate void searchModeOff() {\n\t\tif (mTopBarMode == TopBarMode.Search) {\n\t\t\tmTopBarMode = TopBarMode.Main;\n\t\t\thideKeyboard();\n\t\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t\t\tSearchTaskResult.set(null);\n\t\t\t// Make the ReaderView act on the change to mSearchTaskResult\n\t\t\t// via overridden onChildSetup method.\n\t\t\tmDocView.resetupChildren();\n\t\t}\n\t}\n\n\tprivate void updatePageNumView(int index) {\n\t\tif (core == null)\n\t\t\treturn;\n\t\tmPageNumberView.setText(String.format(\"%d / %d\", index+1, core.countPages()));\n\t}\n\n\tprivate void printDoc() {\n\t\tif (!core.fileFormat().startsWith(\"PDF\")) {\n\t\t\tshowInfo(getString(R.string.format_currently_not_supported));\n\t\t\treturn;\n\t\t}\n\n\t\tIntent myIntent = getIntent();\n\t\tUri docUri = myIntent != null ? myIntent.getData() : null;\n\n\t\tif (docUri == null) {\n\t\t\tshowInfo(getString(R.string.print_failed));\n\t\t}\n\n\t\tif (docUri.getScheme() == null)\n\t\t\tdocUri = Uri.parse(\"file://\"+docUri.toString());\n\n\t\tIntent printIntent = new Intent(this, PrintDialogActivity.class);\n\t\tprintIntent.setDataAndType(docUri, \"aplication/pdf\");\n\t\tprintIntent.putExtra(\"title\", mFileName);\n\t\tstartActivityForResult(printIntent, PRINT_REQUEST);\n\t}\n\n\tprivate void showInfo(String message) {\n\t\tmInfoView.setText(message);\n\n\t\tint currentApiVersion = android.os.Build.VERSION.SDK_INT;\n\t\tif (currentApiVersion >= android.os.Build.VERSION_CODES.HONEYCOMB) {\n\t\t\tSafeAnimatorInflater safe = new SafeAnimatorInflater((Activity)this, R.animator.info, (View)mInfoView);\n\t\t} else {\n\t\t\tmInfoView.setVisibility(View.VISIBLE);\n\t\t\tmHandler.postDelayed(new Runnable() {\n\t\t\t\tpublic void run() {\n\t\t\t\t\tmInfoView.setVisibility(View.INVISIBLE);\n\t\t\t\t}\n\t\t\t}, 500);\n\t\t}\n\t}\n\n\tprivate void makeButtonsView() {\n\t\tmButtonsView = getLayoutInflater().inflate(R.layout.buttons,null);\n\t\tmFilenameView = (TextView)mButtonsView.findViewById(R.id.docNameText);\n\t\tmPageSlider = (SeekBar)mButtonsView.findViewById(R.id.pageSlider);\n\t\tmPageNumberView = (TextView)mButtonsView.findViewById(R.id.pageNumber);\n\t\tmInfoView = (TextView)mButtonsView.findViewById(R.id.info);\n\t\tmSearchButton = (ImageButton)mButtonsView.findViewById(R.id.searchButton);\n\t\tmReflowButton = (ImageButton)mButtonsView.findViewById(R.id.reflowButton);\n\t\tmOutlineButton = (ImageButton)mButtonsView.findViewById(R.id.outlineButton);\n\t\tmAnnotButton = (ImageButton)mButtonsView.findViewById(R.id.editAnnotButton);\n\t\tmAnnotTypeText = (TextView)mButtonsView.findViewById(R.id.annotType);\n\t\tmTopBarSwitcher = (ViewAnimator)mButtonsView.findViewById(R.id.switcher);\n\t\tmSearchBack = (ImageButton)mButtonsView.findViewById(R.id.searchBack);\n\t\tmSearchFwd = (ImageButton)mButtonsView.findViewById(R.id.searchForward);\n\t\tmSearchText = (EditText)mButtonsView.findViewById(R.id.searchText);\n\t\tmLinkButton = (ImageButton)mButtonsView.findViewById(R.id.linkButton);\n\t\tmMoreButton = (ImageButton)mButtonsView.findViewById(R.id.moreButton);\n\t\tmTopBarSwitcher.setVisibility(View.INVISIBLE);\n\t\tmPageNumberView.setVisibility(View.INVISIBLE);\n\t\tmInfoView.setVisibility(View.INVISIBLE);\n\t\tmPageSlider.setVisibility(View.INVISIBLE);\n\t}\n\n\tpublic void OnMoreButtonClick(View v) {\n\t\tmTopBarMode = TopBarMode.More;\n\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t}\n\n\tpublic void OnCancelMoreButtonClick(View v) {\n\t\tmTopBarMode = TopBarMode.Main;\n\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t}\n\n\tpublic void OnPrintButtonClick(View v) {\n\t\tprintDoc();\n\t}\n\n\tpublic void OnCopyTextButtonClick(View v) {\n\t\tmTopBarMode = TopBarMode.Accept;\n\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t\tmAcceptMode = AcceptMode.CopyText;\n\t\tmDocView.setMode(MuPDFReaderView.Mode.Selecting);\n\t\tmAnnotTypeText.setText(getString(R.string.copy_text));\n\t\tshowInfo(getString(R.string.select_text));\n\t}\n\n\tpublic void OnEditAnnotButtonClick(View v) {\n\t\tmTopBarMode = TopBarMode.Annot;\n\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t}\n\n\tpublic void OnCancelAnnotButtonClick(View v) {\n\t\tmTopBarMode = TopBarMode.More;\n\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t}\n\n\tpublic void OnHighlightButtonClick(View v) {\n\t\tmTopBarMode = TopBarMode.Accept;\n\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t\tmAcceptMode = AcceptMode.Highlight;\n\t\tmDocView.setMode(MuPDFReaderView.Mode.Selecting);\n\t\tmAnnotTypeText.setText(R.string.highlight);\n\t\tshowInfo(getString(R.string.select_text));\n\t}\n\n\tpublic void OnUnderlineButtonClick(View v) {\n\t\tmTopBarMode = TopBarMode.Accept;\n\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t\tmAcceptMode = AcceptMode.Underline;\n\t\tmDocView.setMode(MuPDFReaderView.Mode.Selecting);\n\t\tmAnnotTypeText.setText(R.string.underline);\n\t\tshowInfo(getString(R.string.select_text));\n\t}\n\n\tpublic void OnStrikeOutButtonClick(View v) {\n\t\tmTopBarMode = TopBarMode.Accept;\n\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t\tmAcceptMode = AcceptMode.StrikeOut;\n\t\tmDocView.setMode(MuPDFReaderView.Mode.Selecting);\n\t\tmAnnotTypeText.setText(R.string.strike_out);\n\t\tshowInfo(getString(R.string.select_text));\n\t}\n\n\tpublic void OnInkButtonClick(View v) {\n\t\tmTopBarMode = TopBarMode.Accept;\n\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t\tmAcceptMode = AcceptMode.Ink;\n\t\tmDocView.setMode(MuPDFReaderView.Mode.Drawing);\n\t\tmAnnotTypeText.setText(R.string.ink);\n\t\tshowInfo(getString(R.string.draw_annotation));\n\t}\n\n\tpublic void OnCancelAcceptButtonClick(View v) {\n\t\tMuPDFView pageView = (MuPDFView) mDocView.getDisplayedView();\n\t\tif (pageView != null) {\n\t\t\tpageView.deselectText();\n\t\t\tpageView.cancelDraw();\n\t\t}\n\t\tmDocView.setMode(MuPDFReaderView.Mode.Viewing);\n\t\tswitch (mAcceptMode) {\n\t\tcase CopyText:\n\t\t\tmTopBarMode = TopBarMode.More;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tmTopBarMode = TopBarMode.Annot;\n\t\t\tbreak;\n\t\t}\n\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t}\n\n\tpublic void OnAcceptButtonClick(View v) {\n\t\tMuPDFView pageView = (MuPDFView) mDocView.getDisplayedView();\n\t\tboolean success = false;\n\t\tswitch (mAcceptMode) {\n\t\tcase CopyText:\n\t\t\tif (pageView != null)\n\t\t\t\tsuccess = pageView.copySelection();\n\t\t\tmTopBarMode = TopBarMode.More;\n\t\t\tshowInfo(success?getString(R.string.copied_to_clipboard):getString(R.string.no_text_selected));\n\t\t\tbreak;\n\n\t\tcase Highlight:\n\t\t\tif (pageView != null)\n\t\t\t\tsuccess = pageView.markupSelection(Annotation.Type.HIGHLIGHT);\n\t\t\tmTopBarMode = TopBarMode.Annot;\n\t\t\tif (!success)\n\t\t\t\tshowInfo(getString(R.string.no_text_selected));\n\t\t\tbreak;\n\n\t\tcase Underline:\n\t\t\tif (pageView != null)\n\t\t\t\tsuccess = pageView.markupSelection(Annotation.Type.UNDERLINE);\n\t\t\tmTopBarMode = TopBarMode.Annot;\n\t\t\tif (!success)\n\t\t\t\tshowInfo(getString(R.string.no_text_selected));\n\t\t\tbreak;\n\n\t\tcase StrikeOut:\n\t\t\tif (pageView != null)\n\t\t\t\tsuccess = pageView.markupSelection(Annotation.Type.STRIKEOUT);\n\t\t\tmTopBarMode = TopBarMode.Annot;\n\t\t\tif (!success)\n\t\t\t\tshowInfo(getString(R.string.no_text_selected));\n\t\t\tbreak;\n\n\t\tcase Ink:\n\t\t\tif (pageView != null)\n\t\t\t\tsuccess = pageView.saveDraw();\n\t\t\tmTopBarMode = TopBarMode.Annot;\n\t\t\tif (!success)\n\t\t\t\tshowInfo(getString(R.string.nothing_to_save));\n\t\t\tbreak;\n\t\t}\n\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t\tmDocView.setMode(MuPDFReaderView.Mode.Viewing);\n\t}\n\n\tpublic void OnCancelSearchButtonClick(View v) {\n\t\tsearchModeOff();\n\t}\n\n\tpublic void OnDeleteButtonClick(View v) {\n\t\tMuPDFView pageView = (MuPDFView) mDocView.getDisplayedView();\n\t\tif (pageView != null)\n\t\t\tpageView.deleteSelectedAnnotation();\n\t\tmTopBarMode = TopBarMode.Annot;\n\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t}\n\n\tpublic void OnCancelDeleteButtonClick(View v) {\n\t\tMuPDFView pageView = (MuPDFView) mDocView.getDisplayedView();\n\t\tif (pageView != null)\n\t\t\tpageView.deselectAnnotation();\n\t\tmTopBarMode = TopBarMode.Annot;\n\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t}\n\n\tprivate void showKeyboard() {\n\t\tInputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);\n\t\tif (imm != null)\n\t\t\timm.showSoftInput(mSearchText, 0);\n\t}\n\n\tprivate void hideKeyboard() {\n\t\tInputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);\n\t\tif (imm != null)\n\t\t\timm.hideSoftInputFromWindow(mSearchText.getWindowToken(), 0);\n\t}\n\n\tprivate void search(int direction) {\n\t\thideKeyboard();\n\t\tint displayPage = mDocView.getDisplayedViewIndex();\n\t\tSearchTaskResult r = SearchTaskResult.get();\n\t\tint searchPage = r != null ? r.pageNumber : -1;\n\t\tmSearchTask.go(mSearchText.getText().toString(), direction, displayPage, searchPage);\n\t}\n\n\t@Override\n\tpublic boolean onSearchRequested() {\n\t\tif (mButtonsVisible && mTopBarMode == TopBarMode.Search) {\n\t\t\thideButtons();\n\t\t} else {\n\t\t\tshowButtons();\n\t\t\tsearchModeOn();\n\t\t}\n\t\treturn super.onSearchRequested();\n\t}\n\n\t@Override\n\tpublic boolean onPrepareOptionsMenu(Menu menu) {\n\t\tif (mButtonsVisible && mTopBarMode != TopBarMode.Search) {\n\t\t\thideButtons();\n\t\t} else {\n\t\t\tshowButtons();\n\t\t\tsearchModeOff();\n\t\t}\n\t\treturn super.onPrepareOptionsMenu(menu);\n\t}\n\n\t@Override\n\tprotected void onStart() {\n\t\tif (core != null)\n\t\t{\n\t\t\tcore.startAlerts();\n\t\t\tcreateAlertWaiter();\n\t\t}\n\n\t\tsuper.onStart();\n\t}\n\n\t@Override\n\tprotected void onStop() {\n\t\tif (core != null)\n\t\t{\n\t\t\tdestroyAlertWaiter();\n\t\t\tcore.stopAlerts();\n\t\t}\n\n\t\tsuper.onStop();\n\t}\n\n\t@Override\n\tpublic void onBackPressed() {\n\t\tif (core != null && core.hasChanges()) {\n\t\t\tDialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {\n\t\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\t\tif (which == AlertDialog.BUTTON_POSITIVE)\n\t\t\t\t\t\tcore.save();\n\n\t\t\t\t\tfinish();\n\t\t\t\t}\n\t\t\t};\n\t\t\tAlertDialog alert = mAlertBuilder.create();\n\t\t\talert.setTitle(\"MuPDF\");\n\t\t\talert.setMessage(getString(R.string.document_has_changes_save_them_));\n\t\t\talert.setButton(AlertDialog.BUTTON_POSITIVE, getString(R.string.yes), listener);\n\t\t\talert.setButton(AlertDialog.BUTTON_NEGATIVE, getString(R.string.no), listener);\n\t\t\talert.show();\n\t\t} else {\n\t\t\tsuper.onBackPressed();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void performPickFor(FilePicker picker) {\n\t\tmFilePicker = picker;\n\t\tIntent intent = new Intent(this, ChoosePDFActivity.class);\n\t\tintent.setAction(ChoosePDFActivity.PICK_KEY_FILE);\n\t\tstartActivityForResult(intent, FILEPICK_REQUEST);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/MuPDFAlert.java",
    "content": "package com.artifex.mupdfdemo;\n\npublic class MuPDFAlert {\n\tpublic enum IconType {Error,Warning,Question,Status};\n\tpublic enum ButtonPressed {None,Ok,Cancel,No,Yes};\n\tpublic enum ButtonGroupType {Ok,OkCancel,YesNo,YesNoCancel};\n\n\tpublic final String message;\n\tpublic final IconType iconType;\n\tpublic final ButtonGroupType buttonGroupType;\n\tpublic final String title;\n\tpublic ButtonPressed buttonPressed;\n\n\tMuPDFAlert(String aMessage, IconType aIconType, ButtonGroupType aButtonGroupType, String aTitle, ButtonPressed aButtonPressed) {\n\t\tmessage = aMessage;\n\t\ticonType = aIconType;\n\t\tbuttonGroupType = aButtonGroupType;\n\t\ttitle = aTitle;\n\t\tbuttonPressed = aButtonPressed;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/MuPDFAlertInternal.java",
    "content": "package com.artifex.mupdfdemo;\n\n// Version of MuPDFAlert without enums to simplify JNI\npublic class MuPDFAlertInternal {\n\tpublic final String message;\n\tpublic final int iconType;\n\tpublic final int buttonGroupType;\n\tpublic final String title;\n\tpublic int buttonPressed;\n\n\tMuPDFAlertInternal(String aMessage, int aIconType, int aButtonGroupType, String aTitle, int aButtonPressed) {\n\t\tmessage = aMessage;\n\t\ticonType = aIconType;\n\t\tbuttonGroupType = aButtonGroupType;\n\t\ttitle = aTitle;\n\t\tbuttonPressed = aButtonPressed;\n\t}\n\n\tMuPDFAlertInternal(MuPDFAlert alert) {\n\t\tmessage = alert.message;\n\t\ticonType = alert.iconType.ordinal();\n\t\tbuttonGroupType = alert.buttonGroupType.ordinal();\n\t\ttitle = alert.message;\n\t\tbuttonPressed = alert.buttonPressed.ordinal();\n\t}\n\n\tMuPDFAlert toAlert() {\n\t\treturn new MuPDFAlert(message, MuPDFAlert.IconType.values()[iconType], MuPDFAlert.ButtonGroupType.values()[buttonGroupType], title, MuPDFAlert.ButtonPressed.values()[buttonPressed]);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/MuPDFCancellableTaskDefinition.java",
    "content": "package com.artifex.mupdfdemo;\n\npublic abstract class MuPDFCancellableTaskDefinition<Params, Result> implements CancellableTaskDefinition<Params, Result>\n{\n\tprivate MuPDFCore.Cookie cookie;\n\n\tpublic MuPDFCancellableTaskDefinition(MuPDFCore core)\n\t{\n\t\tthis.cookie = core.new Cookie();\n\t}\n\n\t@Override\n\tpublic void doCancel()\n\t{\n\t\tif (cookie == null)\n\t\t\treturn;\n\n\t\tcookie.abort();\n\t}\n\n\t@Override\n\tpublic void doCleanup()\n\t{\n\t\tif (cookie == null)\n\t\t\treturn;\n\n\t\tcookie.destroy();\n\t\tcookie = null;\n\t}\n\n\t@Override\n\tpublic final Result doInBackground(Params ... params)\n\t{\n\t\treturn doInBackground(cookie, params);\n\t}\n\n\tpublic abstract Result doInBackground(MuPDFCore.Cookie cookie, Params ... params);\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/MuPDFCore.java",
    "content": "package com.artifex.mupdfdemo;\nimport java.util.ArrayList;\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.graphics.Bitmap.Config;\nimport android.graphics.PointF;\nimport android.graphics.RectF;\n\npublic class MuPDFCore\n{\n\t/* load our native library */\n\tstatic {\n\t\tSystem.loadLibrary(\"mupdf\");\n\t}\n\n\t/* Readable members */\n\tprivate int numPages = -1;\n\tprivate float pageWidth;\n\tprivate float pageHeight;\n\tprivate long globals;\n\tprivate byte fileBuffer[];\n\tprivate String file_format;\n\tprivate boolean isUnencryptedPDF;\n\tprivate final boolean wasOpenedFromBuffer;\n\n\t/* The native functions */\n\tprivate native long openFile(String filename);\n\tprivate native long openBuffer(String magic);\n\tprivate native String fileFormatInternal();\n\tprivate native boolean isUnencryptedPDFInternal();\n\tprivate native int countPagesInternal();\n\tprivate native void gotoPageInternal(int localActionPageNum);\n\tprivate native float getPageWidth();\n\tprivate native float getPageHeight();\n\tprivate native void drawPage(Bitmap bitmap,\n\t\t\tint pageW, int pageH,\n\t\t\tint patchX, int patchY,\n\t\t\tint patchW, int patchH,\n\t\t\tlong cookiePtr);\n\tprivate native void updatePageInternal(Bitmap bitmap,\n\t\t\tint page,\n\t\t\tint pageW, int pageH,\n\t\t\tint patchX, int patchY,\n\t\t\tint patchW, int patchH,\n\t\t\tlong cookiePtr);\n\tprivate native RectF[] searchPage(String text);\n\tprivate native TextChar[][][][] text();\n\tprivate native byte[] textAsHtml();\n\tprivate native void addMarkupAnnotationInternal(PointF[] quadPoints, int type);\n\tprivate native void addInkAnnotationInternal(PointF[][] arcs);\n\tprivate native void deleteAnnotationInternal(int annot_index);\n\tprivate native int passClickEventInternal(int page, float x, float y);\n\tprivate native void setFocusedWidgetChoiceSelectedInternal(String [] selected);\n\tprivate native String [] getFocusedWidgetChoiceSelected();\n\tprivate native String [] getFocusedWidgetChoiceOptions();\n\tprivate native int getFocusedWidgetSignatureState();\n\tprivate native String checkFocusedSignatureInternal();\n\tprivate native boolean signFocusedSignatureInternal(String keyFile, String password);\n\tprivate native int setFocusedWidgetTextInternal(String text);\n\tprivate native String getFocusedWidgetTextInternal();\n\tprivate native int getFocusedWidgetTypeInternal();\n\tprivate native LinkInfo [] getPageLinksInternal(int page);\n\tprivate native RectF[] getWidgetAreasInternal(int page);\n\tprivate native Annotation[] getAnnotationsInternal(int page);\n\tprivate native OutlineItem [] getOutlineInternal();\n\tprivate native boolean hasOutlineInternal();\n\tprivate native boolean needsPasswordInternal();\n\tprivate native boolean authenticatePasswordInternal(String password);\n\tprivate native MuPDFAlertInternal waitForAlertInternal();\n\tprivate native void replyToAlertInternal(MuPDFAlertInternal alert);\n\tprivate native void startAlertsInternal();\n\tprivate native void stopAlertsInternal();\n\tprivate native void destroying();\n\tprivate native boolean hasChangesInternal();\n\tprivate native void saveInternal();\n\tprivate native long createCookie();\n\tprivate native void destroyCookie(long cookie);\n\tprivate native void abortCookie(long cookie);\n\n\tpublic native boolean javascriptSupported();\n\n\tpublic class Cookie\n\t{\n\t\tprivate final long cookiePtr;\n\n\t\tpublic Cookie()\n\t\t{\n\t\t\tcookiePtr = createCookie();\n\t\t\tif (cookiePtr == 0)\n\t\t\t\tthrow new OutOfMemoryError();\n\t\t}\n\n\t\tpublic void abort()\n\t\t{\n\t\t\tabortCookie(cookiePtr);\n\t\t}\n\n\t\tpublic void destroy()\n\t\t{\n\t\t\t// We could do this in finalize, but there's no guarantee that\n\t\t\t// a finalize will occur before the muPDF context occurs.\n\t\t\tdestroyCookie(cookiePtr);\n\t\t}\n\t}\n\n\tpublic MuPDFCore(Context context, String filename) throws Exception\n\t{\n\t\tglobals = openFile(filename);\n\t\tif (globals == 0)\n\t\t{\n\t\t\tthrow new Exception(String.format(context.getString(R.string.cannot_open_file_Path), filename));\n\t\t}\n\t\tfile_format = fileFormatInternal();\n\t\tisUnencryptedPDF = isUnencryptedPDFInternal();\n\t\twasOpenedFromBuffer = false;\n\t}\n\n\tpublic MuPDFCore(Context context, byte buffer[], String magic) throws Exception {\n\t\tfileBuffer = buffer;\n\t\tglobals = openBuffer(magic != null ? magic : \"\");\n\t\tif (globals == 0)\n\t\t{\n\t\t\tthrow new Exception(context.getString(R.string.cannot_open_buffer));\n\t\t}\n\t\tfile_format = fileFormatInternal();\n\t\tisUnencryptedPDF = isUnencryptedPDFInternal();\n\t\twasOpenedFromBuffer = true;\n\t}\n\n\tpublic  int countPages()\n\t{\n\t\tif (numPages < 0)\n\t\t\tnumPages = countPagesSynchronized();\n\n\t\treturn numPages;\n\t}\n\n\tpublic String fileFormat()\n\t{\n\t\treturn file_format;\n\t}\n\n\tpublic boolean isUnencryptedPDF()\n\t{\n\t\treturn isUnencryptedPDF;\n\t}\n\n\tpublic boolean wasOpenedFromBuffer()\n\t{\n\t\treturn wasOpenedFromBuffer;\n\t}\n\n\tprivate synchronized int countPagesSynchronized() {\n\t\treturn countPagesInternal();\n\t}\n\n\t/* Shim function */\n\tprivate void gotoPage(int page)\n\t{\n\t\tif (page > numPages-1)\n\t\t\tpage = numPages-1;\n\t\telse if (page < 0)\n\t\t\tpage = 0;\n\t\tgotoPageInternal(page);\n\t\tthis.pageWidth = getPageWidth();\n\t\tthis.pageHeight = getPageHeight();\n\t}\n\n\tpublic synchronized PointF getPageSize(int page) {\n\t\tgotoPage(page);\n\t\treturn new PointF(pageWidth, pageHeight);\n\t}\n\n\tpublic MuPDFAlert waitForAlert() {\n\t\tMuPDFAlertInternal alert = waitForAlertInternal();\n\t\treturn alert != null ? alert.toAlert() : null;\n\t}\n\n\tpublic void replyToAlert(MuPDFAlert alert) {\n\t\treplyToAlertInternal(new MuPDFAlertInternal(alert));\n\t}\n\n\tpublic void stopAlerts() {\n\t\tstopAlertsInternal();\n\t}\n\n\tpublic void startAlerts() {\n\t\tstartAlertsInternal();\n\t}\n\n\tpublic synchronized void onDestroy() {\n\t\tdestroying();\n\t\tglobals = 0;\n\t}\n\n\tpublic synchronized void drawPage(Bitmap bm, int page,\n\t\t\tint pageW, int pageH,\n\t\t\tint patchX, int patchY,\n\t\t\tint patchW, int patchH,\n\t\t\tCookie cookie) {\n\t\tgotoPage(page);\n\t\tdrawPage(bm, pageW, pageH, patchX, patchY, patchW, patchH, cookie.cookiePtr);\n\t}\n\n\tpublic synchronized void updatePage(Bitmap bm, int page,\n\t\t\tint pageW, int pageH,\n\t\t\tint patchX, int patchY,\n\t\t\tint patchW, int patchH,\n\t\t\tCookie cookie) {\n\t\tupdatePageInternal(bm, page, pageW, pageH, patchX, patchY, patchW, patchH, cookie.cookiePtr);\n\t}\n\n\tpublic synchronized PassClickResult passClickEvent(int page, float x, float y) {\n\t\tboolean changed = passClickEventInternal(page, x, y) != 0;\n\n\t\tswitch (WidgetType.values()[getFocusedWidgetTypeInternal()])\n\t\t{\n\t\tcase TEXT:\n\t\t\treturn new PassClickResultText(changed, getFocusedWidgetTextInternal());\n\t\tcase LISTBOX:\n\t\tcase COMBOBOX:\n\t\t\treturn new PassClickResultChoice(changed, getFocusedWidgetChoiceOptions(), getFocusedWidgetChoiceSelected());\n\t\tcase SIGNATURE:\n\t\t\treturn new PassClickResultSignature(changed, getFocusedWidgetSignatureState());\n\t\tdefault:\n\t\t\treturn new PassClickResult(changed);\n\t\t}\n\n\t}\n\n\tpublic synchronized boolean setFocusedWidgetText(int page, String text) {\n\t\tboolean success;\n\t\tgotoPage(page);\n\t\tsuccess = setFocusedWidgetTextInternal(text) != 0 ? true : false;\n\n\t\treturn success;\n\t}\n\n\tpublic synchronized void setFocusedWidgetChoiceSelected(String [] selected) {\n\t\tsetFocusedWidgetChoiceSelectedInternal(selected);\n\t}\n\n\tpublic synchronized String checkFocusedSignature() {\n\t\treturn checkFocusedSignatureInternal();\n\t}\n\n\tpublic synchronized boolean signFocusedSignature(String keyFile, String password) {\n\t\treturn signFocusedSignatureInternal(keyFile, password);\n\t}\n\n\tpublic synchronized LinkInfo [] getPageLinks(int page) {\n\t\treturn getPageLinksInternal(page);\n\t}\n\n\tpublic synchronized RectF [] getWidgetAreas(int page) {\n\t\treturn getWidgetAreasInternal(page);\n\t}\n\n\tpublic synchronized Annotation [] getAnnoations(int page) {\n\t\treturn getAnnotationsInternal(page);\n\t}\n\n\tpublic synchronized RectF [] searchPage(int page, String text) {\n\t\tgotoPage(page);\n\t\treturn searchPage(text);\n\t}\n\n\tpublic synchronized byte[] html(int page) {\n\t\tgotoPage(page);\n\t\treturn textAsHtml();\n\t}\n\n\tpublic synchronized TextWord [][] textLines(int page) {\n\t\tgotoPage(page);\n\t\tTextChar[][][][] chars = text();\n\n\t\t// The text of the page held in a hierarchy (blocks, lines, spans).\n\t\t// Currently we don't need to distinguish the blocks level or\n\t\t// the spans, and we need to collect the text into words.\n\t\tArrayList<TextWord[]> lns = new ArrayList<TextWord[]>();\n\n\t\tfor (TextChar[][][] bl: chars) {\n\t\t\tif (bl == null)\n\t\t\t\tcontinue;\n\t\t\tfor (TextChar[][] ln: bl) {\n\t\t\t\tArrayList<TextWord> wds = new ArrayList<TextWord>();\n\t\t\t\tTextWord wd = new TextWord();\n\n\t\t\t\tfor (TextChar[] sp: ln) {\n\t\t\t\t\tfor (TextChar tc: sp) {\n\t\t\t\t\t\tif (tc.c != ' ') {\n\t\t\t\t\t\t\twd.Add(tc);\n\t\t\t\t\t\t} else if (wd.w.length() > 0) {\n\t\t\t\t\t\t\twds.add(wd);\n\t\t\t\t\t\t\twd = new TextWord();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (wd.w.length() > 0)\n\t\t\t\t\twds.add(wd);\n\n\t\t\t\tif (wds.size() > 0)\n\t\t\t\t\tlns.add(wds.toArray(new TextWord[wds.size()]));\n\t\t\t}\n\t\t}\n\n\t\treturn lns.toArray(new TextWord[lns.size()][]);\n\t}\n\n\tpublic synchronized void addMarkupAnnotation(int page, PointF[] quadPoints, Annotation.Type type) {\n\t\tgotoPage(page);\n\t\taddMarkupAnnotationInternal(quadPoints, type.ordinal());\n\t}\n\n\tpublic synchronized void addInkAnnotation(int page, PointF[][] arcs) {\n\t\tgotoPage(page);\n\t\taddInkAnnotationInternal(arcs);\n\t}\n\n\tpublic synchronized void deleteAnnotation(int page, int annot_index) {\n\t\tgotoPage(page);\n\t\tdeleteAnnotationInternal(annot_index);\n\t}\n\n\tpublic synchronized boolean hasOutline() {\n\t\treturn hasOutlineInternal();\n\t}\n\n\tpublic synchronized OutlineItem [] getOutline() {\n\t\treturn getOutlineInternal();\n\t}\n\n\tpublic synchronized boolean needsPassword() {\n\t\treturn needsPasswordInternal();\n\t}\n\n\tpublic synchronized boolean authenticatePassword(String password) {\n\t\treturn authenticatePasswordInternal(password);\n\t}\n\n\tpublic synchronized boolean hasChanges() {\n\t\treturn hasChangesInternal();\n\t}\n\n\tpublic synchronized void save() {\n\t\tsaveInternal();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/MuPDFFragment.java",
    "content": "package com.artifex.mupdfdemo;\n\nimport java.io.InputStream;\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.List;\n\nimport com.artifex.utils.DigitalizedEventCallback;\nimport com.artifex.utils.PdfBitmap;\n\nimport android.app.Activity;\nimport android.app.AlertDialog;\nimport android.content.Context;\nimport android.content.DialogInterface;\nimport android.content.DialogInterface.OnCancelListener;\nimport android.content.Intent;\nimport android.content.SharedPreferences;\nimport android.content.res.Configuration;\nimport android.content.res.Resources;\nimport android.database.Cursor;\nimport android.graphics.Color;\nimport android.graphics.Point;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.text.Editable;\nimport android.text.TextWatcher;\nimport android.text.method.PasswordTransformationMethod;\nimport android.util.Log;\nimport android.view.KeyEvent;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.animation.Animation;\nimport android.view.animation.TranslateAnimation;\nimport android.view.inputmethod.EditorInfo;\nimport android.view.inputmethod.InputMethodManager;\nimport android.widget.EditText;\nimport android.widget.ImageButton;\nimport android.widget.RelativeLayout;\nimport android.widget.SeekBar;\nimport android.widget.TextView;\nimport android.widget.ViewAnimator;\n\nimport androidx.fragment.app.Fragment;\n\npublic class MuPDFFragment extends Fragment implements FilePicker.FilePickerSupport\n{\n    private static final String TAG = \"MuPDFFragment\";\n    public static final String PARAM_SIGN_BITMAP_PATH = \"paramSignBitmapPath\";\n    public static final String PARAM_DIGITALIZED_IMAGE = \"paramDigitalizedImage\";\n    public static final String PARAM_PATH_PDF = \"paramPathPdf\";\n    public static final String PARAM_SHOW_CONTROLS = \"paramShowControls\";\n    public static final String PARAM_MODE_SIGN = \"doSign\";\n\tpublic static final String PARAM_PASSWORD_PDF = \"paramPasswordPdf\";\n\n    /* State restoration */\n    private static final String BUNDLE_FILENAME = \"savedFileName\";\n    private static final String BUNDLE_BUTTONS_HIDDEN = \"savedButtonsHidden\";\n\n\t/* The core rendering instance */\n\tenum TopBarMode {Main, Search, Annot, Delete, More, Accept};\n\tenum AcceptMode {Highlight, Underline, StrikeOut, Ink, CopyText};\n\n    private Context mContext;\n\tprivate final int    OUTLINE_REQUEST=0;\n\tprivate final int    PRINT_REQUEST=1;\n\tprivate final int    FILEPICK_REQUEST=2;\n\tprivate MuPDFCore    core;\n\tprivate String       mFileName;\n\tprivate MuPDFReaderView mDocView;\n\tprivate View         mButtonsView;\n\tprivate boolean      mButtonsVisible;\n\tprivate EditText     mPasswordView;\n\tprivate TextView     mFilenameView;\n\tprivate SeekBar      mPageSlider;\n\tprivate int          mPageSliderRes;\n\tprivate TextView     mPageNumberView;\n\tprivate TextView     mInfoView;\n\tprivate ImageButton  mSearchButton;\n\tprivate ImageButton  mReflowButton;\n\tprivate ImageButton  mOutlineButton;\n\tprivate ImageButton\tmMoreButton;\n\tprivate TextView     mAnnotTypeText;\n\tprivate ImageButton mAnnotButton;\n\tprivate ViewAnimator mTopBarSwitcher;\n\tprivate ImageButton  mLinkButton;\n\tprivate TopBarMode   mTopBarMode = TopBarMode.Main;\n\tprivate AcceptMode   mAcceptMode;\n\tprivate ImageButton  mSearchBack;\n\tprivate ImageButton  mSearchFwd;\n\tprivate EditText     mSearchText;\n\tprivate SearchTask   mSearchTask;\n\tprivate AlertDialog.Builder mAlertBuilder;\n\tprivate boolean \tmDoSign;\n    private DigitalizedEventCallback eventCallback;\n\tprivate boolean    mLinkHighlight = false;\n\tprivate final Handler mHandler = new Handler();\n\tprivate boolean mAlertsActive= false;\n\tprivate boolean mReflow = false;\n\tprivate AsyncTask<Void,Void,MuPDFAlert> mAlertTask;\n\tprivate AlertDialog mAlertDialog;\n\tprivate FilePicker mFilePicker;\n\tprivate Collection<PdfBitmap> pdfBitmaps;\n\tprivate byte[] byteArrayPdf;\n\tprivate int mPageNumber = 0;\n\n\tpublic void createAlertWaiter() {\n\t\tmAlertsActive = true;\n\t\t// All mupdf library calls are performed on asynchronous tasks to avoid stalling\n\t\t// the UI. Some calls can lead to javascript-invoked requests to display an\n\t\t// alert dialog and collect a reply from the user. The task has to be blocked\n\t\t// until the user's reply is received. This method creates an asynchronous task,\n\t\t// the purpose of which is to wait of these requests and produce the dialog\n\t\t// in response, while leaving the core blocked. When the dialog receives the\n\t\t// user's response, it is sent to the core via replyToAlert, unblocking it.\n\t\t// Another alert-waiting task is then created to pick up the next alert.\n\t\tif (mAlertTask != null) {\n\t\t\tmAlertTask.cancel(true);\n\t\t\tmAlertTask = null;\n\t\t}\n\t\tif (mAlertDialog != null) {\n\t\t\tmAlertDialog.cancel();\n\t\t\tmAlertDialog = null;\n\t\t}\n\t\tmAlertTask = new AsyncTask<Void,Void,MuPDFAlert>() {\n\n\t\t\t@Override\n\t\t\tprotected MuPDFAlert doInBackground(Void... arg0) {\n\t\t\t\tif (!mAlertsActive)\n\t\t\t\t\treturn null;\n\n\t\t\t\treturn core.waitForAlert();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected void onPostExecute(final MuPDFAlert result) {\n\t\t\t\t// core.waitForAlert may return null when shutting down\n\t\t\t\tif (result == null)\n\t\t\t\t\treturn;\n\t\t\t\tfinal MuPDFAlert.ButtonPressed pressed[] = new MuPDFAlert.ButtonPressed[3];\n\t\t\t\tfor(int i = 0; i < 3; i++)\n\t\t\t\t\tpressed[i] = MuPDFAlert.ButtonPressed.None;\n\t\t\t\tDialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {\n\t\t\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\t\t\tmAlertDialog = null;\n\t\t\t\t\t\tif (mAlertsActive) {\n\t\t\t\t\t\t\tint index = 0;\n\t\t\t\t\t\t\tswitch (which) {\n\t\t\t\t\t\t\tcase AlertDialog.BUTTON1: index=0; break;\n\t\t\t\t\t\t\tcase AlertDialog.BUTTON2: index=1; break;\n\t\t\t\t\t\t\tcase AlertDialog.BUTTON3: index=2; break;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tresult.buttonPressed = pressed[index];\n\t\t\t\t\t\t\t// Send the user's response to the core, so that it can\n\t\t\t\t\t\t\t// continue processing.\n\t\t\t\t\t\t\tcore.replyToAlert(result);\n\t\t\t\t\t\t\t// Create another alert-waiter to pick up the next alert.\n\t\t\t\t\t\t\tcreateAlertWaiter();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tmAlertDialog = mAlertBuilder.create();\n\t\t\t\tmAlertDialog.setTitle(result.title);\n\t\t\t\tmAlertDialog.setMessage(result.message);\n\t\t\t\tswitch (result.iconType)\n\t\t\t\t{\n\t\t\t\tcase Error:\n\t\t\t\t\tbreak;\n\t\t\t\tcase Warning:\n\t\t\t\t\tbreak;\n\t\t\t\tcase Question:\n\t\t\t\t\tbreak;\n\t\t\t\tcase Status:\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tswitch (result.buttonGroupType)\n\t\t\t\t{\n\t\t\t\tcase OkCancel:\n\t\t\t\t\tmAlertDialog.setButton(AlertDialog.BUTTON2, getString(R.string.cancel), listener);\n\t\t\t\t\tpressed[1] = MuPDFAlert.ButtonPressed.Cancel;\n\t\t\t\tcase Ok:\n\t\t\t\t\tmAlertDialog.setButton(AlertDialog.BUTTON1, getString(R.string.okay), listener);\n\t\t\t\t\tpressed[0] = MuPDFAlert.ButtonPressed.Ok;\n\t\t\t\t\tbreak;\n\t\t\t\tcase YesNoCancel:\n\t\t\t\t\tmAlertDialog.setButton(AlertDialog.BUTTON3, getString(R.string.cancel), listener);\n\t\t\t\t\tpressed[2] = MuPDFAlert.ButtonPressed.Cancel;\n\t\t\t\tcase YesNo:\n\t\t\t\t\tmAlertDialog.setButton(AlertDialog.BUTTON1, getString(R.string.yes), listener);\n\t\t\t\t\tpressed[0] = MuPDFAlert.ButtonPressed.Yes;\n\t\t\t\t\tmAlertDialog.setButton(AlertDialog.BUTTON2, getString(R.string.no), listener);\n\t\t\t\t\tpressed[1] = MuPDFAlert.ButtonPressed.No;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tmAlertDialog.setOnCancelListener(new OnCancelListener() {\n\t\t\t\t\tpublic void onCancel(DialogInterface dialog) {\n\t\t\t\t\t\tmAlertDialog = null;\n\t\t\t\t\t\tif (mAlertsActive) {\n\t\t\t\t\t\t\tresult.buttonPressed = MuPDFAlert.ButtonPressed.None;\n\t\t\t\t\t\t\tcore.replyToAlert(result);\n\t\t\t\t\t\t\tcreateAlertWaiter();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tmAlertDialog.show();\n\t\t\t}\n\t\t};\n\n\t\tmAlertTask.executeOnExecutor(new ThreadPerTaskExecutor());\n\t}\n\n\tpublic void destroyAlertWaiter() {\n\t\tmAlertsActive = false;\n\t\tif (mAlertDialog != null) {\n\t\t\tmAlertDialog.cancel();\n\t\t\tmAlertDialog = null;\n\t\t}\n\t\tif (mAlertTask != null) {\n\t\t\tmAlertTask.cancel(true);\n\t\t\tmAlertTask = null;\n\t\t}\n\t}\n\n\tprivate MuPDFCore openFile(String path)\n\t{\n\t\tint lastSlashPos = path.lastIndexOf('/');\n\t\tmFileName = new String(lastSlashPos == -1\n\t\t\t\t\t? path\n\t\t\t\t\t: path.substring(lastSlashPos+1));\n\t\tSystem.out.println(\"Trying to open \" + path);\n\t\ttry\n\t\t{\n\t\t\tcore = new MuPDFCore(mContext, path);\n\t\t\t// New file: drop the old outline data\n\t\t\tOutlineActivityData.set(null);\n\t\t}\n\t\tcatch (Exception e)\n\t\t{\n\t\t\tSystem.out.println(e);\n\t\t\treturn null;\n\t\t}\n\t\treturn core;\n\t}\n\n    \n\n\tprivate MuPDFCore openBuffer(byte buffer[], String magic)\n\t{\n\t\tSystem.out.println(\"Trying to open byte buffer\");\n\t\ttry\n\t\t{\n\t\t\tcore = new MuPDFCore(mContext, buffer, magic);\n\t\t\t// New file: drop the old outline data\n\t\t\tOutlineActivityData.set(null);\n\t\t}\n\t\tcatch (Exception e)\n\t\t{\n\t\t\tSystem.out.println(e);\n\t\t\treturn null;\n\t\t}\n\t\treturn core;\n\t}\n\n\t/** Called when the activity is first created. */\n\t@Override\n\tpublic View onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)\n\t{\n\t\tsuper.onCreate(savedInstanceState);\n\n\t\t//Fuerza la orientacion a landscape\n\t\t//setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);\n\n\t\tmAlertBuilder = new AlertDialog.Builder(mContext);\n\n\t\tif (core == null) {\n\t\t\tcore = (MuPDFCore)getActivity().getLastNonConfigurationInstance();\n\n\t\t\tif (savedInstanceState != null && savedInstanceState.containsKey(\"FileName\")) {\n\t\t\t\tmFileName = savedInstanceState.getString(\"FileName\");\n\t\t\t}\n\t\t}\n\t\tif (core == null) {\n\t\t\tIntent intent = getActivity().getIntent();\n\t\t\tbyte buffer[] = null;\n\n            boolean hasIntent = Intent.ACTION_VIEW.equals(intent.getAction());\n            boolean hasArguments = getArguments() != null && getArguments().getString(PARAM_PATH_PDF) != null;\n            \n\t\t\tif (hasIntent || hasArguments) {\n                Uri uri;\n                if (hasArguments) {\n                    uri = Uri.parse(getArguments().getString(PARAM_PATH_PDF));\n                } else {\n                    uri = intent.getData();\n                    mDoSign = intent.getBooleanExtra(PARAM_MODE_SIGN, true);\n                }\n\t\t\t\tif ((uri != null && uri.toString().startsWith(\"content://\")) || byteArrayPdf != null) {\n\t\t\t\t\tString reason = null;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (byteArrayPdf != null) {\n\t\t\t\t\t\t\tbuffer = byteArrayPdf;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tInputStream is = mContext.getContentResolver().openInputStream(uri);\n\t\t\t\t\t\t\tint len = is.available();\n\t\t\t\t\t\t\tbuffer = new byte[len];\n\t\t\t\t\t\t\tis.read(buffer, 0, len);\n\t\t\t\t\t\t\tis.close();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tcatch (OutOfMemoryError e) {\n\t\t\t\t\t\tSystem.out.println(\"Out of memory during buffer reading\");\n\t\t\t\t\t\treason = e.toString();\n\t\t\t\t\t}\n\t\t\t\t\tcatch (Exception e) {\n\t\t\t\t\t\tSystem.out.println(\"Exception reading from stream: \" + e);\n\n\t\t\t\t\t\t// Handle view requests from the Transformer Prime's file manager\n\t\t\t\t\t\t// Hopefully other file managers will use this same scheme, if not\n\t\t\t\t\t\t// using explicit paths.\n\t\t\t\t\t\t// I'm hoping that this case below is no longer needed...but it's\n\t\t\t\t\t\t// hard to test as the file manager seems to have changed in 4.x.\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tCursor cursor = mContext.getContentResolver().query(uri, new String[]{\"_data\"}, null, null, null);\n\t\t\t\t\t\t\tif (cursor.moveToFirst()) {\n\t\t\t\t\t\t\t\tString str = cursor.getString(0);\n\t\t\t\t\t\t\t\tif (str == null) {\n\t\t\t\t\t\t\t\t\treason = \"Couldn't parse data in intent\";\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\turi = Uri.parse(str);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcatch (Exception e2) {\n\t\t\t\t\t\t\tSystem.out.println(\"Exception in Transformer Prime file manager code: \" + e2);\n\t\t\t\t\t\t\treason = e2.toString();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (reason != null) {\n\t\t\t\t\t\tbuffer = null;\n\t\t\t\t\t\tResources res = getResources();\n\t\t\t\t\t\tAlertDialog alert = mAlertBuilder.create();\n\t\t\t\t\t\talert.setTitle(String.format(res.getString(R.string.cannot_open_document_Reason), reason));\n\t\t\t\t\t\talert.setButton(AlertDialog.BUTTON_POSITIVE, getString(R.string.dismiss),\n\t\t\t\t\t\t\t\tnew DialogInterface.OnClickListener() {\n\t\t\t\t\t\t\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\t\t\t\t\t\t\tgetActivity().finish();\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\talert.show();\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (buffer != null) {\n\t\t\t\t\tcore = openBuffer(buffer, intent.getType());\n\t\t\t\t} else {\n\t\t\t\t\tcore = openFile(Uri.decode(uri.getEncodedPath()));\n\t\t\t\t}\n\t\t\t\tSearchTaskResult.set(null);\n\t\t\t}\n\t\t\tif (core != null && core.needsPassword()) {\n\t\t\t\tif (getArguments() != null && getArguments().getString(PARAM_PASSWORD_PDF) != null) {\n\t\t\t\t\tString password = getArguments().getString(PARAM_PASSWORD_PDF);\n\t\t\t\t\tcore.authenticatePassword(password);\n\t\t\t\t} else {\n\t\t\t\t\trequestPassword(savedInstanceState);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (core != null && core.countPages() == 0)\n\t\t\t{\n\t\t\t\tcore = null;\n\t\t\t}\n\t\t}\n\t\tif (core == null)\n\t\t{\n\t\t\tAlertDialog alert = mAlertBuilder.create();\n\t\t\talert.setTitle(R.string.cannot_open_document);\n\t\t\talert.setButton(AlertDialog.BUTTON_POSITIVE, getString(R.string.dismiss),\n\t\t\t\t\tnew DialogInterface.OnClickListener() {\n\t\t\t\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\t\t\t\tgetActivity().finish();\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\talert.setOnCancelListener(new OnCancelListener() {\n\n\t\t\t\t@Override\n\t\t\t\tpublic void onCancel(DialogInterface dialog) {\n\t\t\t\t\tgetActivity().finish();\n\t\t\t\t}\n\t\t\t});\n\t\t\talert.show();\n\t\t\treturn null;\n\t\t}\n\n\t\treturn createUI(savedInstanceState, mContext);\n\t}\n\n\tpublic void requestPassword(final Bundle savedInstanceState) {\n\t\tmPasswordView = new EditText(mContext);\n\t\tmPasswordView.setInputType(EditorInfo.TYPE_TEXT_VARIATION_PASSWORD);\n\t\tmPasswordView.setTransformationMethod(new PasswordTransformationMethod());\n\n\t\tAlertDialog alert = mAlertBuilder.create();\n\t\talert.setTitle(R.string.enter_password);\n\t\talert.setView(mPasswordView);\n\t\talert.setButton(AlertDialog.BUTTON_POSITIVE, getString(R.string.okay),\n\t\t\t\tnew DialogInterface.OnClickListener() {\n\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\tif (core.authenticatePassword(mPasswordView.getText().toString())) {\n\t\t\t\t\tcreateUI(savedInstanceState, mContext);\n\t\t\t\t} else {\n\t\t\t\t\trequestPassword(savedInstanceState);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\talert.setButton(AlertDialog.BUTTON_NEGATIVE, getString(R.string.cancel),\n\t\t\t\tnew DialogInterface.OnClickListener() {\n\n\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\tgetActivity().finish();\n\t\t\t}\n\t\t});\n\t\talert.show();\n\t}\n\n\tpublic View createUI(Bundle savedInstanceState, final Context context) {\n\t\tif (core == null)\n\t\t\treturn null;\n\n\t\t// Now create the UI.\n\t\t// First create the document view\n\t\tmDocView = new MuPDFReaderView(context) {\n\t\t\t@Override\n\t\t\tprotected void onMoveToChild(int i) {\n\t\t\t\tif (core == null)\n\t\t\t\t\treturn;\n\t\t\t\tmPageNumberView.setText(String.format(\"%d / %d\", i + 1,\n\t\t\t\t\t\tcore.countPages()));\n\t\t\t\tmPageSlider.setMax((core.countPages() - 1) * mPageSliderRes);\n\t\t\t\tmPageSlider.setProgress(i * mPageSliderRes);\n\t\t\t\tsuper.onMoveToChild(i);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected void onTapMainDocArea() {\n\t\t\t\tif (!mButtonsVisible) {\n\t\t\t\t\tshowButtons();\n\t\t\t\t} else {\n\t\t\t\t\tif (mTopBarMode == TopBarMode.Main)\n\t\t\t\t\t\thideButtons();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected void onDocMotion() {\n\t\t\t\thideButtons();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected void onHit(Hit item) {\n\t\t\t\tswitch (mTopBarMode) {\n\t\t\t\tcase Annot:\n\t\t\t\t\tif (item == Hit.Annotation) {\n\t\t\t\t\t\tshowButtons();\n\t\t\t\t\t\tmTopBarMode = TopBarMode.Delete;\n\t\t\t\t\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase Delete:\n\t\t\t\t\tmTopBarMode = TopBarMode.Annot;\n\t\t\t\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t\t\t\t// fall through\n\t\t\t\tdefault:\n\t\t\t\t\t// Not in annotation editing mode, but the pageview will\n\t\t\t\t\t// still select and highlight hit annotations, so\n\t\t\t\t\t// deselect just in case.\n\t\t\t\t\tMuPDFView pageView = (MuPDFView) mDocView.getDisplayedView();\n\t\t\t\t\tif (pageView != null)\n\t\t\t\t\t\tpageView.deselectAnnotation();\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t};\n        MuPDFPageAdapter adapter = new MuPDFPageAdapter(context, this, core);\n\t\tmDocView.setAdapter(adapter);\n        mDocView.setEventCallback(eventCallback);\n\t\tmDocView.setPdfBitmapList(pdfBitmaps);\n\n\t\tmSearchTask = new SearchTask(context, core) {\n\t\t\t@Override\n\t\t\tprotected void onTextFound(SearchTaskResult result) {\n\t\t\t\tSearchTaskResult.set(result);\n\t\t\t\t// Ask the ReaderView to move to the resulting page\n\t\t\t\tmDocView.setDisplayedViewIndex(result.pageNumber);\n\t\t\t\t// Make the ReaderView act on the change to SearchTaskResult\n\t\t\t\t// via overridden onChildSetup method.\n\t\t\t\tmDocView.resetupChildren();\n\t\t\t}\n\t\t};\n\n\t\t// Make the buttons overlay, and store all its\n\t\t// controls in variables\n\t\tmakeButtonsView();\n\n\t\t// Set up the page slider\n\t\tint smax = Math.max(core.countPages()-1,1);\n\t\tmPageSliderRes = ((10 + smax - 1)/smax) * 2;\n\n\t\t// Set the file-name text\n\t\tmFilenameView.setText(mFileName);\n\n\t\t// Activate the seekbar\n\t\tmPageSlider.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {\n\t\t\tpublic void onStopTrackingTouch(SeekBar seekBar) {\n\t\t\t\tmDocView.setDisplayedViewIndex((seekBar.getProgress()+mPageSliderRes/2)/mPageSliderRes);\n\t\t\t}\n\n\t\t\tpublic void onStartTrackingTouch(SeekBar seekBar) {}\n\n\t\t\tpublic void onProgressChanged(SeekBar seekBar, int progress,\n\t\t\t\t\tboolean fromUser) {\n\t\t\t\tint page = (progress+mPageSliderRes/2)/mPageSliderRes;\n\t\t\t\tupdatePageNumView(page);\n\t\t\t\tif (eventCallback != null) {\n\t\t\t\t\teventCallback.pageChanged(page);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t// Activate the search-preparing button\n\t\tmSearchButton.setOnClickListener(new View.OnClickListener() {\n\t\t\tpublic void onClick(View v) {\n\t\t\t\tsearchModeOn();\n\t\t\t}\n\t\t});\n\n\t\t// Activate the reflow button\n\t\tmReflowButton.setOnClickListener(new View.OnClickListener() {\n\t\t\tpublic void onClick(View v) {\n\t\t\t\ttoggleReflow();\n\t\t\t}\n\t\t});\n\n\t\tif (core.fileFormat().startsWith(\"PDF\") && core.isUnencryptedPDF() && !core.wasOpenedFromBuffer())\n\t\t{\n\t\t\tmAnnotButton.setOnClickListener(new View.OnClickListener() {\n\t\t\t\tpublic void onClick(View v) {\n\t\t\t\t\tmTopBarMode = TopBarMode.Annot;\n\t\t\t\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmAnnotButton.setVisibility(View.GONE);\n\t\t}\n\n\t\t// Search invoking buttons are disabled while there is no text specified\n\t\tmSearchBack.setEnabled(false);\n\t\tmSearchFwd.setEnabled(false);\n\t\tmSearchBack.setColorFilter(Color.argb(255, 128, 128, 128));\n\t\tmSearchFwd.setColorFilter(Color.argb(255, 128, 128, 128));\n\n\t\t// React to interaction with the text widget\n\t\tmSearchText.addTextChangedListener(new TextWatcher() {\n\n\t\t\tpublic void afterTextChanged(Editable s) {\n\t\t\t\tboolean haveText = s.toString().length() > 0;\n\t\t\t\tsetButtonEnabled(mSearchBack, haveText);\n\t\t\t\tsetButtonEnabled(mSearchFwd, haveText);\n\n\t\t\t\t// Remove any previous search results\n\t\t\t\tif (SearchTaskResult.get() != null && !mSearchText.getText().toString().equals(SearchTaskResult.get().txt)) {\n\t\t\t\t\tSearchTaskResult.set(null);\n\t\t\t\t\tmDocView.resetupChildren();\n\t\t\t\t}\n\t\t\t}\n\t\t\tpublic void beforeTextChanged(CharSequence s, int start, int count,\n\t\t\t\t\tint after) {}\n\t\t\tpublic void onTextChanged(CharSequence s, int start, int before,\n\t\t\t\t\tint count) {}\n\t\t});\n\n\t\t//React to Done button on keyboard\n\t\tmSearchText.setOnEditorActionListener(new TextView.OnEditorActionListener() {\n\t\t\tpublic boolean onEditorAction(TextView v, int actionId, KeyEvent event) {\n\t\t\t\tif (actionId == EditorInfo.IME_ACTION_DONE)\n\t\t\t\t\tsearch(1);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t});\n\n\t\tmSearchText.setOnKeyListener(new View.OnKeyListener() {\n\t\t\tpublic boolean onKey(View v, int keyCode, KeyEvent event) {\n\t\t\t\tif (event.getAction() == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER)\n\t\t\t\t\tsearch(1);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t});\n\n\t\t// Activate search invoking buttons\n\t\tmSearchBack.setOnClickListener(new View.OnClickListener() {\n\t\t\tpublic void onClick(View v) {\n\t\t\t\tsearch(-1);\n\t\t\t}\n\t\t});\n\t\tmSearchFwd.setOnClickListener(new View.OnClickListener() {\n\t\t\tpublic void onClick(View v) {\n\t\t\t\tsearch(1);\n\t\t\t}\n\t\t});\n\n\t\tmLinkButton.setOnClickListener(new View.OnClickListener() {\n\t\t\tpublic void onClick(View v) {\n\t\t\t\tsetLinkHighlight(!mLinkHighlight);\n\t\t\t}\n\t\t});\n\n\t\tif (core.hasOutline()) {\n\t\t\tmOutlineButton.setOnClickListener(new View.OnClickListener() {\n\t\t\t\tpublic void onClick(View v) {\n\t\t\t\t\tOutlineItem outline[] = core.getOutline();\n\t\t\t\t\tif (outline != null) {\n\t\t\t\t\t\tOutlineActivityData.get().items = outline;\n\t\t\t\t\t\tIntent intent = new Intent(context, OutlineActivity.class);\n\t\t\t\t\t\tstartActivityForResult(intent, OUTLINE_REQUEST);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t} else {\n\t\t\tmOutlineButton.setVisibility(View.GONE);\n\t\t}\n\n\t\t// Reenstate last state if it was recorded\n\t\tSharedPreferences prefs = getActivity().getPreferences(Context.MODE_PRIVATE);\n        int lastPage = prefs.getInt(\"page\"+mFileName, 0);\n//        mDocView.setDisplayedViewIndex(lastPage);\n\t\tif (mPageNumber < core.countPages()) {\n\t\t\tmDocView.setDisplayedViewIndex(mPageNumber);\n\t\t} else {\n\t\t\tmDocView.setDisplayedViewIndex(0);\n\t\t}\n\n\n\t\tif (savedInstanceState == null || !savedInstanceState.getBoolean(\"ButtonsHidden\", false))\n\t\t\tshowButtons();\n\n\t\tif(savedInstanceState != null && savedInstanceState.getBoolean(\"SearchMode\", false))\n\t\t\tsearchModeOn();\n\n\t\tif(savedInstanceState != null && savedInstanceState.getBoolean(\"ReflowMode\", false))\n\t\t\treflowModeSet(true);\n\n\t\t// Stick the document view and the buttons overlay into a parent view\n\t\tRelativeLayout layout = new RelativeLayout(context);\n\t\tlayout.addView(mDocView);\n\t\tlayout.addView(mButtonsView);\n\n        if (getArguments() != null && getArguments().getBoolean(PARAM_SHOW_CONTROLS)) {\n            mButtonsView.setVisibility(View.VISIBLE);\n        } else {\n            mButtonsView.setVisibility(View.GONE);\n        }\n\n        // Performs tap event to refresh view.\n        Handler handler = new Handler();\n        handler.postDelayed(runnable, msRedraw);\n        \n        return layout;\n\t}\n\n\tpublic Object onRetainNonConfigurationInstance()\n\t{\n\t\tMuPDFCore mycore = core;\n\t\tcore = null;\n\t\treturn mycore;\n\t}\n\n\tprivate void reflowModeSet(boolean reflow)\n\t{\n\t\tmReflow = reflow;\n\t\tmDocView.setAdapter(mReflow ? new MuPDFReflowAdapter(mContext, core) : new MuPDFPageAdapter(getActivity(), this, core));\n\t\tmReflowButton.setColorFilter(mReflow ? Color.argb(0xFF, 172, 114, 37) : Color.argb(0xFF, 255, 255, 255));\n\t\tsetButtonEnabled(mAnnotButton, !reflow);\n\t\tsetButtonEnabled(mSearchButton, !reflow);\n\t\tif (reflow) setLinkHighlight(false);\n\t\tsetButtonEnabled(mLinkButton, !reflow);\n\t\tsetButtonEnabled(mMoreButton, !reflow);\n\t\tmDocView.refresh(mReflow);\n\t}\n\n\tprivate void toggleReflow() {\n\t\treflowModeSet(!mReflow);\n\t\tshowInfo(mReflow ? getString(R.string.entering_reflow_mode) : getString(R.string.leaving_reflow_mode));\n\t}\n\n\t@Override\n\tpublic void onSaveInstanceState(Bundle outState) {\n\t\tsuper.onSaveInstanceState(outState);\n\n\t\tif (mFileName != null && mDocView != null) {\n\t\t\toutState.putString(\"FileName\", mFileName);\n\n\t\t\t// Store current page in the prefs against the file name,\n\t\t\t// so that we can pick it up each time the file is loaded\n\t\t\t// Other info is needed only for screen-orientation change,\n\t\t\t// so it can go in the bundle\n\t\t\tSharedPreferences prefs = getActivity().getPreferences(Context.MODE_PRIVATE);\n\t\t\tSharedPreferences.Editor edit = prefs.edit();\n\t\t\tedit.putInt(\"page\"+mFileName, mDocView.getDisplayedViewIndex());\n\t\t\tedit.commit();\n\t\t}\n\n\t\tif (!mButtonsVisible)\n\t\t\toutState.putBoolean(\"ButtonsHidden\", true);\n\n\t\tif (mTopBarMode == TopBarMode.Search)\n\t\t\toutState.putBoolean(\"SearchMode\", true);\n\n\t\tif (mReflow)\n\t\t\toutState.putBoolean(\"ReflowMode\", true);\n\t}\n\n\t@Override\n\tpublic void onPause() {\n\t\tsuper.onPause();\n\n\t\tif (mSearchTask != null)\n\t\t\tmSearchTask.stop();\n\n\t\tif (mFileName != null && mDocView != null) {\n\t\t\tSharedPreferences prefs = getActivity().getPreferences(Context.MODE_PRIVATE);\n\t\t\tSharedPreferences.Editor edit = prefs.edit();\n\t\t\tedit.putInt(\"page\"+mFileName, mDocView.getDisplayedViewIndex());\n\t\t\tedit.commit();\n\t\t}\n\t}\n\n\tpublic void onDestroy()\n\t{\n\t\tif (mDocView != null) {\n\t\t\tmDocView.applyToChildren(new ReaderView.ViewMapper() {\n\t\t\t\tvoid applyToView(View view) {\n\t\t\t\t\t((MuPDFView) view).releaseResources();\n\t\t\t\t}\n\t\t\t});\n\t\t\tmDocView.setEventCallback(null);\n\t\t}\n\t\tif (core != null)\n\t\t\tcore.onDestroy();\n\t\tif (mAlertTask != null) {\n\t\t\tmAlertTask.cancel(true);\n\t\t\tmAlertTask = null;\n\t\t}\n\t\teventCallback = null;\n\t\tcore = null;\n\n\t\t// Android is not releasing the memory recycled from the bitmaps on certain circumstances, which leads to OutOfMemory errors.\n\t\t// Somehow the gc is not called automatically in those situations...\n\t\t//\n\t\tSystem.gc();\n\n\t\tsuper.onDestroy();\n\t}\n\n\tprivate void setButtonEnabled(ImageButton button, boolean enabled) {\n\t\tbutton.setEnabled(enabled);\n\t\tbutton.setColorFilter(enabled ? Color.argb(255, 255, 255, 255) : Color.argb(255, 128, 128, 128));\n\t}\n\n\tprivate void setLinkHighlight(boolean highlight) {\n\t\tmLinkHighlight = highlight;\n\t\t// LINK_COLOR tint\n\t\tmLinkButton.setColorFilter(highlight ? Color.argb(0xFF, 172, 114, 37) : Color.argb(0xFF, 255, 255, 255));\n\t\t// Inform pages of the change.\n\t\tmDocView.setLinksEnabled(highlight);\n\t}\n\n\tprivate void showButtons() {\n\t\tif (core == null)\n\t\t\treturn;\n\t\tif (!mButtonsVisible) {\n\t\t\tmButtonsVisible = true;\n\t\t\t// Update page number text and slider\n\t\t\tint index = mDocView.getDisplayedViewIndex();\n\t\t\tupdatePageNumView(index);\n\t\t\tmPageSlider.setMax((core.countPages()-1)*mPageSliderRes);\n\t\t\tmPageSlider.setProgress(index * mPageSliderRes);\n\t\t\tif (mTopBarMode == TopBarMode.Search) {\n\t\t\t\tmSearchText.requestFocus();\n\t\t\t\tshowKeyboard();\n\t\t\t}\n\n\t\t\tAnimation anim = new TranslateAnimation(0, 0, -mTopBarSwitcher.getHeight(), 0);\n\t\t\tanim.setDuration(200);\n\t\t\tanim.setAnimationListener(new Animation.AnimationListener() {\n\t\t\t\tpublic void onAnimationStart(Animation animation) {\n\t\t\t\t\tmTopBarSwitcher.setVisibility(View.VISIBLE);\n\t\t\t\t}\n\t\t\t\tpublic void onAnimationRepeat(Animation animation) {}\n\t\t\t\tpublic void onAnimationEnd(Animation animation) {}\n\t\t\t});\n\t\t\tmTopBarSwitcher.startAnimation(anim);\n\n\t\t\tanim = new TranslateAnimation(0, 0, mPageSlider.getHeight(), 0);\n\t\t\tanim.setDuration(200);\n\t\t\tanim.setAnimationListener(new Animation.AnimationListener() {\n\t\t\t\tpublic void onAnimationStart(Animation animation) {\n\t\t\t\t\tmPageSlider.setVisibility(View.VISIBLE);\n\t\t\t\t}\n\n\t\t\t\tpublic void onAnimationRepeat(Animation animation) {\n\t\t\t\t}\n\n\t\t\t\tpublic void onAnimationEnd(Animation animation) {\n\t\t\t\t\tmPageNumberView.setVisibility(View.VISIBLE);\n\t\t\t\t}\n\t\t\t});\n\t\t\tmPageSlider.startAnimation(anim);\n\t\t}\n\t}\n\n\tprivate void hideButtons() {\n\t\tif (mButtonsVisible) {\n\t\t\tmButtonsVisible = false;\n\t\t\thideKeyboard();\n\n\t\t\tAnimation anim = new TranslateAnimation(0, 0, 0, -mTopBarSwitcher.getHeight());\n\t\t\tanim.setDuration(200);\n\t\t\tanim.setAnimationListener(new Animation.AnimationListener() {\n\t\t\t\tpublic void onAnimationStart(Animation animation) {}\n\t\t\t\tpublic void onAnimationRepeat(Animation animation) {}\n\t\t\t\tpublic void onAnimationEnd(Animation animation) {\n\t\t\t\t\tmTopBarSwitcher.setVisibility(View.INVISIBLE);\n\t\t\t\t}\n\t\t\t});\n\t\t\tmTopBarSwitcher.startAnimation(anim);\n\n\t\t\tanim = new TranslateAnimation(0, 0, 0, mPageSlider.getHeight());\n\t\t\tanim.setDuration(200);\n\t\t\tanim.setAnimationListener(new Animation.AnimationListener() {\n\t\t\t\tpublic void onAnimationStart(Animation animation) {\n\t\t\t\t\tmPageNumberView.setVisibility(View.INVISIBLE);\n\t\t\t\t}\n\n\t\t\t\tpublic void onAnimationRepeat(Animation animation) {\n\t\t\t\t}\n\n\t\t\t\tpublic void onAnimationEnd(Animation animation) {\n\t\t\t\t\tmPageSlider.setVisibility(View.INVISIBLE);\n\t\t\t\t}\n\t\t\t});\n\t\t\tmPageSlider.startAnimation(anim);\n\t\t}\n\t}\n\n\tprivate void searchModeOn() {\n\t\tif (mTopBarMode != TopBarMode.Search) {\n\t\t\tmTopBarMode = TopBarMode.Search;\n\t\t\t//Focus on EditTextWidget\n\t\t\tmSearchText.requestFocus();\n\t\t\tshowKeyboard();\n\t\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t\t}\n\t}\n\n\tprivate void searchModeOff() {\n\t\tif (mTopBarMode == TopBarMode.Search) {\n\t\t\tmTopBarMode = TopBarMode.Main;\n\t\t\thideKeyboard();\n\t\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t\t\tSearchTaskResult.set(null);\n\t\t\t// Make the ReaderView act on the change to mSearchTaskResult\n\t\t\t// via overridden onChildSetup method.\n\t\t\tmDocView.resetupChildren();\n\t\t}\n\t}\n\n\tprivate void updatePageNumView(int index) {\n\t\tif (core == null)\n\t\t\treturn;\n\t\tmPageNumberView.setText(String.format(\"%d / %d\", index + 1, core.countPages()));\n\t}\n\n\tprivate void printDoc() {\n\t\tif (!core.fileFormat().startsWith(\"PDF\")) {\n\t\t\tshowInfo(getString(R.string.format_currently_not_supported));\n\t\t\treturn;\n\t\t}\n\n\t\tIntent myIntent = getActivity().getIntent();\n\t\tUri docUri = myIntent != null ? myIntent.getData() : null;\n\n\t\tif (docUri == null) {\n\t\t\tshowInfo(getString(R.string.print_failed));\n\t\t}\n\n\t\tif (docUri.getScheme() == null)\n\t\t\tdocUri = Uri.parse(\"file://\"+docUri.toString());\n\n\t\tIntent printIntent = new Intent(mContext, PrintDialogActivity.class);\n\t\tprintIntent.setDataAndType(docUri, \"aplication/pdf\");\n\t\tprintIntent.putExtra(\"title\", mFileName);\n\t\tstartActivityForResult(printIntent, PRINT_REQUEST);\n\t}\n\n\tprivate void showInfo(String message) {\n\t\tmInfoView.setText(message);\n\n\t\tint currentApiVersion = android.os.Build.VERSION.SDK_INT;\n\t\tif (currentApiVersion >= android.os.Build.VERSION_CODES.HONEYCOMB) {\n\t\t\tSafeAnimatorInflater safe = new SafeAnimatorInflater(getActivity(), R.animator.info, (View)mInfoView);\n\t\t} else {\n\t\t\tmInfoView.setVisibility(View.VISIBLE);\n\t\t\tmHandler.postDelayed(new Runnable() {\n\t\t\t\tpublic void run() {\n\t\t\t\t\tmInfoView.setVisibility(View.INVISIBLE);\n\t\t\t\t}\n\t\t\t}, 500);\n\t\t}\n\t}\n\n\tprivate void makeButtonsView() {\n\t\tmButtonsView = getActivity().getLayoutInflater().inflate(R.layout.buttons,null);\n\t\tmFilenameView = (TextView)mButtonsView.findViewById(R.id.docNameText);\n\t\tmPageSlider = (SeekBar)mButtonsView.findViewById(R.id.pageSlider);\n\t\tmPageNumberView = (TextView)mButtonsView.findViewById(R.id.pageNumber);\n\t\tmInfoView = (TextView)mButtonsView.findViewById(R.id.info);\n\t\tmSearchButton = (ImageButton)mButtonsView.findViewById(R.id.searchButton);\n\t\tmReflowButton = (ImageButton)mButtonsView.findViewById(R.id.reflowButton);\n\t\tmOutlineButton = (ImageButton)mButtonsView.findViewById(R.id.outlineButton);\n\t\tmAnnotButton = (ImageButton)mButtonsView.findViewById(R.id.editAnnotButton);\n\t\tmAnnotTypeText = (TextView)mButtonsView.findViewById(R.id.annotType);\n\t\tmTopBarSwitcher = (ViewAnimator)mButtonsView.findViewById(R.id.switcher);\n\t\tmSearchBack = (ImageButton)mButtonsView.findViewById(R.id.searchBack);\n\t\tmSearchFwd = (ImageButton)mButtonsView.findViewById(R.id.searchForward);\n\t\tmSearchText = (EditText)mButtonsView.findViewById(R.id.searchText);\n\t\tmLinkButton = (ImageButton)mButtonsView.findViewById(R.id.linkButton);\n\t\tmMoreButton = (ImageButton)mButtonsView.findViewById(R.id.moreButton);\n\t\tmTopBarSwitcher.setVisibility(View.INVISIBLE);\n\t\tmPageNumberView.setVisibility(View.INVISIBLE);\n\t\tmInfoView.setVisibility(View.INVISIBLE);\n\t\tmPageSlider.setVisibility(View.INVISIBLE);\n\t}\n\n\tpublic void OnMoreButtonClick(View v) {\n\t\tmTopBarMode = TopBarMode.More;\n\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t}\n\n\tpublic void OnCancelMoreButtonClick(View v) {\n\t\tmTopBarMode = TopBarMode.Main;\n\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t}\n\n\tpublic void OnPrintButtonClick(View v) {\n\t\tprintDoc();\n\t}\n\n\tpublic void OnCopyTextButtonClick(View v) {\n\t\tmTopBarMode = TopBarMode.Accept;\n\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t\tmAcceptMode = AcceptMode.CopyText;\n\t\tmDocView.setMode(MuPDFReaderView.Mode.Selecting);\n\t\tmAnnotTypeText.setText(getString(R.string.copy_text));\n\t\tshowInfo(getString(R.string.select_text));\n\t}\n\n\tpublic void OnEditAnnotButtonClick(View v) {\n\t\tmTopBarMode = TopBarMode.Annot;\n\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t}\n\n\tpublic void OnCancelAnnotButtonClick(View v) {\n\t\tmTopBarMode = TopBarMode.More;\n\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t}\n\n\tpublic void OnHighlightButtonClick(View v) {\n\t\tmTopBarMode = TopBarMode.Accept;\n\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t\tmAcceptMode = AcceptMode.Highlight;\n\t\tmDocView.setMode(MuPDFReaderView.Mode.Selecting);\n\t\tmAnnotTypeText.setText(R.string.highlight);\n\t\tshowInfo(getString(R.string.select_text));\n\t}\n\n\tpublic void OnUnderlineButtonClick(View v) {\n\t\tmTopBarMode = TopBarMode.Accept;\n\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t\tmAcceptMode = AcceptMode.Underline;\n\t\tmDocView.setMode(MuPDFReaderView.Mode.Selecting);\n\t\tmAnnotTypeText.setText(R.string.underline);\n\t\tshowInfo(getString(R.string.select_text));\n\t}\n\n\tpublic void OnStrikeOutButtonClick(View v) {\n\t\tmTopBarMode = TopBarMode.Accept;\n\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t\tmAcceptMode = AcceptMode.StrikeOut;\n\t\tmDocView.setMode(MuPDFReaderView.Mode.Selecting);\n\t\tmAnnotTypeText.setText(R.string.strike_out);\n\t\tshowInfo(getString(R.string.select_text));\n\t}\n\n\tpublic void OnInkButtonClick(View v) {\n\t\tmTopBarMode = TopBarMode.Accept;\n\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t\tmAcceptMode = AcceptMode.Ink;\n\t\tmDocView.setMode(MuPDFReaderView.Mode.Drawing);\n\t\tmAnnotTypeText.setText(R.string.ink);\n\t\tshowInfo(getString(R.string.draw_annotation));\n\t}\n\n\tpublic void OnCancelAcceptButtonClick(View v) {\n\t\tMuPDFView pageView = (MuPDFView) mDocView.getDisplayedView();\n\t\tif (pageView != null) {\n\t\t\tpageView.deselectText();\n\t\t\tpageView.cancelDraw();\n\t\t}\n\t\tmDocView.setMode(MuPDFReaderView.Mode.Viewing);\n\t\tswitch (mAcceptMode) {\n\t\tcase CopyText:\n\t\t\tmTopBarMode = TopBarMode.More;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tmTopBarMode = TopBarMode.Annot;\n\t\t\tbreak;\n\t\t}\n\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t}\n\n\tpublic void OnAcceptButtonClick(View v) {\n\t\tMuPDFView pageView = (MuPDFView) mDocView.getDisplayedView();\n\t\tboolean success = false;\n\t\tswitch (mAcceptMode) {\n\t\tcase CopyText:\n\t\t\tif (pageView != null)\n\t\t\t\tsuccess = pageView.copySelection();\n\t\t\tmTopBarMode = TopBarMode.More;\n\t\t\tshowInfo(success?getString(R.string.copied_to_clipboard):getString(R.string.no_text_selected));\n\t\t\tbreak;\n\n\t\tcase Highlight:\n\t\t\tif (pageView != null)\n\t\t\t\tsuccess = pageView.markupSelection(Annotation.Type.HIGHLIGHT);\n\t\t\tmTopBarMode = TopBarMode.Annot;\n\t\t\tif (!success)\n\t\t\t\tshowInfo(getString(R.string.no_text_selected));\n\t\t\tbreak;\n\n\t\tcase Underline:\n\t\t\tif (pageView != null)\n\t\t\t\tsuccess = pageView.markupSelection(Annotation.Type.UNDERLINE);\n\t\t\tmTopBarMode = TopBarMode.Annot;\n\t\t\tif (!success)\n\t\t\t\tshowInfo(getString(R.string.no_text_selected));\n\t\t\tbreak;\n\n\t\tcase StrikeOut:\n\t\t\tif (pageView != null)\n\t\t\t\tsuccess = pageView.markupSelection(Annotation.Type.STRIKEOUT);\n\t\t\tmTopBarMode = TopBarMode.Annot;\n\t\t\tif (!success)\n\t\t\t\tshowInfo(getString(R.string.no_text_selected));\n\t\t\tbreak;\n\n\t\tcase Ink:\n\t\t\tif (pageView != null)\n\t\t\t\tsuccess = pageView.saveDraw();\n\t\t\tmTopBarMode = TopBarMode.Annot;\n\t\t\tif (!success)\n\t\t\t\tshowInfo(getString(R.string.nothing_to_save));\n\t\t\tbreak;\n\t\t}\n\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t\tmDocView.setMode(MuPDFReaderView.Mode.Viewing);\n\t}\n\n\tpublic void OnCancelSearchButtonClick(View v) {\n\t\tsearchModeOff();\n\t}\n\n\tpublic void OnDeleteButtonClick(View v) {\n\t\tMuPDFView pageView = (MuPDFView) mDocView.getDisplayedView();\n\t\tif (pageView != null)\n\t\t\tpageView.deleteSelectedAnnotation();\n\t\tmTopBarMode = TopBarMode.Annot;\n\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t}\n\n\tpublic void OnCancelDeleteButtonClick(View v) {\n\t\tMuPDFView pageView = (MuPDFView) mDocView.getDisplayedView();\n\t\tif (pageView != null)\n\t\t\tpageView.deselectAnnotation();\n\t\tmTopBarMode = TopBarMode.Annot;\n\t\tmTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());\n\t}\n\n\tprivate void showKeyboard() {\n\t\tInputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);\n\t\tif (imm != null)\n\t\t\timm.showSoftInput(mSearchText, 0);\n\t}\n\n\tprivate void hideKeyboard() {\n\t\tInputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);\n\t\tif (imm != null)\n\t\t\timm.hideSoftInputFromWindow(mSearchText.getWindowToken(), 0);\n\t}\n\n\tpublic void search(int direction) {\n\t\thideKeyboard();\n\t\tint displayPage = mDocView.getDisplayedViewIndex();\n\t\tSearchTaskResult r = SearchTaskResult.get();\n\t\tint searchPage = r != null ? r.pageNumber : -1;\n\t\tmSearchTask.go(mSearchText.getText().toString(), direction, displayPage, searchPage);\n\t}\n\n\t@Override\n\tpublic void onStart() {\n\t\tif (core != null)\n\t\t{\n\t\t\tcore.startAlerts();\n\t\t\tcreateAlertWaiter();\n\t\t}\n\n\t\tsuper.onStart();\n\t}\n\n\t@Override\n\tpublic void onStop() {\n\t\tif (core != null)\n\t\t{\n\t\t\tdestroyAlertWaiter();\n\t\t\tcore.stopAlerts();\n\t\t}\n\n\t\tsuper.onStop();\n\t}\n\n\t@Override\n\tpublic void performPickFor(FilePicker picker) {\n\t\tmFilePicker = picker;\n\t\tIntent intent = new Intent(mContext, ChoosePDFActivity.class);\n\t\tintent.setAction(ChoosePDFActivity.PICK_KEY_FILE);\n\t\tstartActivityForResult(intent, FILEPICK_REQUEST);\n\t}\n\n    \n    // @viafirma: Custom methods\n\n    public static MuPDFFragment newInstance (String signBitmapPath, List<PdfBitmap> digitalizedImage, String pathPdf) {\n        return newInstance(signBitmapPath, digitalizedImage, pathPdf, true);\n    }\n\n\tpublic void setPageNumber(int pageNumber) {\n\t\tthis.mPageNumber = pageNumber;\n\t\tif (mDocView != null && core != null) {\n\t\t\tif (pageNumber < core.countPages()) {\n\t\t\t\tmDocView.setDisplayedViewIndex(pageNumber);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic int getCurrentPage() {\n\t\tint page = 0;\n\t\tif (mDocView != null) {\n\t\t\tpage = mDocView.getDisplayedViewIndex();\n\t\t}\n\t\treturn page;\n\t}\n\n\tpublic static MuPDFFragment newInstance (String signBitmapPath, List<PdfBitmap> digitalizedImage, String pathPdf, boolean showControls) {\n\t\tMuPDFFragment f = newInstance(signBitmapPath, digitalizedImage, pathPdf, null, showControls);\n\t\treturn f;\n\t}\n\n\tpublic static MuPDFFragment newInstance (String signBitmapPath, List<PdfBitmap> digitalizedImage, String pathPdf, String passwordPdf, boolean showControls) {\n        MuPDFFragment f = new MuPDFFragment();\n        Bundle args = new Bundle();\n\n        if (digitalizedImage != null && digitalizedImage.size() > 0) {\n            args.putParcelable(PARAM_DIGITALIZED_IMAGE, digitalizedImage.get(0));\n        }\n\n        if (signBitmapPath != null) {\n            args.putString(PARAM_SIGN_BITMAP_PATH, signBitmapPath);\n        }\n\n        if (pathPdf != null) {\n            args.putString(PARAM_PATH_PDF, pathPdf);\n        }\n\n        if (passwordPdf != null) {\n\t\t\targs.putString(PARAM_PASSWORD_PDF, passwordPdf);\n\t\t}\n\n        args.putBoolean(PARAM_SHOW_CONTROLS, showControls);\n        f.setArguments(args);\n        return f;\n    }\n\n\tpublic static MuPDFFragment newInstance (byte[] bufferedPdf) {\n\t\tMuPDFFragment f = newInstance(null, null, null, null, false);\n\t\tf.setByteArrayPdf(bufferedPdf);\n\t\treturn f;\n\t}\n\n\tpublic static MuPDFFragment newInstance (String pathPdf) {\n\t\tMuPDFFragment f = newInstance(null, null, pathPdf, null, false);\n\t\treturn f;\n\t}\n\n\tpublic static MuPDFFragment newInstance (String pathPdf, String passwordPdf) {\n\t\tMuPDFFragment f = newInstance(null, null, pathPdf, passwordPdf, false);\n\t\treturn f;\n\t}\n\n\tpublic static MuPDFFragment newInstance (byte[] bufferedPdf, boolean showControls) {\n\t\tMuPDFFragment f = newInstance(null, null, null, null, showControls);\n\t\tf.setByteArrayPdf(bufferedPdf);\n\t\treturn f;\n\t}\n\n    public static MuPDFFragment newInstance (String pathPdf, boolean showControls) {\n        MuPDFFragment f = newInstance(null, null, pathPdf, null, showControls);\n        return f;\n    }\n\n    public void addBitmap(PdfBitmap pdfBitmap) {\n        if (mDocView != null) {\n            mDocView.addBitmap(pdfBitmap);\n        } else {\n            Log.e(TAG, \"Couldn't add Bitmap. DocView is NULL.\");\n        }\n    }\n\n\tpublic void setPdfBitmapList(Collection<PdfBitmap> pdfBitmaps) {\n\t\tthis.pdfBitmaps = pdfBitmaps;\n\t\tif (mDocView != null) {\n\t\t\tmDocView.setPdfBitmapList(pdfBitmaps);\n\t\t}\n\t}\n\n\tpublic Collection<PdfBitmap> getBitmapList() {\n\t\tif (mDocView != null) {\n\t\t\treturn mDocView.getBitmapList();\n\t\t} else {\n\t\t\tLog.e(TAG, \"Couldn't get bitmap list. DocView is NULL.\");\n\t\t\treturn new HashSet<>();\n\t\t}\n\t}\n\n    public boolean removeBitmapOnPosition(float x, float y) {\n        boolean removed = false;\n        if (mDocView != null) {\n            Point point = new Point((int)x, (int)y);\n            removed = mDocView.removeBitmapOnPosition(point);\n        } else {\n            Log.e(TAG, \"Couldn't remove Bitmap. DocView is NULL.\");\n        }\n        return removed;\n    };\n\n\tpublic void setByteArrayPdf(byte[] byteArrayPdf) {\n\t\tthis.byteArrayPdf = byteArrayPdf;\n\t}\n\n\t@Override\n    public void onAttach(Activity activity) {\n        super.onAttach(activity);\n        this.mContext = activity;\n    }\n\n    public boolean checkSign(){\n\n        MuPDFPageAdapter adapter = (MuPDFPageAdapter)mDocView.getAdapter();\n        if (adapter.getNumSignature() > 0 || !mDoSign) {\n            return true;\n        } else {\n            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());\n            builder.setTitle(getResources().getString(R.string.noSignOnPdfTitle));\n            builder.setMessage(R.string.noSignOnPdf);\n            builder.setNegativeButton(R.string.OkKey, null);\n            builder.show();\n            return false;\n        }\n    }\n\n    public DigitalizedEventCallback getEventCallback() {\n        return this.eventCallback;\n    }\n\n    public void setEventCallback(DigitalizedEventCallback eventCallback) {\n        this.eventCallback = eventCallback;\n        if (mDocView != null) {\n            mDocView.setEventCallback(eventCallback);\n        }\n    }\n\n    @Override\n    public void onConfigurationChanged(Configuration newConfig) {\n        super.onConfigurationChanged(newConfig);\n        Handler handler = new Handler();\n        // Excalibur line: if you can replace it, please do it.\n        handler.postDelayed(runnable, msRedraw);\n    }\n\n\tpublic void updateCurrentPage() {\n\t\tif (mDocView != null) {\n\t\t\tmDocView.updateCurrentPage();\n\t\t}\n\t}\n\n\tpublic void redrawAll() {\n\t\tif (mDocView != null) {\n\t\t\tmDocView.redrawAll();\n\t\t}\n\t}\n\n\n    private Runnable runnable = new Runnable() {\n        @Override\n        public void run() {\n            redrawTouch();\n        }\n    };\n    \n    private final int msRedraw = 500;\n\n    private void redrawTouch(){\n        if (mDocView != null) {\n            // Dispatch touch event to view\n            mDocView.refreshView();\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/MuPDFPageAdapter.java",
    "content": "package com.artifex.mupdfdemo;\n\nimport android.content.Context;\nimport android.graphics.Point;\nimport android.graphics.PointF;\nimport android.util.SparseArray;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.BaseAdapter;\n\nimport com.artifex.utils.PdfBitmap;\n\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.Set;\n\npublic class MuPDFPageAdapter extends BaseAdapter {\n\tprivate final Context mContext;\n\tprivate final FilePicker.FilePickerSupport mFilePickerSupport;\n\tprivate final MuPDFCore mCore;\n\tprivate final SparseArray<PointF> mPageSizes = new SparseArray<PointF>();\n    private SparseArray<MuPDFPageView> pages = new SparseArray<MuPDFPageView>();\n    private Collection<PdfBitmap> pdfBitmapList; // Each signature for each page.\n    private int numSignature;\n\n\tpublic MuPDFPageAdapter(Context c, FilePicker.FilePickerSupport filePickerSupport, MuPDFCore core) {\n\t\tmContext = c;\n\t\tmFilePickerSupport = filePickerSupport;\n\t\tmCore = core;\n\t}\n\n\tpublic int getCount() {\n\t\treturn mCore.countPages();\n\t}\n\n\tpublic Object getItem(int position) {\n        return pages.get(position);\n\t}\n\n\tpublic long getItemId(int position) {\n\t\treturn 0;\n\t}\n\n\tpublic View getView(final int position, View convertView, ViewGroup parent) {\n\n        final MuPDFPageView pageView;\n        if (pages.get(position) == null) {\n            pageView = new MuPDFPageView(mContext, mFilePickerSupport, mCore, new Point(parent.getWidth(), parent.getHeight()), this);\n            pages.put(position, pageView);\n        } else {\n            pageView = pages.get(position);\n        }\n\n\t\t//Limit the pages cache to improve memory usage\n\t\tif(pages.size()>3){\n\t\t\tif(position>1) {\n\t\t\t\tMuPDFPageView previous = pages.get(position - 2);\n\t\t\t\tif(previous!=null){\n\t\t\t\t\tpages.removeAt(pages.indexOfValue(previous));\n\t\t\t\t\tprevious=null;\n\t\t\t\t}\n\t\t\t\tMuPDFPageView post = pages.get(position + 2);\n\t\t\t\tif (post != null) {\n\t\t\t\t\tpages.removeAt(pages.indexOfValue(post));\n\t\t\t\t\tpost = null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tPointF pageSize = mPageSizes.get(position);\n\t\tif (pageSize != null) {\n\t\t\t// We already know the page size. Set it up\n\t\t\t// immediately\n\t\t\tpageView.setPage(position, pageSize);\n\t\t} else {\n\t\t\t// Page size as yet unknown. Blank it for now, and\n\t\t\t// start a background task to find the size\n\t\t\tpageView.blank(position);\n\t\t\tAsyncTask<Void,Void,PointF> sizingTask = new AsyncTask<Void,Void,PointF>() {\n\t\t\t\t@Override\n\t\t\t\tprotected PointF doInBackground(Void... arg0) {\n\t\t\t\t\treturn mCore.getPageSize(position);\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tprotected void onPostExecute(PointF result) {\n\t\t\t\t\tsuper.onPostExecute(result);\n\t\t\t\t\t// We now know the page size\n\t\t\t\t\tmPageSizes.put(position, result);\n\t\t\t\t\t// Check that this view hasn't been reused for\n\t\t\t\t\t// another page since we started\n\t\t\t\t\tif (pageView.getPage() == position)\n\t\t\t\t\t\tpageView.setPage(position, result);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tsizingTask.execute((Void)null);\n\t\t}\n\t\treturn pageView;\n\t}\n\n    public Collection<PdfBitmap> getPdfBitmapList() {\n\t\tif (pdfBitmapList == null) {\n\t\t\tpdfBitmapList = new HashSet<PdfBitmap>();\n\t\t}\n        return pdfBitmapList;\n    }\n\n    public void setPdfBitmapList(Collection<PdfBitmap> pdfBitmapList) {\n        this.pdfBitmapList = pdfBitmapList;\n    }\n\n    public int getNumSignature() {\n        return numSignature;\n    }\n\n    public void setNumSignature(int numSignature) {\n        this.numSignature = numSignature;\n    }\n\n\tpublic void addBitmaps(Set<PdfBitmap> pdfBitmaps) {\n\t\tif (pdfBitmaps != null) {\n\t\t\tfor (PdfBitmap pdfBitmap : pdfBitmaps) {\n\t\t\t\taddBitmap(pdfBitmap);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void addBitmap(PdfBitmap pdfBitmap) {\n\t\tif (pdfBitmap.getType() == PdfBitmap.Type.SIGNATURE) { //mAdapter null ???\n\t\t\tnumSignature = numSignature + 1;\n\t\t}\n\t\tgetPdfBitmapList().add(pdfBitmap);\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/MuPDFPageView.java",
    "content": "package com.artifex.mupdfdemo;\n\nimport java.util.ArrayList;\n\nimport com.artifex.mupdfdemo.MuPDFCore.Cookie;\n\nimport android.annotation.TargetApi;\nimport android.app.AlertDialog;\nimport android.content.ClipData;\nimport android.content.Context;\nimport android.content.DialogInterface;\nimport android.graphics.Bitmap;\nimport android.graphics.Point;\nimport android.graphics.PointF;\nimport android.graphics.RectF;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.text.method.PasswordTransformationMethod;\nimport android.view.LayoutInflater;\nimport android.view.WindowManager;\nimport android.view.inputmethod.EditorInfo;\nimport android.widget.EditText;\n\n/* This enum should be kept in line with the cooresponding C enum in mupdf.c */\nenum SignatureState {\n\tNoSupport,\n\tUnsigned,\n\tSigned\n}\n\nabstract class PassClickResultVisitor {\n\tpublic abstract void visitText(PassClickResultText result);\n\tpublic abstract void visitChoice(PassClickResultChoice result);\n\tpublic abstract void visitSignature(PassClickResultSignature result);\n}\n\nclass PassClickResult {\n\tpublic final boolean changed;\n\n\tpublic PassClickResult(boolean _changed) {\n\t\tchanged = _changed;\n\t}\n\n\tpublic void acceptVisitor(PassClickResultVisitor visitor) {\n\t}\n}\n\nclass PassClickResultText extends PassClickResult {\n\tpublic final String text;\n\n\tpublic PassClickResultText(boolean _changed, String _text) {\n\t\tsuper(_changed);\n\t\ttext = _text;\n\t}\n\n\tpublic void acceptVisitor(PassClickResultVisitor visitor) {\n\t\tvisitor.visitText(this);\n\t}\n}\n\nclass PassClickResultChoice extends PassClickResult {\n\tpublic final String [] options;\n\tpublic final String [] selected;\n\n\tpublic PassClickResultChoice(boolean _changed, String [] _options, String [] _selected) {\n\t\tsuper(_changed);\n\t\toptions = _options;\n\t\tselected = _selected;\n\t}\n\n\tpublic void acceptVisitor(PassClickResultVisitor visitor) {\n\t\tvisitor.visitChoice(this);\n\t}\n}\n\nclass PassClickResultSignature extends PassClickResult {\n\tpublic final SignatureState state;\n\n\tpublic PassClickResultSignature(boolean _changed, int _state) {\n\t\tsuper(_changed);\n\t\tstate = SignatureState.values()[_state];\n\t}\n\n\tpublic void acceptVisitor(PassClickResultVisitor visitor) {\n\t\tvisitor.visitSignature(this);\n\t}\n}\n\npublic class MuPDFPageView extends PageView implements MuPDFView {\n\tfinal private FilePicker.FilePickerSupport mFilePickerSupport;\n\tprivate final MuPDFCore mCore;\n\tprivate AsyncTask<Void,Void,PassClickResult> mPassClick;\n\tprivate RectF mWidgetAreas[];\n\tprivate Annotation mAnnotations[];\n\tprivate int mSelectedAnnotationIndex = -1;\n\tprivate AsyncTask<Void,Void,RectF[]> mLoadWidgetAreas;\n\tprivate AsyncTask<Void,Void,Annotation[]> mLoadAnnotations;\n\tprivate AlertDialog.Builder mTextEntryBuilder;\n\tprivate AlertDialog.Builder mChoiceEntryBuilder;\n\tprivate AlertDialog.Builder mSigningDialogBuilder;\n\tprivate AlertDialog.Builder mSignatureReportBuilder;\n\tprivate AlertDialog.Builder mPasswordEntryBuilder;\n\tprivate EditText mPasswordText;\n\tprivate AlertDialog mTextEntry;\n\tprivate AlertDialog mPasswordEntry;\n\tprivate EditText mEditText;\n\tprivate AsyncTask<String,Void,Boolean> mSetWidgetText;\n\tprivate AsyncTask<String,Void,Void> mSetWidgetChoice;\n\tprivate AsyncTask<PointF[],Void,Void> mAddStrikeOut;\n\tprivate AsyncTask<PointF[][],Void,Void> mAddInk;\n\tprivate AsyncTask<Integer,Void,Void> mDeleteAnnotation;\n\tprivate AsyncTask<Void,Void,String> mCheckSignature;\n\tprivate AsyncTask<Void,Void,Boolean> mSign;\n\tprivate Runnable changeReporter;\n\n\tpublic MuPDFPageView(Context c, FilePicker.FilePickerSupport filePickerSupport, MuPDFCore core, Point parentSize, MuPDFPageAdapter adapter) {\n\t\tsuper(c, parentSize, adapter);\n\t\tmFilePickerSupport = filePickerSupport;\n\t\tmCore = core;\n\t\tmTextEntryBuilder = new AlertDialog.Builder(c);\n\t\tmTextEntryBuilder.setTitle(getContext().getString(R.string.fill_out_text_field));\n\t\tLayoutInflater inflater = (LayoutInflater)c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);\n\t\tmEditText = (EditText)inflater.inflate(R.layout.textentry, null);\n\t\tmTextEntryBuilder.setView(mEditText);\n\t\tmTextEntryBuilder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {\n\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\tdialog.dismiss();\n\t\t\t}\n\t\t});\n\t\tmTextEntryBuilder.setPositiveButton(R.string.okay, new DialogInterface.OnClickListener() {\n\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\tmSetWidgetText = new AsyncTask<String,Void,Boolean> () {\n\t\t\t\t\t@Override\n\t\t\t\t\tprotected Boolean doInBackground(String... arg0) {\n\t\t\t\t\t\treturn mCore.setFocusedWidgetText(mPageNumber, arg0[0]);\n\t\t\t\t\t}\n\t\t\t\t\t@Override\n\t\t\t\t\tprotected void onPostExecute(Boolean result) {\n\t\t\t\t\t\tchangeReporter.run();\n\t\t\t\t\t\tif (!result)\n\t\t\t\t\t\t\tinvokeTextDialog(mEditText.getText().toString());\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tmSetWidgetText.execute(mEditText.getText().toString());\n\t\t\t}\n\t\t});\n\t\tmTextEntry = mTextEntryBuilder.create();\n\n\t\tmChoiceEntryBuilder = new AlertDialog.Builder(c);\n\t\tmChoiceEntryBuilder.setTitle(getContext().getString(R.string.choose_value));\n\n\t\tmSigningDialogBuilder = new AlertDialog.Builder(c);\n\t\tmSigningDialogBuilder.setTitle(\"Select certificate and sign?\");\n\t\tmSigningDialogBuilder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {\n\t\t\t@Override\n\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\tdialog.dismiss();\n\t\t\t}\n\t\t});\n\t\tmSigningDialogBuilder.setPositiveButton(R.string.okay, new DialogInterface.OnClickListener() {\n\t\t\t@Override\n\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\tFilePicker picker = new FilePicker(mFilePickerSupport) {\n\t\t\t\t\t@Override\n\t\t\t\t\tvoid onPick(Uri uri) {\n\t\t\t\t\t\tsignWithKeyFile(uri);\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tpicker.pick();\n\t\t\t}\n\t\t});\n\n\t\tmSignatureReportBuilder = new AlertDialog.Builder(c);\n\t\tmSignatureReportBuilder.setTitle(\"Signature checked\");\n\t\tmSignatureReportBuilder.setPositiveButton(R.string.okay, new DialogInterface.OnClickListener() {\n\t\t\t@Override\n\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\tdialog.dismiss();\n\t\t\t}\n\t\t});\n\n\t\tmPasswordText = new EditText(c);\n\t\tmPasswordText.setInputType(EditorInfo.TYPE_TEXT_VARIATION_PASSWORD);\n\t\tmPasswordText.setTransformationMethod(new PasswordTransformationMethod());\n\n\t\tmPasswordEntryBuilder = new AlertDialog.Builder(c);\n\t\tmPasswordEntryBuilder.setTitle(R.string.enter_password);\n\t\tmPasswordEntryBuilder.setView(mPasswordText);\n\t\tmPasswordEntryBuilder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {\n\t\t\t@Override\n\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\tdialog.dismiss();\n\t\t\t}\n\t\t});\n\n\t\tmPasswordEntry = mPasswordEntryBuilder.create();\n\t}\n\n\tprivate void signWithKeyFile(final Uri uri) {\n\t\tmPasswordEntry.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);\n\t\tmPasswordEntry.setButton(AlertDialog.BUTTON_POSITIVE, \"Sign\", new DialogInterface.OnClickListener() {\n\t\t\t@Override\n\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\tdialog.dismiss();\n\t\t\t\tsignWithKeyFileAndPassword(uri, mPasswordText.getText().toString());\n\t\t\t}\n\t\t});\n\n\t\tmPasswordEntry.show();\n\t}\n\n\tprivate void signWithKeyFileAndPassword(final Uri uri, final String password) {\n\t\tmSign = new AsyncTask<Void,Void,Boolean>() {\n\t\t\t@Override\n\t\t\tprotected Boolean doInBackground(Void... params) {\n\t\t\t\treturn mCore.signFocusedSignature(Uri.decode(uri.getEncodedPath()), password);\n\t\t\t}\n\t\t\t@Override\n\t\t\tprotected void onPostExecute(Boolean result) {\n\t\t\t\tif (result)\n\t\t\t\t{\n\t\t\t\t\tchangeReporter.run();\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tmPasswordText.setText(\"\");\n\t\t\t\t\tsignWithKeyFile(uri);\n\t\t\t\t}\n\t\t\t}\n\n\t\t};\n\n\t\tmSign.execute();\n\t}\n\n\tpublic LinkInfo hitLink(float x, float y) {\n\t\t// Since link highlighting was implemented, the super class\n\t\t// PageView has had sufficient information to be able to\n\t\t// perform this method directly. Making that change would\n\t\t// make MuPDFCore.hitLinkPage superfluous.\n\t\tfloat scale = mSourceScale*(float)getWidth()/(float)mSize.x;\n\t\tfloat docRelX = (x - getLeft())/scale;\n\t\tfloat docRelY = (y - getTop())/scale;\n\n\t\tfor (LinkInfo l: mLinks)\n\t\t\tif (l.rect.contains(docRelX, docRelY))\n\t\t\t\treturn l;\n\n\t\treturn null;\n\t}\n\n\tprivate void invokeTextDialog(String text) {\n\t\tmEditText.setText(text);\n\t\tmTextEntry.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);\n\t\tmTextEntry.show();\n\t}\n\n\tprivate void invokeChoiceDialog(final String [] options) {\n\t\tmChoiceEntryBuilder.setItems(options, new DialogInterface.OnClickListener() {\n\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\tmSetWidgetChoice = new AsyncTask<String,Void,Void>() {\n\t\t\t\t\t@Override\n\t\t\t\t\tprotected Void doInBackground(String... params) {\n\t\t\t\t\t\tString [] sel = {params[0]};\n\t\t\t\t\t\tmCore.setFocusedWidgetChoiceSelected(sel);\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\n\t\t\t\t\t@Override\n\t\t\t\t\tprotected void onPostExecute(Void result) {\n\t\t\t\t\t\tchangeReporter.run();\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tmSetWidgetChoice.execute(options[which]);\n\t\t\t}\n\t\t});\n\t\tAlertDialog dialog = mChoiceEntryBuilder.create();\n\t\tdialog.show();\n\t}\n\n\tprivate void invokeSignatureCheckingDialog() {\n\t\tmCheckSignature = new AsyncTask<Void,Void,String> () {\n\t\t\t@Override\n\t\t\tprotected String doInBackground(Void... params) {\n\t\t\t\treturn mCore.checkFocusedSignature();\n\t\t\t}\n\t\t\t@Override\n\t\t\tprotected void onPostExecute(String result) {\n\t\t\t\tAlertDialog report = mSignatureReportBuilder.create();\n\t\t\t\treport.setMessage(result);\n\t\t\t\treport.show();\n\t\t\t}\n\t\t};\n\n\t\tmCheckSignature.execute();\n\t}\n\n\tprivate void invokeSigningDialog() {\n\t\tAlertDialog dialog = mSigningDialogBuilder.create();\n\t\tdialog.show();\n\t}\n\n\tprivate void warnNoSignatureSupport() {\n\t\tAlertDialog dialog = mSignatureReportBuilder.create();\n\t\tdialog.setTitle(\"App built with no signature support\");\n\t\tdialog.show();\n\t}\n\n\tpublic void setChangeReporter(Runnable reporter) {\n\t\tchangeReporter = reporter;\n\t}\n\n\tpublic Hit passClickEvent(float x, float y) {\n\t\tfloat scale = mSourceScale*(float)getWidth()/(float)mSize.x;\n\t\tfinal float docRelX = (x - getLeft())/scale;\n\t\tfinal float docRelY = (y - getTop())/scale;\n\t\tboolean hit = false;\n\t\tint i;\n\n\t\tif (mAnnotations != null) {\n\t\t\tfor (i = 0; i < mAnnotations.length; i++)\n\t\t\t\tif (mAnnotations[i].contains(docRelX, docRelY)) {\n\t\t\t\t\thit = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\tif (hit) {\n\t\t\t\tswitch (mAnnotations[i].type) {\n\t\t\t\tcase HIGHLIGHT:\n\t\t\t\tcase UNDERLINE:\n\t\t\t\tcase SQUIGGLY:\n\t\t\t\tcase STRIKEOUT:\n\t\t\t\tcase INK:\n\t\t\t\t\tmSelectedAnnotationIndex = i;\n\t\t\t\t\tsetItemSelectBox(mAnnotations[i]);\n\t\t\t\t\treturn Hit.Annotation;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tmSelectedAnnotationIndex = -1;\n\t\tsetItemSelectBox(null);\n\n\t\tif (!mCore.javascriptSupported())\n\t\t\treturn Hit.Nothing;\n\n\t\tif (mWidgetAreas != null) {\n\t\t\tfor (i = 0; i < mWidgetAreas.length && !hit; i++)\n\t\t\t\tif (mWidgetAreas[i].contains(docRelX, docRelY))\n\t\t\t\t\thit = true;\n\t\t}\n\n\t\tif (hit) {\n\t\t\tmPassClick = new AsyncTask<Void,Void,PassClickResult>() {\n\t\t\t\t@Override\n\t\t\t\tprotected PassClickResult doInBackground(Void... arg0) {\n\t\t\t\t\treturn mCore.passClickEvent(mPageNumber, docRelX, docRelY);\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tprotected void onPostExecute(PassClickResult result) {\n\t\t\t\t\tif (result.changed) {\n\t\t\t\t\t\tchangeReporter.run();\n\t\t\t\t\t}\n\n\t\t\t\t\tresult.acceptVisitor(new PassClickResultVisitor() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void visitText(PassClickResultText result) {\n\t\t\t\t\t\t\tinvokeTextDialog(result.text);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void visitChoice(PassClickResultChoice result) {\n\t\t\t\t\t\t\tinvokeChoiceDialog(result.options);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void visitSignature(PassClickResultSignature result) {\n\t\t\t\t\t\t\tswitch (result.state) {\n\t\t\t\t\t\t\tcase NoSupport:\n\t\t\t\t\t\t\t\twarnNoSignatureSupport();\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase Unsigned:\n\t\t\t\t\t\t\t\tinvokeSigningDialog();\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\tcase Signed:\n\t\t\t\t\t\t\t\tinvokeSignatureCheckingDialog();\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tmPassClick.execute();\n\t\t\treturn Hit.Widget;\n\t\t}\n\n\t\treturn Hit.Nothing;\n\t}\n\n\t@TargetApi(11)\n\tpublic boolean copySelection() {\n\t\tfinal StringBuilder text = new StringBuilder();\n\n\t\tprocessSelectedText(new TextProcessor() {\n\t\t\tStringBuilder line;\n\n\t\t\tpublic void onStartLine() {\n\t\t\t\tline = new StringBuilder();\n\t\t\t}\n\n\t\t\tpublic void onWord(TextWord word) {\n\t\t\t\tif (line.length() > 0)\n\t\t\t\t\tline.append(' ');\n\t\t\t\tline.append(word.w);\n\t\t\t}\n\n\t\t\tpublic void onEndLine() {\n\t\t\t\tif (text.length() > 0)\n\t\t\t\t\ttext.append('\\n');\n\t\t\t\ttext.append(line);\n\t\t\t}\n\t\t});\n\n\t\tif (text.length() == 0)\n\t\t\treturn false;\n\n\t\tint currentApiVersion = Build.VERSION.SDK_INT;\n\t\tif (currentApiVersion >= Build.VERSION_CODES.HONEYCOMB) {\n\t\t\tandroid.content.ClipboardManager cm = (android.content.ClipboardManager)mContext.getSystemService(Context.CLIPBOARD_SERVICE);\n\n\t\t\tcm.setPrimaryClip(ClipData.newPlainText(\"MuPDF\", text));\n\t\t} else {\n\t\t\tandroid.text.ClipboardManager cm = (android.text.ClipboardManager)mContext.getSystemService(Context.CLIPBOARD_SERVICE);\n\t\t\tcm.setText(text);\n\t\t}\n\n\t\tdeselectText();\n\n\t\treturn true;\n\t}\n\n\tpublic boolean markupSelection(final Annotation.Type type) {\n\t\tfinal ArrayList<PointF> quadPoints = new ArrayList<PointF>();\n\t\tprocessSelectedText(new TextProcessor() {\n\t\t\tRectF rect;\n\n\t\t\tpublic void onStartLine() {\n\t\t\t\trect = new RectF();\n\t\t\t}\n\n\t\t\tpublic void onWord(TextWord word) {\n\t\t\t\trect.union(word);\n\t\t\t}\n\n\t\t\tpublic void onEndLine() {\n\t\t\t\tif (!rect.isEmpty()) {\n\t\t\t\t\tquadPoints.add(new PointF(rect.left, rect.bottom));\n\t\t\t\t\tquadPoints.add(new PointF(rect.right, rect.bottom));\n\t\t\t\t\tquadPoints.add(new PointF(rect.right, rect.top));\n\t\t\t\t\tquadPoints.add(new PointF(rect.left, rect.top));\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tif (quadPoints.size() == 0)\n\t\t\treturn false;\n\n\t\tmAddStrikeOut = new AsyncTask<PointF[],Void,Void>() {\n\t\t\t@Override\n\t\t\tprotected Void doInBackground(PointF[]... params) {\n\t\t\t\taddMarkup(params[0], type);\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected void onPostExecute(Void result) {\n\t\t\t\tloadAnnotations();\n\t\t\t\tupdate();\n\t\t\t}\n\t\t};\n\n\t\tmAddStrikeOut.execute(quadPoints.toArray(new PointF[quadPoints.size()]));\n\n\t\tdeselectText();\n\n\t\treturn true;\n\t}\n\n\tpublic void deleteSelectedAnnotation() {\n\t\tif (mSelectedAnnotationIndex != -1) {\n\t\t\tif (mDeleteAnnotation != null)\n\t\t\t\tmDeleteAnnotation.cancel(true);\n\n\t\t\tmDeleteAnnotation = new AsyncTask<Integer,Void,Void>() {\n\t\t\t\t@Override\n\t\t\t\tprotected Void doInBackground(Integer... params) {\n\t\t\t\t\tmCore.deleteAnnotation(mPageNumber, params[0]);\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\n\t\t\t\t@Override\n\t\t\t\tprotected void onPostExecute(Void result) {\n\t\t\t\t\tloadAnnotations();\n\t\t\t\t\tupdate();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tmDeleteAnnotation.execute(mSelectedAnnotationIndex);\n\n\t\t\tmSelectedAnnotationIndex = -1;\n\t\t\tsetItemSelectBox(null);\n\t\t}\n\t}\n\n\tpublic void deselectAnnotation() {\n\t\tmSelectedAnnotationIndex = -1;\n\t\tsetItemSelectBox(null);\n\t}\n\n\tpublic boolean saveDraw() {\n\t\tPointF[][] path = getDraw();\n\n\t\tif (path == null)\n\t\t\treturn false;\n\n\t\tif (mAddInk != null) {\n\t\t\tmAddInk.cancel(true);\n\t\t\tmAddInk = null;\n\t\t}\n\t\tmAddInk = new AsyncTask<PointF[][],Void,Void>() {\n\t\t\t@Override\n\t\t\tprotected Void doInBackground(PointF[][]... params) {\n\t\t\t\tmCore.addInkAnnotation(mPageNumber, params[0]);\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected void onPostExecute(Void result) {\n\t\t\t\tloadAnnotations();\n\t\t\t\tupdate();\n\t\t\t}\n\n\t\t};\n\n\t\tmAddInk.execute(getDraw());\n\t\tcancelDraw();\n\n\t\treturn true;\n\t}\n\n\n\t@Override\n\tprotected CancellableTaskDefinition<Void, Void> getDrawPageTask(final Bitmap bm, final int sizeX, final int sizeY,\n\t\t\tfinal int patchX, final int patchY, final int patchWidth, final int patchHeight) {\n\t\treturn new MuPDFCancellableTaskDefinition<Void, Void>(mCore) {\n\t\t\t@Override\n\t\t\tpublic Void doInBackground(Cookie cookie, Void ... params) {\n\t\t\t\t// Workaround bug in Android Honeycomb 3.x, where the bitmap generation count\n\t\t\t\t// is not incremented when drawing.\n\t\t\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB &&\n\t\t\t\t\t\tBuild.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH)\n\t\t\t\t\tbm.eraseColor(0);\n\t\t\t\tmCore.drawPage(bm, mPageNumber, sizeX, sizeY, patchX, patchY, patchWidth, patchHeight, cookie);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t};\n\n\t}\n\n\tprotected CancellableTaskDefinition<Void, Void> getUpdatePageTask(final Bitmap bm, final int sizeX, final int sizeY,\n\t\t\tfinal int patchX, final int patchY, final int patchWidth, final int patchHeight)\n\t{\n\t\treturn new MuPDFCancellableTaskDefinition<Void, Void>(mCore) {\n\n\t\t\t@Override\n\t\t\tpublic Void doInBackground(Cookie cookie, Void ... params) {\n\t\t\t\t// Workaround bug in Android Honeycomb 3.x, where the bitmap generation count\n\t\t\t\t// is not incremented when drawing.\n\t\t\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB &&\n\t\t\t\t\t\tBuild.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH)\n\t\t\t\t\tbm.eraseColor(0);\n\t\t\t\tmCore.updatePage(bm, mPageNumber, sizeX, sizeY, patchX, patchY, patchWidth, patchHeight, cookie);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t};\n\t}\n\n\t@Override\n\tprotected LinkInfo[] getLinkInfo() {\n\t\treturn mCore.getPageLinks(mPageNumber);\n\t}\n\n\t@Override\n\tprotected TextWord[][] getText() {\n\t\treturn mCore.textLines(mPageNumber);\n\t}\n\n\t@Override\n\tprotected void addMarkup(PointF[] quadPoints, Annotation.Type type) {\n\t\tmCore.addMarkupAnnotation(mPageNumber, quadPoints, type);\n\t}\n\n\tprivate void loadAnnotations() {\n\t\tmAnnotations = null;\n\t\tif (mLoadAnnotations != null)\n\t\t\tmLoadAnnotations.cancel(true);\n\t\tmLoadAnnotations = new AsyncTask<Void,Void,Annotation[]> () {\n\t\t\t@Override\n\t\t\tprotected Annotation[] doInBackground(Void... params) {\n\t\t\t\treturn mCore.getAnnoations(mPageNumber);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected void onPostExecute(Annotation[] result) {\n\t\t\t\tmAnnotations = result;\n\t\t\t}\n\t\t};\n\n\t\tmLoadAnnotations.execute();\n\t}\n\n\t@Override\n\tpublic void setPage(final int page, PointF size) {\n\t\tloadAnnotations();\n\n\t\tmLoadWidgetAreas = new AsyncTask<Void,Void,RectF[]> () {\n\t\t\t@Override\n\t\t\tprotected RectF[] doInBackground(Void... arg0) {\n\t\t\t\treturn mCore.getWidgetAreas(page);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected void onPostExecute(RectF[] result) {\n\t\t\t\tmWidgetAreas = result;\n\t\t\t}\n\t\t};\n\n\t\tmLoadWidgetAreas.execute();\n\n\t\tsuper.setPage(page, size);\n\t}\n\n\tpublic void setScale(float scale) {\n\t\t// This type of view scales automatically to fit the size\n\t\t// determined by the parent view groups during layout\n\t}\n\n\t@Override\n\tpublic void releaseResources() {\n\t\tif (mPassClick != null) {\n\t\t\tmPassClick.cancel(true);\n\t\t\tmPassClick = null;\n\t\t}\n\n\t\tif (mLoadWidgetAreas != null) {\n\t\t\tmLoadWidgetAreas.cancel(true);\n\t\t\tmLoadWidgetAreas = null;\n\t\t}\n\n\t\tif (mLoadAnnotations != null) {\n\t\t\tmLoadAnnotations.cancel(true);\n\t\t\tmLoadAnnotations = null;\n\t\t}\n\n\t\tif (mSetWidgetText != null) {\n\t\t\tmSetWidgetText.cancel(true);\n\t\t\tmSetWidgetText = null;\n\t\t}\n\n\t\tif (mSetWidgetChoice != null) {\n\t\t\tmSetWidgetChoice.cancel(true);\n\t\t\tmSetWidgetChoice = null;\n\t\t}\n\n\t\tif (mAddStrikeOut != null) {\n\t\t\tmAddStrikeOut.cancel(true);\n\t\t\tmAddStrikeOut = null;\n\t\t}\n\n\t\tif (mDeleteAnnotation != null) {\n\t\t\tmDeleteAnnotation.cancel(true);\n\t\t\tmDeleteAnnotation = null;\n\t\t}\n\n\t\tsuper.releaseResources();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/MuPDFReaderView.java",
    "content": "package com.artifex.mupdfdemo;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.net.Uri;\nimport android.util.AttributeSet;\nimport android.util.DisplayMetrics;\nimport android.view.MotionEvent;\nimport android.view.ScaleGestureDetector;\nimport android.view.View;\nimport android.view.WindowManager;\n\npublic class MuPDFReaderView extends ReaderView {\n\tenum Mode {Viewing, Selecting, Drawing}\n\tprivate final Context mContext;\n\tprivate boolean mLinksEnabled = false;\n\tprivate Mode mMode = Mode.Viewing;\n\tprivate boolean tapDisabled = false;\n\tprivate int tapPageMargin;\n    \n    private final boolean TAP_PAGING_ENABLED = false;\n\n\tprotected void onTapMainDocArea() {}\n\tprotected void onDocMotion() {}\n\tprotected void onHit(Hit item) {};\n\n\tpublic void setLinksEnabled(boolean b) {\n\t\tmLinksEnabled = b;\n\t\tresetupChildren();\n\t}\n\n\tpublic void setMode(Mode m) {\n\t\tmMode = m;\n\t}\n\n\tprivate void setup()\n\t{\n\t\t// Get the screen size etc to customise tap margins.\n\t\t// We calculate the size of 1 inch of the screen for tapping.\n\t\t// On some devices the dpi values returned are wrong, so we\n\t\t// sanity check it: we first restrict it so that we are never\n\t\t// less than 100 pixels (the smallest Android device screen\n\t\t// dimension I've seen is 480 pixels or so). Then we check\n\t\t// to ensure we are never more than 1/5 of the screen width.\n\t\tDisplayMetrics dm = new DisplayMetrics();\n\t\tWindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);\n\t\twm.getDefaultDisplay().getMetrics(dm);\n\t\ttapPageMargin = (int)dm.xdpi;\n\t\tif (tapPageMargin < 100)\n\t\t\ttapPageMargin = 100;\n\t\tif (tapPageMargin > dm.widthPixels/5)\n\t\t\ttapPageMargin = dm.widthPixels/5;\n\t}\n\n\tpublic MuPDFReaderView(Context context) {\n\t\tsuper(context);\n\t\tmContext = context;\n\t\tsetup();\n\t}\n\n\tpublic MuPDFReaderView(Context context, AttributeSet attrs)\n\t{\n\t\tsuper(context, attrs);\n\t\tmContext = context;\n\t\tsetup();\n\t}\n\n\tpublic boolean onSingleTapUp(MotionEvent e) {\n\t\tLinkInfo link = null;\n\n\t\tif (mMode == Mode.Viewing && !tapDisabled) {\n\t\t\tMuPDFView pageView = (MuPDFView) getDisplayedView();\n\t\t\tHit item = pageView.passClickEvent(e.getX(), e.getY());\n\t\t\tonHit(item);\n\t\t\tif (item == Hit.Nothing) {\n\t\t\t\tif (mLinksEnabled && pageView != null\n\t\t\t\t&& (link = pageView.hitLink(e.getX(), e.getY())) != null) {\n\t\t\t\t\tlink.acceptVisitor(new LinkInfoVisitor() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void visitInternal(LinkInfoInternal li) {\n\t\t\t\t\t\t\t// Clicked on an internal (GoTo) link\n\t\t\t\t\t\t\tsetDisplayedViewIndex(li.pageNumber);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void visitExternal(LinkInfoExternal li) {\n\t\t\t\t\t\t\tIntent intent = new Intent(Intent.ACTION_VIEW, Uri\n\t\t\t\t\t\t\t\t\t.parse(li.url));\n\t\t\t\t\t\t\tmContext.startActivity(intent);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void visitRemote(LinkInfoRemote li) {\n\t\t\t\t\t\t\t// Clicked on a remote (GoToR) link\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t} else if (TAP_PAGING_ENABLED && e.getX() < tapPageMargin) {\n\t\t\t\t\tsuper.smartMoveBackwards();\n\t\t\t\t} else if (TAP_PAGING_ENABLED && e.getX() > super.getWidth() - tapPageMargin) {\n\t\t\t\t\tsuper.smartMoveForwards();\n\t\t\t\t} else if (TAP_PAGING_ENABLED && e.getY() < tapPageMargin) {\n\t\t\t\t\tsuper.smartMoveBackwards();\n\t\t\t\t} else if (TAP_PAGING_ENABLED && e.getY() > super.getHeight() - tapPageMargin) {\n\t\t\t\t\tsuper.smartMoveForwards();\n\t\t\t\t} else {\n\t\t\t\t\tonTapMainDocArea();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn super.onSingleTapUp(e);\n\t}\n\n\t@Override\n\tpublic boolean onDown(MotionEvent e) {\n\n\t\treturn super.onDown(e);\n\t}\n\n\tpublic boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,\n\t\t\tfloat distanceY) {\n\t\tMuPDFView pageView = (MuPDFView)getDisplayedView();\n\t\tswitch (mMode) {\n\t\tcase Viewing:\n\t\t\tif (!tapDisabled)\n\t\t\t\tonDocMotion();\n\n\t\t\treturn super.onScroll(e1, e2, distanceX, distanceY);\n\t\tcase Selecting:\n\t\t\tif (pageView != null)\n\t\t\t\tpageView.selectText(e1.getX(), e1.getY(), e2.getX(), e2.getY());\n\t\t\treturn true;\n\t\tdefault:\n\t\t\treturn true;\n\t\t}\n\t}\n\n\t@Override\n\tpublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,\n\t\t\tfloat velocityY) {\n\t\tswitch (mMode) {\n\t\tcase Viewing:\n\t\t\treturn super.onFling(e1, e2, velocityX, velocityY);\n\t\tdefault:\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tpublic boolean onScaleBegin(ScaleGestureDetector d) {\n\t\t// Disabled showing the buttons until next touch.\n\t\t// Not sure why this is needed, but without it\n\t\t// pinch zoom can make the buttons appear\n\t\ttapDisabled = true;\n\t\treturn super.onScaleBegin(d);\n\t}\n\n\tpublic boolean onTouchEvent(MotionEvent event) {\n\n\t\tif ( mMode == Mode.Drawing )\n\t\t{\n\t\t\tfloat x = event.getX();\n\t\t\tfloat y = event.getY();\n\t\t\tswitch (event.getAction())\n\t\t\t{\n\t\t\t\tcase MotionEvent.ACTION_DOWN:\n\t\t\t\t\ttouch_start(x, y);\n\t\t\t\t\tbreak;\n\t\t\t\tcase MotionEvent.ACTION_MOVE:\n\t\t\t\t\ttouch_move(x, y);\n\t\t\t\t\tbreak;\n\t\t\t\tcase MotionEvent.ACTION_UP:\n\t\t\t\t\ttouch_up();\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tif ((event.getAction() & event.getActionMasked()) == MotionEvent.ACTION_DOWN)\n\t\t{\n\t\t\ttapDisabled = false;\n\t\t}\n\n\t\treturn super.onTouchEvent(event);\n\t}\n\n\tprivate float mX, mY;\n\n\tprivate static final float TOUCH_TOLERANCE = 2;\n\n\tprivate void touch_start(float x, float y) {\n\n\t\tMuPDFView pageView = (MuPDFView)getDisplayedView();\n\t\tif (pageView != null)\n\t\t{\n\t\t\tpageView.startDraw(x, y);\n\t\t}\n\t\tmX = x;\n\t\tmY = y;\n\t}\n\n\tprivate void touch_move(float x, float y) {\n\n\t\tfloat dx = Math.abs(x - mX);\n\t\tfloat dy = Math.abs(y - mY);\n\t\tif (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE)\n\t\t{\n\t\t\tMuPDFView pageView = (MuPDFView)getDisplayedView();\n\t\t\tif (pageView != null)\n\t\t\t{\n\t\t\t\tpageView.continueDraw(x, y);\n\t\t\t}\n\t\t\tmX = x;\n\t\t\tmY = y;\n\t\t}\n\t}\n\n\tprivate void touch_up() {\n\n\t\t// NOOP\n\t}\n\n\tprotected void onChildSetup(int i, View v) {\n\t\tif (SearchTaskResult.get() != null\n\t\t\t\t&& SearchTaskResult.get().pageNumber == i)\n\t\t\t((MuPDFView) v).setSearchBoxes(SearchTaskResult.get().searchBoxes);\n\t\telse\n\t\t\t((MuPDFView) v).setSearchBoxes(null);\n\n\t\t((MuPDFView) v).setLinkHighlighting(mLinksEnabled);\n\n\t\t((MuPDFView) v).setChangeReporter(new Runnable() {\n\t\t\tpublic void run() {\n\t\t\t\tapplyToChildren(new ReaderView.ViewMapper() {\n\t\t\t\t\t@Override\n\t\t\t\t\tvoid applyToView(View view) {\n\t\t\t\t\t\t((MuPDFView) view).update();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t}\n\n\tprotected void onMoveToChild(int i) {\n\t\tif (SearchTaskResult.get() != null\n\t\t\t\t&& SearchTaskResult.get().pageNumber != i) {\n\t\t\tSearchTaskResult.set(null);\n\t\t\tresetupChildren();\n\t\t}\n\t}\n\n\t@Override\n\tprotected void onMoveOffChild(int i) {\n\t\tView v = getView(i);\n\t\tif (v != null)\n\t\t\t((MuPDFView)v).deselectAnnotation();\n\t}\n\n\tprotected void onSettle(View v) {\n\t\t// When the layout has settled ask the page to render\n\t\t// in HQ\n\t\t((MuPDFView) v).updateHq(false);\n\t}\n\n\tprotected void onUnsettle(View v) {\n\t\t// When something changes making the previous settled view\n\t\t// no longer appropriate, tell the page to remove HQ\n\t\t((MuPDFView) v).removeHq();\n\t}\n\n\t@Override\n\tprotected void onNotInUse(View v) {\n\t\t((MuPDFView) v).releaseResources();\n\t}\n\n\t@Override\n\tprotected void onScaleChild(View v, Float scale) {\n\t\t((MuPDFView) v).setScale(scale);\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/MuPDFReflowAdapter.java",
    "content": "package com.artifex.mupdfdemo;\n\nimport android.content.Context;\nimport android.graphics.Point;\nimport android.graphics.PointF;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.BaseAdapter;\n\npublic class MuPDFReflowAdapter extends BaseAdapter {\n\tprivate final Context mContext;\n\tprivate final MuPDFCore mCore;\n\n\tpublic MuPDFReflowAdapter(Context c, MuPDFCore core) {\n\t\tmContext = c;\n\t\tmCore = core;\n\t}\n\n\tpublic int getCount() {\n\t\treturn mCore.countPages();\n\t}\n\n\tpublic Object getItem(int arg0) {\n\t\treturn null;\n\t}\n\n\tpublic long getItemId(int arg0) {\n\t\treturn 0;\n\t}\n\n\tpublic View getView(int position, View convertView, ViewGroup parent) {\n\t\tfinal MuPDFReflowView reflowView;\n\t\tif (convertView == null) {\n\t\t\treflowView = new MuPDFReflowView(mContext, mCore, new Point(parent.getWidth(), parent.getHeight()));\n\t\t} else {\n\t\t\treflowView = (MuPDFReflowView) convertView;\n\t\t}\n\n\t\treflowView.setPage(position, new PointF());\n\n\t\treturn reflowView;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/MuPDFReflowView.java",
    "content": "package com.artifex.mupdfdemo;\n\nimport android.content.Context;\nimport android.graphics.Point;\nimport android.graphics.PointF;\nimport android.graphics.RectF;\nimport android.os.Handler;\nimport android.util.Base64;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.webkit.WebView;\nimport android.webkit.WebViewClient;\n\npublic class MuPDFReflowView extends WebView implements MuPDFView {\n\tprivate final MuPDFCore mCore;\n\tprivate final Handler mHandler;\n\tprivate final Point mParentSize;\n\tprivate int mPage;\n\tprivate float mScale;\n\tprivate int mContentHeight;\n\tAsyncTask<Void,Void,byte[]> mLoadHTML;\n\n\tpublic MuPDFReflowView(Context c, MuPDFCore core, Point parentSize) {\n\t\tsuper(c);\n\t\tmHandler = new Handler();\n\t\tmCore = core;\n\t\tmParentSize = parentSize;\n\t\tmScale = 1.0f;\n\t\tmContentHeight = parentSize.y;\n\t\tgetSettings().setJavaScriptEnabled(true);\n\t\taddJavascriptInterface(new Object(){\n\t\t\tpublic void reportContentHeight(String value) {\n\t\t\t\tmContentHeight = (int)Float.parseFloat(value);\n\t\t\t\tmHandler.post(new Runnable() {\n\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\trequestLayout();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t}, \"HTMLOUT\");\n\t\tsetWebViewClient(new WebViewClient() {\n\t\t\t@Override\n\t\t\tpublic void onPageFinished(WebView view, String url) {\n\t\t\t\tsetScale(mScale);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void requestHeight() {\n\t\t// Get the webview to report the content height via the interface setup\n\t\t// above. Workaround for getContentHeight not working\n\t\tloadUrl(\"javascript:elem=document.getElementById('content');window.HTMLOUT.reportContentHeight(\"+mParentSize.x+\"*elem.offsetHeight/elem.offsetWidth)\");\n\t}\n\n\tpublic void setPage(int page, PointF size) {\n\t\tmPage = page;\n\t\tif (mLoadHTML != null) {\n\t\t\tmLoadHTML.cancel(true);\n\t\t}\n\t\tmLoadHTML = new AsyncTask<Void,Void,byte[]>() {\n\t\t\t@Override\n\t\t\tprotected byte[] doInBackground(Void... params) {\n\t\t\t\treturn mCore.html(mPage);\n\t\t\t}\n\t\t\t@Override\n\t\t\tprotected void onPostExecute(byte[] result) {\n\t\t\t\tString b64 = Base64.encodeToString(result, Base64.DEFAULT);\n\t\t\t\tloadData(b64, \"text/html; charset=utf-8\", \"base64\");\n\t\t\t}\n\t\t};\n\t\tmLoadHTML.execute();\n\t}\n\n\tpublic int getPage() {\n\t\treturn mPage;\n\t}\n\n\tpublic void setScale(float scale) {\n\t\tmScale = scale;\n\t\tloadUrl(\"javascript:document.getElementById('content').style.zoom=\\\"\"+(int)(mScale*100)+\"%\\\"\");\n\t\trequestHeight();\n\t}\n\n\tpublic void blank(int page) {\n\t}\n\n\tpublic Hit passClickEvent(float x, float y) {\n\t\treturn Hit.Nothing;\n\t}\n\n\tpublic LinkInfo hitLink(float x, float y) {\n\t\treturn null;\n\t}\n\n\tpublic void selectText(float x0, float y0, float x1, float y1) {\n\t}\n\n\tpublic void deselectText() {\n\t}\n\n\tpublic boolean copySelection() {\n\t\treturn false;\n\t}\n\n\tpublic boolean markupSelection(Annotation.Type type) {\n\t\treturn false;\n\t}\n\n\tpublic void startDraw(float x, float y) {\n\t}\n\n\tpublic void continueDraw(float x, float y) {\n\t}\n\n\tpublic void cancelDraw() {\n\t}\n\n\tpublic boolean saveDraw() {\n\t\treturn false;\n\t}\n\n\tpublic void setSearchBoxes(RectF[] searchBoxes) {\n\t}\n\n\tpublic void setLinkHighlighting(boolean f) {\n\t}\n\n\tpublic void deleteSelectedAnnotation() {\n\t}\n\n\tpublic void deselectAnnotation() {\n\t}\n\n\tpublic void setChangeReporter(Runnable reporter) {\n\t}\n\n\tpublic void update() {\n\t}\n\n\tpublic void updateHq(boolean update) {\n\t}\n\n\tpublic void removeHq() {\n\t}\n\n\tpublic void releaseResources() {\n\t\tif (mLoadHTML != null) {\n\t\t\tmLoadHTML.cancel(true);\n\t\t\tmLoadHTML = null;\n\t\t}\n\t}\n\n\tpublic void releaseBitmaps() {\n\t}\n\n\t@Override\n\tprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n\t\tint x, y;\n\t\tswitch(MeasureSpec.getMode(widthMeasureSpec)) {\n\t\tcase MeasureSpec.UNSPECIFIED:\n\t\t\tx = mParentSize.x;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tx = MeasureSpec.getSize(widthMeasureSpec);\n\t\t}\n\t\tswitch(MeasureSpec.getMode(heightMeasureSpec)) {\n\t\tcase MeasureSpec.UNSPECIFIED:\n\t\t\ty = mContentHeight;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\ty = MeasureSpec.getSize(heightMeasureSpec);\n\t\t}\n\n\t\tsetMeasuredDimension(x, y);\n\t}\n\n\t@Override\n\tpublic boolean onTouchEvent(MotionEvent ev) {\n\t\t// TODO Auto-generated method stub\n\t\treturn false;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/MuPDFView.java",
    "content": "package com.artifex.mupdfdemo;\n\nimport android.graphics.PointF;\nimport android.graphics.RectF;\n\nenum Hit {Nothing, Widget, Annotation};\n\npublic interface MuPDFView {\n\tpublic void setPage(int page, PointF size);\n\tpublic void setScale(float scale);\n\tpublic int getPage();\n\tpublic void blank(int page);\n\tpublic Hit passClickEvent(float x, float y);\n\tpublic LinkInfo hitLink(float x, float y);\n\tpublic void selectText(float x0, float y0, float x1, float y1);\n\tpublic void deselectText();\n\tpublic boolean copySelection();\n\tpublic boolean markupSelection(Annotation.Type type);\n\tpublic void deleteSelectedAnnotation();\n\tpublic void setSearchBoxes(RectF searchBoxes[]);\n\tpublic void setLinkHighlighting(boolean f);\n\tpublic void deselectAnnotation();\n\tpublic void startDraw(float x, float y);\n\tpublic void continueDraw(float x, float y);\n\tpublic void cancelDraw();\n\tpublic boolean saveDraw();\n\tpublic void setChangeReporter(Runnable reporter);\n\tpublic void update();\n\tpublic void updateHq(boolean update);\n\tpublic void removeHq();\n\tpublic void releaseResources();\n\tpublic void releaseBitmaps();\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/OutlineActivity.java",
    "content": "package com.artifex.mupdfdemo;\n\nimport android.app.ListActivity;\nimport android.os.Bundle;\nimport android.view.View;\nimport android.widget.ListView;\n\npublic class OutlineActivity extends ListActivity {\n\tOutlineItem mItems[];\n\n\t/** Called when the activity is first created. */\n\t@Override\n\tpublic void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\n\t\tmItems = OutlineActivityData.get().items;\n\t\tsetListAdapter(new OutlineAdapter(getLayoutInflater(),mItems));\n\t\t// Restore the position within the list from last viewing\n\t\tgetListView().setSelection(OutlineActivityData.get().position);\n\t\tgetListView().setDividerHeight(0);\n\t\tsetResult(-1);\n\t}\n\n\t@Override\n\tprotected void onListItemClick(ListView l, View v, int position, long id) {\n\t\tsuper.onListItemClick(l, v, position, id);\n\t\tOutlineActivityData.get().position = getListView().getFirstVisiblePosition();\n\t\tsetResult(mItems[position].page);\n\t\tfinish();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/OutlineActivityData.java",
    "content": "package com.artifex.mupdfdemo;\n\npublic class OutlineActivityData {\n\tpublic OutlineItem items[];\n\tpublic int         position;\n\tstatic private OutlineActivityData singleton;\n\n\tstatic public void set(OutlineActivityData d) {\n\t\tsingleton = d;\n\t}\n\n\tstatic public OutlineActivityData get() {\n\t\tif (singleton == null)\n\t\t\tsingleton = new OutlineActivityData();\n\t\treturn singleton;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/OutlineAdapter.java",
    "content": "package com.artifex.mupdfdemo;\n\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.BaseAdapter;\nimport android.widget.TextView;\n\npublic class OutlineAdapter extends BaseAdapter {\n\tprivate final OutlineItem    mItems[];\n\tprivate final LayoutInflater mInflater;\n\tpublic OutlineAdapter(LayoutInflater inflater, OutlineItem items[]) {\n\t\tmInflater = inflater;\n\t\tmItems    = items;\n\t}\n\n\tpublic int getCount() {\n\t\treturn mItems.length;\n\t}\n\n\tpublic Object getItem(int arg0) {\n\t\treturn null;\n\t}\n\n\tpublic long getItemId(int arg0) {\n\t\treturn 0;\n\t}\n\n\tpublic View getView(int position, View convertView, ViewGroup parent) {\n\t\tView v;\n\t\tif (convertView == null) {\n\t\t\tv = mInflater.inflate(R.layout.outline_entry, null);\n\t\t} else {\n\t\t\tv = convertView;\n\t\t}\n\t\tint level = mItems[position].level;\n\t\tif (level > 8) level = 8;\n\t\tString space = \"\";\n\t\tfor (int i=0; i<level;i++)\n\t\t\tspace += \"   \";\n\t\t((TextView)v.findViewById(R.id.title)).setText(space+mItems[position].title);\n\t\t((TextView)v.findViewById(R.id.page)).setText(String.valueOf(mItems[position].page+1));\n\t\treturn v;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/OutlineItem.java",
    "content": "package com.artifex.mupdfdemo;\n\npublic class OutlineItem {\n\tpublic final int    level;\n\tpublic final String title;\n\tpublic final int    page;\n\n\tOutlineItem(int _level, String _title, int _page) {\n\t\tlevel = _level;\n\t\ttitle = _title;\n\t\tpage  = _page;\n\t}\n\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/PageView.java",
    "content": "package com.artifex.mupdfdemo;\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.graphics.Bitmap.Config;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Matrix;\nimport android.graphics.Paint;\nimport android.graphics.Path;\nimport android.graphics.Point;\nimport android.graphics.PointF;\nimport android.graphics.Rect;\nimport android.graphics.RectF;\nimport android.os.Handler;\nimport android.util.Log;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ImageView;\nimport android.widget.ProgressBar;\n\nimport com.artifex.utils.DigitalizedEventCallback;\nimport com.artifex.utils.PdfBitmap;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\n\n// Make our ImageViews opaque to optimize redraw\nclass OpaqueImageView extends ImageView {\n\n    public OpaqueImageView(Context context) {\n        super(context);\n    }\n\n    @Override\n    public boolean isOpaque() {\n        return true;\n    }\n}\n\ninterface TextProcessor {\n    void onStartLine();\n\n    void onWord(TextWord word);\n\n    void onEndLine();\n}\n\nclass TextSelector {\n    final private TextWord[][] mText;\n    final private RectF mSelectBox;\n\n    public TextSelector(TextWord[][] text, RectF selectBox) {\n        mText = text;\n        mSelectBox = selectBox;\n    }\n\n    public void select(TextProcessor tp) {\n        if (mText == null || mSelectBox == null)\n            return;\n\n        ArrayList<TextWord[]> lines = new ArrayList<TextWord[]>();\n        for (TextWord[] line : mText)\n            if (line[0].bottom > mSelectBox.top && line[0].top < mSelectBox.bottom)\n                lines.add(line);\n\n        Iterator<TextWord[]> it = lines.iterator();\n        while (it.hasNext()) {\n            TextWord[] line = it.next();\n            boolean firstLine = line[0].top < mSelectBox.top;\n            boolean lastLine = line[0].bottom > mSelectBox.bottom;\n            float start = Float.NEGATIVE_INFINITY;\n            float end = Float.POSITIVE_INFINITY;\n\n            if (firstLine && lastLine) {\n                start = Math.min(mSelectBox.left, mSelectBox.right);\n                end = Math.max(mSelectBox.left, mSelectBox.right);\n            } else if (firstLine) {\n                start = mSelectBox.left;\n            } else if (lastLine) {\n                end = mSelectBox.right;\n            }\n\n            tp.onStartLine();\n\n            for (TextWord word : line)\n                if (word.right > start && word.left < end)\n                    tp.onWord(word);\n\n            tp.onEndLine();\n        }\n    }\n}\n\npublic abstract class PageView extends ViewGroup {\n    private static final int HIGHLIGHT_COLOR = 0x802572AC;\n    private static final int LINK_COLOR = 0x80AC7225;\n    private static final int BOX_COLOR = 0xFF4444FF;\n    private static final int INK_COLOR = 0xFFFF0000;\n    private static final float INK_THICKNESS = 10.0f;\n    private static final int BACKGROUND_COLOR = 0xFFFFFFFF;\n    private static final int PROGRESS_DIALOG_DELAY = 200;\n    private static final String TAG = \"PageView\";\n\n    private static final int SIGN_HEIGHT = 50;\n    private static final int SIGN_WIDTH = 100;\n\n    protected final Context mContext;\n    protected int mPageNumber;\n    private Point mParentSize; // Size of the view containing the pdf viewer. It could be the same as the screen if this view is full screen.\n    protected Point mSize;   // Size of page at minimum zoom\n    protected float mSourceScale;\n\n    private ImageView mEntire; // Image rendered at minimum zoom\n    private Bitmap mEntireBm; // Bitmap used to draw the entire page at minimum zoom.\n    private Matrix mEntireMat;\n    private AsyncTask<Void, Void, TextWord[][]> mGetText;\n    private AsyncTask<Void, Void, LinkInfo[]> mGetLinkInfo;\n    private CancellableAsyncTask<Void, Void> mDrawEntire;\n\n    private Point mPatchViewSize; // View size on the basis of which the patch was created. After zoom.\n    private Rect mPatchArea; // Area of the screen zoomed.\n    private ImageView mPatch; // Image rendered at zoom resolution.\n    private Bitmap mPatchBm; // Bitmap used to draw the zoomed image.\n    private CancellableAsyncTask<Void, Void> mDrawPatch;\n    private RectF mSearchBoxes[];\n    protected LinkInfo mLinks[];\n    private RectF mSelectBox;\n    private TextWord mText[][];\n    private RectF mItemSelectBox;\n    protected ArrayList<ArrayList<PointF>> mDrawing;\n    private View mSearchView;\n    private boolean mIsBlank;\n    private boolean mHighlightLinks;\n\n    private ProgressBar mBusyIndicator;\n    private final Handler mHandler = new Handler();\n\n    private static boolean flagPositions = true; // Concurrency flag to avoid entering twice onDoubleTap method.\n    private Bitmap signBitmap; // Bitmap for signature at higher resolution. // *BACKWARD COMPATIBILITY*\n    private Point signBitmapSize; // Bitmap size, scaled to screen size and pdf.\n    private static DigitalizedEventCallback eventCallback; // Callback for the app. The library fires an event when the user touched longPress or doubleTap, and the app can manage the behaviour.\n\n    private Paint mBitmapPaint;\n    private MuPDFPageAdapter mAdapter;\n\n    private PointF pdfSize;\n    private PdfBitmap picturePdfBitmap; // *BACKWARD COMPATIBILITY*\n\n    private MuPDFCore core;\n\n    public PageView(Context c, Point parentSize, MuPDFPageAdapter adapter) {\n        super(c);\n        mContext = c;\n        flagPositions = true;\n        mParentSize = parentSize;\n        setBackgroundColor(BACKGROUND_COLOR);\n        mEntireMat = new Matrix();\n        mAdapter = adapter;\n    }\n\n    protected abstract CancellableTaskDefinition<Void, Void> getDrawPageTask(Bitmap bm, int sizeX, int sizeY, int patchX, int patchY, int patchWidth, int patchHeight);\n\n    protected abstract CancellableTaskDefinition<Void, Void> getUpdatePageTask(Bitmap bm, int sizeX, int sizeY, int patchX, int patchY, int patchWidth, int patchHeight);\n\n    protected abstract LinkInfo[] getLinkInfo();\n\n    protected abstract TextWord[][] getText();\n\n    protected abstract void addMarkup(PointF[] quadPoints, Annotation.Type type);\n\n    private void reinit() {\n        // Cancel pending render task\n        if (mDrawEntire != null) {\n            mDrawEntire.cancelAndWait();\n            mDrawEntire = null;\n        }\n\n        if (mDrawPatch != null) {\n            mDrawPatch.cancelAndWait();\n            mDrawPatch = null;\n        }\n\n        if (mGetLinkInfo != null) {\n            mGetLinkInfo.cancel(true);\n            mGetLinkInfo = null;\n        }\n\n        if (mGetText != null) {\n            mGetText.cancel(true);\n            mGetText = null;\n        }\n\n        mIsBlank = true;\n        mPageNumber = 0;\n\n        if (mSize == null)\n            mSize = mParentSize;\n\n        if (mEntire != null) {\n            mEntire.setImageBitmap(null);\n            mEntire.invalidate();\n        }\n\n        if (mPatch != null) {\n            mPatch.setImageBitmap(null);\n            mPatch.invalidate();\n        }\n\n        mPatchViewSize = null;\n        mPatchArea = null;\n\n        mSearchBoxes = null;\n        mLinks = null;\n        mSelectBox = null;\n        mText = null;\n        mItemSelectBox = null;\n    }\n\n    public void releaseResources() {\n        releaseBitmaps();\n\n        reinit();\n\n        if (mBusyIndicator != null) {\n            removeView(mBusyIndicator);\n            mBusyIndicator = null;\n        }\n    }\n\n    public void releaseBitmaps() {\n        if (mEntire != null) {\n            mEntire.setImageBitmap(null);\n            mEntire.invalidate();\n        }\n\n        if (mPatch != null) {\n            mPatch.setImageBitmap(null);\n            mPatch.invalidate();\n        }\n\n        Log.i(TAG, \"Recycle mEntire on releaseBitmaps: \" + mEntireBm);\n        recycleBitmap(mEntireBm);\n        mEntireBm = null;\n        Log.i(TAG, \"Recycle mPathBm on releaseBitmaps: \" + mPatchBm);\n        recycleBitmap(mPatchBm);\n        mPatchBm = null;\n    }\n\n    public void blank(int page) {\n        reinit();\n        mPageNumber = page;\n\n        if (mBusyIndicator == null) {\n            mBusyIndicator = new ProgressBar(mContext);\n            mBusyIndicator.setIndeterminate(true);\n            mBusyIndicator.setBackgroundResource(R.drawable.busy);\n            addView(mBusyIndicator);\n        }\n\n        setBackgroundColor(BACKGROUND_COLOR);\n    }\n\n    public void setPage(int page, PointF size) {\n        pdfSize = correctBugMuPdf(size);\n\n        if (mEntireBm == null) {\n            try {\n                mEntireBm = Bitmap.createBitmap(mParentSize.x, mParentSize.y, Config.ARGB_8888);\n            } catch (OutOfMemoryError e) {\n                e.printStackTrace();\n            }\n        }\n\n        // Cancel pending render task\n        if (mDrawEntire != null) {\n            mDrawEntire.cancelAndWait();\n            mDrawEntire = null;\n        }\n\n        mIsBlank = false;\n        // Highlights may be missing because mIsBlank was true on last draw\n        if (mSearchView != null)\n            mSearchView.invalidate();\n\n        mPageNumber = page;\n        if (mEntire == null) {\n            mEntire = new OpaqueImageView(mContext);\n            mEntire.setScaleType(ImageView.ScaleType.MATRIX);\n            addView(mEntire);\n        }\n\n        // Calculate scaled size that fits within the screen limits\n        // This is the size at minimum zoom\n        mSourceScale = Math.min(mParentSize.x / size.x, mParentSize.y / size.y);\n        Point newSize = new Point((int) (size.x * mSourceScale), (int) (size.y * mSourceScale));\n        mSize = newSize;\n\n        mEntire.setImageBitmap(null);\n        mEntire.invalidate();\n\n        // Get the link info in the background\n        mGetLinkInfo = new AsyncTask<Void, Void, LinkInfo[]>() {\n            protected LinkInfo[] doInBackground(Void... v) {\n                return getLinkInfo();\n            }\n\n            protected void onPostExecute(LinkInfo[] v) {\n                mLinks = v;\n                if (mSearchView != null)\n                    mSearchView.invalidate();\n            }\n        };\n\n        mGetLinkInfo.execute();\n\n        updateEntireCanvas(false);\n\n        if (mSearchView == null) {\n            mSearchView = new View(mContext) {\n                @Override\n                protected void onDraw(final Canvas canvas) {\n                    super.onDraw(canvas);\n                    // Work out current total scale factor\n                    // from source to view\n                    final float scale = mSourceScale * (float) getWidth() / (float) mSize.x;\n                    final Paint paint = new Paint();\n\n                    if (!mIsBlank && mSearchBoxes != null) {\n                        paint.setColor(HIGHLIGHT_COLOR);\n                        for (RectF rect : mSearchBoxes)\n                            canvas.drawRect(rect.left * scale, rect.top * scale,\n                                    rect.right * scale, rect.bottom * scale,\n                                    paint);\n                    }\n\n                    if (!mIsBlank && mLinks != null && mHighlightLinks) {\n                        paint.setColor(LINK_COLOR);\n                        for (LinkInfo link : mLinks)\n                            canvas.drawRect(link.rect.left * scale, link.rect.top * scale,\n                                    link.rect.right * scale, link.rect.bottom * scale,\n                                    paint);\n                    }\n\n                    if (mSelectBox != null && mText != null) {\n                        paint.setColor(HIGHLIGHT_COLOR);\n                        processSelectedText(new TextProcessor() {\n                            RectF rect;\n\n                            public void onStartLine() {\n                                rect = new RectF();\n                            }\n\n                            public void onWord(TextWord word) {\n                                rect.union(word);\n                            }\n\n                            public void onEndLine() {\n                                if (!rect.isEmpty())\n                                    canvas.drawRect(rect.left * scale, rect.top * scale, rect.right * scale, rect.bottom * scale, paint);\n                            }\n                        });\n                    }\n\n                    if (mItemSelectBox != null) {\n                        paint.setStyle(Paint.Style.STROKE);\n                        paint.setColor(BOX_COLOR);\n                        canvas.drawRect(mItemSelectBox.left * scale, mItemSelectBox.top * scale, mItemSelectBox.right * scale, mItemSelectBox.bottom * scale, paint);\n                    }\n\n                    if (mDrawing != null) {\n                        Path path = new Path();\n                        PointF p;\n\n                        paint.setAntiAlias(true);\n                        paint.setDither(true);\n                        paint.setStrokeJoin(Paint.Join.ROUND);\n                        paint.setStrokeCap(Paint.Cap.ROUND);\n\n                        paint.setStyle(Paint.Style.FILL);\n                        paint.setStrokeWidth(INK_THICKNESS * scale);\n                        paint.setColor(INK_COLOR);\n\n                        Iterator<ArrayList<PointF>> it = mDrawing.iterator();\n                        while (it.hasNext()) {\n                            ArrayList<PointF> arc = it.next();\n                            if (arc.size() >= 2) {\n                                Iterator<PointF> iit = arc.iterator();\n                                p = iit.next();\n                                float mX = p.x * scale;\n                                float mY = p.y * scale;\n                                path.moveTo(mX, mY);\n                                while (iit.hasNext()) {\n                                    p = iit.next();\n                                    float x = p.x * scale;\n                                    float y = p.y * scale;\n                                    path.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);\n                                    mX = x;\n                                    mY = y;\n                                }\n                                path.lineTo(mX, mY);\n                            } else {\n                                p = arc.get(0);\n                                canvas.drawCircle(p.x * scale, p.y * scale, INK_THICKNESS * scale / 2, paint);\n                            }\n                        }\n\n                        paint.setStyle(Paint.Style.STROKE);\n                        canvas.drawPath(path, paint);\n                    }\n                }\n            };\n\n            addView(mSearchView);\n        }\n        requestLayout();\n    }\n\n    public void updateEntireCanvas(final boolean updateZoomed) {\n        // Render the page in the background\n        mDrawEntire = new CancellableAsyncTask<Void, Void>(getDrawPageTask(mEntireBm, mSize.x, mSize.y, 0, 0, mSize.x, mSize.y)) {\n\n            @Override\n            public void cancelAndWait() {\n                super.cancelAndWait();\n                flagHQ = false;\n            }\n\n            @Override\n            public void onPreExecute() {\n                setBackgroundColor(BACKGROUND_COLOR);\n                mEntire.setImageBitmap(null);\n                mEntire.invalidate();\n\n                if (mBusyIndicator == null) {\n                    mBusyIndicator = new ProgressBar(mContext);\n                    mBusyIndicator.setIndeterminate(true);\n                    mBusyIndicator.setBackgroundResource(R.drawable.busy);\n                    addView(mBusyIndicator);\n                    mBusyIndicator.setVisibility(INVISIBLE);\n                    mHandler.postDelayed(new Runnable() {\n                        public void run() {\n                            if (mBusyIndicator != null)\n                                mBusyIndicator.setVisibility(VISIBLE);\n                        }\n                    }, PROGRESS_DIALOG_DELAY);\n                }\n            }\n\n            @Override\n            public void onPostExecute(Void result) {\n                removeView(mBusyIndicator);\n                mBusyIndicator = null;\n                mEntire.setImageBitmap(mEntireBm);\n\n                // Draws the signatures on EntireCanvas after changing pages (post loading).\n                if (mEntireBm != null && !mEntireBm.isRecycled()) {\n                    Canvas entireCanvas = new Canvas(mEntireBm);\n                    drawBitmaps(entireCanvas, null, null);\n                }\n\n                if (updateZoomed && (mPatchBm != null) && !mPatchBm.isRecycled()) {\n                    Canvas zoomedCanvas = new Canvas(mPatchBm);\n                    drawBitmaps(zoomedCanvas, mPatchViewSize, mPatchArea);\n                }\n                flagHQ = false;\n                mEntire.invalidate();\n                setBackgroundColor(Color.TRANSPARENT);\n\n            }\n        };\n\n        mDrawEntire.execute();\n\n    }\n\n    public void setSearchBoxes(RectF searchBoxes[]) {\n        mSearchBoxes = searchBoxes;\n        if (mSearchView != null)\n            mSearchView.invalidate();\n    }\n\n    public void setLinkHighlighting(boolean f) {\n        mHighlightLinks = f;\n        if (mSearchView != null)\n            mSearchView.invalidate();\n    }\n\n    public void deselectText() {\n        mSelectBox = null;\n        mSearchView.invalidate();\n    }\n\n    public void selectText(float x0, float y0, float x1, float y1) {\n        float scale = mSourceScale * (float) getWidth() / (float) mSize.x;\n        float docRelX0 = (x0 - getLeft()) / scale;\n        float docRelY0 = (y0 - getTop()) / scale;\n        float docRelX1 = (x1 - getLeft()) / scale;\n        float docRelY1 = (y1 - getTop()) / scale;\n        // Order on Y but maintain the point grouping\n        if (docRelY0 <= docRelY1)\n            mSelectBox = new RectF(docRelX0, docRelY0, docRelX1, docRelY1);\n        else\n            mSelectBox = new RectF(docRelX1, docRelY1, docRelX0, docRelY0);\n\n        if (mSearchView != null)\n            mSearchView.invalidate();\n\n        if (mGetText == null) {\n            mGetText = new AsyncTask<Void, Void, TextWord[][]>() {\n                @Override\n                protected TextWord[][] doInBackground(Void... params) {\n                    return getText();\n                }\n\n                @Override\n                protected void onPostExecute(TextWord[][] result) {\n                    mText = result;\n                    if (mSearchView != null)\n                        mSearchView.invalidate();\n                }\n            };\n\n            mGetText.execute();\n        }\n    }\n\n    public void startDraw(float x, float y) {\n        float scale = mSourceScale * (float) getWidth() / (float) mSize.x;\n        float docRelX = (x - getLeft()) / scale;\n        float docRelY = (y - getTop()) / scale;\n        if (mDrawing == null)\n            mDrawing = new ArrayList<ArrayList<PointF>>();\n\n        ArrayList<PointF> arc = new ArrayList<PointF>();\n        arc.add(new PointF(docRelX, docRelY));\n        mDrawing.add(arc);\n        if (mSearchView != null)\n            mSearchView.invalidate();\n    }\n\n    public void continueDraw(float x, float y) {\n        float scale = mSourceScale * (float) getWidth() / (float) mSize.x;\n        float docRelX = (x - getLeft()) / scale;\n        float docRelY = (y - getTop()) / scale;\n\n        if (mDrawing != null && mDrawing.size() > 0) {\n            ArrayList<PointF> arc = mDrawing.get(mDrawing.size() - 1);\n            arc.add(new PointF(docRelX, docRelY));\n            if (mSearchView != null)\n                mSearchView.invalidate();\n        }\n    }\n\n    public void cancelDraw() {\n        mDrawing = null;\n        if (mSearchView != null)\n            mSearchView.invalidate();\n    }\n\n    protected PointF[][] getDraw() {\n        if (mDrawing == null)\n            return null;\n\n        PointF[][] path = new PointF[mDrawing.size()][];\n\n        for (int i = 0; i < mDrawing.size(); i++) {\n            ArrayList<PointF> arc = mDrawing.get(i);\n            path[i] = arc.toArray(new PointF[arc.size()]);\n        }\n\n        return path;\n    }\n\n    protected void processSelectedText(TextProcessor tp) {\n        (new TextSelector(mText, mSelectBox)).select(tp);\n    }\n\n    public void setItemSelectBox(RectF rect) {\n        mItemSelectBox = rect;\n        if (mSearchView != null)\n            mSearchView.invalidate();\n    }\n\n    @Override\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n        int x, y;\n        switch (MeasureSpec.getMode(widthMeasureSpec)) {\n            case MeasureSpec.UNSPECIFIED:\n                x = mSize.x;\n                break;\n            default:\n                x = MeasureSpec.getSize(widthMeasureSpec);\n        }\n        switch (MeasureSpec.getMode(heightMeasureSpec)) {\n            case MeasureSpec.UNSPECIFIED:\n                y = mSize.y;\n                break;\n            default:\n                y = MeasureSpec.getSize(heightMeasureSpec);\n        }\n\n        setMeasuredDimension(x, y);\n\n        if (mBusyIndicator != null) {\n            int limit = Math.min(mParentSize.x, mParentSize.y) / 2;\n            mBusyIndicator.measure(MeasureSpec.AT_MOST | limit, MeasureSpec.AT_MOST | limit);\n        }\n    }\n\n    @Override\n    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {\n        int w = right - left;\n        int h = bottom - top;\n\n        if (mEntire != null) {\n            if (mEntire.getWidth() != w || mEntire.getHeight() != h) {\n                mEntireMat.setScale(w / (float) mSize.x, h / (float) mSize.y);\n                mEntire.setImageMatrix(mEntireMat);\n                mEntire.invalidate();\n            }\n            mEntire.layout(0, 0, w, h);\n        }\n\n        if (mSearchView != null) {\n            mSearchView.layout(0, 0, w, h);\n        }\n\n        if (mPatchViewSize != null) {\n            if (mPatchViewSize.x != w || mPatchViewSize.y != h) {\n                // Zoomed since patch was created\n                mPatchViewSize = null;\n                mPatchArea = null;\n                if (mPatch != null) {\n                    mPatch.setImageBitmap(null);\n                    mPatch.invalidate();\n                }\n            } else {\n                mPatch.layout(mPatchArea.left, mPatchArea.top, mPatchArea.right, mPatchArea.bottom);\n            }\n        }\n\n        if (mBusyIndicator != null) {\n            int bw = mBusyIndicator.getMeasuredWidth();\n            int bh = mBusyIndicator.getMeasuredHeight();\n\n            mBusyIndicator.layout((w - bw) / 2, (h - bh) / 2, (w + bw) / 2, (h + bh) / 2);\n        }\n    }\n\n    private boolean flagHQ = false;\n    public void updateHq(boolean update) {\n        if(!flagHQ) {\n            flagHQ = true;\n            Rect viewArea = new Rect(getLeft(), getTop(), getRight(), getBottom());\n\n            if (viewArea.width() == mSize.x || viewArea.height() == mSize.y) {\n                // If the viewArea's size matches the unzoomed size, there is no need for an hq patch\n                if (mPatch != null) {\n                    mPatch.setImageBitmap(null);\n                    mPatch.invalidate();\n                }\n                flagHQ = false;\n            } else {\n                final Point patchViewSize = new Point(viewArea.width(), viewArea.height());\n                final Rect patchArea = new Rect(0, 0, mParentSize.x, mParentSize.y);\n\n                // Intersect and test that there is an intersection\n                if (!patchArea.intersect(viewArea)) {\n                    flagHQ = false;\n                    return;\n                }\n\n                // Offset patch area to be relative to the view top left\n                patchArea.offset(-viewArea.left, -viewArea.top);\n\n                boolean area_unchanged = patchArea.equals(mPatchArea) && patchViewSize.equals(mPatchViewSize);\n\n                // If being asked for the same area as last time and not because of an update then nothing to do\n//            if (area_unchanged && !update)\n//                return;\n//\n//            boolean completeRedraw = !(area_unchanged && update);\n                boolean completeRedraw = !area_unchanged || update;\n\n                // Stop the drawing of previous patch if still going\n                if (mDrawPatch != null) {\n                    mDrawPatch.cancelAndWait();\n                    mDrawPatch = null;\n                }\n\n                // Create and add the image view if not already done\n                if (mPatch == null) {\n                    mPatch = new OpaqueImageView(mContext);\n                    mPatch.setScaleType(ImageView.ScaleType.MATRIX);\n                    addView(mPatch);\n                    if (mSearchView != null) {\n                        mSearchView.bringToFront();\n                    }\n                }\n\n                CancellableTaskDefinition<Void, Void> task;\n\n                final Bitmap oldPatchBm = mPatchBm;\n                try {\n                    int mPatchAreaHeight = patchArea.bottom - patchArea.top;\n                    int mPatchAreaWidth = patchArea.right - patchArea.left;\n                    mPatchBm = Bitmap.createBitmap(mPatchAreaWidth, mPatchAreaHeight, Bitmap.Config.ARGB_8888);\n                    Log.i(TAG, \"Recycle oldPatchBm on updateHQ: \" + oldPatchBm);\n                    cancelDraw();\n                } catch (OutOfMemoryError e) {\n                    Log.e(TAG, e.getMessage(), e);\n                    flagHQ = false;\n                }\n\n                if (completeRedraw)\n                    task = getDrawPageTask(mPatchBm, patchViewSize.x, patchViewSize.y,\n                            patchArea.left, patchArea.top,\n                            patchArea.width(), patchArea.height());\n                else\n                    task = getUpdatePageTask(mPatchBm, patchViewSize.x, patchViewSize.y,\n                            patchArea.left, patchArea.top,\n                            patchArea.width(), patchArea.height());\n\n                mDrawPatch = new CancellableAsyncTask<Void, Void>(task) {\n\n                    @Override\n                    public void cancelAndWait() {\n                        super.cancelAndWait();\n                        flagHQ = false;\n                    }\n\n                    public void onPostExecute(Void result) {\n                        mPatchViewSize = patchViewSize;\n                        mPatchArea = patchArea;\n\n                        if (mPatchBm != null && !mPatchBm.isRecycled()) {\n                            Canvas zoomedCanvas = new Canvas(mPatchBm);\n                            drawBitmaps(zoomedCanvas, mPatchViewSize, mPatchArea);\n                            mPatch.setImageBitmap(mPatchBm);\n                            mPatch.invalidate();\n                        }\n\n                        //requestLayout();\n                        // Calling requestLayout here doesn't lead to a later call to layout. No idea\n                        // why, but apparently others have run into the problem.\n                        mPatch.layout(mPatchArea.left, mPatchArea.top, mPatchArea.right, mPatchArea.bottom);\n\n                        if (mPatchBm != null && !mPatchBm.equals(oldPatchBm)) {\n                            recycleBitmap(oldPatchBm);\n                        }\n                        flagHQ = false;\n                    }\n                };\n\n                mDrawPatch.execute();\n            }\n        }\n    }\n\n    public void update() {\n        // Cancel pending render task\n        if (mDrawEntire != null) {\n            mDrawEntire.cancelAndWait();\n            mDrawEntire = null;\n        }\n\n        if (mDrawPatch != null) {\n            mDrawPatch.cancelAndWait();\n            mDrawPatch = null;\n        }\n\n        mDrawEntire = new CancellableAsyncTask<Void, Void>(getUpdatePageTask(mEntireBm, mSize.x, mSize.y, 0, 0, mSize.x, mSize.y)) {\n\n            @Override\n            public void cancelAndWait() {\n                super.cancelAndWait();\n                flagHQ = false;\n            }\n\n            public void onPostExecute(Void result) {\n                if (mEntireBm != null && !mEntireBm.isRecycled()) {\n                    Canvas entireCanvas = new Canvas(mEntireBm);\n                    drawBitmaps(entireCanvas, null, null);\n                    mEntire.setImageBitmap(mEntireBm);\n                    mEntire.invalidate();\n                    flagHQ=false;\n                }\n            }\n        };\n\n        mDrawEntire.execute();\n\n        updateHq(true);\n    }\n\n    public void removeHq() {\n        // Stop the drawing of the patch if still going\n        if (mDrawPatch != null) {\n            mDrawPatch.cancelAndWait();\n            mDrawPatch = null;\n        }\n\n        // And get rid of it\n        mPatchViewSize = null;\n        mPatchArea = null;\n        if (mPatch != null) {\n            mPatch.setImageBitmap(null);\n            mPatch.invalidate();\n        }\n        flagHQ = false;\n    }\n\n    public int getPage() {\n        return mPageNumber;\n    }\n\n    @Override\n    public boolean isOpaque() {\n        return true;\n    }\n\n    protected void redrawEntireBitmaps() {\n        if (mEntireBm != null && !mEntireBm.isRecycled()) {\n            Canvas entireCanvas = new Canvas(mEntireBm);\n            drawBitmaps(entireCanvas, null, null);\n            mEntire.setImageBitmap(mEntireBm);\n            mEntire.invalidate();\n        }\n    }\n\n    private void redrawZoomedBitmaps() {\n        if (mPatchBm != null && !mPatchBm.isRecycled()) {\n            Canvas zoomedCanvas = new Canvas(mPatchBm);\n            drawBitmaps(zoomedCanvas, mPatchViewSize, mPatchArea);\n            mPatch.setImageBitmap(mPatchBm);\n            mPatch.invalidate();\n        }\n    }\n\n\n    public boolean removeBitmapOnPosition(Point point) {\n        boolean removed = false;\n        switch (removeIfExistSign(point)) {\n            case -1:\n                removed = false;\n                break;\n            case 0:\n                removed = true;\n                break;\n            case 1:\n                removed = true;\n                break;\n        }\n        //Forzamos pintado de pantalla\n        invalidate();\n\n        return removed;\n    }\n\n    public void onLongPress(MotionEvent e, float mScale) {\n        if (eventCallback != null) {\n            float x = e.getX();\n            float y = e.getY();\n\n            //Comprobamos si ha picado dentro o fuera del espacio del pdf\n            if (x < getLeft() || x > getRight()) {\n                eventCallback.error(DigitalizedEventCallback.ERROR_OUTSIDE_HORIZONTAL);\n            }\n\n            if (y < getTop() || y > getBottom()) {\n                eventCallback.error(DigitalizedEventCallback.ERROR_OUTSIDE_VERTICAL);\n            }\n\n            float[] coords = translateCoords(mScale, x, y);\n            if (coords != null) {\n                eventCallback.longPressOnPdfPosition(mPageNumber, coords[0], coords[1], coords[2], coords[3]);\n            }\n        }\n    }\n\n    public void onSingleTap(MotionEvent e, float mScale) {\n        if (eventCallback != null) {\n            float x = e.getX();\n            float y = e.getY();\n\n            //Comprobamos si ha picado dentro o fuera del espacio del pdf\n            if (x < getLeft() || x > getRight()) {\n                eventCallback.error(DigitalizedEventCallback.ERROR_OUTSIDE_HORIZONTAL);\n            }\n\n            if (y < getTop() || y > getBottom()) {\n                eventCallback.error(DigitalizedEventCallback.ERROR_OUTSIDE_VERTICAL);\n            }\n\n            float[] coords = translateCoords(mScale, x, y);\n            if (coords != null) {\n                eventCallback.singleTapOnPdfPosition(mPageNumber, coords[0], coords[1], coords[2], coords[3]);\n            }\n        }\n    }\n\n    private float[] translateCoords(float mScale, float x, float y) {\n        float screenX, screenY, percentX, percentY;\n\n        if (pdfSize != null && mSize != null) {\n            //Factor de corrección por si se gira\n            float factorRotationX = ((float) mSize.x / (float) getWidth()) * mScale;\n            float factorRotationY = ((float) mSize.y / (float) getHeight()) * mScale;\n\n            //Posicion en la pantalla respecto a las coordenadas del pdf (el 0.0 es la esquina arriba izquierda del pdf). Usado para poder dibujar las firmas encima del PDF. En esta representación, el PDF tendría de alto valores similares al alto de la pantalla en la que se muestra.\n            screenX = ((x - getLeft()) / mScale) * factorRotationX;\n            screenY = ((y - getTop()) / mScale) * factorRotationY;\n\n            // Calculamos posicion en el pdf. No se usa en la visualización, pero es necesario para conocer la posición. En esta representación, el alto del pdf será de unos 900 píxeles, y no variará se muestre donde se muestre.\n            percentX = (x - getLeft()) / getWidth();\n            percentY = (y - getTop()) / getHeight(); //Se coge la posicion en porcentaje\n\n            float pdfX = percentX * pdfSize.x; //Se calcula X el punto en el pdf\n            float pdfY = (1 - percentY) * pdfSize.y;//Se calcula Y\n\n            // Proportions: screenX / mSize.x == pdfX / pdfSize.x !!!\n            return new float[]{screenX, screenY, pdfX, pdfY};\n        } else {\n            return null;\n        }\n    }\n\n    private float[] pdfCoordsToScreen(float pdfX, float pdfY) {\n        float screenX = (pdfX * mSize.x) / pdfSize.x;\n        float screenY = ((pdfSize.y - pdfY) * mSize.y) / pdfSize.y;\n        return new float[]{screenX, screenY};\n    }\n\n    public boolean onDoubleTap(MotionEvent e, float mScale) {\n\n        if (flagPositions) {\n            flagPositions = false;\n\n            float x = e.getX();\n            float y = e.getY();\n\n            //Comprobamos si ha picado dentro o fuera del espacio del pdf\n            if (x < getLeft() || x > getRight()) {\n                flagPositions = true;\n                if (eventCallback != null) {\n                    eventCallback.error(DigitalizedEventCallback.ERROR_OUTSIDE_HORIZONTAL);\n                }\n                return true;\n            }\n            if (y < getTop() || y > getBottom()) {\n                flagPositions = true;\n                if (eventCallback != null) {\n                    eventCallback.error(DigitalizedEventCallback.ERROR_OUTSIDE_VERTICAL);\n                }\n                return true;\n            }\n\n            float[] coords = translateCoords(mScale, x, y);\n            if (coords != null) {\n                float screenX = coords[0];\n                float screenY = coords[1];\n                float pdfX = coords[2];\n                float pdfY = coords[3];\n\n                if (eventCallback != null) {\n                    flagPositions = true;\n                    eventCallback.doubleTapOnPdfPosition(mPageNumber, screenX, screenY, pdfX, pdfY);\n                    return true;\n                }\n\n                //Salvamos la posicion donde se ha elegido estampar la firma\n                Point point = new Point((int) screenX, (int) screenY);\n                boolean removed = removeBitmapOnPosition(point);\n\n                if (signBitmap != null && signBitmapSize != null && !removed) {\n                    PdfBitmap newPdfBitmap = new PdfBitmap(signBitmap, SIGN_WIDTH, SIGN_HEIGHT, (int) screenX, (int) screenY, mPageNumber, PdfBitmap.Type.SIGNATURE);\n                    mAdapter.getPdfBitmapList().add(newPdfBitmap);\n                    mAdapter.setNumSignature(mAdapter.getNumSignature() + 1);\n                }\n            }\n            flagPositions = true;\n        }\n        return true;\n    }\n\n    /**\n     * Check if a Bitmap exists in the point coordinates, and remove it.\n     *\n     * @param screenPoint Point for the pdf to check\n     * @return\n     */\n    private int removeIfExistSign(Point screenPoint) {\n        PdfBitmap toRemove = null;\n        for (PdfBitmap pdfBitmap : mAdapter.getPdfBitmapList()) {\n            if (pdfBitmap.getPageNumber() == mPageNumber) {\n                float[] scaledSize = scaledSize(pdfBitmap.getWidth(), pdfBitmap.getHeight());\n                int originalW = (int) scaledSize[0];\n                int originalH = (int) scaledSize[1];\n\n                float[] screenCoords = pdfCoordsToScreen(pdfBitmap.getPdfX(), pdfBitmap.getPdfY());\n                int screenX = (int) screenCoords[0];\n                int screenY = (int) screenCoords[1];\n\n                Rect r = new Rect(screenX - (originalW / 2), screenY + (originalH / 2), screenX + (originalW / 2), screenY - (originalH / 2));\n                if (screenPoint.x > r.left && screenPoint.x < r.right && screenPoint.y < r.top && screenPoint.y > r.bottom) {\n                    toRemove = pdfBitmap;\n\n                    boolean indexOf = mAdapter.getPdfBitmapList().contains(toRemove);\n                    if (indexOf && toRemove.isRemovable()) {\n                        mAdapter.getPdfBitmapList().remove(toRemove);\n                        mAdapter.setNumSignature(mAdapter.getNumSignature() - 1);\n\n                        // We need to remove the previous entireBm (with the bitmaps added), and create a new one empty (the bitmaps will be added on update)\n                        Log.i(TAG, \"Recycle mEntire on removeIfExistSign: \" + mEntireBm);\n                        final Bitmap oldEntireBm = mEntireBm;\n                        try {\n                            mEntireBm = Bitmap.createBitmap(mParentSize.x, mParentSize.y, Bitmap.Config.ARGB_8888);\n                            updateEntireCanvas(true);\n                            updateHq(true);\n                        } catch (OutOfMemoryError e) {\n                            Log.e(TAG, e.getMessage(), e);\n                        }\n                        if(oldEntireBm!=null && !oldEntireBm.equals(mEntireBm)) {\n                            recycleBitmap(oldEntireBm);\n                        }\n                        // Bitmap removed\n                        return 0;\n                    }\n                }\n            }\n        }\n        // No bitmap removed\n        return -1;\n    }\n\n    @Override\n    protected void onDraw(Canvas canvas) {\n        super.onDraw(canvas);\n    }\n\n    /**\n     * Por defecto la medida de pagina que devuelve MuPdf parece ser dos veces superior al correcto\n     *\n     * @param size\n     * @return\n     */\n    private PointF correctBugMuPdf(PointF size) {\n        return new PointF(size.x / 2, size.y / 2);\n    }\n\n    public DigitalizedEventCallback getEventCallback() {\n        return eventCallback;\n    }\n\n    public void setEventCallback(DigitalizedEventCallback eventCallback) {\n        this.eventCallback = eventCallback;\n    }\n\n    private void drawBitmaps(Canvas canvas, Point patchViewSize, Rect patchArea) {\n        // Sólo ejecutamos este código en caso de que tengamos un Bitmap de firma:\n        for (PdfBitmap pdfBitmap : mAdapter.getPdfBitmapList()) {\n\n            float[] scaledSize = scaledSize(pdfBitmap.getWidth(), pdfBitmap.getHeight());\n\n            float originalW = scaledSize[0];\n            float originalH = scaledSize[1];\n            float zoomRatio = patchViewSize != null ? (float) patchViewSize.y / (float) mSize.y : 1.0f;\n            float newWidth = originalW * zoomRatio;\n            float newHeight = originalH * zoomRatio;\n\n            if (pdfBitmap.getPageNumber() == getPage()) {\n\n                float[] screenCoords = pdfCoordsToScreen(pdfBitmap.getPdfX(), pdfBitmap.getPdfY());\n\n                float newGlobalPosX = (screenCoords[0] * zoomRatio);\n                float newGlobalPosY = (screenCoords[1] * zoomRatio);\n                float newZoomPosX = patchArea != null ? newGlobalPosX - patchArea.left : newGlobalPosX;\n                float newZoomPosY = patchArea != null ? newGlobalPosY - patchArea.top : newGlobalPosY;\n\n                float leftGlobalMargin = newGlobalPosX - newWidth / 2;\n                float rightGlobalMargin = newGlobalPosX + newWidth / 2;\n                float topGlobalMargin = newGlobalPosY - newHeight / 2;\n                float bottomGlobalMargin = newGlobalPosY + newHeight / 2;\n\n                Rect signZoomedRect = new Rect(\n                        (int) newZoomPosX - (int) newWidth / 2,\n                        (int) newZoomPosY - (int) newHeight / 2,\n                        (int) newZoomPosX + (int) newWidth / 2,\n                        (int) newZoomPosY + (int) newHeight / 2);\n\n                boolean outside;\n                if (patchArea == null) {\n                    outside = false;\n                } else {\n                    outside = (rightGlobalMargin <= patchArea.left ||\n                            leftGlobalMargin >= patchArea.right ||\n                            topGlobalMargin >= patchArea.bottom ||\n                            bottomGlobalMargin <= patchArea.top);\n                }\n\n                if (!outside) {\n                    Bitmap bitmap = pdfBitmap.getBitmapImage();\n                    try {\n                        if (!isBitmapRecycled(bitmap)) {\n                            canvas.drawBitmap(bitmap, new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()), signZoomedRect, mBitmapPaint);\n                            canvas.save();\n                        } else {\n                            Log.i(TAG, \"Avoided using recycled bitmap\");\n                        }\n                    } catch (RuntimeException e) {\n                        Log.e(TAG, e.getLocalizedMessage(), e);\n                    }\n                }\n            }\n        }\n    }\n\n    private float[] scaledSize(int width, int height) {\n        float x = 0, y = 0;\n        if (pdfSize != null && mSize != null) {\n            x = (width * mSize.x) / pdfSize.x;\n            y = (height * mSize.y) / pdfSize.y;\n        }\n        return new float[]{x, y};\n    }\n\n    public void setParentSize(Point parentSize) {\n        this.mParentSize = parentSize;\n    }\n\n    public boolean isBitmapRecycled(Bitmap bitmap) {\n        if (android.os.Build.VERSION.SDK_INT < 17) {\n            return bitmap.isRecycled();\n        } else {\n            return bitmap.isRecycled() || (!bitmap.isPremultiplied() && bitmap.getConfig() == Bitmap.Config.ARGB_8888 && bitmap.hasAlpha());\n        }\n    }\n\n    public void recycleBitmap(Bitmap bitmap) {\n        if (bitmap != null) {\n            Log.d(TAG, \"Recycling bitmap \" + bitmap.toString());\n            bitmap.recycle();\n            if(!bitmap.isRecycled()){\n                Log.e(TAG, \"NOT Recycled bitmap \" + bitmap.toString());\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/PrintDialogActivity.java",
    "content": "package com.artifex.mupdfdemo;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.InputStream;\n\nimport android.app.Activity;\nimport android.content.ActivityNotFoundException;\nimport android.content.ContentResolver;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.util.Base64;\nimport android.webkit.WebSettings;\nimport android.webkit.WebView;\nimport android.webkit.WebViewClient;\n\npublic class PrintDialogActivity extends Activity {\n\tprivate static final String PRINT_DIALOG_URL = \"https://www.google.com/cloudprint/dialog.html\";\n\tprivate static final String JS_INTERFACE = \"AndroidPrintDialog\";\n\tprivate static final String CONTENT_TRANSFER_ENCODING = \"base64\";\n\n\tprivate static final String ZXING_URL = \"http://zxing.appspot.com\";\n\tprivate static final int ZXING_SCAN_REQUEST = 65743;\n\n\t/**\n\t * Post message that is sent by Print Dialog web page when the printing dialog\n\t * needs to be closed.\n\t */\n\tprivate static final String CLOSE_POST_MESSAGE_NAME = \"cp-dialog-on-close\";\n\n\t/**\n\t * Web view element to show the printing dialog in.\n\t */\n\tprivate WebView dialogWebView;\n\n\t/**\n\t * Intent that started the action.\n\t */\n\tIntent cloudPrintIntent;\n\n\tprivate int resultCode;\n\n\t@Override\n\tpublic void onCreate(Bundle icicle) {\n\t\tsuper.onCreate(icicle);\n\n\t\tresultCode = RESULT_OK;\n\t\tsetContentView(R.layout.print_dialog);\n\t\tdialogWebView = (WebView) findViewById(R.id.webview);\n\t\tcloudPrintIntent = this.getIntent();\n\n\t\tWebSettings settings = dialogWebView.getSettings();\n\t\tsettings.setJavaScriptEnabled(true);\n\n\t\tdialogWebView.setWebViewClient(new PrintDialogWebClient());\n\t\tdialogWebView.addJavascriptInterface(\n\t\t\tnew PrintDialogJavaScriptInterface(), JS_INTERFACE);\n\n\t\tdialogWebView.loadUrl(PRINT_DIALOG_URL);\n\t}\n\n\t@Override\n\tpublic void onActivityResult(int requestCode, int resultCode, Intent intent) {\n\t\tif (requestCode == ZXING_SCAN_REQUEST && resultCode == RESULT_OK) {\n\t\t\tdialogWebView.loadUrl(intent.getStringExtra(\"SCAN_RESULT\"));\n\t\t}\n\t}\n\n\tfinal class PrintDialogJavaScriptInterface {\n\t\tpublic String getType() {\n\t\t\treturn cloudPrintIntent.getType();\n\t\t}\n\n\t\tpublic String getTitle() {\n\t\t\treturn cloudPrintIntent.getExtras().getString(\"title\");\n\t\t}\n\n\t\tpublic String getContent() {\n\t\t\ttry {\n\t\t\t\tContentResolver contentResolver = getContentResolver();\n\t\t\t\tInputStream is = contentResolver.openInputStream(cloudPrintIntent.getData());\n\t\t\t\tByteArrayOutputStream baos = new ByteArrayOutputStream();\n\n\t\t\t\tbyte[] buffer = new byte[4096];\n\t\t\t\tint n = is.read(buffer);\n\t\t\t\twhile (n >= 0) {\n\t\t\t\t\tbaos.write(buffer, 0, n);\n\t\t\t\t\tn = is.read(buffer);\n\t\t\t\t}\n\t\t\t\tis.close();\n\t\t\t\tbaos.flush();\n\n\t\t\t\treturn Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT);\n\t\t\t} catch (Throwable e) {\n\t\t\t\tresultCode = RESULT_CANCELED;\n\t\t\t\tsetResult(resultCode);\n\t\t\t\tfinish();\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t\treturn \"\";\n\t\t}\n\n\t\tpublic String getEncoding() {\n\t\t\treturn CONTENT_TRANSFER_ENCODING;\n\t\t}\n\n\t\tpublic void onPostMessage(String message) {\n\t\t\tif (message.startsWith(CLOSE_POST_MESSAGE_NAME)) {\n\t\t\t\tsetResult(resultCode);\n\t\t\t\tfinish();\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate final class PrintDialogWebClient extends WebViewClient {\n\t\t@Override\n\t\tpublic boolean shouldOverrideUrlLoading(WebView view, String url) {\n\t\t\tif (url.startsWith(ZXING_URL)) {\n\t\t\t\tIntent intentScan = new Intent(\"com.google.zxing.client.android.SCAN\");\n\t\t\t\tintentScan.putExtra(\"SCAN_MODE\", \"QR_CODE_MODE\");\n\t\t\t\ttry {\n\t\t\t\t\tstartActivityForResult(intentScan, ZXING_SCAN_REQUEST);\n\t\t\t\t} catch (ActivityNotFoundException error) {\n\t\t\t\t\tview.loadUrl(url);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tview.loadUrl(url);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t@Override\n\t\tpublic void onPageFinished(WebView view, String url) {\n\t\t\tif (PRINT_DIALOG_URL.equals(url)) {\n\t\t\t\t// Submit print document.\n\t\t\t\tview.loadUrl(\"javascript:printDialog.setPrintDocument(printDialog.createPrintDocument(\"\n\t\t\t\t\t+ \"window.\" + JS_INTERFACE + \".getType(),window.\" + JS_INTERFACE + \".getTitle(),\"\n\t\t\t\t\t+ \"window.\" + JS_INTERFACE + \".getContent(),window.\" + JS_INTERFACE + \".getEncoding()))\");\n\n\t\t\t\t// Add post messages listener.\n\t\t\t\tview.loadUrl(\"javascript:window.addEventListener('message',\"\n\t\t\t\t\t\t+ \"function(evt){window.\" + JS_INTERFACE + \".onPostMessage(evt.data)}, false)\");\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/ReaderView.java",
    "content": "package com.artifex.mupdfdemo;\n\nimport android.content.Context;\nimport android.graphics.Point;\nimport android.graphics.Rect;\nimport android.os.SystemClock;\nimport androidx.core.view.MotionEventCompat;\nimport android.util.AttributeSet;\nimport android.util.SparseArray;\nimport android.view.GestureDetector;\nimport android.view.MotionEvent;\nimport android.view.ScaleGestureDetector;\nimport android.view.View;\nimport android.widget.Adapter;\nimport android.widget.AdapterView;\nimport android.widget.Scroller;\n\nimport com.artifex.utils.DigitalizedEventCallback;\nimport com.artifex.utils.PdfBitmap;\n\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.LinkedList;\nimport java.util.NoSuchElementException;\n\npublic class ReaderView\n\t\textends AdapterView<Adapter>\n\t\timplements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener, ScaleGestureDetector.OnScaleGestureListener, Runnable {\n\tprivate static final int  MOVING_DIAGONALLY = 0;\n\tprivate static final int  MOVING_LEFT       = 1;\n\tprivate static final int  MOVING_RIGHT      = 2;\n\tprivate static final int  MOVING_UP         = 3;\n\tprivate static final int  MOVING_DOWN       = 4;\n\n\tprivate static final int  FLING_MARGIN      = 100;\n\tprivate static final int  GAP               = 20;\n\n\tprivate static final float MIN_SCALE        = 1.0f;\n\tprivate static final float MAX_SCALE        = 3.0f;\n\tprivate static final float REFLOW_SCALE_FACTOR = 0.5f;\n\n\tprivate static final boolean HORIZONTAL_SCROLLING = true;\n\n\tprivate Adapter           mAdapter;\n\tprivate int               mCurrent;    // Adapter's index for the current view\n\tprivate boolean           mResetLayout;\n\tprivate final SparseArray<View>\n\t\t\t\t  mChildViews = new SparseArray<View>(3);\n\t\t\t\t\t       // Shadows the children of the adapter view\n\t\t\t\t\t       // but with more sensible indexing\n\tprivate final LinkedList<View> mViewCache = new LinkedList<View>();\n\tprivate boolean           mUserInteracting;  // Whether the user is interacting\n\tprivate boolean           mScaling;    // Whether the user is currently pinch zooming\n\tprivate float             mScale     = 1.0f;\n\tprivate int               mXScroll;    // Scroll amounts recorded from events.\n\tprivate int               mYScroll;    // and then accounted for in onLayout\n\tprivate boolean           mReflow = false;\n\tprivate boolean           mReflowChanged = false;\n\tprivate final GestureDetector\n\t\t\t\t  mGestureDetector;\n\tprivate final ScaleGestureDetector\n\t\t\t\t  mScaleGestureDetector;\n\tprivate final Scroller    mScroller;\n\tprivate final Stepper     mStepper;\n\tprivate int               mScrollerLastX;\n\tprivate int               mScrollerLastY;\n\n    private PageView currentPage;\n    private DigitalizedEventCallback eventCallback;\n\n    private float             mLastTouchX;\n    private float             mLastTouchY;\n\n\tprivate Collection<PdfBitmap> pdfBitmaps;\n\n\tstatic abstract class ViewMapper {\n\t\tabstract void applyToView(View view);\n\t}\n\n\tpublic ReaderView(Context context) {\n\t\tsuper(context);\n\t\tmGestureDetector = new GestureDetector(this);\n\t\tmScaleGestureDetector = new ScaleGestureDetector(context, this);\n\t\tmScroller        = new Scroller(context);\n\t\tmStepper = new Stepper(this, this);\n\t}\n\n\tpublic ReaderView(Context context, AttributeSet attrs) {\n\t\tsuper(context, attrs);\n\t\t\n\t\t// \"Edit mode\" means when the View is being displayed in the Android GUI editor. (this class\n\t\t// is instantiated in the IDE, so we need to be a bit careful what we do).\n\t\tif (isInEditMode())\n\t\t{\n\t\t\tmGestureDetector = null;\n\t\t\tmScaleGestureDetector = null;\n\t\t\tmScroller = null;\n\t\t\tmStepper = null;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmGestureDetector = new GestureDetector(this);\n\t\t\tmScaleGestureDetector = new ScaleGestureDetector(context, this);\n\t\t\tmScroller        = new Scroller(context);\n\t\t\tmStepper = new Stepper(this, this);\n\t\t}\n\t}\n\n\tpublic ReaderView(Context context, AttributeSet attrs, int defStyle) {\n\t\tsuper(context, attrs, defStyle);\n\t\tmGestureDetector = new GestureDetector(this);\n\t\tmScaleGestureDetector = new ScaleGestureDetector(context, this);\n\t\tmScroller        = new Scroller(context);\n\t\tmStepper = new Stepper(this, this);\n\t}\n\n\tpublic int getDisplayedViewIndex() {\n\t\treturn mCurrent;\n\t}\n\n\tpublic void setDisplayedViewIndex(int i) {\n\t\tif (0 <= i && i < mAdapter.getCount()) {\n\t\t\tonMoveOffChild(mCurrent);\n\t\t\tmCurrent = i;\n\t\t\tonMoveToChild(i);\n\t\t\tmResetLayout = true;\n\t\t\trequestLayout();\n\t\t}\n\t}\n\n\tpublic void moveToNext() {\n\t\tView v = mChildViews.get(mCurrent+1);\n\t\tif (v != null)\n\t\t\tslideViewOntoScreen(v);\n\t}\n\n\tpublic void moveToPrevious() {\n\t\tView v = mChildViews.get(mCurrent-1);\n\t\tif (v != null)\n\t\t\tslideViewOntoScreen(v);\n\t}\n\n\t// When advancing down the page, we want to advance by about\n\t// 90% of a screenful. But we'd be happy to advance by between\n\t// 80% and 95% if it means we hit the bottom in a whole number\n\t// of steps.\n\tprivate int smartAdvanceAmount(int screenHeight, int max) {\n\t\tint advance = (int)(screenHeight * 0.9 + 0.5);\n\t\tint leftOver = max % advance;\n\t\tint steps = max / advance;\n\t\tif (leftOver == 0) {\n\t\t\t// We'll make it exactly. No adjustment\n\t\t} else if ((float)leftOver / steps <= screenHeight * 0.05) {\n\t\t\t// We can adjust up by less than 5% to make it exact.\n\t\t\tadvance += (int)((float)leftOver/steps + 0.5);\n\t\t} else {\n\t\t\tint overshoot = advance - leftOver;\n\t\t\tif ((float)overshoot / steps <= screenHeight * 0.1) {\n\t\t\t\t// We can adjust down by less than 10% to make it exact.\n\t\t\t\tadvance -= (int)((float)overshoot/steps + 0.5);\n\t\t\t}\n\t\t}\n\t\tif (advance > max)\n\t\t\tadvance = max;\n\t\treturn advance;\n\t}\n\n\tpublic void smartMoveForwards() {\n\t\tView v = mChildViews.get(mCurrent);\n\t\tif (v == null)\n\t\t\treturn;\n\n\t\t// The following code works in terms of where the screen is on the views;\n\t\t// so for example, if the currentView is at (-100,-100), the visible\n\t\t// region would be at (100,100). If the previous page was (2000, 3000) in\n\t\t// size, the visible region of the previous page might be (2100 + GAP, 100)\n\t\t// (i.e. off the previous page). This is different to the way the rest of\n\t\t// the code in this file is written, but it's easier for me to think about.\n\t\t// At some point we may refactor this to fit better with the rest of the\n\t\t// code.\n\n\t\t// screenWidth/Height are the actual width/height of the screen. e.g. 480/800\n\t\tint screenWidth  = getWidth();\n\t\tint screenHeight = getHeight();\n\t\t// We might be mid scroll; we want to calculate where we scroll to based on\n\t\t// where this scroll would end, not where we are now (to allow for people\n\t\t// bashing 'forwards' very fast.\n\t\tint remainingX = mScroller.getFinalX() - mScroller.getCurrX();\n\t\tint remainingY = mScroller.getFinalY() - mScroller.getCurrY();\n\t\t// right/bottom is in terms of pixels within the scaled document; e.g. 1000\n\t\tint top = -(v.getTop()  + mYScroll + remainingY);\n\t\tint right  = screenWidth -(v.getLeft() + mXScroll + remainingX);\n\t\tint bottom = screenHeight+top;\n\t\t// docWidth/Height are the width/height of the scaled document e.g. 2000x3000\n\t\tint docWidth  = v.getMeasuredWidth();\n\t\tint docHeight = v.getMeasuredHeight();\n\n\t\tint xOffset, yOffset;\n\t\tif (bottom >= docHeight) {\n\t\t\t// We are flush with the bottom. Advance to next column.\n\t\t\tif (right + screenWidth > docWidth) {\n\t\t\t\t// No room for another column - go to next page\n\t\t\t\tView nv = mChildViews.get(mCurrent+1);\n\t\t\t\tif (nv == null) // No page to advance to\n\t\t\t\t\treturn;\n\t\t\t\tint nextTop  = -(nv.getTop() + mYScroll + remainingY);\n\t\t\t\tint nextLeft = -(nv.getLeft() + mXScroll + remainingX);\n\t\t\t\tint nextDocWidth = nv.getMeasuredWidth();\n\t\t\t\tint nextDocHeight = nv.getMeasuredHeight();\n\n\t\t\t\t// Allow for the next page maybe being shorter than the screen is high\n\t\t\t\tyOffset = (nextDocHeight < screenHeight ? ((nextDocHeight - screenHeight)>>1) : 0);\n\n\t\t\t\tif (nextDocWidth < screenWidth) {\n\t\t\t\t\t// Next page is too narrow to fill the screen. Scroll to the top, centred.\n\t\t\t\t\txOffset = (nextDocWidth - screenWidth)>>1;\n\t\t\t\t} else {\n\t\t\t\t\t// Reset X back to the left hand column\n\t\t\t\t\txOffset = right % screenWidth;\n\t\t\t\t\t// Adjust in case the previous page is less wide\n\t\t\t\t\tif (xOffset + screenWidth > nextDocWidth)\n\t\t\t\t\t\txOffset = nextDocWidth - screenWidth;\n\t\t\t\t}\n\t\t\t\txOffset -= nextLeft;\n\t\t\t\tyOffset -= nextTop;\n\t\t\t} else {\n\t\t\t\t// Move to top of next column\n\t\t\t\txOffset = screenWidth;\n\t\t\t\tyOffset = screenHeight - bottom;\n\t\t\t}\n\t\t} else {\n\t\t\t// Advance by 90% of the screen height downwards (in case lines are partially cut off)\n\t\t\txOffset = 0;\n\t\t\tyOffset = smartAdvanceAmount(screenHeight, docHeight - bottom);\n\t\t}\n\t\tmScrollerLastX = mScrollerLastY = 0;\n\t\tmScroller.startScroll(0, 0, remainingX - xOffset, remainingY - yOffset, 400);\n\t\tmStepper.prod();\n\t}\n\n\tpublic void smartMoveBackwards() {\n\t\tView v = mChildViews.get(mCurrent);\n\t\tif (v == null)\n\t\t\treturn;\n\n\t\t// The following code works in terms of where the screen is on the views;\n\t\t// so for example, if the currentView is at (-100,-100), the visible\n\t\t// region would be at (100,100). If the previous page was (2000, 3000) in\n\t\t// size, the visible region of the previous page might be (2100 + GAP, 100)\n\t\t// (i.e. off the previous page). This is different to the way the rest of\n\t\t// the code in this file is written, but it's easier for me to think about.\n\t\t// At some point we may refactor this to fit better with the rest of the\n\t\t// code.\n\n\t\t// screenWidth/Height are the actual width/height of the screen. e.g. 480/800\n\t\tint screenWidth  = getWidth();\n\t\tint screenHeight = getHeight();\n\t\t// We might be mid scroll; we want to calculate where we scroll to based on\n\t\t// where this scroll would end, not where we are now (to allow for people\n\t\t// bashing 'forwards' very fast.\n\t\tint remainingX = mScroller.getFinalX() - mScroller.getCurrX();\n\t\tint remainingY = mScroller.getFinalY() - mScroller.getCurrY();\n\t\t// left/top is in terms of pixels within the scaled document; e.g. 1000\n\t\tint left  = -(v.getLeft() + mXScroll + remainingX);\n\t\tint top   = -(v.getTop()  + mYScroll + remainingY);\n\t\t// docWidth/Height are the width/height of the scaled document e.g. 2000x3000\n\t\tint docHeight = v.getMeasuredHeight();\n\n\t\tint xOffset, yOffset;\n\t\tif (top <= 0) {\n\t\t\t// We are flush with the top. Step back to previous column.\n\t\t\tif (left < screenWidth) {\n\t\t\t\t/* No room for previous column - go to previous page */\n\t\t\t\tView pv = mChildViews.get(mCurrent-1);\n\t\t\t\tif (pv == null) /* No page to advance to */\n\t\t\t\t\treturn;\n\t\t\t\tint prevDocWidth = pv.getMeasuredWidth();\n\t\t\t\tint prevDocHeight = pv.getMeasuredHeight();\n\n\t\t\t\t// Allow for the next page maybe being shorter than the screen is high\n\t\t\t\tyOffset = (prevDocHeight < screenHeight ? ((prevDocHeight - screenHeight)>>1) : 0);\n\n\t\t\t\tint prevLeft  = -(pv.getLeft() + mXScroll);\n\t\t\t\tint prevTop  = -(pv.getTop() + mYScroll);\n\t\t\t\tif (prevDocWidth < screenWidth) {\n\t\t\t\t\t// Previous page is too narrow to fill the screen. Scroll to the bottom, centred.\n\t\t\t\t\txOffset = (prevDocWidth - screenWidth)>>1;\n\t\t\t\t} else {\n\t\t\t\t\t// Reset X back to the right hand column\n\t\t\t\t\txOffset = (left > 0 ? left % screenWidth : 0);\n\t\t\t\t\tif (xOffset + screenWidth > prevDocWidth)\n\t\t\t\t\t\txOffset = prevDocWidth - screenWidth;\n\t\t\t\t\twhile (xOffset + screenWidth*2 < prevDocWidth)\n\t\t\t\t\t\txOffset += screenWidth;\n\t\t\t\t}\n\t\t\t\txOffset -= prevLeft;\n\t\t\t\tyOffset -= prevTop-prevDocHeight+screenHeight;\n\t\t\t} else {\n\t\t\t\t// Move to bottom of previous column\n\t\t\t\txOffset = -screenWidth;\n\t\t\t\tyOffset = docHeight - screenHeight + top;\n\t\t\t}\n\t\t} else {\n\t\t\t// Retreat by 90% of the screen height downwards (in case lines are partially cut off)\n\t\t\txOffset = 0;\n\t\t\tyOffset = -smartAdvanceAmount(screenHeight, top);\n\t\t}\n\t\tmScrollerLastX = mScrollerLastY = 0;\n\t\tmScroller.startScroll(0, 0, remainingX - xOffset, remainingY - yOffset, 400);\n\t\tmStepper.prod();\n\t}\n\n\tpublic void resetupChildren() {\n\t\tfor (int i = 0; i < mChildViews.size(); i++)\n\t\t\tonChildSetup(mChildViews.keyAt(i), mChildViews.valueAt(i));\n\t}\n\n\tpublic void applyToChildren(ViewMapper mapper) {\n\t\tfor (int i = 0; i < mChildViews.size(); i++)\n\t\t\tmapper.applyToView(mChildViews.valueAt(i));\n\t}\n\n\tpublic void refresh(boolean reflow) {\n\t\tmReflow = reflow;\n\t\tmReflowChanged = true;\n\t\tmResetLayout = true;\n\n\t\tmScale = 1.0f;\n\t\tmXScroll = mYScroll = 0;\n\n\t\trequestLayout();\n\t}\n\n\tprotected void onChildSetup(int i, View v) {}\n\n\tprotected void onMoveToChild(int i) {}\n\n\tprotected void onMoveOffChild(int i) {}\n\n\tprotected void onSettle(View v) {};\n\n\tprotected void onUnsettle(View v) {};\n\n\tprotected void onNotInUse(View v) {\n\t\t((PageView)v).releaseResources();\n\t};\n\n\tprotected void onScaleChild(View v, Float scale) {};\n\n\tpublic View getView(int i) {\n\t\treturn mChildViews.get(i);\n\t}\n\n\tpublic View getDisplayedView() {\n\t\treturn mChildViews.get(mCurrent);\n\t}\n\n\tpublic void run() {\n\t\tif (!mScroller.isFinished()) {\n\t\t\tmScroller.computeScrollOffset();\n\t\t\tint x = mScroller.getCurrX();\n\t\t\tint y = mScroller.getCurrY();\n\t\t\tmXScroll += x - mScrollerLastX;\n\t\t\tmYScroll += y - mScrollerLastY;\n\t\t\tmScrollerLastX = x;\n\t\t\tmScrollerLastY = y;\n\t\t\trequestLayout();\n\t\t\tmStepper.prod();\n\t\t}\n\t\telse if (!mUserInteracting) {\n\t\t\t// End of an inertial scroll and the user is not interacting.\n\t\t\t// The layout is stable\n\t\t\tView v = mChildViews.get(mCurrent);\n\t\t\tif (v != null)\n\t\t\t\tpostSettle(v);\n\t\t}\n\t}\n\n\tpublic boolean onDown(MotionEvent arg0) {\n\t\tmScroller.forceFinished(true);\n\t\treturn true;\n\t}\n\n\tpublic boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,\n\t\t\tfloat velocityY) {\n\t\tif (mScaling)\n\t\t\treturn true;\n\n\t\tView v = mChildViews.get(mCurrent);\n\t\tif (v != null) {\n\t\t\tRect bounds = getScrollBounds(v);\n\t\t\tswitch(directionOfTravel(velocityX, velocityY)) {\n\t\t\tcase MOVING_LEFT:\n\t\t\t\tif (HORIZONTAL_SCROLLING && bounds.left >= 0) {\n\t\t\t\t\t// Fling off to the left bring next view onto screen\n\t\t\t\t\tView vl = mChildViews.get(mCurrent+1);\n\n\t\t\t\t\tif (vl != null) {\n\t\t\t\t\t\tslideViewOntoScreen(vl);\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase MOVING_UP:\n\t\t\t\tif (!HORIZONTAL_SCROLLING && bounds.top >= 0) {\n\t\t\t\t\t// Fling off to the top bring next view onto screen\n\t\t\t\t\tView vl = mChildViews.get(mCurrent+1);\n\n\t\t\t\t\tif (vl != null) {\n\t\t\t\t\t\tslideViewOntoScreen(vl);\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase MOVING_RIGHT:\n\t\t\t\tif (HORIZONTAL_SCROLLING && bounds.right <= 0) {\n\t\t\t\t\t// Fling off to the right bring previous view onto screen\n\t\t\t\t\tView vr = mChildViews.get(mCurrent-1);\n\n\t\t\t\t\tif (vr != null) {\n\t\t\t\t\t\tslideViewOntoScreen(vr);\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase MOVING_DOWN:\n\t\t\t\tif (!HORIZONTAL_SCROLLING && bounds.bottom <= 0) {\n\t\t\t\t\t// Fling off to the bottom bring previous view onto screen\n\t\t\t\t\tView vr = mChildViews.get(mCurrent-1);\n\n\t\t\t\t\tif (vr != null) {\n\t\t\t\t\t\tslideViewOntoScreen(vr);\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tmScrollerLastX = mScrollerLastY = 0;\n\t\t\t// If the page has been dragged out of bounds then we want to spring back\n\t\t\t// nicely. fling jumps back into bounds instantly, so we don't want to use\n\t\t\t// fling in that case. On the other hand, we don't want to forgo a fling\n\t\t\t// just because of a slightly off-angle drag taking us out of bounds other\n\t\t\t// than in the direction of the drag, so we test for out of bounds only\n\t\t\t// in the direction of travel.\n\t\t\t//\n\t\t\t// Also don't fling if out of bounds in any direction by more than fling\n\t\t\t// margin\n\t\t\tRect expandedBounds = new Rect(bounds);\n\t\t\texpandedBounds.inset(-FLING_MARGIN, -FLING_MARGIN);\n\n\t\t\tif(withinBoundsInDirectionOfTravel(bounds, velocityX, velocityY)\n\t\t\t\t\t&& expandedBounds.contains(0, 0)) {\n\t\t\t\tmScroller.fling(0, 0, (int)velocityX, (int)velocityY, bounds.left, bounds.right, bounds.top, bounds.bottom);\n\t\t\t\tmStepper.prod();\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tpublic boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,\n\t\t\tfloat distanceY) {\n\t\tif (!mScaling) {\n\t\t\tmXScroll -= distanceX;\n\t\t\tmYScroll -= distanceY;\n\t\t\trequestLayout();\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic void onShowPress(MotionEvent e) {\n\t}\n\n\tpublic boolean onSingleTapUp(MotionEvent e) {\n\t\treturn false;\n\t}\n\n\tpublic boolean onScale(ScaleGestureDetector detector) {\n\t\tfloat previousScale = mScale;\n\t\tfloat scale_factor = mReflow ? REFLOW_SCALE_FACTOR : 1.0f;\n\t\tfloat min_scale = MIN_SCALE * scale_factor;\n\t\tfloat max_scale = MAX_SCALE * scale_factor;\n\t\tmScale = Math.min(Math.max(mScale * detector.getScaleFactor(), min_scale), max_scale);\n\n\t\tif (mReflow) {\n\t\t\tView v = mChildViews.get(mCurrent);\n\t\t\tif (v != null)\n\t\t\t\tonScaleChild(v, mScale);\n\t\t} else {\n\t\t\tfloat factor = mScale/previousScale;\n\n\t\t\tView v = mChildViews.get(mCurrent);\n\t\t\tif (v != null) {\n\t\t\t\t// Work out the focus point relative to the view top left\n\t\t\t\tint viewFocusX = (int)detector.getFocusX() - (v.getLeft() + mXScroll);\n\t\t\t\tint viewFocusY = (int)detector.getFocusY() - (v.getTop() + mYScroll);\n\t\t\t\t// Scroll to maintain the focus point\n\t\t\t\tmXScroll += viewFocusX - viewFocusX * factor;\n\t\t\t\tmYScroll += viewFocusY - viewFocusY * factor;\n\t\t\t\trequestLayout();\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic boolean onScaleBegin(ScaleGestureDetector detector) {\n\t\tmScaling = true;\n\t\t// Ignore any scroll amounts yet to be accounted for: the\n\t\t// screen is not showing the effect of them, so they can\n\t\t// only confuse the user\n\t\tmXScroll = mYScroll = 0;\n\t\treturn true;\n\t}\n\n\tpublic void onScaleEnd(ScaleGestureDetector detector) {\n\t\tif (mReflow) {\n\t\t\tapplyToChildren(new ViewMapper() {\n\t\t\t\t@Override\n\t\t\t\tvoid applyToView(View view) {\n\t\t\t\t\tonScaleChild(view, mScale);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\tmScaling = false;\n\t}\n\n    @Override\n    public boolean onTouchEvent(MotionEvent event) {\n\n        boolean movementEnd = false;\n\n        // We need this check to avoid refreshing the screen after a \"tap\" or \"double tap\". We only want to refresh the PDF after a pan, pinch or drag.\n        int ident = MotionEventCompat.getActionIndex(event);\n        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {\n            mLastTouchX = MotionEventCompat.getX(event, ident);\n            mLastTouchY = MotionEventCompat.getY(event, ident);\n        }\n\n        if (event.getActionMasked() == MotionEvent.ACTION_UP) {\n            float upX = MotionEventCompat.getX(event, ident);\n            float upY = MotionEventCompat.getY(event, ident);\n            int displacementX = (int) Math.abs(mLastTouchX - upX);\n            int displacementY = (int) Math.abs(mLastTouchY - upY);\n            movementEnd = (displacementX > 10) || (displacementY > 10);\n        }\n\n        processTouchEvent(event, movementEnd);\n\n        return true;\n    }\n    \n    private void processTouchEvent(MotionEvent event, boolean withRefresh) {\n\t\tmScaleGestureDetector.onTouchEvent(event);\n\t\tmGestureDetector.onTouchEvent(event);\n\n\t\tif ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {\n\t\t\tmUserInteracting = true;\n\t\t}\n\t\tif ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {\n\t\t\tmUserInteracting = false;\n\n\t\t\tView v = mChildViews.get(mCurrent);\n\t\t\tif (v != null && withRefresh) {\n\t\t\t\tif (mScroller.isFinished()) {\n\t\t\t\t\t// If, at the end of user interaction, there is no\n\t\t\t\t\t// current inertial scroll in operation then animate\n\t\t\t\t\t// the view onto screen if necessary\n\t\t\t\t\tslideViewOntoScreen(v);\n\t\t\t\t}\n\n\t\t\t\tif (mScroller.isFinished()) {\n\t\t\t\t\t// If still there is no inertial scroll in operation\n\t\t\t\t\t// then the layout is stable\n\t\t\t\t\tpostSettle(v);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\trequestLayout();\n\t}\n\n\t@Override\n\tprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n\t\tsuper.onMeasure(widthMeasureSpec, heightMeasureSpec);\n\n\t\tint n = getChildCount();\n\t\tfor (int i = 0; i < n; i++)\n\t\t\tmeasureView(getChildAt(i));\n\t}\n\n\t@Override\n\tprotected void onLayout(boolean changed, int left, int top, int right,\n\t\t\tint bottom) {\n\t\tsuper.onLayout(changed, left, top, right, bottom);\n\n\t\t// \"Edit mode\" means when the View is being displayed in the Android GUI editor. (this class\n\t\t// is instantiated in the IDE, so we need to be a bit careful what we do).\n\t\tif (isInEditMode())\n\t\t\treturn;\n\n\t\tView cv = mChildViews.get(mCurrent);\n\t\tPoint cvOffset;\n\n\t\tif (!mResetLayout) {\n\t\t\t// Move to next or previous if current is sufficiently off center\n\t\t\tif (cv != null) {\n\t\t\t\tboolean move;\n\t\t\t\tcvOffset = subScreenSizeOffset(cv);\n\t\t\t\t// cv.getRight() may be out of date with the current scale\n\t\t\t\t// so add left to the measured width for the correct position\n\t\t\t\tif (HORIZONTAL_SCROLLING)\n\t\t\t\t\tmove = cv.getLeft() + cv.getMeasuredWidth() + cvOffset.x + GAP/2 + mXScroll < getWidth()/2;\n\t\t\t\telse\n\t\t\t\t\tmove = cv.getTop() + cv.getMeasuredHeight() + cvOffset.y + GAP/2 + mYScroll < getHeight()/2;\n\t\t\t\tif (move && mCurrent + 1 < mAdapter.getCount()) {\n\t\t\t\t\tpostUnsettle(cv);\n\t\t\t\t\t// post to invoke test for end of animation\n\t\t\t\t\t// where we must set hq area for the new current view\n\t\t\t\t\tmStepper.prod();\n\n\t\t\t\t\tonMoveOffChild(mCurrent);\n\t\t\t\t\tmCurrent++;\n\t\t\t\t\tonMoveToChild(mCurrent);\n\t\t\t\t}\n\n\t\t\t\tif (HORIZONTAL_SCROLLING)\n\t\t\t\t\tmove = cv.getLeft() - cvOffset.x - GAP/2 + mXScroll >= getWidth()/2;\n\t\t\t\telse\n\t\t\t\t\tmove = cv.getTop() - cvOffset.y - GAP/2 + mYScroll >= getHeight()/2;\n\t\t\t\tif (move && mCurrent > 0) {\n\t\t\t\t\tpostUnsettle(cv);\n\t\t\t\t\t// post to invoke test for end of animation\n\t\t\t\t\t// where we must set hq area for the new current view\n\t\t\t\t\tmStepper.prod();\n\n\t\t\t\t\tonMoveOffChild(mCurrent);\n\t\t\t\t\tmCurrent--;\n\t\t\t\t\tonMoveToChild(mCurrent);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Remove not needed children and hold them for reuse\n\t\t\tint numChildren = mChildViews.size();\n\t\t\tint childIndices[] = new int[numChildren];\n\t\t\tfor (int i = 0; i < numChildren; i++)\n\t\t\t\tchildIndices[i] = mChildViews.keyAt(i);\n\n\t\t\tfor (int i = 0; i < numChildren; i++) {\n\t\t\t\tint ai = childIndices[i];\n\t\t\t\tif (ai < mCurrent - 1 || ai > mCurrent + 1) {\n\t\t\t\t\tView v = mChildViews.get(ai);\n\t\t\t\t\tonNotInUse(v);\n\t\t\t\t\tmViewCache.add(v);\n\t\t\t\t\tremoveViewInLayout(v);\n\t\t\t\t\tmChildViews.remove(ai);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tmResetLayout = false;\n\t\t\tmXScroll = mYScroll = 0;\n\n\t\t\t// Remove all children and hold them for reuse\n\t\t\tint numChildren = mChildViews.size();\n\t\t\tfor (int i = 0; i < numChildren; i++) {\n\t\t\t\tView v = mChildViews.valueAt(i);\n\t\t\t\tonNotInUse(v);\n\t\t\t\tmViewCache.add(v);\n\t\t\t\tremoveViewInLayout(v);\n\t\t\t}\n\t\t\tmChildViews.clear();\n\n\t\t\t// Don't reuse cached views if the adapter has changed\n\t\t\tif (mReflowChanged) {\n\t\t\t\tmReflowChanged = false;\n\t\t\t\tmViewCache.clear();\n\t\t\t}\n\n\t\t\t// post to ensure generation of hq area\n\t\t\tmStepper.prod();\n\t\t}\n\n\t\t// Ensure current view is present\n\t\tint cvLeft, cvRight, cvTop, cvBottom;\n\t\tboolean notPresent = (mChildViews.get(mCurrent) == null);\n\t\tcv = getOrCreateChild(mCurrent);\n        currentPage = (PageView) cv;\n\t\tcurrentPage.setEventCallback(eventCallback);\n\n        currentPage.setParentSize(new Point(right-left, bottom-top));\n\t\t// When the view is sub-screen-size in either dimension we\n\t\t// offset it to center within the screen area, and to keep\n\t\t// the views spaced out\n\t\tcvOffset = subScreenSizeOffset(cv);\n\t\tif (notPresent) {\n\t\t\t//Main item not already present. Just place it top left\n\t\t\tcvLeft = cvOffset.x;\n\t\t\tcvTop  = cvOffset.y;\n\t\t} else {\n\t\t\t// Main item already present. Adjust by scroll offsets\n\t\t\tcvLeft = cv.getLeft() + mXScroll;\n\t\t\tcvTop  = cv.getTop()  + mYScroll;\n\t\t}\n\t\t// Scroll values have been accounted for\n\t\tmXScroll = mYScroll = 0;\n\t\tcvRight  = cvLeft + cv.getMeasuredWidth();\n\t\tcvBottom = cvTop  + cv.getMeasuredHeight();\n\n\t\tif (!mUserInteracting && mScroller.isFinished()) {\n\t\t\tPoint corr = getCorrection(getScrollBounds(cvLeft, cvTop, cvRight, cvBottom));\n\t\t\tcvRight  += corr.x;\n\t\t\tcvLeft   += corr.x;\n\t\t\tcvTop    += corr.y;\n\t\t\tcvBottom += corr.y;\n\t\t} else if (HORIZONTAL_SCROLLING && cv.getMeasuredHeight() <= getHeight()) {\n\t\t\t// When the current view is as small as the screen in height, clamp\n\t\t\t// it vertically\n\t\t\tPoint corr = getCorrection(getScrollBounds(cvLeft, cvTop, cvRight, cvBottom));\n\t\t\tcvTop    += corr.y;\n\t\t\tcvBottom += corr.y;\n\t\t} else if (!HORIZONTAL_SCROLLING && cv.getMeasuredWidth() <= getWidth()) {\n\t\t\t// When the current view is as small as the screen in width, clamp\n\t\t\t// it horizontally\n\t\t\tPoint corr = getCorrection(getScrollBounds(cvLeft, cvTop, cvRight, cvBottom));\n\t\t\tcvRight  += corr.x;\n\t\t\tcvLeft   += corr.x;\n\t\t}\n\n\t\tcv.layout(cvLeft, cvTop, cvRight, cvBottom);\n\n\t\tif (mCurrent > 0) {\n\t\t\tView lv = getOrCreateChild(mCurrent - 1);\n\t\t\tPoint leftOffset = subScreenSizeOffset(lv);\n\t\t\tif (HORIZONTAL_SCROLLING)\n\t\t\t{\n\t\t\t\tint gap = leftOffset.x + GAP + cvOffset.x;\n\t\t\t\tlv.layout(cvLeft - lv.getMeasuredWidth() - gap,\n\t\t\t\t\t\t(cvBottom + cvTop - lv.getMeasuredHeight())/2,\n\t\t\t\t\t\tcvLeft - gap,\n\t\t\t\t\t\t(cvBottom + cvTop + lv.getMeasuredHeight())/2);\n\t\t\t} else {\n\t\t\t\tint gap = leftOffset.y + GAP + cvOffset.y;\n\t\t\t\tlv.layout((cvLeft + cvRight - lv.getMeasuredWidth())/2,\n\t\t\t\t\t\tcvTop - lv.getMeasuredHeight() - gap,\n\t\t\t\t\t\t(cvLeft + cvRight + lv.getMeasuredWidth())/2,\n\t\t\t\t\t\tcvTop - gap);\n\t\t\t}\n\t\t}\n\n\t\tif (mCurrent + 1 < mAdapter.getCount()) {\n\t\t\tView rv = getOrCreateChild(mCurrent + 1);\n\t\t\tPoint rightOffset = subScreenSizeOffset(rv);\n\t\t\tif (HORIZONTAL_SCROLLING)\n\t\t\t{\n\t\t\t\tint gap = cvOffset.x + GAP + rightOffset.x;\n\t\t\t\trv.layout(cvRight + gap,\n\t\t\t\t\t\t(cvBottom + cvTop - rv.getMeasuredHeight())/2,\n\t\t\t\t\t\tcvRight + rv.getMeasuredWidth() + gap,\n\t\t\t\t\t\t(cvBottom + cvTop + rv.getMeasuredHeight())/2);\n\t\t\t} else {\n\t\t\t\tint gap = cvOffset.y + GAP + rightOffset.y;\n\t\t\t\trv.layout((cvLeft + cvRight - rv.getMeasuredWidth())/2,\n\t\t\t\t\t\tcvBottom + gap,\n\t\t\t\t\t\t(cvLeft + cvRight + rv.getMeasuredWidth())/2,\n\t\t\t\t\t\tcvBottom + gap + rv.getMeasuredHeight());\n\t\t\t}\n\t\t}\n\n\t\tinvalidate();\n\t}\n\n\t@Override\n\tpublic Adapter getAdapter() {\n\t\treturn mAdapter;\n\t}\n\n\t@Override\n\tpublic View getSelectedView() {\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic void setAdapter(Adapter adapter) {\n\t\tmAdapter = adapter;\n\n\t\trequestLayout();\n\t}\n\n\t@Override\n\tpublic void setSelection(int arg0) {\n\t\tthrow new UnsupportedOperationException(getContext().getString(R.string.not_supported));\n\t}\n\n\tprivate View getCached() {\n\t\tif (mViewCache.size() == 0)\n\t\t\treturn null;\n\t\telse\n\t\t\treturn mViewCache.removeFirst();\n\t}\n\n\tprivate View getOrCreateChild(int i) {\n\t\tView v = mChildViews.get(i);\n\t\tif (v == null) {\n\t\t\tv = mAdapter.getView(i, getCached(), this);\n\t\t\taddAndMeasureChild(i, v);\n\t\t\tonChildSetup(i, v);\n\t\t\tonScaleChild(v, mScale);\n\t\t}\n\n\t\treturn v;\n\t}\n\n\tprivate void addAndMeasureChild(int i, View v) {\n\t\tLayoutParams params = v.getLayoutParams();\n\t\tif (params == null) {\n\t\t\tparams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);\n\t\t}\n\t\taddViewInLayout(v, 0, params, true);\n\t\tmChildViews.append(i, v); // Record the view against it's adapter index\n\t\tmeasureView(v);\n\t}\n\n\tprivate void measureView(View v) {\n\t\t// See what size the view wants to be\n\t\tv.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);\n\n\t\tif (!mReflow) {\n\t\t// Work out a scale that will fit it to this view\n//\t\tfloat scale = Math.min((float)getWidth()/(float)v.getMeasuredWidth(),\n//\t\t\t\t\t(float)getHeight()/(float)v.getMeasuredHeight());\n        float scale = (float)getWidth()/(float)v.getMeasuredWidth();\n\t\t// Use the fitting values scaled by our current scale factor\n\t\tv.measure(MeasureSpec.EXACTLY | (int)(v.getMeasuredWidth()*scale*mScale),\n\t\t\t\tMeasureSpec.EXACTLY | (int)(v.getMeasuredHeight()*scale*mScale));\n\t\t} else {\n\t\t\tv.measure(MeasureSpec.EXACTLY | (int)(v.getMeasuredWidth()),\n\t\t\t\t\tMeasureSpec.EXACTLY | (int)(v.getMeasuredHeight()));\n\t\t}\n\t}\n\n\tprivate Rect getScrollBounds(int left, int top, int right, int bottom) {\n\t\tint xmin = getWidth() - right;\n\t\tint xmax = -left;\n\t\tint ymin = getHeight() - bottom;\n\t\tint ymax = -top;\n\n\t\t// In either dimension, if view smaller than screen then\n\t\t// constrain it to be central\n\t\tif (xmin > xmax) xmin = xmax = (xmin + xmax)/2;\n\t\tif (ymin > ymax) ymin = ymax = (ymin + ymax)/2;\n\n\t\treturn new Rect(xmin, ymin, xmax, ymax);\n\t}\n\n\tprivate Rect getScrollBounds(View v) {\n\t\t// There can be scroll amounts not yet accounted for in\n\t\t// onLayout, so add mXScroll and mYScroll to the current\n\t\t// positions when calculating the bounds.\n\t\treturn getScrollBounds(v.getLeft() + mXScroll,\n\t\t\t\t               v.getTop() + mYScroll,\n\t\t\t\t               v.getLeft() + v.getMeasuredWidth() + mXScroll,\n\t\t\t\t               v.getTop() + v.getMeasuredHeight() + mYScroll);\n\t}\n\n\tprivate Point getCorrection(Rect bounds) {\n\t\treturn new Point(Math.min(Math.max(0,bounds.left),bounds.right),\n\t\t\t\t         Math.min(Math.max(0,bounds.top),bounds.bottom));\n\t}\n\n\tprivate void postSettle(final View v) {\n\t\t// onSettle and onUnsettle are posted so that the calls\n\t\t// wont be executed until after the system has performed\n\t\t// layout.\n\t\tpost(new Runnable() {\n\t\t\tpublic void run() {\n\t\t\t\tonSettle(v);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void postUnsettle(final View v) {\n\t\tpost (new Runnable() {\n\t\t\tpublic void run () {\n\t\t\t\tonUnsettle(v);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void slideViewOntoScreen(View v) {\n\t\tPoint corr = getCorrection(getScrollBounds(v));\n\t\tif (corr.x != 0 || corr.y != 0) {\n\t\t\tmScrollerLastX = mScrollerLastY = 0;\n\t\t\tmScroller.startScroll(0, 0, corr.x, corr.y, 400);\n\t\t\tmStepper.prod();\n\t\t}\n\t}\n\n\tprivate Point subScreenSizeOffset(View v) {\n\t\treturn new Point(Math.max((getWidth() - v.getMeasuredWidth()) / 2, 0),\n\t\t\t\tMath.max((getHeight() - v.getMeasuredHeight()) / 2, 0));\n\t}\n\n\tprivate static int directionOfTravel(float vx, float vy) {\n\t\tif (Math.abs(vx) > 2 * Math.abs(vy))\n\t\t\treturn (vx > 0) ? MOVING_RIGHT : MOVING_LEFT;\n\t\telse if (Math.abs(vy) > 2 * Math.abs(vx))\n\t\t\treturn (vy > 0) ? MOVING_DOWN : MOVING_UP;\n\t\telse\n\t\t\treturn MOVING_DIAGONALLY;\n\t}\n\n\tprivate static boolean withinBoundsInDirectionOfTravel(Rect bounds, float vx, float vy) {\n\t\tswitch (directionOfTravel(vx, vy)) {\n\t\tcase MOVING_DIAGONALLY: return bounds.contains(0, 0);\n\t\tcase MOVING_LEFT:       return bounds.left <= 0;\n\t\tcase MOVING_RIGHT:      return bounds.right >= 0;\n\t\tcase MOVING_UP:         return bounds.top <= 0;\n\t\tcase MOVING_DOWN:       return bounds.bottom >= 0;\n\t\tdefault: throw new NoSuchElementException();\n\t\t}\n\t}\n\n    // Viafirma Code:\n\n    public void addBitmap(PdfBitmap pdfBitmap) {\n\t\tif (mAdapter instanceof MuPDFPageAdapter) {\n\t\t\t// Add the bitmap to the adapter.\n\t\t\t((MuPDFPageAdapter)mAdapter).addBitmap(pdfBitmap);\n\t\t\t// Update the view to see the added bitmap.\n\t\t\tupdateCurrentPage();\n\t\t}\n    }\n\n\tpublic void setPdfBitmapList(Collection<PdfBitmap> pdfBitmaps) {\n\t\tthis.pdfBitmaps = pdfBitmaps;\n\t\tif (mAdapter instanceof MuPDFPageAdapter) {\n\t\t\t((MuPDFPageAdapter) mAdapter).setPdfBitmapList(pdfBitmaps);\n\t\t}\n\t}\n\n\tpublic Collection<PdfBitmap> getBitmapList() {\n\t\tif (mAdapter instanceof MuPDFPageAdapter) {\n\t\t\treturn ((MuPDFPageAdapter)mAdapter).getPdfBitmapList();\n\t\t} else {\n\t\t\treturn new HashSet<>();\n\t\t}\n\t}\n\n    public boolean removeBitmapOnPosition(Point point) {\n        if (currentPage != null) {\n            return currentPage.removeBitmapOnPosition(point);\n        } else {\n            return false;\n        }\n    }\n    \n    public void refreshView(){\n        long downTime = SystemClock.uptimeMillis()+200;\n        long eventTime = SystemClock.uptimeMillis() + 210;\n        float x = 1.0f;\n        float y = 1.0f;\n\n        int metaState = 0;\n        MotionEvent motionEvent = MotionEvent.obtain(\n                downTime,\n                eventTime,\n                MotionEvent.ACTION_UP,\n                x,\n                y,\n                metaState\n        );\n\n        processTouchEvent(motionEvent, true);\n    }\n\n\tpublic void updateCurrentPage() {\n\t\tif (currentPage != null) {\n\t\t\t//setDisplayedViewIndex(currentPage.getPage());\n\t\t\tcurrentPage.redrawEntireBitmaps(); // No repinta el zoomed si ya estoy zoomed.\n\t\t\tcurrentPage.updateHq(true);\n\t\t}\n\t}\n\n\tpublic void redrawAll() {\n\t\tredrawPage(currentPage);\n\t\tif (mCurrent-1 >= 0) {\n\t\t\tPageView prevPage = (PageView) mChildViews.get(mCurrent - 1);\n\t\t\tredrawPage(prevPage);\n\t\t}\n\t\tif (mCurrent+1 < mChildViews.size()) {\n\t\t\tPageView posPage = (PageView) mChildViews.get(mCurrent + 1);\n\t\t\tredrawPage(posPage);\n\t\t}\n\t}\n\n\tprivate void redrawPage(PageView pageView) {\n\t\tif (pageView != null) {\n\t\t\tpageView.updateEntireCanvas(false);\n\t\t\tpageView.updateHq(true);\n\t\t}\n\t}\n\n    @Override\n    public boolean onDoubleTap(MotionEvent e) {\n        if (currentPage != null) {\n            return currentPage.onDoubleTap(e, mScale);\n        } else {\n            return false;\n        }\n    }\n\n    @Override\n    public void onLongPress(MotionEvent e) {\n        if (currentPage != null) {\n            currentPage.onLongPress(e, mScale);\n        }\n    }\n\n    @Override\n    public boolean onDoubleTapEvent(MotionEvent e) {\n        return false;\n    }\n\n    @Override\n    public boolean onSingleTapConfirmed(MotionEvent e) {\n        if (currentPage != null) {\n            currentPage.onSingleTap(e, mScale);\n        }\n        return false;\n    }\n\n    public DigitalizedEventCallback getEventCallback() {\n        DigitalizedEventCallback result = null;\n        if (currentPage != null) {\n            result = currentPage.getEventCallback();\n        }\n        return result;\n    }\n\n    public void setEventCallback(DigitalizedEventCallback eventCallback) {\n        this.eventCallback = eventCallback;\n        if (currentPage != null) {\n            currentPage.setEventCallback(eventCallback);\n        }\n    }\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/SafeAnimatorInflater.java",
    "content": "package com.artifex.mupdfdemo;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorInflater;\nimport android.animation.AnimatorSet;\nimport android.app.Activity;\nimport android.view.View;\n\npublic class SafeAnimatorInflater\n{\n\tprivate View mView;\n\n\tpublic SafeAnimatorInflater(Activity activity, int animation, View view)\n\t{\n\t\tAnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(activity, R.animator.info);\n\t\tmView = view;\n\t\tset.setTarget(view);\n\t\tset.addListener(new Animator.AnimatorListener() {\n\t\t\tpublic void onAnimationStart(Animator animation) {\n\t\t\t\tmView.setVisibility(View.VISIBLE);\n\t\t\t}\n\n\t\t\tpublic void onAnimationRepeat(Animator animation) {\n\t\t\t}\n\n\t\t\tpublic void onAnimationEnd(Animator animation) {\n\t\t\t\tmView.setVisibility(View.INVISIBLE);\n\t\t\t}\n\n\t\t\tpublic void onAnimationCancel(Animator animation) {\n\t\t\t}\n\t\t});\n\t\tset.start();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/SearchTask.java",
    "content": "package com.artifex.mupdfdemo;\n\nimport android.app.AlertDialog;\nimport android.app.ProgressDialog;\nimport android.content.Context;\nimport android.content.DialogInterface;\nimport android.graphics.RectF;\nimport android.os.Handler;\n\nclass ProgressDialogX extends ProgressDialog {\n\tpublic ProgressDialogX(Context context) {\n\t\tsuper(context);\n\t}\n\n\tprivate boolean mCancelled = false;\n\n\tpublic boolean isCancelled() {\n\t\treturn mCancelled;\n\t}\n\n\t@Override\n\tpublic void cancel() {\n\t\tmCancelled = true;\n\t\tsuper.cancel();\n\t}\n}\n\npublic abstract class SearchTask {\n\tprivate static final int SEARCH_PROGRESS_DELAY = 200;\n\tprivate final Context mContext;\n\tprivate final MuPDFCore mCore;\n\tprivate final Handler mHandler;\n\tprivate final AlertDialog.Builder mAlertBuilder;\n\tprivate AsyncTask<Void,Integer,SearchTaskResult> mSearchTask;\n\n\tpublic SearchTask(Context context, MuPDFCore core) {\n\t\tmContext = context;\n\t\tmCore = core;\n\t\tmHandler = new Handler();\n\t\tmAlertBuilder = new AlertDialog.Builder(context);\n\t}\n\n\tprotected abstract void onTextFound(SearchTaskResult result);\n\n\tpublic void stop() {\n\t\tif (mSearchTask != null) {\n\t\t\tmSearchTask.cancel(true);\n\t\t\tmSearchTask = null;\n\t\t}\n\t}\n\n\tpublic void go(final String text, int direction, int displayPage, int searchPage) {\n\t\tif (mCore == null)\n\t\t\treturn;\n\t\tstop();\n\n\t\tfinal int increment = direction;\n\t\tfinal int startIndex = searchPage == -1 ? displayPage : searchPage + increment;\n\n\t\tfinal ProgressDialogX progressDialog = new ProgressDialogX(mContext);\n\t\tprogressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);\n\t\tprogressDialog.setTitle(mContext.getString(R.string.searching_));\n\t\tprogressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {\n\t\t\tpublic void onCancel(DialogInterface dialog) {\n\t\t\t\tstop();\n\t\t\t}\n\t\t});\n\t\tprogressDialog.setMax(mCore.countPages());\n\n\t\tmSearchTask = new AsyncTask<Void,Integer,SearchTaskResult>() {\n\t\t\t@Override\n\t\t\tprotected SearchTaskResult doInBackground(Void... params) {\n\t\t\t\tint index = startIndex;\n\n\t\t\t\twhile (0 <= index && index < mCore.countPages() && !isCancelled()) {\n\t\t\t\t\tpublishProgress(index);\n\t\t\t\t\tRectF searchHits[] = mCore.searchPage(index, text);\n\n\t\t\t\t\tif (searchHits != null && searchHits.length > 0)\n\t\t\t\t\t\treturn new SearchTaskResult(text, index, searchHits);\n\n\t\t\t\t\tindex += increment;\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected void onPostExecute(SearchTaskResult result) {\n\t\t\t\tprogressDialog.cancel();\n\t\t\t\tif (result != null) {\n\t\t\t\t    onTextFound(result);\n\t\t\t\t} else {\n\t\t\t\t\tmAlertBuilder.setTitle(SearchTaskResult.get() == null ? R.string.text_not_found : R.string.no_further_occurrences_found);\n\t\t\t\t\tAlertDialog alert = mAlertBuilder.create();\n\t\t\t\t\talert.setButton(AlertDialog.BUTTON_POSITIVE, mContext.getString(R.string.dismiss),\n\t\t\t\t\t\t\t(DialogInterface.OnClickListener)null);\n\t\t\t\t\talert.show();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected void onCancelled() {\n\t\t\t\tprogressDialog.cancel();\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected void onProgressUpdate(Integer... values) {\n\t\t\t\tprogressDialog.setProgress(values[0].intValue());\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tprotected void onPreExecute() {\n\t\t\t\tsuper.onPreExecute();\n\t\t\t\tmHandler.postDelayed(new Runnable() {\n\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\tif (!progressDialog.isCancelled())\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tprogressDialog.show();\n\t\t\t\t\t\t\tprogressDialog.setProgress(startIndex);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}, SEARCH_PROGRESS_DELAY);\n\t\t\t}\n\t\t};\n\n\t\tmSearchTask.execute();\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/SearchTaskResult.java",
    "content": "package com.artifex.mupdfdemo;\n\nimport android.graphics.RectF;\n\npublic class SearchTaskResult {\n\tpublic final String txt;\n\tpublic final int   pageNumber;\n\tpublic final RectF searchBoxes[];\n\tstatic private SearchTaskResult singleton;\n\n\tSearchTaskResult(String _txt, int _pageNumber, RectF _searchBoxes[]) {\n\t\ttxt = _txt;\n\t\tpageNumber = _pageNumber;\n\t\tsearchBoxes = _searchBoxes;\n\t}\n\n\tstatic public SearchTaskResult get() {\n\t\treturn singleton;\n\t}\n\n\tstatic public void set(SearchTaskResult r) {\n\t\tsingleton = r;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/Stepper.java",
    "content": "package com.artifex.mupdfdemo;\n\nimport android.annotation.SuppressLint;\nimport android.os.Build;\nimport android.view.View;\n\npublic class Stepper {\n\tprotected final View mPoster;\n\tprotected final Runnable mTask;\n\tprotected boolean mPending;\n\n\tpublic Stepper(View v, Runnable r) {\n\t\tmPoster = v;\n\t\tmTask = r;\n\t\tmPending = false;\n\t}\n\n\t@SuppressLint(\"NewApi\")\n\tpublic void prod() {\n\t\tif (!mPending) {\n\t\t\tmPending = true;\n\t\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n\t\t\t\tmPoster.postOnAnimation(new Runnable() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\tmPending = false;\n\t\t\t\t\t\tmTask.run();\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tmPoster.post(new Runnable() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\tmPending = false;\n\t\t\t\t\t\tmTask.run();\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/TextChar.java",
    "content": "package com.artifex.mupdfdemo;\n\nimport android.graphics.RectF;\n\npublic class TextChar extends RectF {\n\tpublic char c;\n\n\tpublic TextChar(float x0, float y0, float x1, float y1, char _c) {\n\t\tsuper(x0, y0, x1, y1);\n\t\tc = _c;\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/TextWord.java",
    "content": "package com.artifex.mupdfdemo;\n\nimport android.graphics.RectF;\n\npublic class TextWord extends RectF {\n\tpublic String w;\n\n\tpublic TextWord() {\n\t\tsuper();\n\t\tw = new String();\n\t}\n\n\tpublic void Add(TextChar tc) {\n\t\tsuper.union(tc);\n\t\tw = w.concat(new String(new char[]{tc.c}));\n\t}\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/mupdfdemo/WidgetType.java",
    "content": "package com.artifex.mupdfdemo;\n\npublic enum WidgetType {\n\tNONE,\n\tTEXT,\n\tLISTBOX,\n\tCOMBOBOX,\n\tSIGNATURE\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/utils/DigitalizedEventCallback.java",
    "content": "package com.artifex.utils;\n\n/**\n * Created by @elage on 6/2/15.\n */\npublic interface DigitalizedEventCallback {\n\n    public static final String ERROR_OUTSIDE_VERTICAL = \"ERROR_OUTSIDE_VERTICAL\";\n    public static final String ERROR_OUTSIDE_HORIZONTAL = \"ERROR_OUTSIDE_HORIZONTAL\";\n\n    public void longPressOnPdfPosition(int page, float viewX, float viewY, float pdfX, float pdfY);\n    public void doubleTapOnPdfPosition(int page, float viewX, float viewY, float pdfX, float pdfY);\n    public void singleTapOnPdfPosition(int page, float viewX, float viewY, float pdfX, float pdfY);\n    public void pageChanged(int page);\n    public void error(String message);\n}\n"
  },
  {
    "path": "src/main/java/com/artifex/utils/PdfBitmap.java",
    "content": "package com.artifex.utils;\n\nimport android.graphics.Bitmap;\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.util.Log;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\n\npublic class PdfBitmap implements Parcelable {\n\n\tpublic enum Type {\n\t\tSIGNATURE,\t\t\t\t// Signature used to sign the document\n\t\tSIGNATURE_USER_IMAGE, \t// User image of some older versions on Viafirma, where we sent the image to the server for it to process that along with the signature.\n\t\tIMAGE\t\t\t\t\t// All generic images shown\n\t};\n\n\tprivate Bitmap image;\n\tprivate int height;\n\tprivate int width;\n\tprivate int pageNumber;\n    private int pdfX;\n    private int pdfY;\n\tprivate Type type;\n\tprivate boolean isRemovable;\n\tprivate HashMap<String, String> metadata;\n\n    /**\n     * This class is used to store the information of each stamp and annotation on the PDF.\n     * @param image The bitmap in charge of storing the stamp or annotation\n     * @param height The height defined for the drawing\n     * @param width The width defined for the drawing\n     * @param pdfX The X coordinate position defined for the drawing\n     * @param pdfY The Y coordinate position defined for the drawing\n     * @param page The page of the PDF where the bitmap is added\n     */\n\tpublic PdfBitmap(Bitmap image, int width, int height, int pdfX, int pdfY, int page, Type type) {\n\t\tthis.image = image;\n\t\tthis.height = height;\n\t\tthis.width = width;\n        this.pdfX = pdfX;\n        this.pdfY = pdfY;\n\t\tthis.pageNumber = page;// first page is 0\n\t\tthis.type = type;\n\t\tthis.isRemovable = true;\n\t\tthis.metadata = new HashMap<>();\n\t}\n\t\n\tpublic PdfBitmap(Parcel in) {\n\t\t// We just need to read back each\n\t\t// field in the order that it was\n        image = in.readParcelable(Bitmap.class.getClassLoader());\n        height = in.readInt();\n        width = in.readInt();\n        pdfX = in.readInt();\n        pdfY = in.readInt();\n        pageNumber = in.readInt();\n\t\tString typeString = in.readString();\n\t\tif (typeString != null) {\n\t\t\ttype = Type.valueOf(typeString);\n\t\t}\n\t\tisRemovable = in.readByte() != 0;\n\t\tin.readMap(metadata, HashMap.class.getClassLoader());\n\t}\n\t\n\tpublic Bitmap getBitmapImage() {\n\t\treturn image;\n\t}\n\n\tpublic int getWidth() {\n\t\treturn width;\n\t}\n\t\n\tpublic int getHeight() {\n\t\treturn height;\n\t}\n\t\n\tpublic int getPageNumber() {\n\t\treturn pageNumber;\n\t}\n\n    @Override\n\tpublic int describeContents() {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic void writeToParcel(Parcel dest, int flags) {\n        dest.writeParcelable(image, flags);\n\t\tdest.writeInt(height);\n\t\tdest.writeInt(width);\n        dest.writeInt(pdfX);\n        dest.writeInt(pdfY);\n\t\tdest.writeInt(pageNumber);\n\t\tdest.writeString(type.name());\n\t\tdest.writeByte((byte)(isRemovable ? 1 : 0));\n\t\tdest.writeMap(metadata);\n\t}\n\n\tpublic static final Creator CREATOR = new Creator() {\n\t\tpublic PdfBitmap createFromParcel(Parcel in) {\n\t\t\treturn new PdfBitmap(in);\n\t\t}\n\t\tpublic PdfBitmap[] newArray(int size) {\n\t\t\treturn new PdfBitmap[size];\n\t\t}\n\t};\n\n    public int getPdfX() {\n        return pdfX;\n    }\n\n    public int getPdfY() {\n        return pdfY;\n    }\n\n\tpublic Type getType() {\n\t\treturn type;\n\t}\n\n\tpublic void setType(Type type) {\n\t\tthis.type = type;\n\t}\n\n\tpublic boolean isRemovable() {\n\t\treturn isRemovable;\n\t}\n\n\tpublic void setIsRemovable(boolean isRemovable) {\n\t\tthis.isRemovable = isRemovable;\n\t}\n\n\tpublic HashMap<String, String> getMetadata() {\n\t\treturn metadata;\n\t}\n\n\tpublic void setMetadata(HashMap<String, String> metadata) {\n\t\tthis.metadata = metadata;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\tString result = \"page:\"+pageNumber+\", x:\"+pdfX+\", y:\"+pdfY+\", width:\"+width+\", height:\"+height+\", type:\"+type.name();\n\t\treturn result;\n\t}\n\n\t@Override\n\tpublic boolean equals(Object o) {\n\t\tboolean result = false;\n\t\ttry {\n\t\t\tif (o == this) {\n\t\t\t\tresult = true;\n\t\t\t} else if (o instanceof PdfBitmap) {\n\t\t\t\tPdfBitmap that = (PdfBitmap) o;\n\t\t\t\tboolean sameBitmaps = that.getBitmapImage().sameAs(image);\n\t\t\t\tresult = (that.getPdfX() == pdfX) && (that.getPdfY() == pdfY) && (that.getHeight() == height) && (that.getWidth() == width) && (that.getPageNumber() == pageNumber) && sameBitmaps;\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLog.e(\"PdfBitmap\", e.getLocalizedMessage(), e);\n\t\t}\n\t\treturn result;\n\t}\n}\n"
  },
  {
    "path": "src/main/res/animator/info.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:ordering=\"sequentially\" >\n\n\t<objectAnimator\n\t\tandroid:propertyName=\"alpha\"\n\t\tandroid:valueFrom=\"0.0\"\n\t\tandroid:valueTo=\"1.0\"\n\t\tandroid:duration=\"200\" />\n\n\t<objectAnimator\n\t\tandroid:propertyName=\"alpha\"\n\t\tandroid:valueTo=\"1.0\"\n\t\tandroid:duration=\"800\" />\n\n\t<objectAnimator\n\t\tandroid:propertyName=\"alpha\"\n\t\tandroid:valueTo=\"0.0\"\n\t\tandroid:duration=\"400\" />\n</set>\n"
  },
  {
    "path": "src/main/res/drawable/busy.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:shape=\"rectangle\">\n\t<corners android:radius=\"12dp\" />\n\t<padding android:left=\"16dp\"\n\t\tandroid:right=\"16dp\"\n\t\tandroid:top=\"16dp\"\n\t\tandroid:bottom=\"16dp\" />\n\t<solid android:color=\"@color/busy_indicator\" />\n</shape>\n"
  },
  {
    "path": "src/main/res/drawable/button.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\t<item android:state_pressed=\"true\">\n\t\t<shape>\n\t\t\t<solid android:color=\"@color/button_pressed\" />\n\t\t\t<padding android:left=\"8dp\" android:top=\"8dp\" android:right=\"8dp\" android:bottom=\"8dp\" />\n\t\t</shape>\n\t</item>\n\t<item android:state_focused=\"true\">\n\t\t<shape>\n\t\t\t<solid android:color=\"@color/button_normal\" />\n\t\t\t<stroke android:width=\"4dp\" android:color=\"@color/button_pressed\" />\n\t\t\t<padding android:left=\"8dp\" android:top=\"8dp\" android:right=\"8dp\" android:bottom=\"8dp\" />\n\t\t</shape>\n\t</item>\n\t<item>\n\t\t<shape>\n\t\t\t<solid android:color=\"@color/button_normal\" />\n\t\t\t<padding android:left=\"8dp\" android:top=\"8dp\" android:right=\"8dp\" android:bottom=\"8dp\" />\n\t\t</shape>\n\t</item>\n</selector>\n"
  },
  {
    "path": "src/main/res/drawable/page_num.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:shape=\"rectangle\">\n\t<padding android:left=\"8dp\"\n\t\tandroid:right=\"8dp\"\n\t\tandroid:top=\"1dp\"\n\t\tandroid:bottom=\"2dp\" />\n\t<solid android:color=\"@color/page_indicator\" />\n</shape>\n"
  },
  {
    "path": "src/main/res/drawable/search.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\t<item android:state_pressed=\"true\">\n\t\t<shape android:shape=\"rectangle\">\n\t\t\t<solid android:color=\"@color/text_pressed\" />\n\t\t\t<stroke android:width=\"4dp\" android:color=\"@color/text_border_pressed\" />\n\t\t\t<padding\n\t\t\t\tandroid:left=\"12dp\"\n\t\t\t\tandroid:right=\"12dp\"\n\t\t\t\tandroid:top=\"8dp\"\n\t\t\t\tandroid:bottom=\"8dp\" />\n\t\t</shape>\n\t</item>\n\t<item android:state_focused=\"true\">\n\t\t<shape>\n\t\t\t<solid android:color=\"@color/text_normal\" />\n\t\t\t<stroke android:width=\"4dp\" android:color=\"@color/text_border_focused\" />\n\t\t\t<padding\n\t\t\t\tandroid:left=\"12dp\"\n\t\t\t\tandroid:right=\"12dp\"\n\t\t\t\tandroid:top=\"8dp\"\n\t\t\t\tandroid:bottom=\"8dp\" />\n\t\t</shape>\n\t</item>\n\t<item>\n\t\t<shape>\n\t\t\t<solid android:color=\"@color/text_normal\" />\n\t\t\t<stroke android:width=\"4dp\" android:color=\"@color/text_border_normal\" />\n\t\t\t<padding\n\t\t\t\tandroid:left=\"12dp\"\n\t\t\t\tandroid:right=\"12dp\"\n\t\t\t\tandroid:top=\"8dp\"\n\t\t\t\tandroid:bottom=\"8dp\" />\n\t\t</shape>\n\t</item>\n</selector>\n"
  },
  {
    "path": "src/main/res/drawable/seek_progress.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:shape=\"line\" >\n\t<stroke android:width=\"6dp\" android:color=\"@color/seek_progress\" />\n</shape>\n"
  },
  {
    "path": "src/main/res/drawable/seek_thumb.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:shape=\"oval\" >\n\t<size android:width=\"28dp\" android:height=\"28dp\" />\n\t<solid android:color=\"@color/seek_thumb\" />\n</shape>\n"
  },
  {
    "path": "src/main/res/drawable/tiled_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<bitmap xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:src=\"@drawable/darkdenim3\"\n\tandroid:tileMode=\"repeat\" />\n"
  },
  {
    "path": "src/main/res/layout/buttons.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"match_parent\" >\n\n\t<ViewAnimator\n\t\tandroid:id=\"@+id/switcher\"\n\t\tandroid:layout_width=\"wrap_content\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:layout_alignParentTop=\"true\"\n\t\tandroid:layout_centerHorizontal=\"true\" >\n\n\t\t<RelativeLayout\n\t\t\tandroid:id=\"@+id/topBar0Main\"\n\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:background=\"@color/toolbar\" >\n\n\t\t\t<TextView\n\t\t\t\tandroid:id=\"@+id/docNameText\"\n\t\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\tandroid:layout_centerVertical=\"true\"\n\t\t\t\tandroid:layout_toLeftOf=\"@+id/linkButton\"\n\t\t\t\tandroid:layout_alignParentLeft=\"true\"\n\t\t\t\tandroid:paddingLeft=\"16dp\"\n\t\t\t\tandroid:singleLine=\"true\"\n\t\t\t\tandroid:textColor=\"#FFFFFF\"\n\t\t\t\tandroid:textStyle=\"bold\"\n\t\t\t\tandroid:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n\t\t\t<ImageButton\n\t\t\t\tandroid:id=\"@+id/linkButton\"\n\t\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\tandroid:layout_centerVertical=\"true\"\n\t\t\t\tandroid:layout_toLeftOf=\"@+id/reflowButton\"\n\t\t\t\tandroid:contentDescription=\"@string/toggle_links\"\n\t\t\t\tandroid:background=\"@drawable/button\"\n\t\t\t\tandroid:src=\"@drawable/ic_link\" />\n\n\t\t\t<ImageButton\n\t\t\t\tandroid:id=\"@+id/reflowButton\"\n\t\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\tandroid:layout_centerVertical=\"true\"\n\t\t\t\tandroid:layout_toLeftOf=\"@+id/outlineButton\"\n\t\t\t\tandroid:contentDescription=\"@string/toggle_reflow_mode\"\n\t\t\t\tandroid:background=\"@drawable/button\"\n\t\t\t\tandroid:src=\"@drawable/ic_reflow\" />\n\n\t\t\t<ImageButton\n\t\t\t\tandroid:id=\"@+id/outlineButton\"\n\t\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\tandroid:layout_centerVertical=\"true\"\n\t\t\t\tandroid:layout_toLeftOf=\"@+id/searchButton\"\n\t\t\t\tandroid:contentDescription=\"@string/outline_title\"\n\t\t\t\tandroid:background=\"@drawable/button\"\n\t\t\t\tandroid:src=\"@drawable/ic_list\" />\n\n\t\t\t<ImageButton\n\t\t\t\tandroid:id=\"@+id/searchButton\"\n\t\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\tandroid:layout_centerVertical=\"true\"\n\t\t\t\tandroid:layout_toLeftOf=\"@+id/moreButton\"\n\t\t\t\tandroid:contentDescription=\"@string/search_document\"\n\t\t\t\tandroid:background=\"@drawable/button\"\n\t\t\t\tandroid:src=\"@drawable/ic_magnifying_glass\" />\n\n\t\t\t<ImageButton\n\t\t\t\tandroid:id=\"@+id/moreButton\"\n\t\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\tandroid:layout_centerVertical=\"true\"\n\t\t\t\tandroid:layout_alignParentRight=\"true\"\n\t\t\t\tandroid:contentDescription=\"@string/more\"\n\t\t\t\tandroid:background=\"@drawable/button\"\n\t\t\t\tandroid:onClick=\"OnMoreButtonClick\"\n\t\t\t\tandroid:src=\"@drawable/ic_more\" />\n\n\t\t</RelativeLayout>\n\n\t\t<RelativeLayout\n\t\t\tandroid:id=\"@+id/topBar1Search\"\n\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:background=\"@color/toolbar\" >\n\n\t\t\t<ImageButton\n\t\t\t\tandroid:id=\"@+id/cancelSearch\"\n\t\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\tandroid:layout_centerVertical=\"true\"\n\t\t\t\tandroid:layout_alignParentLeft=\"true\"\n\t\t\t\tandroid:contentDescription=\"@string/cancel\"\n\t\t\t\tandroid:background=\"@drawable/button\"\n\t\t\t\tandroid:onClick=\"OnCancelSearchButtonClick\"\n\t\t\t\tandroid:src=\"@drawable/ic_cancel\" />\n\n\t\t\t<EditText\n\t\t\t\tandroid:id=\"@+id/searchText\"\n\t\t\t\tandroid:background=\"@drawable/search\"\n\t\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\tandroid:layout_centerVertical=\"true\"\n\t\t\t\tandroid:layout_toRightOf=\"@+id/cancelSearch\"\n\t\t\t\tandroid:layout_toLeftOf=\"@+id/searchBack\"\n\t\t\t\tandroid:inputType=\"text\"\n\t\t\t\tandroid:hint=\"@string/search\"\n\t\t\t\tandroid:singleLine=\"true\" />\n\n\t\t\t<ImageButton\n\t\t\t\tandroid:id=\"@+id/searchBack\"\n\t\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\tandroid:layout_centerVertical=\"true\"\n\t\t\t\tandroid:layout_toLeftOf=\"@+id/searchForward\"\n\t\t\t\tandroid:contentDescription=\"@string/search_backwards\"\n\t\t\t\tandroid:background=\"@drawable/button\"\n\t\t\t\tandroid:src=\"@drawable/ic_arrow_left\" />\n\n\t\t\t<ImageButton\n\t\t\t\tandroid:id=\"@+id/searchForward\"\n\t\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\tandroid:layout_centerVertical=\"true\"\n\t\t\t\tandroid:layout_alignParentRight=\"true\"\n\t\t\t\tandroid:contentDescription=\"@string/search_forwards\"\n\t\t\t\tandroid:background=\"@drawable/button\"\n\t\t\t\tandroid:src=\"@drawable/ic_arrow_right\" />\n\n\t\t</RelativeLayout>\n\n\t\t<RelativeLayout\n\t\t\tandroid:id=\"@+id/topBar2Annot\"\n\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:background=\"@color/toolbar\" >\n\n\t\t\t<ImageButton\n\t\t\t\tandroid:id=\"@+id/cancelAnnotButton\"\n\t\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\tandroid:layout_centerVertical=\"true\"\n\t\t\t\tandroid:layout_alignParentLeft=\"true\"\n\t\t\t\tandroid:contentDescription=\"@string/cancel\"\n\t\t\t\tandroid:background=\"@drawable/button\"\n\t\t\t\tandroid:onClick=\"OnCancelAnnotButtonClick\"\n\t\t\t\tandroid:src=\"@drawable/ic_cancel\" />\n\n\t\t\t<ImageButton\n\t\t\t\tandroid:id=\"@+id/highlightButton\"\n\t\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\tandroid:layout_centerVertical=\"true\"\n\t\t\t\tandroid:layout_toLeftOf=\"@+id/underlineButton\"\n\t\t\t\tandroid:contentDescription=\"@string/highlight\"\n\t\t\t\tandroid:background=\"@drawable/button\"\n\t\t\t\tandroid:onClick=\"OnHighlightButtonClick\"\n\t\t\t\tandroid:src=\"@drawable/ic_highlight\" />\n\n\t\t\t<ImageButton\n\t\t\t\tandroid:id=\"@+id/underlineButton\"\n\t\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\tandroid:layout_centerVertical=\"true\"\n\t\t\t\tandroid:layout_toLeftOf=\"@+id/strikeOutButton\"\n\t\t\t\tandroid:contentDescription=\"@string/underline\"\n\t\t\t\tandroid:background=\"@drawable/button\"\n\t\t\t\tandroid:onClick=\"OnUnderlineButtonClick\"\n\t\t\t\tandroid:src=\"@drawable/ic_underline\" />\n\n\t\t\t<ImageButton\n\t\t\t\tandroid:id=\"@+id/strikeOutButton\"\n\t\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\tandroid:layout_centerVertical=\"true\"\n\t\t\t\tandroid:layout_toLeftOf=\"@+id/inkButton\"\n\t\t\t\tandroid:contentDescription=\"@string/strike_out\"\n\t\t\t\tandroid:background=\"@drawable/button\"\n\t\t\t\tandroid:onClick=\"OnStrikeOutButtonClick\"\n\t\t\t\tandroid:src=\"@drawable/ic_strike\" />\n\n\t\t\t<ImageButton\n\t\t\t\tandroid:id=\"@+id/inkButton\"\n\t\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\tandroid:layout_centerVertical=\"true\"\n\t\t\t\tandroid:layout_alignParentRight=\"true\"\n\t\t\t\tandroid:contentDescription=\"@string/ink\"\n\t\t\t\tandroid:background=\"@drawable/button\"\n\t\t\t\tandroid:onClick=\"OnInkButtonClick\"\n\t\t\t\tandroid:src=\"@drawable/ic_pen\" />\n\n\t\t</RelativeLayout>\n\n\t\t<RelativeLayout\n\t\t\tandroid:id=\"@+id/topBar3Delete\"\n\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:background=\"@color/toolbar\" >\n\n\t\t\t<ImageButton\n\t\t\t\tandroid:id=\"@+id/cancelDeleteButton\"\n\t\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\tandroid:layout_centerVertical=\"true\"\n\t\t\t\tandroid:layout_alignParentLeft=\"true\"\n\t\t\t\tandroid:contentDescription=\"@string/cancel\"\n\t\t\t\tandroid:background=\"@drawable/button\"\n\t\t\t\tandroid:onClick=\"OnCancelDeleteButtonClick\"\n\t\t\t\tandroid:src=\"@drawable/ic_cancel\" />\n\n\t\t\t<TextView\n\t\t\t\tandroid:id=\"@+id/deleteLabel\"\n\t\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\tandroid:layout_centerVertical=\"true\"\n\t\t\t\tandroid:layout_toRightOf=\"@+id/cancelDeleteButton\"\n\t\t\t\tandroid:layout_toLeftOf=\"@+id/deleteButton\"\n\t\t\t\tandroid:gravity=\"center\"\n\t\t\t\tandroid:singleLine=\"true\"\n\t\t\t\tandroid:textColor=\"#FFFFFF\"\n\t\t\t\tandroid:textStyle=\"bold\"\n\t\t\t\tandroid:text=\"@string/delete\"\n\t\t\t\tandroid:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n\t\t\t<ImageButton\n\t\t\t\tandroid:id=\"@+id/deleteButton\"\n\t\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\tandroid:layout_centerVertical=\"true\"\n\t\t\t\tandroid:layout_alignParentRight=\"true\"\n\t\t\t\tandroid:contentDescription=\"@string/delete\"\n\t\t\t\tandroid:background=\"@drawable/button\"\n\t\t\t\tandroid:onClick=\"OnDeleteButtonClick\"\n\t\t\t\tandroid:src=\"@drawable/ic_trash\" />\n\n\t\t</RelativeLayout>\n\n\t\t<RelativeLayout\n\t\t\tandroid:id=\"@+id/topBar4More\"\n\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:background=\"@color/toolbar\" >\n\n\t\t\t<ImageButton\n\t\t\t\tandroid:id=\"@+id/cancelMoreButton\"\n\t\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\tandroid:layout_centerVertical=\"true\"\n\t\t\t\tandroid:layout_alignParentLeft=\"true\"\n\t\t\t\tandroid:contentDescription=\"@string/cancel\"\n\t\t\t\tandroid:background=\"@drawable/button\"\n\t\t\t\tandroid:onClick=\"OnCancelMoreButtonClick\"\n\t\t\t\tandroid:src=\"@drawable/ic_cancel\" />\n\n\t\t\t<ImageButton\n\t\t\t\tandroid:id=\"@+id/printButton\"\n\t\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\tandroid:layout_centerVertical=\"true\"\n\t\t\t\tandroid:layout_toLeftOf=\"@+id/copyTextButton\"\n\t\t\t\tandroid:contentDescription=\"@string/print\"\n\t\t\t\tandroid:background=\"@drawable/button\"\n\t\t\t\tandroid:onClick=\"OnPrintButtonClick\"\n\t\t\t\tandroid:src=\"@drawable/ic_print\" />\n\n\t\t\t<ImageButton\n\t\t\t\tandroid:id=\"@+id/copyTextButton\"\n\t\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\tandroid:layout_centerVertical=\"true\"\n\t\t\t\tandroid:layout_toLeftOf=\"@+id/editAnnotButton\"\n\t\t\t\tandroid:layout_alignWithParentIfMissing=\"true\"\n\t\t\t\tandroid:contentDescription=\"@string/copy_text_to_the_clipboard\"\n\t\t\t\tandroid:background=\"@drawable/button\"\n\t\t\t\tandroid:onClick=\"OnCopyTextButtonClick\"\n\t\t\t\tandroid:src=\"@drawable/ic_clipboard\" />\n\n\t\t\t<ImageButton\n\t\t\t\tandroid:id=\"@+id/editAnnotButton\"\n\t\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\tandroid:layout_centerVertical=\"true\"\n\t\t\t\tandroid:layout_alignParentRight=\"true\"\n\t\t\t\tandroid:contentDescription=\"@string/edit_annotations\"\n\t\t\t\tandroid:background=\"@drawable/button\"\n\t\t\t\tandroid:onClick=\"OnEditAnnotButtonClick\"\n\t\t\t\tandroid:src=\"@drawable/ic_annotation\" />\n\t\t</RelativeLayout>\n\n\t\t<RelativeLayout\n\t\t\tandroid:id=\"@+id/topBar5Accept\"\n\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:background=\"@color/toolbar\" >\n\n\t\t\t<ImageButton\n\t\t\t\tandroid:id=\"@+id/cancelAcceptButton\"\n\t\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\tandroid:layout_centerVertical=\"true\"\n\t\t\t\tandroid:layout_alignParentLeft=\"true\"\n\t\t\t\tandroid:contentDescription=\"@string/cancel\"\n\t\t\t\tandroid:background=\"@drawable/button\"\n\t\t\t\tandroid:onClick=\"OnCancelAcceptButtonClick\"\n\t\t\t\tandroid:src=\"@drawable/ic_cancel\" />\n\n\t\t\t<TextView\n\t\t\t\tandroid:id=\"@+id/annotType\"\n\t\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\tandroid:layout_centerVertical=\"true\"\n\t\t\t\tandroid:layout_toRightOf=\"@+id/cancelAcceptButton\"\n\t\t\t\tandroid:layout_toLeftOf=\"@+id/acceptButton\"\n\t\t\t\tandroid:gravity=\"center\"\n\t\t\t\tandroid:singleLine=\"true\"\n\t\t\t\tandroid:textColor=\"#FFFFFF\"\n\t\t\t\tandroid:textStyle=\"bold\"\n\t\t\t\tandroid:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n\t\t\t<ImageButton\n\t\t\t\tandroid:id=\"@+id/acceptButton\"\n\t\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\t\tandroid:layout_centerVertical=\"true\"\n\t\t\t\tandroid:layout_alignParentRight=\"true\"\n\t\t\t\tandroid:contentDescription=\"@string/accept\"\n\t\t\t\tandroid:background=\"@drawable/button\"\n\t\t\t\tandroid:onClick=\"OnAcceptButtonClick\"\n\t\t\t\tandroid:src=\"@drawable/ic_check\" />\n\t\t</RelativeLayout>\n\t\t</ViewAnimator>\n\n\t<RelativeLayout\n\t\tandroid:id=\"@+id/lowerButtons\"\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:layout_alignParentBottom=\"true\"\n\t\tandroid:layout_centerHorizontal=\"true\" >\n\n\t\t<SeekBar\n\t\t\tandroid:id=\"@+id/pageSlider\"\n\t\t\tandroid:layout_width=\"match_parent\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:layout_alignParentBottom=\"true\"\n\t\t\tandroid:layout_centerHorizontal=\"true\"\n\t\t\tandroid:layout_margin=\"0dp\"\n\t\t\tandroid:thumb=\"@drawable/seek_thumb\"\n\t\t\tandroid:progressDrawable=\"@drawable/seek_progress\"\n\t\t\tandroid:paddingLeft=\"16dp\"\n\t\t\tandroid:paddingRight=\"16dp\"\n\t\t\tandroid:paddingTop=\"12dp\"\n\t\t\tandroid:paddingBottom=\"8dp\"\n\t\t\tandroid:background=\"@color/toolbar\"\n\t\t\t/>\n\n\t\t<TextView\n\t\t\tandroid:id=\"@+id/pageNumber\"\n\t\t\tandroid:layout_width=\"wrap_content\"\n\t\t\tandroid:layout_height=\"wrap_content\"\n\t\t\tandroid:layout_above=\"@+id/pageSlider\"\n\t\t\tandroid:layout_centerHorizontal=\"true\"\n\t\t\tandroid:layout_marginBottom=\"16dp\"\n\t\t\tandroid:background=\"@drawable/page_num\"\n\t\t\tandroid:textColor=\"#FFFFFF\"\n\t\t\tandroid:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n\t</RelativeLayout>\n\n\t<TextView\n\t\tandroid:id=\"@+id/info\"\n\t\tandroid:layout_width=\"wrap_content\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:layout_above=\"@+id/pageSlider\"\n\t\tandroid:layout_centerHorizontal=\"true\"\n\t\tandroid:layout_centerVertical=\"true\"\n\t\tandroid:background=\"@drawable/page_num\"\n\t\tandroid:textAppearance=\"?android:attr/textAppearanceMedium\"\n\t\tandroid:textColor=\"#FFFFFF\" />\n</RelativeLayout>\n"
  },
  {
    "path": "src/main/res/layout/main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:orientation=\"vertical\"\n\tandroid:layout_width=\"fill_parent\"\n\tandroid:layout_height=\"fill_parent\" />\n"
  },
  {
    "path": "src/main/res/layout/outline_entry.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"wrap_content\" >\n\n\t<TextView\n\t\tandroid:id=\"@+id/title\"\n\t\tandroid:layout_width=\"wrap_content\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:layout_alignParentLeft=\"true\"\n\t\tandroid:layout_toLeftOf=\"@+id/page\"\n\t\tandroid:singleLine=\"true\"\n\t\tandroid:layout_centerVertical=\"true\"\n\t\tandroid:paddingLeft=\"8dp\"\n\t\tandroid:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n\t<TextView\n\t\tandroid:id=\"@+id/page\"\n\t\tandroid:layout_width=\"wrap_content\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:layout_alignBaseline=\"@+id/title\"\n\t\tandroid:layout_alignBottom=\"@+id/title\"\n\t\tandroid:layout_alignParentRight=\"true\"\n\t\tandroid:paddingRight=\"8dp\"\n\t\tandroid:textAppearance=\"?android:attr/textAppearanceMedium\" />\n\n</RelativeLayout>\n"
  },
  {
    "path": "src/main/res/layout/picker_entry.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"wrap_content\"\n\tandroid:paddingLeft=\"8dp\" >\n\n\t<ImageView\n\t\tandroid:id=\"@+id/icon\"\n\t\tandroid:layout_width=\"wrap_content\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:layout_centerVertical=\"true\"\n\t\tandroid:layout_alignParentLeft=\"true\" />\n\n\t<TextView\n\t\tandroid:id=\"@+id/name\"\n\t\tandroid:layout_width=\"match_parent\"\n\t\tandroid:layout_height=\"wrap_content\"\n\t\tandroid:layout_toRightOf=\"@+id/icon\"\n\t\tandroid:paddingBottom=\"8dp\"\n\t\tandroid:paddingLeft=\"12dp\"\n\t\tandroid:paddingRight=\"12dp\"\n\t\tandroid:paddingTop=\"8dp\"\n\t\tandroid:textAppearance=\"?android:attr/textAppearanceLarge\" />\n\n</RelativeLayout>\n"
  },
  {
    "path": "src/main/res/layout/print_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:layout_width=\"fill_parent\"\n\tandroid:layout_height=\"fill_parent\">\n\t<WebView android:id=\"@+id/webview\"\n\t\tandroid:layout_width=\"fill_parent\"\n\t\tandroid:layout_height=\"fill_parent\"/>\n</RelativeLayout>\n"
  },
  {
    "path": "src/main/res/layout/textentry.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<EditText xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:singleLine=\"false\"\n\tandroid:minLines=\"3\"\n\tandroid:inputType=\"textMultiLine\"\n\tandroid:layout_width=\"match_parent\"\n\tandroid:layout_height=\"wrap_content\" >\n</EditText>\n"
  },
  {
    "path": "src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<color name=\"canvas\">#404040</color>\n\t<color name=\"toolbar\">#C0000000</color>\n\t<color name=\"page_indicator\">#C0202020</color>\n\t<color name=\"busy_indicator\">#C0202020</color>\n\t<color name=\"button_normal\">#00000000</color>\n\t<color name=\"button_pressed\">#FF2572AC</color>\n\t<color name=\"text_normal\">#FFFFFF</color>\n\t<color name=\"text_pressed\">#FFFFFF</color>\n\t<color name=\"text_border_normal\">#000000</color>\n\t<color name=\"text_border_pressed\">#2572AC</color>\n\t<color name=\"text_border_focused\">#000000</color>\n\t<color name=\"seek_thumb\">#2572AC</color>\n\t<color name=\"seek_progress\">#FFFFFF</color>\n</resources>\n"
  },
  {
    "path": "src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"version\">1.6 (git build)</string>\n\t<string name=\"no_media_warning\">Storage media not present</string>\n\t<string name=\"no_media_hint\">Sharing the storage media with a PC can make it inaccessible</string>\n\t<string name=\"cancel\">Cancel</string>\n\t<string name=\"search_backwards\">Search backwards</string>\n\t<string name=\"search_forwards\">Search forwards</string>\n\t<string name=\"search_document\">Search document</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s %2$s: %3$s</string>\n\t<string name=\"outline_title\">Table of Contents</string>\n\t<string name=\"enter_password\">Enter password</string>\n\t<string name=\"text_not_found\">Text not found</string>\n\t<string name=\"searching_\">Searching&#8230;</string>\n\t<string name=\"toggle_links\">Highlight and enable links</string>\n\t<string name=\"no_further_occurrences_found\">No further occurrences found</string>\n\t<string name=\"select\">Select</string>\n\t<string name=\"search\">Search</string>\n\t<string name=\"copy\">Copy</string>\n\t<string name=\"strike_out\">Strike-out</string>\n\t<string name=\"delete\">Delete</string>\n\t<string name=\"highlight\">Highlight</string>\n\t<string name=\"underline\">Underline</string>\n\t<string name=\"edit_annotations\">Edit annotations</string>\n\t<string name=\"ink\">Ink</string>\n\t<string name=\"save\">Save</string>\n\t<string name=\"print\">Print</string>\n\t<string name=\"dismiss\">Dismiss</string>\n\t<string name=\"parent_directory\">[Up one level]</string>\n\t<string name=\"yes\">Yes</string>\n\t<string name=\"no\">No</string>\n\t<string name=\"entering_reflow_mode\">Entering reflow mode</string>\n\t<string name=\"leaving_reflow_mode\">Leaving reflow mode</string>\n\t<string name=\"print_failed\">Print failed</string>\n\t<string name=\"select_text\">Select text</string>\n\t<string name=\"copied_to_clipboard\">Copied to clipboard</string>\n\t<string name=\"no_text_selected\">No text selected</string>\n\t<string name=\"draw_annotation\">Draw annotation</string>\n\t<string name=\"nothing_to_save\">Nothing to save</string>\n\t<string name=\"document_has_changes_save_them_\">Document has changes. Save them?</string>\n\t<string name=\"cannot_open_document\">Cannot open document</string>\n\t<string name=\"cannot_open_document_Reason\">Cannot open document: %1$s</string>\n\t<string name=\"cannot_open_file_Path\">Cannot open file: %1$s</string>\n\t<string name=\"cannot_open_buffer\">Cannot open buffer</string>\n\t<string name=\"fill_out_text_field\">Fill out text field</string>\n\t<string name=\"okay\">Okay</string>\n\t<string name=\"choose_value\">Choose value</string>\n\t<string name=\"not_supported\">Not supported</string>\n\t<string name=\"copy_text_to_the_clipboard\">Copy text to the clipboard</string>\n\t<string name=\"more\">More</string>\n\t<string name=\"accept\">Accept</string>\n\t<string name=\"copy_text\">copy text</string>\n\t<string name=\"format_currently_not_supported\">Format currently not supported</string>\n\t<string name=\"toggle_reflow_mode\">Toggle reflow mode</string>\n\n    <string name=\"open_failed\">Unable to open the document</string>\n    <string name=\"helpPutSign\">Double-tap where you want to place your signature</string>\n    <string name=\"noSignOnPdfTitle\">Warning</string>\n    <string name=\"noSignOnPdf\">The document has not been signed. Please, double-tap where you want to place your signature</string>\n    <string name=\"OkKey\">Ok</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values/styles.xml",
    "content": "<resources>\n    <style name=\"AppBaseTheme\" parent=\"@android:style/Theme.NoTitleBar.Fullscreen\">\n        <item name=\"android:windowBackground\">@drawable/tiled_background</item>\n   </style>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-ar/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">قبول</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">إلغاء</string>\n\t<string name=\"cannot_open_buffer\">تعذر فتح المخزن المؤقت</string>\n\t<string name=\"cannot_open_document\">تعذر فتح المستند</string>\n\t<string name=\"cannot_open_document_Reason\">تعذر فتح المستند: %1$s</string>\n\t<string name=\"cannot_open_file_Path\">تعذر فتح الملف: %1$s</string>\n\t<string name=\"choose_value\">اختر قيمة</string>\n\t<string name=\"copied_to_clipboard\">تم النسخ إلى الحافظة</string>\n\t<string name=\"copy\">نسخ</string>\n\t<string name=\"copy_text\">نسخ النص</string>\n\t<string name=\"copy_text_to_the_clipboard\">نسخ النص إلى الحافظة</string>\n\t<string name=\"delete\">حذف</string>\n\t<string name=\"dismiss\">تجاهل</string>\n\t<string name=\"document_has_changes_save_them_\">يحتوي المستند على تغييرات. هل تريد حفظها؟</string>\n\t<string name=\"draw_annotation\">سحب تعليق توضيحي</string>\n\t<string name=\"edit_annotations\">تعديل التعليقات التوضيحية</string>\n\t<string name=\"enter_password\">أدخل كلمة المرور</string>\n\t<string name=\"entering_reflow_mode\">دخول إلى وضع إعادة التدفق</string>\n\t<string name=\"fill_out_text_field\">تعبئة حقل النص</string>\n\t<string name=\"format_currently_not_supported\">التنسيق غير مدعوم حاليًا</string>\n\t<string name=\"highlight\">تظليل</string>\n\t<string name=\"ink\">حبر</string>\n\t<string name=\"leaving_reflow_mode\">خروج من وضع إعادة التدفق</string>\n\t<string name=\"more\">المزيد</string>\n\t<string name=\"no\">لا</string>\n\t<string name=\"no_further_occurrences_found\">لم يتم العثور على متكررات أخرى</string>\n\t<string name=\"no_media_hint\">مشاركة وسائط التخزين مع حاسوب شخصي قد يمنع الوصول إليها</string>\n\t<string name=\"no_media_warning\">وسائط التخزين غير موجودة</string>\n\t<string name=\"no_text_selected\">لم يتم تحديد نص</string>\n\t<string name=\"not_supported\">غير مدعوم</string>\n\t<string name=\"nothing_to_save\">لا يوجد شيء لحفظه</string>\n\t<string name=\"okay\">موافق</string>\n\t<string name=\"outline_title\">جدول المحتويات</string>\n\t<string name=\"parent_directory\">[أعلى مستوى واحد]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s %2$s: %3$s</string>\n\t<string name=\"print\">طباعة</string>\n\t<string name=\"print_failed\">فشلت الطباعة</string>\n\t<string name=\"save\">حفظ</string>\n\t<string name=\"search\">بحث</string>\n\t<string name=\"search_backwards\">بحث إلى الخلف</string>\n\t<string name=\"search_document\">بحث في المستند</string>\n\t<string name=\"search_forwards\">بحث إلى الأمام</string>\n\t<string name=\"searching_\">جاري البحث في&amp;#8230;</string>\n\t<string name=\"select\">تحديد</string>\n\t<string name=\"select_text\">تحديد النص</string>\n\t<string name=\"strike_out\">شطب</string>\n\t<string name=\"text_not_found\">لم يتم العثور على النص</string>\n\t<string name=\"toggle_links\">تظليل وتمكين الروابط</string>\n\t<string name=\"underline\">تسطير</string>\n\t<string name=\"yes\">نعم</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-ca/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">Acceptar</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">Cancel·lar</string>\n\t<string name=\"cannot_open_buffer\">No es pot obrir el buffer</string>\n\t<string name=\"cannot_open_document\">No es pot obrir el document</string>\n\t<string name=\"cannot_open_document_Reason\">No es pot obrir el document: %1$s</string>\n\t<string name=\"cannot_open_file_Path\">No es pot obrir l\\'arxiu: %1$s</string>\n\t<string name=\"choose_value\">Tria el valor</string>\n\t<string name=\"copied_to_clipboard\">Copiat al portapapers</string>\n\t<string name=\"copy\">Copiar</string>\n\t<string name=\"copy_text\">copiar text</string>\n\t<string name=\"copy_text_to_the_clipboard\">Copiar text al portapapers</string>\n\t<string name=\"delete\">Esborrar</string>\n\t<string name=\"dismiss\">Descartar</string>\n\t<string name=\"document_has_changes_save_them_\">El document té canvis. Desar?</string>\n\t<string name=\"draw_annotation\">Dibuixar anotació</string>\n\t<string name=\"edit_annotations\">Editar anotacions</string>\n\t<string name=\"enter_password\">Introduir contrasenya</string>\n\t<string name=\"entering_reflow_mode\">Entrant en modo de reflux</string>\n\t<string name=\"fill_out_text_field\">Emplena el camp de text</string>\n\t<string name=\"format_currently_not_supported\">Format no suportat actualment</string>\n\t<string name=\"highlight\">Destacar</string>\n\t<string name=\"ink\">Tinta</string>\n\t<string name=\"leaving_reflow_mode\">Abandonant modo de reflux</string>\n\t<string name=\"more\">Més</string>\n\t<string name=\"no\">No</string>\n\t<string name=\"no_further_occurrences_found\">No hi ha més coincidències</string>\n\t<string name=\"no_media_hint\">Compartir el mitjà d\\'emmagatzematge amb un PC pot fer que sigui inaccessible</string>\n\t<string name=\"no_media_warning\">Mitjà d\\'emmagatzematge no present</string>\n\t<string name=\"no_text_selected\">No s\\'ha seleccionat text</string>\n\t<string name=\"not_supported\">No compatible</string>\n\t<string name=\"nothing_to_save\">No hi ha gens que guardar</string>\n\t<string name=\"okay\">Acceptar</string>\n\t<string name=\"outline_title\">Índex</string>\n\t<string name=\"parent_directory\">[Pujar un nivell]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s %2$s: %3$s</string>\n\t<string name=\"print\">Imprimir</string>\n\t<string name=\"print_failed\">Fallada al imprimir</string>\n\t<string name=\"save\">Desar</string>\n\t<string name=\"search\">Buscar</string>\n\t<string name=\"search_backwards\">Buscar cap a enrere</string>\n\t<string name=\"search_document\">Buscar document</string>\n\t<string name=\"search_forwards\">Buscar cap a davant</string>\n\t<string name=\"searching_\">Buscant…</string>\n\t<string name=\"select\">Seleccionar</string>\n\t<string name=\"select_text\">Seleccionar text</string>\n\t<string name=\"strike_out\">Ratllat</string>\n\t<string name=\"text_not_found\">Text no trobat</string>\n\t<string name=\"toggle_links\">Ressaltar i habilitar enllaços</string>\n\t<string name=\"underline\">Subratllat</string>\n\t<string name=\"yes\">Sí</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-cs/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">Přijmout</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">Zrušit</string>\n\t<string name=\"cannot_open_buffer\">Nelze otevřít vyrovnávací paměť</string>\n\t<string name=\"cannot_open_document\">Nelze otevřít dokument</string>\n\t<string name=\"cannot_open_document_Reason\">Nelze otevřít dokument: %1$s</string>\n\t<string name=\"cannot_open_file_Path\">Nelze otevřít soubor: %1$s</string>\n\t<string name=\"choose_value\">Zvolte hodnotu</string>\n\t<string name=\"copied_to_clipboard\">Kopírováno do schránky</string>\n\t<string name=\"copy\">Kopírovat</string>\n\t<string name=\"copy_text\">kopírovat text</string>\n\t<string name=\"copy_text_to_the_clipboard\">Kopírovat text do schránky</string>\n\t<string name=\"delete\">Smazat</string>\n\t<string name=\"dismiss\">Odmítnout</string>\n\t<string name=\"document_has_changes_save_them_\">Dokument byl změněn. Uložit?</string>\n\t<string name=\"draw_annotation\">Vložit anotaci</string>\n\t<string name=\"edit_annotations\">Upravit anotace</string>\n\t<string name=\"enter_password\">Zadat heslo</string>\n\t<string name=\"entering_reflow_mode\">Vstup do režimu přeformátování řádků</string>\n\t<string name=\"fill_out_text_field\">Vyplnit textové pole</string>\n\t<string name=\"format_currently_not_supported\">Formát aktuálně nepodporován</string>\n\t<string name=\"highlight\">Zvýraznit</string>\n\t<string name=\"ink\">Inkoust</string>\n\t<string name=\"leaving_reflow_mode\">Odchod z režimu přeformátování řádků</string>\n\t<string name=\"more\">Více</string>\n\t<string name=\"no\">Ne</string>\n\t<string name=\"no_further_occurrences_found\">Nenalezeny další výskyty</string>\n\t<string name=\"no_media_hint\">Při sdílení s PC může být paměťové médium nedostupné</string>\n\t<string name=\"no_media_warning\">Paměťové médim nenalezeno</string>\n\t<string name=\"no_text_selected\">Nevybrán žádný text</string>\n\t<string name=\"not_supported\">Nepodporováno</string>\n\t<string name=\"nothing_to_save\">Nic k uložení</string>\n\t<string name=\"okay\">OK</string>\n\t<string name=\"outline_title\">Obsah</string>\n\t<string name=\"parent_directory\">[Nahoru o jednu úroveň]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s %2$s: %3$s</string>\n\t<string name=\"print\">Tisk</string>\n\t<string name=\"print_failed\">Tisk selhal</string>\n\t<string name=\"save\">Uložit</string>\n\t<string name=\"search\">Hledat</string>\n\t<string name=\"search_backwards\">Hledat zpět</string>\n\t<string name=\"search_document\">Prohledat dokument</string>\n\t<string name=\"search_forwards\">Hledat vpřed</string>\n\t<string name=\"searching_\">Hledání&amp;#8230;</string>\n\t<string name=\"select\">Vybrat</string>\n\t<string name=\"select_text\">Vybrat text</string>\n\t<string name=\"strike_out\">Přeškrtnout</string>\n\t<string name=\"text_not_found\">Text nenalezen</string>\n\t<string name=\"toggle_links\">Zvýraznit a aktivovat odkazy</string>\n\t<string name=\"underline\">Podtrhnout</string>\n\t<string name=\"yes\">Ano</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-da/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">Accepter</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">Annuller</string>\n\t<string name=\"cannot_open_buffer\">Buffer kan ikke åbnes</string>\n\t<string name=\"cannot_open_document\">Dokument kan ikke åbnes</string>\n\t<string name=\"cannot_open_document_Reason\">Kan ikke åbne dokumentet: %1$s</string>\n\t<string name=\"cannot_open_file_Path\">Kan ikke åbne filen: %1$s</string>\n\t<string name=\"choose_value\">Vælg værdi</string>\n\t<string name=\"copied_to_clipboard\">Kopieret til udklipsholder</string>\n\t<string name=\"copy\">Kopier</string>\n\t<string name=\"copy_text\">kopier tekst</string>\n\t<string name=\"copy_text_to_the_clipboard\">kopier tekst til udklipsholder</string>\n\t<string name=\"delete\">Slet</string>\n\t<string name=\"dismiss\">Afvis</string>\n\t<string name=\"document_has_changes_save_them_\">Dokumentet er ændret. Gem ændringer?</string>\n\t<string name=\"draw_annotation\">Lav anmærkning</string>\n\t<string name=\"edit_annotations\">Rediger anmærkninger</string>\n\t<string name=\"enter_password\">Indtast adgangskode</string>\n\t<string name=\"entering_reflow_mode\">Går over til konverteringstilstand</string>\n\t<string name=\"fill_out_text_field\">Udfyld tekstfelt</string>\n\t<string name=\"format_currently_not_supported\">Format ikke understøttet i øjeblikket</string>\n\t<string name=\"highlight\">Fremhæv</string>\n\t<string name=\"ink\">Ink</string>\n\t<string name=\"leaving_reflow_mode\">Forlader konverteringstilstand</string>\n\t<string name=\"more\">Mere</string>\n\t<string name=\"no\">Nej</string>\n\t<string name=\"no_further_occurrences_found\">Der blev ikke fundet flere tilfælde</string>\n\t<string name=\"no_media_hint\">Deles lagermediet med en PC, kan det gøre det utilgængeligt</string>\n\t<string name=\"no_media_warning\">Lagermedie ikke fundet</string>\n\t<string name=\"no_text_selected\">Ingen tekst valgt</string>\n\t<string name=\"not_supported\">Ikke understøttet</string>\n\t<string name=\"nothing_to_save\">Intet at gemme</string>\n\t<string name=\"okay\">Okay</string>\n\t<string name=\"outline_title\">Indholdsfortegnelse</string>\n\t<string name=\"parent_directory\">[Et niveau op]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s %2$s: %3$s</string>\n\t<string name=\"print\">Udskriv</string>\n\t<string name=\"print_failed\">Udskrivning mislykket</string>\n\t<string name=\"save\">Gem</string>\n\t<string name=\"search\">Søg</string>\n\t<string name=\"search_backwards\">Søg bagud</string>\n\t<string name=\"search_document\">Søg i dokument</string>\n\t<string name=\"search_forwards\">Søg fremad</string>\n\t<string name=\"searching_\">Søger&amp;#8230;</string>\n\t<string name=\"select\">Vælg</string>\n\t<string name=\"select_text\">Vælg tekst</string>\n\t<string name=\"strike_out\">Gennemstreget</string>\n\t<string name=\"text_not_found\">Tekst ikke fundet</string>\n\t<string name=\"toggle_links\">Fremhæv og aktiver links</string>\n\t<string name=\"underline\">Understreg</string>\n\t<string name=\"yes\">Ja</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-de/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">Akzeptieren</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">Abbrechen</string>\n\t<string name=\"cannot_open_buffer\">Zwischenspeicher kann nicht geöffnet werden</string>\n\t<string name=\"cannot_open_document\">Dokument kann nicht geöffnet werden</string>\n\t<string name=\"cannot_open_document_Reason\">Dokument kann nicht geöffnet werden: %1$s</string>\n\t<string name=\"cannot_open_file_Path\">Datei kann nicht geöffnet werden: %1$s</string>\n\t<string name=\"choose_value\">Wert auswählen</string>\n\t<string name=\"copied_to_clipboard\">In die Zwischenanlage kopiert</string>\n\t<string name=\"copy\">Kopieren</string>\n\t<string name=\"copy_text\">Text kopieren</string>\n\t<string name=\"copy_text_to_the_clipboard\">Text in Zwischenablage kopieren</string>\n\t<string name=\"delete\">Entfernen</string>\n\t<string name=\"dismiss\">Verwerfen</string>\n\t<string name=\"document_has_changes_save_them_\">Das Dokument wurde verändert. Sollen die Änderungen gespeichert werden?</string>\n\t<string name=\"draw_annotation\">Kommentar einfügen</string>\n\t<string name=\"edit_annotations\">Kommentar bearbeiten</string>\n\t<string name=\"enter_password\">Passwort eingeben</string>\n\t<string name=\"entering_reflow_mode\">Anpassungsmodus wird gestartet</string>\n\t<string name=\"fill_out_text_field\">Textfeld ausfüllen</string>\n\t<string name=\"format_currently_not_supported\">Format wird momentan nicht unterstützt</string>\n\t<string name=\"highlight\">Markieren</string>\n\t<string name=\"ink\">Farbe</string>\n\t<string name=\"leaving_reflow_mode\">Anpassungsmodus wird beendet</string>\n\t<string name=\"more\">Mehr</string>\n\t<string name=\"no\">Nein</string>\n\t<string name=\"no_further_occurrences_found\">Keine weiteren Treffer</string>\n\t<string name=\"no_media_hint\">Die Freigabe des Speichermediums für einen PC kann es unzugänglich machen</string>\n\t<string name=\"no_media_warning\">Speichermedium nicht vorhanden</string>\n\t<string name=\"no_text_selected\">Kein Text ausgewählt</string>\n\t<string name=\"not_supported\">Nicht unterstützt</string>\n\t<string name=\"nothing_to_save\">Nichts zum Speichern</string>\n\t<string name=\"okay\">OK</string>\n\t<string name=\"outline_title\">Inhaltsverzeichnis</string>\n\t<string name=\"parent_directory\">[Eine Ebene nach oben]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s %2$s: %3$s</string>\n\t<string name=\"print\">Drucken</string>\n\t<string name=\"print_failed\">Fehler beim Drucken</string>\n\t<string name=\"save\">Speichern</string>\n\t<string name=\"search\">Suchen</string>\n\t<string name=\"search_backwards\">Rückwärts suchen</string>\n\t<string name=\"search_document\">Dokument durchsuchen</string>\n\t<string name=\"search_forwards\">Vorwärts suchen</string>\n\t<string name=\"searching_\">Suche…</string>\n\t<string name=\"select\">Auswählen</string>\n\t<string name=\"select_text\">Text auswählen</string>\n\t<string name=\"strike_out\">Durchstreichen</string>\n\t<string name=\"text_not_found\">Text konnte nicht gefunden werden</string>\n\t<string name=\"toggle_links\">Markiere und aktiviere Verknüpfungen</string>\n\t<string name=\"underline\">Unterstreichen</string>\n\t<string name=\"yes\">Ja</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-el/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">Αποδοχή</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">Ακύρωση</string>\n\t<string name=\"cannot_open_buffer\">Αδυναμία ανοίγματος buffer </string>\n\t<string name=\"cannot_open_document\">Αδυναμία ανοίγματος εγγράφου</string>\n\t<string name=\"cannot_open_document_Reason\">Αδυναμία ανοίγματος εγγράφου: %1$s</string>\n\t<string name=\"cannot_open_file_Path\">Αδυναμία ανοίγματος αρχείου: %1$s</string>\n\t<string name=\"choose_value\">Επιλογή τιμής</string>\n\t<string name=\"copied_to_clipboard\">Αντιγράφηκε στο πρόχειρο</string>\n\t<string name=\"copy\">Αντιγραφή</string>\n\t<string name=\"copy_text\">αντιγραφή κειμένου</string>\n\t<string name=\"copy_text_to_the_clipboard\">Αντιγραφή κειμένου στο πρόχειρο</string>\n\t<string name=\"delete\">Διαγραφή</string>\n\t<string name=\"dismiss\">Ματαίωση</string>\n\t<string name=\"document_has_changes_save_them_\">Το έγγραφο έχει αλλαγές. Να αποθηκευτούν;</string>\n\t<string name=\"draw_annotation\">Σχεδίαση σχολίου</string>\n\t<string name=\"edit_annotations\">Επεξεργασία σχολίων</string>\n\t<string name=\"enter_password\">Πληκτρολογήστε κωδικό πρόσβασης</string>\n\t<string name=\"entering_reflow_mode\">Είσοδος σε λειτουργία δυναμικής προσαρμογής</string>\n\t<string name=\"fill_out_text_field\">Συμπλήρωση πεδίου κειμένου</string>\n\t<string name=\"format_currently_not_supported\">Αυτή η μορφή δεν υποστηρίζεται τη δεδομένη στιγμή</string>\n\t<string name=\"highlight\">Επισήμανση</string>\n\t<string name=\"ink\">Γραφή</string>\n\t<string name=\"leaving_reflow_mode\">Έξοδος από λειτουργία δυναμικής προσαρμογής</string>\n\t<string name=\"more\">Περισσότερο</string>\n\t<string name=\"no\">Όχι</string>\n\t<string name=\"no_further_occurrences_found\">Δεν βρέθηκαν άλλες εμφανίσεις</string>\n\t<string name=\"no_media_hint\">Η κοινή χρήση του αποθηκευτικού μέσου με έναν υπολογιστή μπορεί να το καταστήσει μη προσβάσιμο</string>\n\t<string name=\"no_media_warning\">Δεν υπάρχει αποθηκευτικό μέσο</string>\n\t<string name=\"no_text_selected\">Δεν έχει επιλεγεί κείμενο</string>\n\t<string name=\"not_supported\">Δεν υποστηρίζεται</string>\n\t<string name=\"nothing_to_save\">Δεν υπάρχει περιεχόμενο για αποθήκευση</string>\n\t<string name=\"okay\">ΟΚ</string>\n\t<string name=\"outline_title\">Πίνακας περιεχομένων</string>\n\t<string name=\"parent_directory\">[Ένα επίπεδο επάνω]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s %2$s: %3$s</string>\n\t<string name=\"print\">Εκτύπωση</string>\n\t<string name=\"print_failed\">Η εκτύπωση απέτυχε</string>\n\t<string name=\"save\">Αποθήκευση</string>\n\t<string name=\"search\">Αναζήτηση</string>\n\t<string name=\"search_backwards\">Αναζήτηση προς τα πίσω</string>\n\t<string name=\"search_document\">Αναζήτηση εγγράφου</string>\n\t<string name=\"search_forwards\">Αναζήτηση προς τα μπροστά</string>\n\t<string name=\"searching_\">Αναζήτηση&amp;#8230;</string>\n\t<string name=\"select\">Επιλογή</string>\n\t<string name=\"select_text\">Επιλογή κειμένου</string>\n\t<string name=\"strike_out\">Διακριτή διαγραφή</string>\n\t<string name=\"text_not_found\">Δεν βρέθηκε το κείμενο</string>\n\t<string name=\"toggle_links\">Επισήμανση και ενεργοποίηση συνδέσεων</string>\n\t<string name=\"underline\">Υπογράμμιση</string>\n\t<string name=\"yes\">Ναι</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-es/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">Aceptar</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">Cancelar</string>\n\t<string name=\"cannot_open_buffer\">No se puede abrir el búfer</string>\n\t<string name=\"cannot_open_document\">No se puede abrir el documento</string>\n\t<string name=\"cannot_open_document_Reason\">No se puede abrir el documento:%1$s</string>\n\t<string name=\"cannot_open_file_Path\">No se puede abrir el archivo: %1$s</string>\n\t<string name=\"choose_value\">Elegir valor</string>\n\t<string name=\"copied_to_clipboard\">Copiado al portapapeles</string>\n\t<string name=\"copy\">Copiar</string>\n\t<string name=\"copy_text\">copiar texto</string>\n\t<string name=\"copy_text_to_the_clipboard\">Copiar texto al portapapeles</string>\n\t<string name=\"delete\">Borrar</string>\n\t<string name=\"dismiss\">Ignorar</string>\n\t<string name=\"document_has_changes_save_them_\">El documento tiene cambios. ¿Guardar?</string>\n\t<string name=\"draw_annotation\">Dibujar anotación</string>\n\t<string name=\"edit_annotations\">Editar anotaicones</string>\n\t<string name=\"enter_password\">Introducir contraseña</string>\n\t<string name=\"entering_reflow_mode\">Entrando en el modo de redistribución</string>\n\t<string name=\"fill_out_text_field\">Rellenar el campo de texto</string>\n\t<string name=\"format_currently_not_supported\">Formato actualmente no soportado</string>\n\t<string name=\"highlight\">Resaltar</string>\n\t<string name=\"ink\">Tinta</string>\n\t<string name=\"leaving_reflow_mode\">Saliendo del modo de redistribución</string>\n\t<string name=\"more\">Más</string>\n\t<string name=\"no\">No</string>\n\t<string name=\"no_further_occurrences_found\">No se han encontrado más casos</string>\n\t<string name=\"no_media_hint\">Compartir el medio de almacenamiento con un PC puede hacerlo inaccesible</string>\n\t<string name=\"no_media_warning\">Medio de almacenimiento no presente</string>\n\t<string name=\"no_text_selected\">Texto no seleccionado</string>\n\t<string name=\"not_supported\">No aceptado</string>\n\t<string name=\"nothing_to_save\">Nada que guardar</string>\n\t<string name=\"okay\">OK</string>\n\t<string name=\"outline_title\">Tabla de contenidos</string>\n\t<string name=\"parent_directory\">[Subir un nivel]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s %2$s: %3$s</string>\n\t<string name=\"print\">Imprimir</string>\n\t<string name=\"print_failed\">No se ha imprimido</string>\n\t<string name=\"save\">Guardar</string>\n\t<string name=\"search\">Buscar</string>\n\t<string name=\"search_backwards\">Buscar hacia atrás</string>\n\t<string name=\"search_document\">Buscar documento</string>\n\t<string name=\"search_forwards\">Buscar hacia adelante</string>\n\t<string name=\"searching_\">Buscando&amp;#8230;</string>\n\t<string name=\"select\">Seleccionar</string>\n\t<string name=\"select_text\">Seleccionar texto</string>\n\t<string name=\"strike_out\">Tachar</string>\n\t<string name=\"text_not_found\">Texto no encontrado</string>\n\t<string name=\"toggle_links\">Resaltar y activar</string>\n\t<string name=\"underline\">Subrayar</string>\n\t<string name=\"yes\">Sí</string>\n\n    <string name=\"open_failed\">No ha sido posible abrir el documento</string>\n    <string name=\"helpPutSign\">Toque dos veces seguidas donde desee ubicar su firma</string>\n    <string name=\"noSignOnPdfTitle\">Atención</string>\n    <string name=\"noSignOnPdf\">El documento no ha sido firmado.Por favor, toque dos veces seguidas donde desee ubicar su firma.</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-et/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">Nõustu</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">Tühista</string>\n\t<string name=\"cannot_open_buffer\">Ei saa avada puhvrit</string>\n\t<string name=\"cannot_open_document\">Ei saa avada dokumenti</string>\n\t<string name=\"cannot_open_document_Reason\">Ei saa avada dokumenti: %1$s</string>\n\t<string name=\"cannot_open_file_Path\">Ei saa avada faili: %1$s</string>\n\t<string name=\"choose_value\">Vali väärtus</string>\n\t<string name=\"copied_to_clipboard\">Kopeeritud lõikelauale</string>\n\t<string name=\"copy\">Kopeeri</string>\n\t<string name=\"copy_text\">kopeeri tekst</string>\n\t<string name=\"copy_text_to_the_clipboard\">Kopeeri tekst lõikelauale</string>\n\t<string name=\"delete\">Kustuta</string>\n\t<string name=\"dismiss\">Lõpeta</string>\n\t<string name=\"document_has_changes_save_them_\">Dokumendis on tehtud muudatusi. Kas salvestada need?</string>\n\t<string name=\"draw_annotation\">Tee marginaal</string>\n\t<string name=\"edit_annotations\">Redigeeri marginaale</string>\n\t<string name=\"enter_password\">Sisesta salasõna</string>\n\t<string name=\"entering_reflow_mode\">Sisenen ümberpaigutamise režiimi</string>\n\t<string name=\"fill_out_text_field\">Täida tekstiväli</string>\n\t<string name=\"format_currently_not_supported\">Vormingul puudub hetkel tugi</string>\n\t<string name=\"highlight\">Tõsta esile</string>\n\t<string name=\"ink\">Tint</string>\n\t<string name=\"leaving_reflow_mode\">Lahkun ümberpaigutamise režiimist</string>\n\t<string name=\"more\">Veel</string>\n\t<string name=\"no\">Ei</string>\n\t<string name=\"no_further_occurrences_found\">Ei leitud rohkem juhtumeid</string>\n\t<string name=\"no_media_hint\">Salvestuskandja jagamine arvutiga võib selle juurdepääsmatuks muuta</string>\n\t<string name=\"no_media_warning\">Salvestuskandja puudub</string>\n\t<string name=\"no_text_selected\">Teksti ei ole valitud</string>\n\t<string name=\"not_supported\">Puudub tugi</string>\n\t<string name=\"nothing_to_save\">Ei ole midagi salvestada</string>\n\t<string name=\"okay\">OK</string>\n\t<string name=\"outline_title\">Sisukord</string>\n\t<string name=\"parent_directory\">[Taseme võrra üles]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s%2$s%3$s</string>\n\t<string name=\"print\">Prindi</string>\n\t<string name=\"print_failed\">Printimine ebaõnnestus</string>\n\t<string name=\"save\">Salvesta</string>\n\t<string name=\"search\">Otsi</string>\n\t<string name=\"search_backwards\">Otsi tagasisuunas</string>\n\t<string name=\"search_document\">Otsi dokumendist</string>\n\t<string name=\"search_forwards\">Otsi edasisuunas</string>\n\t<string name=\"searching_\">Otsin&amp;#8230;</string>\n\t<string name=\"select\">Vali</string>\n\t<string name=\"select_text\">Vali tekst</string>\n\t<string name=\"strike_out\">Läbikriipsutus</string>\n\t<string name=\"text_not_found\">Teksti ei leitud</string>\n\t<string name=\"toggle_links\">Tõsta lingid esile ja luba need</string>\n\t<string name=\"underline\">Jooni alla</string>\n\t<string name=\"yes\">Jah</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-fi/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">Hyväksy</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">Peruuta</string>\n\t<string name=\"cannot_open_buffer\">Puskuria ei voi avata</string>\n\t<string name=\"cannot_open_document\">Tiedostoa ei voi avata</string>\n\t<string name=\"cannot_open_document_Reason\">Ei voi avata tiedostoa: %1$s</string>\n\t<string name=\"cannot_open_file_Path\">Ei voi avata tiedostoa: %1$s</string>\n\t<string name=\"choose_value\">Valitse arvo</string>\n\t<string name=\"copied_to_clipboard\">Kopioitu leikepöydälle</string>\n\t<string name=\"copy\">Kopioi</string>\n\t<string name=\"copy_text\">kopio teksti</string>\n\t<string name=\"copy_text_to_the_clipboard\">Kopioi teksti leikepöydälle</string>\n\t<string name=\"delete\">Poista</string>\n\t<string name=\"dismiss\">Hylkää</string>\n\t<string name=\"document_has_changes_save_them_\">Tiedostossa on muutoksia. Haluatko tallentaa muutokset?</string>\n\t<string name=\"draw_annotation\">Piirrä huomautus</string>\n\t<string name=\"edit_annotations\">Muokkaa huomautuksia</string>\n\t<string name=\"enter_password\">Anna salasana</string>\n\t<string name=\"entering_reflow_mode\">Siirrytään takaisinmuuntotilaan</string>\n\t<string name=\"fill_out_text_field\">Täytä tekstikenttä</string>\n\t<string name=\"format_currently_not_supported\">Muotoa ei tällä hetkellä tueta</string>\n\t<string name=\"highlight\">Korosta</string>\n\t<string name=\"ink\">Muste</string>\n\t<string name=\"leaving_reflow_mode\">Poistutaan takaisinmuuntotilasta</string>\n\t<string name=\"more\">Lisää</string>\n\t<string name=\"no\">Ei</string>\n\t<string name=\"no_further_occurrences_found\">Muita esiintymiä ei löydy</string>\n\t<string name=\"no_media_hint\">Tallennustietovälineen jakaminen tietokoneen kanssa voi estää sen käyttämisen</string>\n\t<string name=\"no_media_warning\">Tallennustietoväline ei ole käytössä</string>\n\t<string name=\"no_text_selected\">Ei valittua tekstiä</string>\n\t<string name=\"not_supported\">Ei tuettu</string>\n\t<string name=\"nothing_to_save\">Ei mitään tallennettavaa</string>\n\t<string name=\"okay\">OK</string>\n\t<string name=\"outline_title\">Sisällys</string>\n\t<string name=\"parent_directory\">[Yksi taso ylöspäin]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s %2$s: %3$s</string>\n\t<string name=\"print\">Tulosta</string>\n\t<string name=\"print_failed\">Tulostus ei onnistunut</string>\n\t<string name=\"save\">Tallenna</string>\n\t<string name=\"search\">Haku</string>\n\t<string name=\"search_backwards\">Hae taaksepäin</string>\n\t<string name=\"search_document\">Hae tiedostosta</string>\n\t<string name=\"search_forwards\">Hae eteenpäin</string>\n\t<string name=\"searching_\">Haetaan &amp;#8230;</string>\n\t<string name=\"select\">Valitse</string>\n\t<string name=\"select_text\">Valitse teksti</string>\n\t<string name=\"strike_out\">Yliviivaa</string>\n\t<string name=\"text_not_found\">Tekstiä ei löydy</string>\n\t<string name=\"toggle_links\">Korosta ja ota käyttöön linkit</string>\n\t<string name=\"underline\">Alleviivaa</string>\n\t<string name=\"yes\">Kyllä</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-fr/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">Accepter</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">Annuler</string>\n\t<string name=\"cannot_open_buffer\">Impossible d\\'ouvrir le buffer</string>\n\t<string name=\"cannot_open_document\">Impossible d\\'ouvrir le document</string>\n\t<string name=\"cannot_open_document_Reason\">Impossible d\\'ouvrir le document : %1$s</string>\n\t<string name=\"cannot_open_file_Path\">Impossible d\\'ouvrir le fichier : %1$s</string>\n\t<string name=\"choose_value\">Choisir la valeur</string>\n\t<string name=\"copied_to_clipboard\">Copié dans le presse-papier</string>\n\t<string name=\"copy\">Copier</string>\n\t<string name=\"copy_text\">copier le texte</string>\n\t<string name=\"copy_text_to_the_clipboard\">Copier le texte sur le presse-papier</string>\n\t<string name=\"delete\">Supprimer</string>\n\t<string name=\"dismiss\">Ignorer</string>\n\t<string name=\"document_has_changes_save_them_\">Des modifications ont été effectuées au document. Les sauvegarder ?</string>\n\t<string name=\"draw_annotation\">Dessiner une note</string>\n\t<string name=\"edit_annotations\">Éditer une note</string>\n\t<string name=\"enter_password\">Introduire le mot de passe</string>\n\t<string name=\"entering_reflow_mode\">Entrer en mode refusion</string>\n\t<string name=\"fill_out_text_field\">Remplir le champ du texte</string>\n\t<string name=\"format_currently_not_supported\">Format non compatible pour l\\'instant</string>\n\t<string name=\"highlight\">Surligner</string>\n\t<string name=\"ink\">Encre</string>\n\t<string name=\"leaving_reflow_mode\">Quitter le mode refusion</string>\n\t<string name=\"more\">Plus</string>\n\t<string name=\"no\">Non</string>\n\t<string name=\"no_further_occurrences_found\">Aucune occurrence trouvée</string>\n\t<string name=\"no_media_hint\">Sauvegarder le support de stockage avec un PC peut le rendre inaccessible</string>\n\t<string name=\"no_media_warning\">Support de stockage absent</string>\n\t<string name=\"no_text_selected\">Aucun texte sélectionné</string>\n\t<string name=\"not_supported\">Non compatible</string>\n\t<string name=\"nothing_to_save\">Rien à sauvegarder</string>\n\t<string name=\"okay\">OK</string>\n\t<string name=\"outline_title\">Table des matières</string>\n\t<string name=\"parent_directory\">[Niveau supérieur]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s%2$s : %3$s</string>\n\t<string name=\"print\">Imprimer</string>\n\t<string name=\"print_failed\">L\\'impression a échoué</string>\n\t<string name=\"save\">Sauvegarder</string>\n\t<string name=\"search\">Rechercher</string>\n\t<string name=\"search_backwards\">Rechercher en arrière</string>\n\t<string name=\"search_document\">Rechercher document</string>\n\t<string name=\"search_forwards\">Rechercher en avant</string>\n\t<string name=\"searching_\">Chercher&amp;#8230 ;</string>\n\t<string name=\"select\">Sélectionner</string>\n\t<string name=\"select_text\">Sélectionner le texte</string>\n\t<string name=\"strike_out\">Rayer</string>\n\t<string name=\"text_not_found\">Texte introuvable</string>\n\t<string name=\"toggle_links\">Surligner et autoriser les liens</string>\n\t<string name=\"underline\">Souligner</string>\n\t<string name=\"yes\">Oui</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-hi/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">स्वीकार करें</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">रद्द करें</string>\n\t<string name=\"cannot_open_buffer\">बफ़र खोल नहीं सके</string>\n\t<string name=\"cannot_open_document\">दस्तावेज़ खोल नहीं सके</string>\n\t<string name=\"cannot_open_document_Reason\">दस्तावेज़ नहीं खोल सके: %1$s</string>\n\t<string name=\"cannot_open_file_Path\">फ़ाइल खोल नहीं सके: %1$s</string>\n\t<string name=\"choose_value\">मान चुनें</string>\n\t<string name=\"copied_to_clipboard\">क्लिपबोर्ड में कॉप कर दिया गया</string>\n\t<string name=\"copy\">कॉपी करें</string>\n\t<string name=\"copy_text\">पाठ कॉपी करें</string>\n\t<string name=\"copy_text_to_the_clipboard\">पाठ को क्लिपबोर्ड में कॉपी करें</string>\n\t<string name=\"delete\">हटाएँ</string>\n\t<string name=\"dismiss\">खारिज करें</string>\n\t<string name=\"document_has_changes_save_them_\">दस्तावेज़ में परिवर्तन हैं। उन्हें सहेजें?</string>\n\t<string name=\"draw_annotation\">एनोटेशन बनाएँ</string>\n\t<string name=\"edit_annotations\">एनोटेशनों को संपादित करें</string>\n\t<string name=\"enter_password\">पासवर्ड दर्ज करें</string>\n\t<string name=\"entering_reflow_mode\">रीफ़्लो मोड में प्रवेश कर रहे हैं</string>\n\t<string name=\"fill_out_text_field\">पाठ फ़ील्ड को भरें</string>\n\t<string name=\"format_currently_not_supported\">इस समय इस फ़ॉर्मेट को समर्थन नहीं प्राप्त है</string>\n\t<string name=\"highlight\">हाइलाइट करें</string>\n\t<string name=\"ink\">स्याही</string>\n\t<string name=\"leaving_reflow_mode\">रीफ़्लो मोड को छोड़ रहे हैं</string>\n\t<string name=\"more\">और भी</string>\n\t<string name=\"no\">नहीं</string>\n\t<string name=\"no_further_occurrences_found\">यह और कहीं नहीं मिला</string>\n\t<string name=\"no_media_hint\">संग्रह माध्यम को पीसी के साथ साझा करने से उस तक पहुँचना मुश्किल हो सकता है</string>\n\t<string name=\"no_media_warning\">संग्रह माध्यम मौजूद नहीं है</string>\n\t<string name=\"no_text_selected\">कोई भी पाठ नहीं चुना गया है</string>\n\t<string name=\"not_supported\">असमर्थित</string>\n\t<string name=\"nothing_to_save\">सहेजने के लिए कुछ नहीं है</string>\n\t<string name=\"okay\">ठीक है</string>\n\t<string name=\"outline_title\">विषय सूची</string>\n\t<string name=\"parent_directory\">[एक स्तर ऊपर]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s%2$s:%3$s</string>\n\t<string name=\"print\">मुद्रित करें</string>\n\t<string name=\"print_failed\">मुद्रण विफल हुआ</string>\n\t<string name=\"save\">सहेजें</string>\n\t<string name=\"search\">खोजें</string>\n\t<string name=\"search_backwards\">पीछे की ओर खोजें</string>\n\t<string name=\"search_document\">दस्तावेज़ में खोजें</string>\n\t<string name=\"search_forwards\">आगे की ओर खोजें</string>\n\t<string name=\"searching_\">&amp;#8230 को खोज रहे हैं;</string>\n\t<string name=\"select\">चुनें</string>\n\t<string name=\"select_text\">पाठ चुनें</string>\n\t<string name=\"strike_out\">काटें</string>\n\t<string name=\"text_not_found\">पाठ नहीं मिला</string>\n\t<string name=\"toggle_links\">लिंकों को हाइलाइट और सक्षम करें</string>\n\t<string name=\"underline\">रेखांकित करें</string>\n\t<string name=\"yes\">हाँ</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-hu/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">Elfogadás</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">Mégse</string>\n\t<string name=\"cannot_open_buffer\">A puffert nem lehet megnyitni</string>\n\t<string name=\"cannot_open_document\">A dokumentumot nem lehet megnyitni</string>\n\t<string name=\"cannot_open_document_Reason\">A dokumentumot nem lehet megnyitni: %1$s</string>\n\t<string name=\"cannot_open_file_Path\">A fájlt nem lehet megnyitni: %1$s</string>\n\t<string name=\"choose_value\">Érték kiválasztása</string>\n\t<string name=\"copied_to_clipboard\">A vágólapra másolva</string>\n\t<string name=\"copy\">Másolás</string>\n\t<string name=\"copy_text\">szöveg másolása</string>\n\t<string name=\"copy_text_to_the_clipboard\">Szöveg másolása a vágólapra</string>\n\t<string name=\"delete\">Törlés</string>\n\t<string name=\"dismiss\">Bezárás</string>\n\t<string name=\"document_has_changes_save_them_\">A dokumentum módosítva lett. Menti a változtatásokat?</string>\n\t<string name=\"draw_annotation\">Jegyzet rajzolása</string>\n\t<string name=\"edit_annotations\">Jegyzetek szerkesztése</string>\n\t<string name=\"enter_password\">Jelszó megadása</string>\n\t<string name=\"entering_reflow_mode\">Belépés az újrarendezési módba</string>\n\t<string name=\"fill_out_text_field\">Szövegmező kitöltése</string>\n\t<string name=\"format_currently_not_supported\">A formátum jelenleg nem támogatott</string>\n\t<string name=\"highlight\">Kiemelés</string>\n\t<string name=\"ink\">Kézírás</string>\n\t<string name=\"leaving_reflow_mode\">Kilépés az újrarendezési módból</string>\n\t<string name=\"more\">Több</string>\n\t<string name=\"no\">Nem</string>\n\t<string name=\"no_further_occurrences_found\">Nincsenek további találatok</string>\n\t<string name=\"no_media_hint\">Az adathordozó a PC-vel való megosztás esetén elérhetetlenné válhat</string>\n\t<string name=\"no_media_warning\">Nincs jelen adathordozó</string>\n\t<string name=\"no_text_selected\">Nincs kijelölt szöveg</string>\n\t<string name=\"not_supported\">Nem támogatott</string>\n\t<string name=\"nothing_to_save\">Nem kell semmit menteni</string>\n\t<string name=\"okay\">OK</string>\n\t<string name=\"outline_title\">Tartalomjegyzék</string>\n\t<string name=\"parent_directory\">[Egy szinttel feljebb]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s %2$s: %3$s</string>\n\t<string name=\"print\">Nyomtatás</string>\n\t<string name=\"print_failed\">Nyomtatás sikertelen</string>\n\t<string name=\"save\">Mentés</string>\n\t<string name=\"search\">Keresés</string>\n\t<string name=\"search_backwards\">Keresés visszafelé</string>\n\t<string name=\"search_document\">Dokumentum keresése</string>\n\t<string name=\"search_forwards\">Keresés előrefelé</string>\n\t<string name=\"searching_\">Keresés:&amp;#8230;</string>\n\t<string name=\"select\">Kijelölés</string>\n\t<string name=\"select_text\">Szöveg kijelölése</string>\n\t<string name=\"strike_out\">Áthúzás</string>\n\t<string name=\"text_not_found\">Szöveg nem található</string>\n\t<string name=\"toggle_links\">Kiemelés és linkek engedélyezése</string>\n\t<string name=\"underline\">Aláhúzás</string>\n\t<string name=\"yes\">Igen</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-in/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">Terima</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">Batal</string>\n\t<string name=\"cannot_open_buffer\">Tidak bisa membuka penyangga</string>\n\t<string name=\"cannot_open_document\">Tidak bisa membuka dokumen</string>\n\t<string name=\"cannot_open_document_Reason\">Tidak bisa membuka dokumen: %1$s</string>\n\t<string name=\"cannot_open_file_Path\">Tidak bisa membuka berkas: %1$s</string>\n\t<string name=\"choose_value\">Pilih nilai</string>\n\t<string name=\"copied_to_clipboard\">Disalin ke papan klip</string>\n\t<string name=\"copy\">Salin</string>\n\t<string name=\"copy_text\">Salin teks</string>\n\t<string name=\"copy_text_to_the_clipboard\">Salin teks ke papan klip</string>\n\t<string name=\"delete\">Hapus</string>\n\t<string name=\"dismiss\">Hilangkan</string>\n\t<string name=\"document_has_changes_save_them_\">Dokumen telah berubah. Simpan perubahan?</string>\n\t<string name=\"draw_annotation\">Gambar anotasi</string>\n\t<string name=\"edit_annotations\">Sunting anotasi</string>\n\t<string name=\"enter_password\">Masukkan kata sandi</string>\n\t<string name=\"entering_reflow_mode\">Masuk mode alir-ulang</string>\n\t<string name=\"fill_out_text_field\">Isi bidang teks</string>\n\t<string name=\"format_currently_not_supported\">Format ini tidak didukung</string>\n\t<string name=\"highlight\">Sorotan</string>\n\t<string name=\"ink\">Tinta</string>\n\t<string name=\"leaving_reflow_mode\">Tinggalkan mode alir-ulang</string>\n\t<string name=\"more\">Selengkapnya</string>\n\t<string name=\"no\">Tidak</string>\n\t<string name=\"no_further_occurrences_found\">Tidak ditemukan kejadian lain</string>\n\t<string name=\"no_media_hint\">Berbagi media penyimpanan dengan PC dapat membuatnya tidak bisa diakses</string>\n\t<string name=\"no_media_warning\">Media penyimpanan tidak ada</string>\n\t<string name=\"no_text_selected\">Tidak ada teks yang dipilih</string>\n\t<string name=\"not_supported\">Tidak didukung</string>\n\t<string name=\"nothing_to_save\">Tidak ada yang disimpan</string>\n\t<string name=\"okay\">Oke</string>\n\t<string name=\"outline_title\">Daftar Isi</string>\n\t<string name=\"parent_directory\">[Naik satu tingkat]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s %2$s: %3$s</string>\n\t<string name=\"print\">Cetak</string>\n\t<string name=\"print_failed\">Pencetakan gagal</string>\n\t<string name=\"save\">Simpan</string>\n\t<string name=\"search\">Cari</string>\n\t<string name=\"search_backwards\">Cari mundur</string>\n\t<string name=\"search_document\">Cari dokumen</string>\n\t<string name=\"search_forwards\">Cari maju</string>\n\t<string name=\"searching_\">Mencari…</string>\n\t<string name=\"select\">Pilih</string>\n\t<string name=\"select_text\">Pilih teks</string>\n\t<string name=\"strike_out\">Gagal</string>\n\t<string name=\"text_not_found\">Teks tidak ditemukan</string>\n\t<string name=\"toggle_links\">Sorot dan aktifkan tautan</string>\n\t<string name=\"underline\">Garis bawah</string>\n\t<string name=\"yes\">Ya</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-it/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">Accetta</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">Annulla</string>\n\t<string name=\"cannot_open_buffer\">Impossibile aprire buffering</string>\n\t<string name=\"cannot_open_document\">Impossibile aprire documento</string>\n\t<string name=\"cannot_open_document_Reason\">Impossibile aprire documento: %1$s</string>\n\t<string name=\"cannot_open_file_Path\">Impossibile aprire file: %1$s</string>\n\t<string name=\"choose_value\">Scegli valore</string>\n\t<string name=\"copied_to_clipboard\">Copiato negli appunti</string>\n\t<string name=\"copy\">Copia</string>\n\t<string name=\"copy_text\">copia testo</string>\n\t<string name=\"copy_text_to_the_clipboard\">Copia testo negli appunti</string>\n\t<string name=\"delete\">Elimina</string>\n\t<string name=\"dismiss\">Ignora</string>\n\t<string name=\"document_has_changes_save_them_\">Il documento contiene modifiche. Salvare?</string>\n\t<string name=\"draw_annotation\">Disegna annotazione</string>\n\t<string name=\"edit_annotations\">Modifica annotazione</string>\n\t<string name=\"enter_password\">Inserisci password</string>\n\t<string name=\"entering_reflow_mode\">Inserimento modalità di adattamento dinamico del contenuto</string>\n\t<string name=\"fill_out_text_field\">Riempi il campo di testo</string>\n\t<string name=\"format_currently_not_supported\">Formato attualmente non supportato</string>\n\t<string name=\"highlight\">Evidenzia</string>\n\t<string name=\"ink\">Inchiostro</string>\n\t<string name=\"leaving_reflow_mode\">Abbandono della modalità di adattamento dinamico del contenuto</string>\n\t<string name=\"more\">Altro</string>\n\t<string name=\"no\">No</string>\n\t<string name=\"no_further_occurrences_found\">Nessun\\'altra occorrenza trovata</string>\n\t<string name=\"no_media_hint\">La condivisione del supporto di archiviazione con un PC può renderlo inaccessibile</string>\n\t<string name=\"no_media_warning\">Supporto di archiviazione non presente</string>\n\t<string name=\"no_text_selected\">Nessun testo selezionato</string>\n\t<string name=\"not_supported\">Non supportato</string>\n\t<string name=\"nothing_to_save\">Niente da salvare</string>\n\t<string name=\"okay\">Ok</string>\n\t<string name=\"outline_title\">Sommario</string>\n\t<string name=\"parent_directory\">[Su di un livello]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s %2$s: %3$s</string>\n\t<string name=\"print\">Stampa</string>\n\t<string name=\"print_failed\">Stampa non riuscita</string>\n\t<string name=\"save\">Salva</string>\n\t<string name=\"search\">Cerca</string>\n\t<string name=\"search_backwards\">Cerca indietro</string>\n\t<string name=\"search_document\">Cerca documento</string>\n\t<string name=\"search_forwards\">Cerca avanti</string>\n\t<string name=\"searching_\">Ricerca...</string>\n\t<string name=\"select\">Seleziona</string>\n\t<string name=\"select_text\">Seleziona testo</string>\n\t<string name=\"strike_out\">Barrato</string>\n\t<string name=\"text_not_found\">Testo non trovato</string>\n\t<string name=\"toggle_links\">Evidenzia e abilita link</string>\n\t<string name=\"underline\">Sottolinea</string>\n\t<string name=\"yes\">Sì</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-iw/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">קבל</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">בטל</string>\n\t<string name=\"cannot_open_buffer\">אין אפשרות לפתוח מאגר</string>\n\t<string name=\"cannot_open_document\">אין אפשרות לפתוח מסמך</string>\n\t<string name=\"cannot_open_document_Reason\">אין אפשרות לפתוח מסמך: %1$s</string>\n\t<string name=\"cannot_open_file_Path\">אין אפשרות לפתוח קובץ: %1$s</string>\n\t<string name=\"choose_value\">בחר ערך</string>\n\t<string name=\"copied_to_clipboard\">הועתק ללוח</string>\n\t<string name=\"copy\">העתק</string>\n\t<string name=\"copy_text\">העתק טקסט</string>\n\t<string name=\"copy_text_to_the_clipboard\">העתק טקסט ללוח</string>\n\t<string name=\"delete\">מחק</string>\n\t<string name=\"dismiss\">התעלם</string>\n\t<string name=\"document_has_changes_save_them_\">קיימים שינויים במסמך. לשמור אותם?</string>\n\t<string name=\"draw_annotation\">רשום ביאור</string>\n\t<string name=\"edit_annotations\">ערוך ביאורים</string>\n\t<string name=\"enter_password\">הזן סיסמה</string>\n\t<string name=\"entering_reflow_mode\">כניסה למצב הזרמה מחדש</string>\n\t<string name=\"fill_out_text_field\">מלא את שדה הטקסט</string>\n\t<string name=\"format_currently_not_supported\">תבנית לא נתמכת כעת</string>\n\t<string name=\"highlight\">הבלטה</string>\n\t<string name=\"ink\">דיו</string>\n\t<string name=\"leaving_reflow_mode\">יציאה ממצב הזרמה מחדש</string>\n\t<string name=\"more\">עוד</string>\n\t<string name=\"no\">לא</string>\n\t<string name=\"no_further_occurrences_found\">לא עוד</string>\n\t<string name=\"no_media_hint\">שיתוף מדיית האחסון עם מחשב עשויה להפוך אותה לבלתי נגישה</string>\n\t<string name=\"no_media_warning\">מדיית אחסון לא קיימת</string>\n\t<string name=\"no_text_selected\">לא נבחר טקסט</string>\n\t<string name=\"not_supported\">לא נתמך</string>\n\t<string name=\"nothing_to_save\">אין מה לשמור</string>\n\t<string name=\"okay\">בסדר</string>\n\t<string name=\"outline_title\">תוכן העניינים</string>\n\t<string name=\"parent_directory\">[למעלה ברמה אחת]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s %2$s: %3$s</string>\n\t<string name=\"print\">הדפס</string>\n\t<string name=\"print_failed\">ההדפסה נכשלה</string>\n\t<string name=\"save\">שמור</string>\n\t<string name=\"search\">חפש</string>\n\t<string name=\"search_backwards\">חפש אחורה</string>\n\t<string name=\"search_document\">חפש במסמך</string>\n\t<string name=\"search_forwards\">חפש קדימה</string>\n\t<string name=\"searching_\">מחפש&amp;#8230;</string>\n\t<string name=\"select\">בחר ערך</string>\n\t<string name=\"select_text\">בחר טקסט</string>\n\t<string name=\"strike_out\">הדגש</string>\n\t<string name=\"text_not_found\">לא נמצא טקסט</string>\n\t<string name=\"toggle_links\">הבלט ואפשר קישורים</string>\n\t<string name=\"underline\">קו תחתון</string>\n\t<string name=\"yes\">כן</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-ja/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">承諾する</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">キャンセル</string>\n\t<string name=\"cannot_open_buffer\">バッファーを開けません</string>\n\t<string name=\"cannot_open_document\">ドキュメントを開けません</string>\n\t<string name=\"cannot_open_document_Reason\">次のドキュメントを開けません:%1$s</string>\n\t<string name=\"cannot_open_file_Path\">次のファイルを開けません: %1$s</string>\n\t<string name=\"choose_value\">バリューを選択してください</string>\n\t<string name=\"copied_to_clipboard\">クリップボードにコピーされました</string>\n\t<string name=\"copy\">コピー</string>\n\t<string name=\"copy_text\">テキストをコピー</string>\n\t<string name=\"copy_text_to_the_clipboard\">テキストをクリップボードにコピー</string>\n\t<string name=\"delete\">削除</string>\n\t<string name=\"dismiss\">却下</string>\n\t<string name=\"document_has_changes_save_them_\">ドキュメントは変更されました。保存しますか？</string>\n\t<string name=\"draw_annotation\">注釈を挿入する</string>\n\t<string name=\"edit_annotations\">注釈を編集する</string>\n\t<string name=\"enter_password\">パスワードを入力する</string>\n\t<string name=\"entering_reflow_mode\">リフローモードを開始する</string>\n\t<string name=\"fill_out_text_field\">テキストフィールドに書き込む</string>\n\t<string name=\"format_currently_not_supported\">このフォーマットは現在サポートされていません</string>\n\t<string name=\"highlight\">ハイライト</string>\n\t<string name=\"ink\">インク</string>\n\t<string name=\"leaving_reflow_mode\">リフローモードを終了する</string>\n\t<string name=\"more\">もっと</string>\n\t<string name=\"no\">いいえ</string>\n\t<string name=\"no_further_occurrences_found\">他にオカレンスは見つかりませんでした</string>\n\t<string name=\"no_media_hint\">記憶媒体をPCとシェアするとアクセスできなくなる可能性があります</string>\n\t<string name=\"no_media_warning\">記憶媒体が見つかりません</string>\n\t<string name=\"no_text_selected\">テキストが選択されていません</string>\n\t<string name=\"not_supported\">サポートされていません</string>\n\t<string name=\"nothing_to_save\">保存するものがありません</string>\n\t<string name=\"okay\">了解</string>\n\t<string name=\"outline_title\">目次</string>\n\t<string name=\"parent_directory\">[一つ上位のレベル]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s %2$s: %3$s</string>\n\t<string name=\"print\">印刷</string>\n\t<string name=\"print_failed\">印刷に失敗しました</string>\n\t<string name=\"save\">保存</string>\n\t<string name=\"search\">検索</string>\n\t<string name=\"search_backwards\">逆方向検索</string>\n\t<string name=\"search_document\">ドキュメントを検索する</string>\n\t<string name=\"search_forwards\">順方向検索</string>\n\t<string name=\"searching_\">検索中</string>\n\t<string name=\"select\">選択</string>\n\t<string name=\"select_text\">テキストを選択する</string>\n\t<string name=\"strike_out\">取り消し線を引く</string>\n\t<string name=\"text_not_found\">テキストが見つかりません</string>\n\t<string name=\"toggle_links\">ハイライトしてリンクを有効にする</string>\n\t<string name=\"underline\">下線を引く</string>\n\t<string name=\"yes\">はい</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-ko/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">수락</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">취소</string>\n\t<string name=\"cannot_open_buffer\">버퍼 열 수 없음</string>\n\t<string name=\"cannot_open_document\">문서 열 수 없음</string>\n\t<string name=\"cannot_open_document_Reason\">문서 열 수 없음: %1$s</string>\n\t<string name=\"cannot_open_file_Path\">파일 열 수 없음: %1$s</string>\n\t<string name=\"choose_value\">값 선택</string>\n\t<string name=\"copied_to_clipboard\">클립보드로 복사됨</string>\n\t<string name=\"copy\">복사</string>\n\t<string name=\"copy_text\">텍스트 복사</string>\n\t<string name=\"copy_text_to_the_clipboard\">클립보드로 텍스트 복사</string>\n\t<string name=\"delete\">삭제</string>\n\t<string name=\"dismiss\">무시</string>\n\t<string name=\"document_has_changes_save_them_\">문서에 변경사항이 있습니다. 저장?</string>\n\t<string name=\"draw_annotation\">주석달기</string>\n\t<string name=\"edit_annotations\">주석 편집</string>\n\t<string name=\"enter_password\">패스워드 입력</string>\n\t<string name=\"entering_reflow_mode\">리플로우 모드 시작</string>\n\t<string name=\"fill_out_text_field\">텍스트 입력란에 기입하십시오.</string>\n\t<string name=\"format_currently_not_supported\">현재 지원되지 않는 포맷</string>\n\t<string name=\"highlight\">주요기능</string>\n\t<string name=\"ink\">잉크</string>\n\t<string name=\"leaving_reflow_mode\">리플로우 모드 해제</string>\n\t<string name=\"more\">기타</string>\n\t<string name=\"no\">아니오</string>\n\t<string name=\"no_further_occurrences_found\">발견된 추가 발생 없음</string>\n\t<string name=\"no_media_hint\">PC와 스토리지 미디어를 공유하면 액세스할 수 없습니다.</string>\n\t<string name=\"no_media_warning\">스토리지 미디어 없음</string>\n\t<string name=\"no_text_selected\">선택된 텍스트 없음</string>\n\t<string name=\"not_supported\">지원 안됨</string>\n\t<string name=\"nothing_to_save\">저장 대상 없음</string>\n\t<string name=\"okay\">확인</string>\n\t<string name=\"outline_title\">목차</string>\n\t<string name=\"parent_directory\">[레벨 한 단계 상승]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s %2$s: %3$s</string>\n\t<string name=\"print\">인쇄</string>\n\t<string name=\"print_failed\">인쇄 실패</string>\n\t<string name=\"save\">저장</string>\n\t<string name=\"search\">검색</string>\n\t<string name=\"search_backwards\">뒤로 검색</string>\n\t<string name=\"search_document\">문서 검색</string>\n\t<string name=\"search_forwards\">앞으로 검색</string>\n\t<string name=\"searching_\">검색 중&amp;#8230;</string>\n\t<string name=\"select\">선택</string>\n\t<string name=\"select_text\">텍스트 선택</string>\n\t<string name=\"strike_out\">삭제</string>\n\t<string name=\"text_not_found\">발견된 텍스트 없음</string>\n\t<string name=\"toggle_links\">하이라이트 및 링크 활성화</string>\n\t<string name=\"underline\">밑줄</string>\n\t<string name=\"yes\">예</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-lt/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">Priimti</string>\n\t<string name=\"mupdf_title\">„MuPDF“</string>\n\t<string name=\"cancel\">Atšaukti</string>\n\t<string name=\"cannot_open_buffer\">Nepavyksta atverti buferinės atmintinės</string>\n\t<string name=\"cannot_open_document\">Nepavyksta atverti dokumento</string>\n\t<string name=\"cannot_open_document_Reason\">Nepavyksta atverti dokumento: %1$s</string>\n\t<string name=\"cannot_open_file_Path\">Nepavyksta atverti failo: %1$s</string>\n\t<string name=\"choose_value\">Pasirinkti vertę</string>\n\t<string name=\"copied_to_clipboard\">Nukopijuota į iškarpinę</string>\n\t<string name=\"copy\">Kopijuoti</string>\n\t<string name=\"copy_text\">kopijuoti tekstą</string>\n\t<string name=\"copy_text_to_the_clipboard\">Kopijuoti tekstą į iškarpinę</string>\n\t<string name=\"delete\">Naikinti</string>\n\t<string name=\"dismiss\">Atmesti</string>\n\t<string name=\"document_has_changes_save_them_\">Dokumente yra pakeitimų. Ar juos įrašyti?</string>\n\t<string name=\"draw_annotation\">Braižyti anotaciją</string>\n\t<string name=\"edit_annotations\">Redaguoti anotacijas</string>\n\t<string name=\"enter_password\">Įvesti slaptažodį</string>\n\t<string name=\"entering_reflow_mode\">Pereinama į pertvarkymo režimą</string>\n\t<string name=\"fill_out_text_field\">Užpildyti teksto lauką</string>\n\t<string name=\"format_currently_not_supported\">Formatas šiuo metu nedera</string>\n\t<string name=\"highlight\">Pažymėti</string>\n\t<string name=\"ink\">Rašalas</string>\n\t<string name=\"leaving_reflow_mode\">Išeinama iš pertvarkymo režimo</string>\n\t<string name=\"more\">Daugiau</string>\n\t<string name=\"no\">Ne</string>\n\t<string name=\"no_further_occurrences_found\">Daugiau įrašų nerasta</string>\n\t<string name=\"no_media_hint\">Pabendrinus laikmeną su kompiuteriu, ji gali tapti nebepasiekiama</string>\n\t<string name=\"no_media_warning\">Laikmenos nėra</string>\n\t<string name=\"no_text_selected\">Neparinktas tekstas</string>\n\t<string name=\"not_supported\">Nedera</string>\n\t<string name=\"nothing_to_save\">Nėra ką įrašyti</string>\n\t<string name=\"okay\">Gerai</string>\n\t<string name=\"outline_title\">Turinys</string>\n\t<string name=\"parent_directory\">[Vienu lygiu aukštyn]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s %2$s: %3$s</string>\n\t<string name=\"print\">Spausdinti</string>\n\t<string name=\"print_failed\">Išspausdinti nepavyko</string>\n\t<string name=\"save\">Įrašyti</string>\n\t<string name=\"search\">Ieškoti</string>\n\t<string name=\"search_backwards\">Ieškoti atgal</string>\n\t<string name=\"search_document\">Ieškoti dokumente</string>\n\t<string name=\"search_forwards\">Ieškoti pirmyn</string>\n\t<string name=\"searching_\">Ieškoma&amp;#8230;</string>\n\t<string name=\"select\">Pasirinkti</string>\n\t<string name=\"select_text\">Pasirinkti tekstą</string>\n\t<string name=\"strike_out\">Išbraukti</string>\n\t<string name=\"text_not_found\">Teksto nerasta</string>\n\t<string name=\"toggle_links\">Pažymėti ir įjungti nuorodas</string>\n\t<string name=\"underline\">Pabraukti</string>\n\t<string name=\"yes\">Taip</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-ms/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">Terima</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">Batal</string>\n\t<string name=\"cannot_open_buffer\">Tidak boleh membuka penimbal</string>\n\t<string name=\"cannot_open_document\">Tidak boleh membuka dokumen</string>\n\t<string name=\"cannot_open_document_Reason\">Tidak boleh membuka dokumen: %1$s</string>\n\t<string name=\"cannot_open_file_Path\">Tidak boleh membuka fail: %1$s</string>\n\t<string name=\"choose_value\">Pilih nilai</string>\n\t<string name=\"copied_to_clipboard\">Disalin ke papan klip</string>\n\t<string name=\"copy\">Salin</string>\n\t<string name=\"copy_text\">salin teks</string>\n\t<string name=\"copy_text_to_the_clipboard\">Salin teks ke papan klip</string>\n\t<string name=\"delete\">Padam</string>\n\t<string name=\"dismiss\">Singkir</string>\n\t<string name=\"document_has_changes_save_them_\">Dokumen mempunyai perubahan. Simpankannya?</string>\n\t<string name=\"draw_annotation\">Lakarkan catatan</string>\n\t<string name=\"edit_annotations\">Suntingkan catatan</string>\n\t<string name=\"enter_password\">Masukkan kata laluan</string>\n\t<string name=\"entering_reflow_mode\">Memasuki mod penyusunan semula</string>\n\t<string name=\"fill_out_text_field\">Mengisi medan teks</string>\n\t<string name=\"format_currently_not_supported\">Format buat masa ini tidak disokong</string>\n\t<string name=\"highlight\">Serlahkan</string>\n\t<string name=\"ink\">Dakwat</string>\n\t<string name=\"leaving_reflow_mode\">Meninggalkan mod penyusunan semula</string>\n\t<string name=\"more\">Lagi</string>\n\t<string name=\"no\">Tidak</string>\n\t<string name=\"no_further_occurrences_found\">Tiada kejadian lanjut ditemui</string>\n\t<string name=\"no_media_hint\">Berkongsi media storan dengan PC boleh menjadikannya tidak dapat dicapai</string>\n\t<string name=\"no_media_warning\">Media storan tidak wujud</string>\n\t<string name=\"no_text_selected\">Tiada teks dipilih</string>\n\t<string name=\"not_supported\">Tidak disokong</string>\n\t<string name=\"nothing_to_save\">Tiada apa untuk disimpan</string>\n\t<string name=\"okay\">Okey</string>\n\t<string name=\"outline_title\">Jadual Kandungan</string>\n\t<string name=\"parent_directory\">[Naik satu tahap]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s %2$s: %3$s</string>\n\t<string name=\"print\">Cetak</string>\n\t<string name=\"print_failed\">Gagal dicetak</string>\n\t<string name=\"save\">Simpan</string>\n\t<string name=\"search\">Carian</string>\n\t<string name=\"search_backwards\">Carian ke belakang</string>\n\t<string name=\"search_document\">Carian dokumen</string>\n\t<string name=\"search_forwards\">Carian ke depan</string>\n\t<string name=\"searching_\">Mencari&amp;#8230;</string>\n\t<string name=\"select\">Pilih</string>\n\t<string name=\"select_text\">Pilih teks</string>\n\t<string name=\"strike_out\">Mansuhkan</string>\n\t<string name=\"text_not_found\">Teks tidak ditemui</string>\n\t<string name=\"toggle_links\">Serlahkan dan dayakan pautan</string>\n\t<string name=\"underline\">Gariskan</string>\n\t<string name=\"yes\">Ya</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-nl/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">Accepteren</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">Annuleren</string>\n\t<string name=\"cannot_open_buffer\">Buffer kan niet geopend worden</string>\n\t<string name=\"cannot_open_document\">Document kan niet geopend worden</string>\n\t<string name=\"cannot_open_document_Reason\">Document kan niet geopend worden: %1$s</string>\n\t<string name=\"cannot_open_file_Path\">Bestand kan niet geopend worden : %1$s</string>\n\t<string name=\"choose_value\">Kies waarde</string>\n\t<string name=\"copied_to_clipboard\">Gekopieerd naar klembord</string>\n\t<string name=\"copy\">Kopiëren</string>\n\t<string name=\"copy_text\">tekst kopiëren</string>\n\t<string name=\"copy_text_to_the_clipboard\">Tekst kopiëren naar klembord</string>\n\t<string name=\"delete\">Verwijderen</string>\n\t<string name=\"dismiss\">Afwijzen</string>\n\t<string name=\"document_has_changes_save_them_\">Het document is gewijzigd. Opslaan?</string>\n\t<string name=\"draw_annotation\">Opmerking tekenen</string>\n\t<string name=\"edit_annotations\">Opmerkingen bewerken</string>\n\t<string name=\"enter_password\">Voer wachtwoord in</string>\n\t<string name=\"entering_reflow_mode\">Conversiemodus wordt geopend</string>\n\t<string name=\"fill_out_text_field\">Vul het tekstveld in</string>\n\t<string name=\"format_currently_not_supported\">Formaat wordt momenteel niet ondersteund</string>\n\t<string name=\"highlight\">Markeren</string>\n\t<string name=\"ink\">Inkten</string>\n\t<string name=\"leaving_reflow_mode\">Conversiemodus wordt beëindigd</string>\n\t<string name=\"more\">Meer</string>\n\t<string name=\"no\">Nee</string>\n\t<string name=\"no_further_occurrences_found\">Geen andere resultaten gevonden</string>\n\t<string name=\"no_media_hint\">Het opslagmedium kan ontoegankelijk worden als het met een pc wordt gedeeld</string>\n\t<string name=\"no_media_warning\">Geen opslagmedium aanwezig</string>\n\t<string name=\"no_text_selected\">Geen tekst geselecteerd</string>\n\t<string name=\"not_supported\">Niet ondersteund</string>\n\t<string name=\"nothing_to_save\">Niets om op te slaan</string>\n\t<string name=\"okay\">Oké</string>\n\t<string name=\"outline_title\">Inhoudsopgave</string>\n\t<string name=\"parent_directory\">[Een niveau hoger]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s %2$s: %3$s</string>\n\t<string name=\"print\">Afdrukken</string>\n\t<string name=\"print_failed\">Afdrukken mislukt</string>\n\t<string name=\"save\">Opslaan</string>\n\t<string name=\"search\">Zoeken</string>\n\t<string name=\"search_backwards\">Achterstevoren zoeken</string>\n\t<string name=\"search_document\">Document doorzoeken</string>\n\t<string name=\"search_forwards\">Vooruit zoeken</string>\n\t<string name=\"searching_\">Aan het zoeken …</string>\n\t<string name=\"select\">Selecteren</string>\n\t<string name=\"select_text\">Tekst selecteren</string>\n\t<string name=\"strike_out\">Doorhalen</string>\n\t<string name=\"text_not_found\">Tekst niet gevonden</string>\n\t<string name=\"toggle_links\">Markeren en koppelingen inschakelen</string>\n\t<string name=\"underline\">Onderstrepen</string>\n\t<string name=\"yes\">Ja</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-no/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">Aksepter</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">Avbryt</string>\n\t<string name=\"cannot_open_buffer\">Kan ikke åpne buffer</string>\n\t<string name=\"cannot_open_document\">Kan ikke åpne dukumentet</string>\n\t<string name=\"cannot_open_document_Reason\">Kan ikke åpne dokumentet: %1$s</string>\n\t<string name=\"cannot_open_file_Path\">Kan ikke åpne filen: %1$s</string>\n\t<string name=\"choose_value\">Velg verdi</string>\n\t<string name=\"copied_to_clipboard\">Kopiert til utklippstavlen</string>\n\t<string name=\"copy\">Kopier</string>\n\t<string name=\"copy_text\">kopier tekst</string>\n\t<string name=\"copy_text_to_the_clipboard\">Kopier teksten til utklippstavlen</string>\n\t<string name=\"delete\">Slett</string>\n\t<string name=\"dismiss\">Avvis</string>\n\t<string name=\"document_has_changes_save_them_\">Det er endringer i dokumentet. Lagre dem?</string>\n\t<string name=\"draw_annotation\">Lag merknad</string>\n\t<string name=\"edit_annotations\">Rediger merknader</string>\n\t<string name=\"enter_password\">Skriv inn passord</string>\n\t<string name=\"entering_reflow_mode\">Bytter til konverteringsmodus</string>\n\t<string name=\"fill_out_text_field\">Fyll ut tekstfeltet</string>\n\t<string name=\"format_currently_not_supported\">Formatet er ikke støttet for øyeblikket</string>\n\t<string name=\"highlight\">Uthev</string>\n\t<string name=\"ink\">Håndskrift</string>\n\t<string name=\"leaving_reflow_mode\">Går ut av konverteringsmodus</string>\n\t<string name=\"more\">Mer</string>\n\t<string name=\"no\">Nei</string>\n\t<string name=\"no_further_occurrences_found\">Ingen flere hendelser funnet</string>\n\t<string name=\"no_media_hint\">Deling av lagringsmedia med en PC kan gjøre det utilgjengelig</string>\n\t<string name=\"no_media_warning\">Lagringsmedia ikke til stede</string>\n\t<string name=\"no_text_selected\">Ingen tekst er valgt</string>\n\t<string name=\"not_supported\">Ikke støttet</string>\n\t<string name=\"nothing_to_save\">Ingenting å lagre</string>\n\t<string name=\"okay\">Ok</string>\n\t<string name=\"outline_title\">Innholdsfortegnelse</string>\n\t<string name=\"parent_directory\">[OPP ett nivå]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s%2$s%3$s</string>\n\t<string name=\"print\">Skriv ut</string>\n\t<string name=\"print_failed\">Kunne ikke skrive ut</string>\n\t<string name=\"save\">Lagre</string>\n\t<string name=\"search\">Søk</string>\n\t<string name=\"search_backwards\">Søk bakover</string>\n\t<string name=\"search_document\">Søk i dokument</string>\n\t<string name=\"search_forwards\">Søk framover</string>\n\t<string name=\"searching_\">Søker&amp;#8230;</string>\n\t<string name=\"select\">Velg</string>\n\t<string name=\"select_text\">Valgt tekst</string>\n\t<string name=\"strike_out\">Gjennomstreking</string>\n\t<string name=\"text_not_found\">Teksten ble ikke funnet</string>\n\t<string name=\"toggle_links\">Uthev og aktiver koblinger</string>\n\t<string name=\"underline\">Understrek</string>\n\t<string name=\"yes\">Ja</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-pl/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">Zaakceptuj</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">Anuluj</string>\n\t<string name=\"cannot_open_buffer\">Nie można otworzyć bufora</string>\n\t<string name=\"cannot_open_document\">Nie można otworzyć dokumentu</string>\n\t<string name=\"cannot_open_document_Reason\">Nie można otworzyć dokumentu: %1$s</string>\n\t<string name=\"cannot_open_file_Path\">Nie można otworzyć pliku: %1$s</string>\n\t<string name=\"choose_value\">Wybierz wartość</string>\n\t<string name=\"copied_to_clipboard\">Skopiowano do schowka</string>\n\t<string name=\"copy\">Kopiuj</string>\n\t<string name=\"copy_text\">kopiuj tekst</string>\n\t<string name=\"copy_text_to_the_clipboard\">Kopiuj tekst do schowka</string>\n\t<string name=\"delete\">Usuń</string>\n\t<string name=\"dismiss\">Odrzuć</string>\n\t<string name=\"document_has_changes_save_them_\">W dokumencie dokonano zmian. Czy chcesz je zapisać?</string>\n\t<string name=\"draw_annotation\">Sporządź notatkę</string>\n\t<string name=\"edit_annotations\">Edytuj notatki</string>\n\t<string name=\"enter_password\">Wprowadź hasło</string>\n\t<string name=\"entering_reflow_mode\">Włączanie trybu zawijania tekstu</string>\n\t<string name=\"fill_out_text_field\">Wypełnij pole tekstowe</string>\n\t<string name=\"format_currently_not_supported\">Format obecnie nieobsługiwany</string>\n\t<string name=\"highlight\">Podświetl</string>\n\t<string name=\"ink\">Atrament</string>\n\t<string name=\"leaving_reflow_mode\">Wyłączanie trybu zawijania tekstu</string>\n\t<string name=\"more\">Więcej</string>\n\t<string name=\"no\">Nie</string>\n\t<string name=\"no_further_occurrences_found\">Nie znaleziono więcej wystąpień</string>\n\t<string name=\"no_media_hint\">Współdzielenie nośnika danych z komputerem PC może sprawić, że będzie niedostępny</string>\n\t<string name=\"no_media_warning\">Nośnik danych niedostępny</string>\n\t<string name=\"no_text_selected\">Nie wybrano tekstu</string>\n\t<string name=\"not_supported\">Nieobsługiwany</string>\n\t<string name=\"nothing_to_save\">Nie ma nic do zapisania</string>\n\t<string name=\"okay\">OK</string>\n\t<string name=\"outline_title\">Spis treści</string>\n\t<string name=\"parent_directory\">[W górę o jeden poziom]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s %2$s: %3$s</string>\n\t<string name=\"print\">Drukuj</string>\n\t<string name=\"print_failed\">Drukowanie nieudane</string>\n\t<string name=\"save\">Zapisz</string>\n\t<string name=\"search\">Szukaj</string>\n\t<string name=\"search_backwards\">Szukaj z tyłu</string>\n\t<string name=\"search_document\">Szukaj w dokumencie</string>\n\t<string name=\"search_forwards\">Szukaj z przodu</string>\n\t<string name=\"searching_\">Wyszukiwanie&amp;#8230;</string>\n\t<string name=\"select\">Wybierz</string>\n\t<string name=\"select_text\">Wybierz tekst</string>\n\t<string name=\"strike_out\">Przekreślenie</string>\n\t<string name=\"text_not_found\">Nie znaleziono tekstu</string>\n\t<string name=\"toggle_links\">Podświetl i aktywuj linki</string>\n\t<string name=\"underline\">Podkreślenie</string>\n\t<string name=\"yes\">Tak</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-pt/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">Aceitar</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">Cancelar</string>\n\t<string name=\"cannot_open_buffer\">Não é possível abrir a memória intermédia</string>\n\t<string name=\"cannot_open_document\">Não é possível abrir o documento</string>\n\t<string name=\"cannot_open_document_Reason\">Não é possível abrir o documento: %1$s</string>\n\t<string name=\"cannot_open_file_Path\">Não é possível abrir o ficheiro: %1$s</string>\n\t<string name=\"choose_value\">Escolha um valor</string>\n\t<string name=\"copied_to_clipboard\">Copiado para a área de transferência</string>\n\t<string name=\"copy\">Copiar</string>\n\t<string name=\"copy_text\">copiar o texto</string>\n\t<string name=\"copy_text_to_the_clipboard\">Copiar o texto para a área de transferência</string>\n\t<string name=\"delete\">Eliminar</string>\n\t<string name=\"dismiss\">Desistir</string>\n\t<string name=\"document_has_changes_save_them_\">Há alterações ao documento. Deseja guardá-las?</string>\n\t<string name=\"draw_annotation\">Adicionar anotação</string>\n\t<string name=\"edit_annotations\">Editar anotações</string>\n\t<string name=\"enter_password\">Escrever a palavra-passe</string>\n\t<string name=\"entering_reflow_mode\">A entrar no modo de refluxo</string>\n\t<string name=\"fill_out_text_field\">Preencher o campo de texto</string>\n\t<string name=\"format_currently_not_supported\">Esse formato não é atualmente suportado</string>\n\t<string name=\"highlight\">Destacar</string>\n\t<string name=\"ink\">Tinta</string>\n\t<string name=\"leaving_reflow_mode\">A sair do modo de refluxo</string>\n\t<string name=\"more\">Mais</string>\n\t<string name=\"no\">Não</string>\n\t<string name=\"no_further_occurrences_found\">Não foram encontradas mais ocorrências</string>\n\t<string name=\"no_media_hint\">Partilhar o dispositivo de armazenamento com um PC poderá torná-lo inacessível</string>\n\t<string name=\"no_media_warning\">O dispositivo de armazenamento não está presente</string>\n\t<string name=\"no_text_selected\">Não há texto selecionado</string>\n\t<string name=\"not_supported\">Não suportado</string>\n\t<string name=\"nothing_to_save\">Não há nada para guardar</string>\n\t<string name=\"okay\">Okay</string>\n\t<string name=\"outline_title\">Índice</string>\n\t<string name=\"parent_directory\">[subir um nível]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s%2$s: %3$s</string>\n\t<string name=\"print\">Imprimir</string>\n\t<string name=\"print_failed\">Falha na Impressão</string>\n\t<string name=\"save\">Guardar</string>\n\t<string name=\"search\">Pesquisar</string>\n\t<string name=\"search_backwards\">Pesquisar para trás</string>\n\t<string name=\"search_document\">Pesquisar no documento</string>\n\t<string name=\"search_forwards\">Pesquisar para a frente</string>\n\t<string name=\"searching_\">A pesquisar&amp;#8230;</string>\n\t<string name=\"select\">Selecionar</string>\n\t<string name=\"select_text\">Selecionar o texto</string>\n\t<string name=\"strike_out\">Rasurado</string>\n\t<string name=\"text_not_found\">Texto não encontrado</string>\n\t<string name=\"toggle_links\">Destacar e permitir links</string>\n\t<string name=\"underline\">Sublinhado</string>\n\t<string name=\"yes\">Sim</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-ru/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">Принять</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">Отмена</string>\n\t<string name=\"cannot_open_buffer\">Невозможно открыть буфер</string>\n\t<string name=\"cannot_open_document\">Невозможно открыть документ</string>\n\t<string name=\"cannot_open_document_Reason\">Невозможно открыть документ: %1$s</string>\n\t<string name=\"cannot_open_file_Path\">Невозможно открыть файл: %1$s</string>\n\t<string name=\"choose_value\">Выберите значение</string>\n\t<string name=\"copied_to_clipboard\">Скопировано в буфер</string>\n\t<string name=\"copy\">Копировать</string>\n\t<string name=\"copy_text\">копировать текст</string>\n\t<string name=\"copy_text_to_the_clipboard\">Копировать текст в буфер</string>\n\t<string name=\"delete\">Удалить</string>\n\t<string name=\"dismiss\">Пропустить</string>\n\t<string name=\"document_has_changes_save_them_\">Документ был изменен. Сохранить изменения?</string>\n\t<string name=\"draw_annotation\">Создать аннтоацию</string>\n\t<string name=\"edit_annotations\">Редактировать аннотации</string>\n\t<string name=\"enter_password\">Введите пароль</string>\n\t<string name=\"entering_reflow_mode\">Переход в режим Reflow</string>\n\t<string name=\"fill_out_text_field\">Заполните текстовое поле</string>\n\t<string name=\"format_currently_not_supported\">Формат не поддерживается</string>\n\t<string name=\"highlight\">Выделить</string>\n\t<string name=\"ink\">Чернила</string>\n\t<string name=\"leaving_reflow_mode\">Выход из режима Reflow</string>\n\t<string name=\"more\">Еще</string>\n\t<string name=\"no\">Нет</string>\n\t<string name=\"no_further_occurrences_found\">Других ошибок не зафиксировано</string>\n\t<string name=\"no_media_hint\">Подключение компьютеров к хранилищу данных может привести к потере доступа к хранилищу</string>\n\t<string name=\"no_media_warning\">Хранилище данных отсутствует</string>\n\t<string name=\"no_text_selected\">Текст не выбран</string>\n\t<string name=\"not_supported\">Не поддерживается</string>\n\t<string name=\"nothing_to_save\">Не выбраны файлы для сохранения</string>\n\t<string name=\"okay\">ОК</string>\n\t<string name=\"outline_title\">Содержание</string>\n\t<string name=\"parent_directory\">[Вверх на один уровень]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s %2$s: %3$s</string>\n\t<string name=\"print\">Печать</string>\n\t<string name=\"print_failed\">Печать не выполнена</string>\n\t<string name=\"save\">Сохранить</string>\n\t<string name=\"search\">Поиск</string>\n\t<string name=\"search_backwards\">Искать в предыдущей части документа</string>\n\t<string name=\"search_document\">Искать в документе</string>\n\t<string name=\"search_forwards\">Искать в остальной части документа</string>\n\t<string name=\"searching_\">Поиск&amp;#8230;</string>\n\t<string name=\"select\">Выбор</string>\n\t<string name=\"select_text\">Выбрать текст</string>\n\t<string name=\"strike_out\">Зачеркнуть</string>\n\t<string name=\"text_not_found\">Текст не найден</string>\n\t<string name=\"toggle_links\">Выделить и включить ссылки</string>\n\t<string name=\"underline\">Подчеркнуть</string>\n\t<string name=\"yes\">Да</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-sk/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">Prijať</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">Zrušiť</string>\n\t<string name=\"cannot_open_buffer\">Buffer sa nedá otvoriť</string>\n\t<string name=\"cannot_open_document\">Dokument sa nedá otvoriť</string>\n\t<string name=\"cannot_open_document_Reason\">Nedá sa otvoriť dokument: %1$s</string>\n\t<string name=\"cannot_open_file_Path\">Nedá sa otvoriť súbor: %1$s</string>\n\t<string name=\"choose_value\">Vyberte si hodnotu</string>\n\t<string name=\"copied_to_clipboard\">Skopírované do vyrovnávacej pamäti</string>\n\t<string name=\"copy\">Kopírovať</string>\n\t<string name=\"copy_text\">kopírovať text</string>\n\t<string name=\"copy_text_to_the_clipboard\">Kopírovať text do vyrovnávacej pamäti</string>\n\t<string name=\"delete\">Zmazať</string>\n\t<string name=\"dismiss\">Zrušiť</string>\n\t<string name=\"document_has_changes_save_them_\">Dokument bol zmený. Uložiť zmeny?</string>\n\t<string name=\"draw_annotation\">Zostaviť anotáciu</string>\n\t<string name=\"edit_annotations\">Upraviť anotácie</string>\n\t<string name=\"enter_password\">Zadať heslo</string>\n\t<string name=\"entering_reflow_mode\">Vstupujem do režimu opätovného nalievania</string>\n\t<string name=\"fill_out_text_field\">Vyplniť textové pole</string>\n\t<string name=\"format_currently_not_supported\">Tento formát momentálne nepodporujem</string>\n\t<string name=\"highlight\">Zvýrazniť</string>\n\t<string name=\"ink\">Atrament</string>\n\t<string name=\"leaving_reflow_mode\">Vystupujem z režimu opätovného nalievania</string>\n\t<string name=\"more\">Viac</string>\n\t<string name=\"no\">Nie</string>\n\t<string name=\"no_further_occurrences_found\">Viac príkladov sa nenašlo</string>\n\t<string name=\"no_media_hint\">Zdieľanie úložného média s PC môže znemožniť prístup</string>\n\t<string name=\"no_media_warning\">Nie je tu úložné médium</string>\n\t<string name=\"no_text_selected\">Žiadny text nie je vybraný</string>\n\t<string name=\"not_supported\">Nepodporované</string>\n\t<string name=\"nothing_to_save\">Niet čo uložiť</string>\n\t<string name=\"okay\">Dobre</string>\n\t<string name=\"outline_title\">Obsah</string>\n\t<string name=\"parent_directory\">[O úroveň vyššie]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s %2$s: %3$s</string>\n\t<string name=\"print\">Tlačiť</string>\n\t<string name=\"print_failed\">Tlačenie zlyhalo</string>\n\t<string name=\"save\">Uložiť</string>\n\t<string name=\"search\">Hľadať</string>\n\t<string name=\"search_backwards\">Hľadať spätne</string>\n\t<string name=\"search_document\">Hľadať v dokumente</string>\n\t<string name=\"search_forwards\">Hľadať dopredu</string>\n\t<string name=\"searching_\">Hľadám&amp;#8230;</string>\n\t<string name=\"select\">Vybrať</string>\n\t<string name=\"select_text\">Vybrať text</string>\n\t<string name=\"strike_out\">Preškrtnúť</string>\n\t<string name=\"text_not_found\">Text sa nenašiel</string>\n\t<string name=\"toggle_links\">Zvýrazniť a zapnúť linky</string>\n\t<string name=\"underline\">Podčiarknúť</string>\n\t<string name=\"yes\">Áno</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-sv/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">Acceptera</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">Avbryt</string>\n\t<string name=\"cannot_open_buffer\">Kan inte öppna buffer</string>\n\t<string name=\"cannot_open_document\">Kan inte öppna dokument</string>\n\t<string name=\"cannot_open_document_Reason\">Kan inte öppna dokument: %1$s</string>\n\t<string name=\"cannot_open_file_Path\">Kan inte öppna fil: %1$s</string>\n\t<string name=\"choose_value\">Välj värde</string>\n\t<string name=\"copied_to_clipboard\">Kopierat till klippbordet</string>\n\t<string name=\"copy\">Kopiera</string>\n\t<string name=\"copy_text\">kopiera text</string>\n\t<string name=\"copy_text_to_the_clipboard\">Kopiera text till klippbordet</string>\n\t<string name=\"delete\">Ta bort</string>\n\t<string name=\"dismiss\">Avfärda</string>\n\t<string name=\"document_has_changes_save_them_\">Dokumentet har ändrats. Spara ändringar?</string>\n\t<string name=\"draw_annotation\">Rita annotation</string>\n\t<string name=\"edit_annotations\">Ändra annotation</string>\n\t<string name=\"enter_password\">Fyll i lösenord</string>\n\t<string name=\"entering_reflow_mode\">Aktiverar reflow-läge</string>\n\t<string name=\"fill_out_text_field\">Fyll i textfält</string>\n\t<string name=\"format_currently_not_supported\">Formatat stöds inte för närvarande</string>\n\t<string name=\"highlight\">Markera</string>\n\t<string name=\"ink\">Bläck</string>\n\t<string name=\"leaving_reflow_mode\">Lämnar reflow-läge</string>\n\t<string name=\"more\">Mer</string>\n\t<string name=\"no\">Nej</string>\n\t<string name=\"no_further_occurrences_found\">Inga flera förekomster hittades</string>\n\t<string name=\"no_media_hint\">Att dela lagringsmediet med en PC kan göra den oåtkomlig</string>\n\t<string name=\"no_media_warning\">Lagringsmedia finns inte</string>\n\t<string name=\"no_text_selected\">Ingen text har valts</string>\n\t<string name=\"not_supported\">Stöds ej</string>\n\t<string name=\"nothing_to_save\">Inget att spara</string>\n\t<string name=\"okay\">OK</string>\n\t<string name=\"outline_title\">Innehållsförteckning</string>\n\t<string name=\"parent_directory\">[Upp en nivå]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s %2$s: %3$s</string>\n\t<string name=\"print\">Skriv ut</string>\n\t<string name=\"print_failed\">Utskrift misslyckades</string>\n\t<string name=\"save\">Spara</string>\n\t<string name=\"search\">Sök</string>\n\t<string name=\"search_backwards\">Sök bakåt</string>\n\t<string name=\"search_document\">Sök dokument</string>\n\t<string name=\"search_forwards\">Sök framåt</string>\n\t<string name=\"searching_\">Letar&amp;#8230;</string>\n\t<string name=\"select\">Välj</string>\n\t<string name=\"select_text\">Välj text</string>\n\t<string name=\"strike_out\">Stryk</string>\n\t<string name=\"text_not_found\">Text hittades ej</string>\n\t<string name=\"toggle_links\">Markera och aktivera länkar</string>\n\t<string name=\"underline\">Understryk</string>\n\t<string name=\"yes\">Ja</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-th/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">ยอมรับ</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">ยกเลิก</string>\n\t<string name=\"cannot_open_buffer\">ไม่สามารถเปิดบัฟเฟอร์</string>\n\t<string name=\"cannot_open_document\">ไม่สามารถเปิดเอกสาร</string>\n\t<string name=\"cannot_open_document_Reason\">ไม่สามารถเปิดเอกสาร: %1$s</string>\n\t<string name=\"cannot_open_file_Path\">ไม่สามารถเปิดไฟล์: %1$s</string>\n\t<string name=\"choose_value\">เลือกค่า</string>\n\t<string name=\"copied_to_clipboard\">คัดลอกไปที่คลิปบอร์ดแล้ว</string>\n\t<string name=\"copy\">คัดลอก</string>\n\t<string name=\"copy_text\">คัดลอกข้อความ</string>\n\t<string name=\"copy_text_to_the_clipboard\">คัดลอกข้อความไปที่คลิปบอร์ด</string>\n\t<string name=\"delete\">ลบ</string>\n\t<string name=\"dismiss\">เลิกใช้</string>\n\t<string name=\"document_has_changes_save_them_\">เอกสารมีการเปลี่ยนแปลง ต้องการบันทึกหรือไม่</string>\n\t<string name=\"draw_annotation\">เขียนคำอธิบายประกอบ</string>\n\t<string name=\"edit_annotations\">แก้ไขคำอธิบายประกอบ</string>\n\t<string name=\"enter_password\">ป้อนรหัสผ่าน</string>\n\t<string name=\"entering_reflow_mode\">เข้าสู่โหมดเรียงหน้ากระดาษใหม่</string>\n\t<string name=\"fill_out_text_field\">เติมในช่องข้อความ</string>\n\t<string name=\"format_currently_not_supported\">ไม่รองรับรูปแบบในขณะนี้</string>\n\t<string name=\"highlight\">ไฮไลท์</string>\n\t<string name=\"ink\">หมึก</string>\n\t<string name=\"leaving_reflow_mode\">ออกจากโหมดเรียงหน้ากระดาษใหม่</string>\n\t<string name=\"more\">เพิ่มเติม</string>\n\t<string name=\"no\">ไม่</string>\n\t<string name=\"no_further_occurrences_found\">ไม่พบเหตุการณ์ที่เกิดขึ้นเพิ่มเติม</string>\n\t<string name=\"no_media_hint\">การแบ่งปันสื่อจัดเก็บข้อมูลกับพีซีสามารถทำให้สื่อจัดเก็บข้อมูลไม่สามารถเข้าถึงได้</string>\n\t<string name=\"no_media_warning\">สื่อเก็บข้อมูลไม่ปรากฏ</string>\n\t<string name=\"no_text_selected\">ไม่มีข้อความที่เลือก</string>\n\t<string name=\"not_supported\">ไม่รองรับ</string>\n\t<string name=\"nothing_to_save\">ไม่มีอะไรให้บันทึก</string>\n\t<string name=\"okay\">ตกลง</string>\n\t<string name=\"outline_title\">สารบัญ</string>\n\t<string name=\"parent_directory\">[ขึ้นหนึ่งระดับ]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s %2$s: %3$s</string>\n\t<string name=\"print\">พิมพ์</string>\n\t<string name=\"print_failed\">พิมพ์ล้มเหลว</string>\n\t<string name=\"save\">บันทึก</string>\n\t<string name=\"search\">ค้นหา</string>\n\t<string name=\"search_backwards\">ค้นหาย้อนกลับ</string>\n\t<string name=\"search_document\">ค้นหาเอกสาร</string>\n\t<string name=\"search_forwards\">ค้นหาไปข้างหน้า</string>\n\t<string name=\"searching_\">กำลังค้นหา&amp;#8230;</string>\n\t<string name=\"select\">เลือก</string>\n\t<string name=\"select_text\">เลือกข้อความ</string>\n\t<string name=\"strike_out\">ขีดทับ</string>\n\t<string name=\"text_not_found\">ไม่พบข้อความ</string>\n\t<string name=\"toggle_links\">ไฮไลท์และเปิดใช้งานลิงก์</string>\n\t<string name=\"underline\">ขีดเส้นใต้</string>\n\t<string name=\"yes\">ใช่</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-tl/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">Tanggapin</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">Kanselahin</string>\n\t<string name=\"cannot_open_buffer\">Hindi mabuksan ang buffer</string>\n\t<string name=\"cannot_open_document\">Hindi mabuksan ang dokumento</string>\n\t<string name=\"cannot_open_document_Reason\">Hindi mabuksan ang dokumentong: %1$s</string>\n\t<string name=\"cannot_open_file_Path\">Hindi mabuksan ang file na: %1$s</string>\n\t<string name=\"choose_value\">Pumili ng halaga</string>\n\t<string name=\"copied_to_clipboard\">Kinopya sa clipboard</string>\n\t<string name=\"copy\">Kopyahin</string>\n\t<string name=\"copy_text\">kopyahin ang teksto</string>\n\t<string name=\"copy_text_to_the_clipboard\">Kopyahin ang teksto sa clipboard</string>\n\t<string name=\"delete\">Alisin</string>\n\t<string name=\"dismiss\">Umalis</string>\n\t<string name=\"document_has_changes_save_them_\">May mga pagbabago sa dokumento. I-save ang mga ito?</string>\n\t<string name=\"draw_annotation\">Gumuhit ng anotasyon</string>\n\t<string name=\"edit_annotations\">I-edit ang mga anotasyon</string>\n\t<string name=\"enter_password\">Ilagay ang password</string>\n\t<string name=\"entering_reflow_mode\">Pumapasok sa reflow mode</string>\n\t<string name=\"fill_out_text_field\">Punan ang puwang para sa teksto</string>\n\t<string name=\"format_currently_not_supported\">Ang format ay kasalukuyang hindi gumagana dito</string>\n\t<string name=\"highlight\">I-highlight</string>\n\t<string name=\"ink\">Lagdaan (Ink)</string>\n\t<string name=\"leaving_reflow_mode\">Umaalis sa reflow mode</string>\n\t<string name=\"more\">Higit pa</string>\n\t<string name=\"no\">Hindi</string>\n\t<string name=\"no_further_occurrences_found\">Walang nahanap na karagdagang paglitaw</string>\n\t<string name=\"no_media_hint\">Ang pagbabahagi ng storage media sa isang PC ay gagawin itong hindi magagamit</string>\n\t<string name=\"no_media_warning\">Walang storage media</string>\n\t<string name=\"no_text_selected\">Walang piniling teksto</string>\n\t<string name=\"not_supported\">Hindi gumagana dito</string>\n\t<string name=\"nothing_to_save\">Walang ise-save</string>\n\t<string name=\"okay\">Okay</string>\n\t<string name=\"outline_title\">Talaan ng Nilalaman</string>\n\t<string name=\"parent_directory\">[Umakyat ng isang antas]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s %2$s: %3$s</string>\n\t<string name=\"print\">I-print</string>\n\t<string name=\"print_failed\">Hindi nai-print</string>\n\t<string name=\"save\">I-save</string>\n\t<string name=\"search\">Maghanap</string>\n\t<string name=\"search_backwards\">Maghanap pabalik</string>\n\t<string name=\"search_document\">Maghanap sa dokumento</string>\n\t<string name=\"search_forwards\">Maghanap nang pasulong</string>\n\t<string name=\"searching_\">Hinahanap ang&amp;#8230;</string>\n\t<string name=\"select\">Piliin</string>\n\t<string name=\"select_text\">Piliin ang teksto</string>\n\t<string name=\"strike_out\">Guhitan ang teksto (strike-out)</string>\n\t<string name=\"text_not_found\">Hindi nahanap ang teksto</string>\n\t<string name=\"toggle_links\">I-highlight at paganahin ang mga link</string>\n\t<string name=\"underline\">Guhitan</string>\n\t<string name=\"yes\">Oo</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-tr/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">Kabul et</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">İptal et</string>\n\t<string name=\"cannot_open_buffer\">Arabellek açılamıyor</string>\n\t<string name=\"cannot_open_document\">Belge açılamıyor</string>\n\t<string name=\"cannot_open_document_Reason\">Belge açılamıyor: %1$s</string>\n\t<string name=\"cannot_open_file_Path\">Dosya açılamıyor: %1$s</string>\n\t<string name=\"choose_value\">Değeri seç</string>\n\t<string name=\"copied_to_clipboard\">Panoya kopyalandı</string>\n\t<string name=\"copy\">Kopyala</string>\n\t<string name=\"copy_text\">metni kopyala</string>\n\t<string name=\"copy_text_to_the_clipboard\">Metni panoya kopyala</string>\n\t<string name=\"delete\">Sil</string>\n\t<string name=\"dismiss\">Bırak</string>\n\t<string name=\"document_has_changes_save_them_\">Belgede değişiklikler var. Kaydetmek istiyor musunuz?</string>\n\t<string name=\"draw_annotation\">Ek açıklama çiz</string>\n\t<string name=\"edit_annotations\">Ek açıklamalar düzenle</string>\n\t<string name=\"enter_password\">Şifreyi gir</string>\n\t<string name=\"entering_reflow_mode\">Yeniden akma moduna giriyor</string>\n\t<string name=\"fill_out_text_field\">Metin alanını doldurun</string>\n\t<string name=\"format_currently_not_supported\">Bu format şu an için desteklenmiyor</string>\n\t<string name=\"highlight\">Vurgula</string>\n\t<string name=\"ink\">Mürekkep</string>\n\t<string name=\"leaving_reflow_mode\">Yeniden akma modundan çıkılıyor</string>\n\t<string name=\"more\">Daha fazla</string>\n\t<string name=\"no\">Hayır</string>\n\t<string name=\"no_further_occurrences_found\">Daha fazla öğe bulunamadı</string>\n\t<string name=\"no_media_hint\">Depolama ortamının bilgisayar ile paylaşımı onu erişilmez yapabilir</string>\n\t<string name=\"no_media_warning\">Depolama ortamı bulunmuyor</string>\n\t<string name=\"no_text_selected\">Seçili metin bulunmuyor</string>\n\t<string name=\"not_supported\">Desteklenmiyor</string>\n\t<string name=\"nothing_to_save\">Kaydedecek bir şey yok</string>\n\t<string name=\"okay\">Tamam</string>\n\t<string name=\"outline_title\">İçindekiler Tablosu</string>\n\t<string name=\"parent_directory\">[Bir seviye üste çık]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s %2$s: %3$s</string>\n\t<string name=\"print\">Yazdır</string>\n\t<string name=\"print_failed\">Yazdırma başarısız oldu</string>\n\t<string name=\"save\">Kaydet</string>\n\t<string name=\"search\">Ara</string>\n\t<string name=\"search_backwards\">Geriye doğru ara</string>\n\t<string name=\"search_document\">Belge ara</string>\n\t<string name=\"search_forwards\">İleriye doğru ara</string>\n\t<string name=\"searching_\">Aranıyor&amp;#8230;</string>\n\t<string name=\"select\">Seç</string>\n\t<string name=\"select_text\">Metin seç</string>\n\t<string name=\"strike_out\">Üstünü çiz</string>\n\t<string name=\"text_not_found\">Metin bulunamadı</string>\n\t<string name=\"toggle_links\">Bağlantıları vurgula ve etkinleştir</string>\n\t<string name=\"underline\">Altını çiz</string>\n\t<string name=\"yes\">Evet</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-zh/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">接受</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">取消</string>\n\t<string name=\"cannot_open_buffer\">无法打开缓冲器</string>\n\t<string name=\"cannot_open_document\">无法打开文档</string>\n\t<string name=\"cannot_open_document_Reason\">无法打开文档： %1$s</string>\n\t<string name=\"cannot_open_file_Path\">无法打开文件：%1$s</string>\n\t<string name=\"choose_value\">选择值</string>\n\t<string name=\"copied_to_clipboard\">已复制到剪贴板</string>\n\t<string name=\"copy\">复制</string>\n\t<string name=\"copy_text\">复制文本</string>\n\t<string name=\"copy_text_to_the_clipboard\">将文本复制到剪贴板</string>\n\t<string name=\"delete\">删除</string>\n\t<string name=\"dismiss\">解除</string>\n\t<string name=\"document_has_changes_save_them_\">文档已变更，保存变更吗？</string>\n\t<string name=\"draw_annotation\">作批注</string>\n\t<string name=\"edit_annotations\">编辑批注</string>\n\t<string name=\"enter_password\">输入密码</string>\n\t<string name=\"entering_reflow_mode\">输入重排模式</string>\n\t<string name=\"fill_out_text_field\">填充文本字段</string>\n\t<string name=\"format_currently_not_supported\">当前不支持此格式</string>\n\t<string name=\"highlight\">高亮</string>\n\t<string name=\"ink\">墨迹</string>\n\t<string name=\"leaving_reflow_mode\">正在离开重排模式</string>\n\t<string name=\"more\">更多</string>\n\t<string name=\"no\">否</string>\n\t<string name=\"no_further_occurrences_found\">未发现更多实例。</string>\n\t<string name=\"no_media_hint\">存储介质在设备和 PC 上共同使用，会导致该存储介质在设备上无法被访问</string>\n\t<string name=\"no_media_warning\">没有存储介质</string>\n\t<string name=\"no_text_selected\">未选择文本</string>\n\t<string name=\"not_supported\">不被支持</string>\n\t<string name=\"nothing_to_save\">没有要保存的内容</string>\n\t<string name=\"okay\">确定</string>\n\t<string name=\"outline_title\">目录</string>\n\t<string name=\"parent_directory\">[向上一级]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s%2$s:%3$s</string>\n\t<string name=\"print\">打印</string>\n\t<string name=\"print_failed\">未能打印</string>\n\t<string name=\"save\">保存</string>\n\t<string name=\"search\">搜索</string>\n\t<string name=\"search_backwards\">向后搜索</string>\n\t<string name=\"search_document\">搜索文档</string>\n\t<string name=\"search_forwards\">向前搜索</string>\n\t<string name=\"searching_\">正在搜索…</string>\n\t<string name=\"select\">选择</string>\n\t<string name=\"select_text\">选择文本</string>\n\t<string name=\"strike_out\">删除线</string>\n\t<string name=\"text_not_found\">未发现文本</string>\n\t<string name=\"toggle_links\">高亮并启用墨迹</string>\n\t<string name=\"underline\">下划线</string>\n\t<string name=\"yes\">是</string>\n</resources>\n"
  },
  {
    "path": "src/main/res/values-zh-rTW/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<string name=\"accept\">同意</string>\n\t<string name=\"mupdf_title\">MuPDF</string>\n\t<string name=\"cancel\">取消</string>\n\t<string name=\"cannot_open_buffer\">未能開啟緩衝</string>\n\t<string name=\"cannot_open_document\">未能開啟文件</string>\n\t<string name=\"cannot_open_document_Reason\">未能開啟文件: %1$s</string>\n\t<string name=\"cannot_open_file_Path\">未能開啟檔案%1$s</string>\n\t<string name=\"choose_value\">選擇數值</string>\n\t<string name=\"copied_to_clipboard\">複製至剪貼簿</string>\n\t<string name=\"copy\">複製</string>\n\t<string name=\"copy_text\">複製文字</string>\n\t<string name=\"copy_text_to_the_clipboard\">複製文字至剪貼簿</string>\n\t<string name=\"delete\">刪除</string>\n\t<string name=\"dismiss\">取消</string>\n\t<string name=\"document_has_changes_save_them_\">你需要儲存已編輯的文件嗎？</string>\n\t<string name=\"draw_annotation\">繪畫註釋</string>\n\t<string name=\"edit_annotations\">編輯註釋</string>\n\t<string name=\"enter_password\">輸入密碼</string>\n\t<string name=\"entering_reflow_mode\">根據螢幕大小顯示</string>\n\t<string name=\"fill_out_text_field\">填寫文字欄</string>\n\t<string name=\"format_currently_not_supported\">暫時不支援此格式</string>\n\t<string name=\"highlight\">標示重點</string>\n\t<string name=\"ink\">墨水</string>\n\t<string name=\"leaving_reflow_mode\">不根據螢幕大小顯示</string>\n\t<string name=\"more\">更多</string>\n\t<string name=\"no\">沒有</string>\n\t<string name=\"no_further_occurrences_found\">沒有相符項目</string>\n\t<string name=\"no_media_hint\">未能與電腦分享存放裝置</string>\n\t<string name=\"no_media_warning\">沒有存放裝置</string>\n\t<string name=\"no_text_selected\">沒有選擇文字</string>\n\t<string name=\"not_supported\">不支援</string>\n\t<string name=\"nothing_to_save\">沒有資料儲存</string>\n\t<string name=\"okay\">完成</string>\n\t<string name=\"outline_title\">目錄</string>\n\t<string name=\"parent_directory\">[升一級]</string>\n\t<string name=\"picker_title_App_Ver_Dir\">%1$s%2$s%3$s</string>\n\t<string name=\"print\">列印</string>\n\t<string name=\"print_failed\">列印失敗</string>\n\t<string name=\"save\">儲存</string>\n\t<string name=\"search\">搜尋</string>\n\t<string name=\"search_backwards\">往後搜尋</string>\n\t<string name=\"search_document\">搜尋文件</string>\n\t<string name=\"search_forwards\">往前搜尋</string>\n\t<string name=\"searching_\">搜尋中&amp;#8230;</string>\n\t<string name=\"select\">選擇</string>\n\t<string name=\"select_text\">選擇文字</string>\n\t<string name=\"strike_out\">刪除線</string>\n\t<string name=\"text_not_found\">未能找到文字</string>\n\t<string name=\"toggle_links\">標示及允許連結</string>\n\t<string name=\"underline\">在下面劃線</string>\n\t<string name=\"yes\">是</string>\n</resources>\n"
  }
]