Projects STRLCPY zygisk-reflutter Commits 55c8b4ed
🤬
  • ■ ■ ■ ■ ■ ■
    .gitignore
     1 +*.iml
     2 +.gradle
     3 +/local.properties
     4 +/.idea/caches
     5 +/.idea/libraries
     6 +/.idea/modules.xml
     7 +/.idea/workspace.xml
     8 +/.idea/navEditor.xml
     9 +/.idea/assetWizardSettings.xml
     10 +.DS_Store
     11 +/build
     12 +/captures
     13 +.externalNativeBuild
     14 +.cxx
     15 +local.properties
     16 +build.bat
  • ■ ■ ■ ■ ■ ■
    LICENSE
     1 + GNU GENERAL PUBLIC LICENSE
     2 + Version 3, 29 June 2007
     3 + 
     4 + Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
     5 + Everyone is permitted to copy and distribute verbatim copies
     6 + of this license document, but changing it is not allowed.
     7 + 
     8 + Preamble
     9 + 
     10 + The GNU General Public License is a free, copyleft license for
     11 +software and other kinds of works.
     12 + 
     13 + The licenses for most software and other practical works are designed
     14 +to take away your freedom to share and change the works. By contrast,
     15 +the GNU General Public License is intended to guarantee your freedom to
     16 +share and change all versions of a program--to make sure it remains free
     17 +software for all its users. We, the Free Software Foundation, use the
     18 +GNU General Public License for most of our software; it applies also to
     19 +any other work released this way by its authors. You can apply it to
     20 +your programs, too.
     21 + 
     22 + When we speak of free software, we are referring to freedom, not
     23 +price. Our General Public Licenses are designed to make sure that you
     24 +have the freedom to distribute copies of free software (and charge for
     25 +them if you wish), that you receive source code or can get it if you
     26 +want it, that you can change the software or use pieces of it in new
     27 +free programs, and that you know you can do these things.
     28 + 
     29 + To protect your rights, we need to prevent others from denying you
     30 +these rights or asking you to surrender the rights. Therefore, you have
     31 +certain responsibilities if you distribute copies of the software, or if
     32 +you modify it: responsibilities to respect the freedom of others.
     33 + 
     34 + For example, if you distribute copies of such a program, whether
     35 +gratis or for a fee, you must pass on to the recipients the same
     36 +freedoms that you received. You must make sure that they, too, receive
     37 +or can get the source code. And you must show them these terms so they
     38 +know their rights.
     39 + 
     40 + Developers that use the GNU GPL protect your rights with two steps:
     41 +(1) assert copyright on the software, and (2) offer you this License
     42 +giving you legal permission to copy, distribute and/or modify it.
     43 + 
     44 + For the developers' and authors' protection, the GPL clearly explains
     45 +that there is no warranty for this free software. For both users' and
     46 +authors' sake, the GPL requires that modified versions be marked as
     47 +changed, so that their problems will not be attributed erroneously to
     48 +authors of previous versions.
     49 + 
     50 + Some devices are designed to deny users access to install or run
     51 +modified versions of the software inside them, although the manufacturer
     52 +can do so. This is fundamentally incompatible with the aim of
     53 +protecting users' freedom to change the software. The systematic
     54 +pattern of such abuse occurs in the area of products for individuals to
     55 +use, which is precisely where it is most unacceptable. Therefore, we
     56 +have designed this version of the GPL to prohibit the practice for those
     57 +products. If such problems arise substantially in other domains, we
     58 +stand ready to extend this provision to those domains in future versions
     59 +of the GPL, as needed to protect the freedom of users.
     60 + 
     61 + Finally, every program is threatened constantly by software patents.
     62 +States should not allow patents to restrict development and use of
     63 +software on general-purpose computers, but in those that do, we wish to
     64 +avoid the special danger that patents applied to a free program could
     65 +make it effectively proprietary. To prevent this, the GPL assures that
     66 +patents cannot be used to render the program non-free.
     67 + 
     68 + The precise terms and conditions for copying, distribution and
     69 +modification follow.
     70 + 
     71 + TERMS AND CONDITIONS
     72 + 
     73 + 0. Definitions.
     74 + 
     75 + "This License" refers to version 3 of the GNU General Public License.
     76 + 
     77 + "Copyright" also means copyright-like laws that apply to other kinds of
     78 +works, such as semiconductor masks.
     79 + 
     80 + "The Program" refers to any copyrightable work licensed under this
     81 +License. Each licensee is addressed as "you". "Licensees" and
     82 +"recipients" may be individuals or organizations.
     83 + 
     84 + To "modify" a work means to copy from or adapt all or part of the work
     85 +in a fashion requiring copyright permission, other than the making of an
     86 +exact copy. The resulting work is called a "modified version" of the
     87 +earlier work or a work "based on" the earlier work.
     88 + 
     89 + A "covered work" means either the unmodified Program or a work based
     90 +on the Program.
     91 + 
     92 + To "propagate" a work means to do anything with it that, without
     93 +permission, would make you directly or secondarily liable for
     94 +infringement under applicable copyright law, except executing it on a
     95 +computer or modifying a private copy. Propagation includes copying,
     96 +distribution (with or without modification), making available to the
     97 +public, and in some countries other activities as well.
     98 + 
     99 + To "convey" a work means any kind of propagation that enables other
     100 +parties to make or receive copies. Mere interaction with a user through
     101 +a computer network, with no transfer of a copy, is not conveying.
     102 + 
     103 + An interactive user interface displays "Appropriate Legal Notices"
     104 +to the extent that it includes a convenient and prominently visible
     105 +feature that (1) displays an appropriate copyright notice, and (2)
     106 +tells the user that there is no warranty for the work (except to the
     107 +extent that warranties are provided), that licensees may convey the
     108 +work under this License, and how to view a copy of this License. If
     109 +the interface presents a list of user commands or options, such as a
     110 +menu, a prominent item in the list meets this criterion.
     111 + 
     112 + 1. Source Code.
     113 + 
     114 + The "source code" for a work means the preferred form of the work
     115 +for making modifications to it. "Object code" means any non-source
     116 +form of a work.
     117 + 
     118 + A "Standard Interface" means an interface that either is an official
     119 +standard defined by a recognized standards body, or, in the case of
     120 +interfaces specified for a particular programming language, one that
     121 +is widely used among developers working in that language.
     122 + 
     123 + The "System Libraries" of an executable work include anything, other
     124 +than the work as a whole, that (a) is included in the normal form of
     125 +packaging a Major Component, but which is not part of that Major
     126 +Component, and (b) serves only to enable use of the work with that
     127 +Major Component, or to implement a Standard Interface for which an
     128 +implementation is available to the public in source code form. A
     129 +"Major Component", in this context, means a major essential component
     130 +(kernel, window system, and so on) of the specific operating system
     131 +(if any) on which the executable work runs, or a compiler used to
     132 +produce the work, or an object code interpreter used to run it.
     133 + 
     134 + The "Corresponding Source" for a work in object code form means all
     135 +the source code needed to generate, install, and (for an executable
     136 +work) run the object code and to modify the work, including scripts to
     137 +control those activities. However, it does not include the work's
     138 +System Libraries, or general-purpose tools or generally available free
     139 +programs which are used unmodified in performing those activities but
     140 +which are not part of the work. For example, Corresponding Source
     141 +includes interface definition files associated with source files for
     142 +the work, and the source code for shared libraries and dynamically
     143 +linked subprograms that the work is specifically designed to require,
     144 +such as by intimate data communication or control flow between those
     145 +subprograms and other parts of the work.
     146 + 
     147 + The Corresponding Source need not include anything that users
     148 +can regenerate automatically from other parts of the Corresponding
     149 +Source.
     150 + 
     151 + The Corresponding Source for a work in source code form is that
     152 +same work.
     153 + 
     154 + 2. Basic Permissions.
     155 + 
     156 + All rights granted under this License are granted for the term of
     157 +copyright on the Program, and are irrevocable provided the stated
     158 +conditions are met. This License explicitly affirms your unlimited
     159 +permission to run the unmodified Program. The output from running a
     160 +covered work is covered by this License only if the output, given its
     161 +content, constitutes a covered work. This License acknowledges your
     162 +rights of fair use or other equivalent, as provided by copyright law.
     163 + 
     164 + You may make, run and propagate covered works that you do not
     165 +convey, without conditions so long as your license otherwise remains
     166 +in force. You may convey covered works to others for the sole purpose
     167 +of having them make modifications exclusively for you, or provide you
     168 +with facilities for running those works, provided that you comply with
     169 +the terms of this License in conveying all material for which you do
     170 +not control copyright. Those thus making or running the covered works
     171 +for you must do so exclusively on your behalf, under your direction
     172 +and control, on terms that prohibit them from making any copies of
     173 +your copyrighted material outside their relationship with you.
     174 + 
     175 + Conveying under any other circumstances is permitted solely under
     176 +the conditions stated below. Sublicensing is not allowed; section 10
     177 +makes it unnecessary.
     178 + 
     179 + 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
     180 + 
     181 + No covered work shall be deemed part of an effective technological
     182 +measure under any applicable law fulfilling obligations under article
     183 +11 of the WIPO copyright treaty adopted on 20 December 1996, or
     184 +similar laws prohibiting or restricting circumvention of such
     185 +measures.
     186 + 
     187 + When you convey a covered work, you waive any legal power to forbid
     188 +circumvention of technological measures to the extent such circumvention
     189 +is effected by exercising rights under this License with respect to
     190 +the covered work, and you disclaim any intention to limit operation or
     191 +modification of the work as a means of enforcing, against the work's
     192 +users, your or third parties' legal rights to forbid circumvention of
     193 +technological measures.
     194 + 
     195 + 4. Conveying Verbatim Copies.
     196 + 
     197 + You may convey verbatim copies of the Program's source code as you
     198 +receive it, in any medium, provided that you conspicuously and
     199 +appropriately publish on each copy an appropriate copyright notice;
     200 +keep intact all notices stating that this License and any
     201 +non-permissive terms added in accord with section 7 apply to the code;
     202 +keep intact all notices of the absence of any warranty; and give all
     203 +recipients a copy of this License along with the Program.
     204 + 
     205 + You may charge any price or no price for each copy that you convey,
     206 +and you may offer support or warranty protection for a fee.
     207 + 
     208 + 5. Conveying Modified Source Versions.
     209 + 
     210 + You may convey a work based on the Program, or the modifications to
     211 +produce it from the Program, in the form of source code under the
     212 +terms of section 4, provided that you also meet all of these conditions:
     213 + 
     214 + a) The work must carry prominent notices stating that you modified
     215 + it, and giving a relevant date.
     216 + 
     217 + b) The work must carry prominent notices stating that it is
     218 + released under this License and any conditions added under section
     219 + 7. This requirement modifies the requirement in section 4 to
     220 + "keep intact all notices".
     221 + 
     222 + c) You must license the entire work, as a whole, under this
     223 + License to anyone who comes into possession of a copy. This
     224 + License will therefore apply, along with any applicable section 7
     225 + additional terms, to the whole of the work, and all its parts,
     226 + regardless of how they are packaged. This License gives no
     227 + permission to license the work in any other way, but it does not
     228 + invalidate such permission if you have separately received it.
     229 + 
     230 + d) If the work has interactive user interfaces, each must display
     231 + Appropriate Legal Notices; however, if the Program has interactive
     232 + interfaces that do not display Appropriate Legal Notices, your
     233 + work need not make them do so.
     234 + 
     235 + A compilation of a covered work with other separate and independent
     236 +works, which are not by their nature extensions of the covered work,
     237 +and which are not combined with it such as to form a larger program,
     238 +in or on a volume of a storage or distribution medium, is called an
     239 +"aggregate" if the compilation and its resulting copyright are not
     240 +used to limit the access or legal rights of the compilation's users
     241 +beyond what the individual works permit. Inclusion of a covered work
     242 +in an aggregate does not cause this License to apply to the other
     243 +parts of the aggregate.
     244 + 
     245 + 6. Conveying Non-Source Forms.
     246 + 
     247 + You may convey a covered work in object code form under the terms
     248 +of sections 4 and 5, provided that you also convey the
     249 +machine-readable Corresponding Source under the terms of this License,
     250 +in one of these ways:
     251 + 
     252 + a) Convey the object code in, or embodied in, a physical product
     253 + (including a physical distribution medium), accompanied by the
     254 + Corresponding Source fixed on a durable physical medium
     255 + customarily used for software interchange.
     256 + 
     257 + b) Convey the object code in, or embodied in, a physical product
     258 + (including a physical distribution medium), accompanied by a
     259 + written offer, valid for at least three years and valid for as
     260 + long as you offer spare parts or customer support for that product
     261 + model, to give anyone who possesses the object code either (1) a
     262 + copy of the Corresponding Source for all the software in the
     263 + product that is covered by this License, on a durable physical
     264 + medium customarily used for software interchange, for a price no
     265 + more than your reasonable cost of physically performing this
     266 + conveying of source, or (2) access to copy the
     267 + Corresponding Source from a network server at no charge.
     268 + 
     269 + c) Convey individual copies of the object code with a copy of the
     270 + written offer to provide the Corresponding Source. This
     271 + alternative is allowed only occasionally and noncommercially, and
     272 + only if you received the object code with such an offer, in accord
     273 + with subsection 6b.
     274 + 
     275 + d) Convey the object code by offering access from a designated
     276 + place (gratis or for a charge), and offer equivalent access to the
     277 + Corresponding Source in the same way through the same place at no
     278 + further charge. You need not require recipients to copy the
     279 + Corresponding Source along with the object code. If the place to
     280 + copy the object code is a network server, the Corresponding Source
     281 + may be on a different server (operated by you or a third party)
     282 + that supports equivalent copying facilities, provided you maintain
     283 + clear directions next to the object code saying where to find the
     284 + Corresponding Source. Regardless of what server hosts the
     285 + Corresponding Source, you remain obligated to ensure that it is
     286 + available for as long as needed to satisfy these requirements.
     287 + 
     288 + e) Convey the object code using peer-to-peer transmission, provided
     289 + you inform other peers where the object code and Corresponding
     290 + Source of the work are being offered to the general public at no
     291 + charge under subsection 6d.
     292 + 
     293 + A separable portion of the object code, whose source code is excluded
     294 +from the Corresponding Source as a System Library, need not be
     295 +included in conveying the object code work.
     296 + 
     297 + A "User Product" is either (1) a "consumer product", which means any
     298 +tangible personal property which is normally used for personal, family,
     299 +or household purposes, or (2) anything designed or sold for incorporation
     300 +into a dwelling. In determining whether a product is a consumer product,
     301 +doubtful cases shall be resolved in favor of coverage. For a particular
     302 +product received by a particular user, "normally used" refers to a
     303 +typical or common use of that class of product, regardless of the status
     304 +of the particular user or of the way in which the particular user
     305 +actually uses, or expects or is expected to use, the product. A product
     306 +is a consumer product regardless of whether the product has substantial
     307 +commercial, industrial or non-consumer uses, unless such uses represent
     308 +the only significant mode of use of the product.
     309 + 
     310 + "Installation Information" for a User Product means any methods,
     311 +procedures, authorization keys, or other information required to install
     312 +and execute modified versions of a covered work in that User Product from
     313 +a modified version of its Corresponding Source. The information must
     314 +suffice to ensure that the continued functioning of the modified object
     315 +code is in no case prevented or interfered with solely because
     316 +modification has been made.
     317 + 
     318 + If you convey an object code work under this section in, or with, or
     319 +specifically for use in, a User Product, and the conveying occurs as
     320 +part of a transaction in which the right of possession and use of the
     321 +User Product is transferred to the recipient in perpetuity or for a
     322 +fixed term (regardless of how the transaction is characterized), the
     323 +Corresponding Source conveyed under this section must be accompanied
     324 +by the Installation Information. But this requirement does not apply
     325 +if neither you nor any third party retains the ability to install
     326 +modified object code on the User Product (for example, the work has
     327 +been installed in ROM).
     328 + 
     329 + The requirement to provide Installation Information does not include a
     330 +requirement to continue to provide support service, warranty, or updates
     331 +for a work that has been modified or installed by the recipient, or for
     332 +the User Product in which it has been modified or installed. Access to a
     333 +network may be denied when the modification itself materially and
     334 +adversely affects the operation of the network or violates the rules and
     335 +protocols for communication across the network.
     336 + 
     337 + Corresponding Source conveyed, and Installation Information provided,
     338 +in accord with this section must be in a format that is publicly
     339 +documented (and with an implementation available to the public in
     340 +source code form), and must require no special password or key for
     341 +unpacking, reading or copying.
     342 + 
     343 + 7. Additional Terms.
     344 + 
     345 + "Additional permissions" are terms that supplement the terms of this
     346 +License by making exceptions from one or more of its conditions.
     347 +Additional permissions that are applicable to the entire Program shall
     348 +be treated as though they were included in this License, to the extent
     349 +that they are valid under applicable law. If additional permissions
     350 +apply only to part of the Program, that part may be used separately
     351 +under those permissions, but the entire Program remains governed by
     352 +this License without regard to the additional permissions.
     353 + 
     354 + When you convey a copy of a covered work, you may at your option
     355 +remove any additional permissions from that copy, or from any part of
     356 +it. (Additional permissions may be written to require their own
     357 +removal in certain cases when you modify the work.) You may place
     358 +additional permissions on material, added by you to a covered work,
     359 +for which you have or can give appropriate copyright permission.
     360 + 
     361 + Notwithstanding any other provision of this License, for material you
     362 +add to a covered work, you may (if authorized by the copyright holders of
     363 +that material) supplement the terms of this License with terms:
     364 + 
     365 + a) Disclaiming warranty or limiting liability differently from the
     366 + terms of sections 15 and 16 of this License; or
     367 + 
     368 + b) Requiring preservation of specified reasonable legal notices or
     369 + author attributions in that material or in the Appropriate Legal
     370 + Notices displayed by works containing it; or
     371 + 
     372 + c) Prohibiting misrepresentation of the origin of that material, or
     373 + requiring that modified versions of such material be marked in
     374 + reasonable ways as different from the original version; or
     375 + 
     376 + d) Limiting the use for publicity purposes of names of licensors or
     377 + authors of the material; or
     378 + 
     379 + e) Declining to grant rights under trademark law for use of some
     380 + trade names, trademarks, or service marks; or
     381 + 
     382 + f) Requiring indemnification of licensors and authors of that
     383 + material by anyone who conveys the material (or modified versions of
     384 + it) with contractual assumptions of liability to the recipient, for
     385 + any liability that these contractual assumptions directly impose on
     386 + those licensors and authors.
     387 + 
     388 + All other non-permissive additional terms are considered "further
     389 +restrictions" within the meaning of section 10. If the Program as you
     390 +received it, or any part of it, contains a notice stating that it is
     391 +governed by this License along with a term that is a further
     392 +restriction, you may remove that term. If a license document contains
     393 +a further restriction but permits relicensing or conveying under this
     394 +License, you may add to a covered work material governed by the terms
     395 +of that license document, provided that the further restriction does
     396 +not survive such relicensing or conveying.
     397 + 
     398 + If you add terms to a covered work in accord with this section, you
     399 +must place, in the relevant source files, a statement of the
     400 +additional terms that apply to those files, or a notice indicating
     401 +where to find the applicable terms.
     402 + 
     403 + Additional terms, permissive or non-permissive, may be stated in the
     404 +form of a separately written license, or stated as exceptions;
     405 +the above requirements apply either way.
     406 + 
     407 + 8. Termination.
     408 + 
     409 + You may not propagate or modify a covered work except as expressly
     410 +provided under this License. Any attempt otherwise to propagate or
     411 +modify it is void, and will automatically terminate your rights under
     412 +this License (including any patent licenses granted under the third
     413 +paragraph of section 11).
     414 + 
     415 + However, if you cease all violation of this License, then your
     416 +license from a particular copyright holder is reinstated (a)
     417 +provisionally, unless and until the copyright holder explicitly and
     418 +finally terminates your license, and (b) permanently, if the copyright
     419 +holder fails to notify you of the violation by some reasonable means
     420 +prior to 60 days after the cessation.
     421 + 
     422 + Moreover, your license from a particular copyright holder is
     423 +reinstated permanently if the copyright holder notifies you of the
     424 +violation by some reasonable means, this is the first time you have
     425 +received notice of violation of this License (for any work) from that
     426 +copyright holder, and you cure the violation prior to 30 days after
     427 +your receipt of the notice.
     428 + 
     429 + Termination of your rights under this section does not terminate the
     430 +licenses of parties who have received copies or rights from you under
     431 +this License. If your rights have been terminated and not permanently
     432 +reinstated, you do not qualify to receive new licenses for the same
     433 +material under section 10.
     434 + 
     435 + 9. Acceptance Not Required for Having Copies.
     436 + 
     437 + You are not required to accept this License in order to receive or
     438 +run a copy of the Program. Ancillary propagation of a covered work
     439 +occurring solely as a consequence of using peer-to-peer transmission
     440 +to receive a copy likewise does not require acceptance. However,
     441 +nothing other than this License grants you permission to propagate or
     442 +modify any covered work. These actions infringe copyright if you do
     443 +not accept this License. Therefore, by modifying or propagating a
     444 +covered work, you indicate your acceptance of this License to do so.
     445 + 
     446 + 10. Automatic Licensing of Downstream Recipients.
     447 + 
     448 + Each time you convey a covered work, the recipient automatically
     449 +receives a license from the original licensors, to run, modify and
     450 +propagate that work, subject to this License. You are not responsible
     451 +for enforcing compliance by third parties with this License.
     452 + 
     453 + An "entity transaction" is a transaction transferring control of an
     454 +organization, or substantially all assets of one, or subdividing an
     455 +organization, or merging organizations. If propagation of a covered
     456 +work results from an entity transaction, each party to that
     457 +transaction who receives a copy of the work also receives whatever
     458 +licenses to the work the party's predecessor in interest had or could
     459 +give under the previous paragraph, plus a right to possession of the
     460 +Corresponding Source of the work from the predecessor in interest, if
     461 +the predecessor has it or can get it with reasonable efforts.
     462 + 
     463 + You may not impose any further restrictions on the exercise of the
     464 +rights granted or affirmed under this License. For example, you may
     465 +not impose a license fee, royalty, or other charge for exercise of
     466 +rights granted under this License, and you may not initiate litigation
     467 +(including a cross-claim or counterclaim in a lawsuit) alleging that
     468 +any patent claim is infringed by making, using, selling, offering for
     469 +sale, or importing the Program or any portion of it.
     470 + 
     471 + 11. Patents.
     472 + 
     473 + A "contributor" is a copyright holder who authorizes use under this
     474 +License of the Program or a work on which the Program is based. The
     475 +work thus licensed is called the contributor's "contributor version".
     476 + 
     477 + A contributor's "essential patent claims" are all patent claims
     478 +owned or controlled by the contributor, whether already acquired or
     479 +hereafter acquired, that would be infringed by some manner, permitted
     480 +by this License, of making, using, or selling its contributor version,
     481 +but do not include claims that would be infringed only as a
     482 +consequence of further modification of the contributor version. For
     483 +purposes of this definition, "control" includes the right to grant
     484 +patent sublicenses in a manner consistent with the requirements of
     485 +this License.
     486 + 
     487 + Each contributor grants you a non-exclusive, worldwide, royalty-free
     488 +patent license under the contributor's essential patent claims, to
     489 +make, use, sell, offer for sale, import and otherwise run, modify and
     490 +propagate the contents of its contributor version.
     491 + 
     492 + In the following three paragraphs, a "patent license" is any express
     493 +agreement or commitment, however denominated, not to enforce a patent
     494 +(such as an express permission to practice a patent or covenant not to
     495 +sue for patent infringement). To "grant" such a patent license to a
     496 +party means to make such an agreement or commitment not to enforce a
     497 +patent against the party.
     498 + 
     499 + If you convey a covered work, knowingly relying on a patent license,
     500 +and the Corresponding Source of the work is not available for anyone
     501 +to copy, free of charge and under the terms of this License, through a
     502 +publicly available network server or other readily accessible means,
     503 +then you must either (1) cause the Corresponding Source to be so
     504 +available, or (2) arrange to deprive yourself of the benefit of the
     505 +patent license for this particular work, or (3) arrange, in a manner
     506 +consistent with the requirements of this License, to extend the patent
     507 +license to downstream recipients. "Knowingly relying" means you have
     508 +actual knowledge that, but for the patent license, your conveying the
     509 +covered work in a country, or your recipient's use of the covered work
     510 +in a country, would infringe one or more identifiable patents in that
     511 +country that you have reason to believe are valid.
     512 + 
     513 + If, pursuant to or in connection with a single transaction or
     514 +arrangement, you convey, or propagate by procuring conveyance of, a
     515 +covered work, and grant a patent license to some of the parties
     516 +receiving the covered work authorizing them to use, propagate, modify
     517 +or convey a specific copy of the covered work, then the patent license
     518 +you grant is automatically extended to all recipients of the covered
     519 +work and works based on it.
     520 + 
     521 + A patent license is "discriminatory" if it does not include within
     522 +the scope of its coverage, prohibits the exercise of, or is
     523 +conditioned on the non-exercise of one or more of the rights that are
     524 +specifically granted under this License. You may not convey a covered
     525 +work if you are a party to an arrangement with a third party that is
     526 +in the business of distributing software, under which you make payment
     527 +to the third party based on the extent of your activity of conveying
     528 +the work, and under which the third party grants, to any of the
     529 +parties who would receive the covered work from you, a discriminatory
     530 +patent license (a) in connection with copies of the covered work
     531 +conveyed by you (or copies made from those copies), or (b) primarily
     532 +for and in connection with specific products or compilations that
     533 +contain the covered work, unless you entered into that arrangement,
     534 +or that patent license was granted, prior to 28 March 2007.
     535 + 
     536 + Nothing in this License shall be construed as excluding or limiting
     537 +any implied license or other defenses to infringement that may
     538 +otherwise be available to you under applicable patent law.
     539 + 
     540 + 12. No Surrender of Others' Freedom.
     541 + 
     542 + If conditions are imposed on you (whether by court order, agreement or
     543 +otherwise) that contradict the conditions of this License, they do not
     544 +excuse you from the conditions of this License. If you cannot convey a
     545 +covered work so as to satisfy simultaneously your obligations under this
     546 +License and any other pertinent obligations, then as a consequence you may
     547 +not convey it at all. For example, if you agree to terms that obligate you
     548 +to collect a royalty for further conveying from those to whom you convey
     549 +the Program, the only way you could satisfy both those terms and this
     550 +License would be to refrain entirely from conveying the Program.
     551 + 
     552 + 13. Use with the GNU Affero General Public License.
     553 + 
     554 + Notwithstanding any other provision of this License, you have
     555 +permission to link or combine any covered work with a work licensed
     556 +under version 3 of the GNU Affero General Public License into a single
     557 +combined work, and to convey the resulting work. The terms of this
     558 +License will continue to apply to the part which is the covered work,
     559 +but the special requirements of the GNU Affero General Public License,
     560 +section 13, concerning interaction through a network will apply to the
     561 +combination as such.
     562 + 
     563 + 14. Revised Versions of this License.
     564 + 
     565 + The Free Software Foundation may publish revised and/or new versions of
     566 +the GNU General Public License from time to time. Such new versions will
     567 +be similar in spirit to the present version, but may differ in detail to
     568 +address new problems or concerns.
     569 + 
     570 + Each version is given a distinguishing version number. If the
     571 +Program specifies that a certain numbered version of the GNU General
     572 +Public License "or any later version" applies to it, you have the
     573 +option of following the terms and conditions either of that numbered
     574 +version or of any later version published by the Free Software
     575 +Foundation. If the Program does not specify a version number of the
     576 +GNU General Public License, you may choose any version ever published
     577 +by the Free Software Foundation.
     578 + 
     579 + If the Program specifies that a proxy can decide which future
     580 +versions of the GNU General Public License can be used, that proxy's
     581 +public statement of acceptance of a version permanently authorizes you
     582 +to choose that version for the Program.
     583 + 
     584 + Later license versions may give you additional or different
     585 +permissions. However, no additional obligations are imposed on any
     586 +author or copyright holder as a result of your choosing to follow a
     587 +later version.
     588 + 
     589 + 15. Disclaimer of Warranty.
     590 + 
     591 + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
     592 +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
     593 +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
     594 +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
     595 +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     596 +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
     597 +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
     598 +ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
     599 + 
     600 + 16. Limitation of Liability.
     601 + 
     602 + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
     603 +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
     604 +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
     605 +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
     606 +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
     607 +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
     608 +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
     609 +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
     610 +SUCH DAMAGES.
     611 + 
     612 + 17. Interpretation of Sections 15 and 16.
     613 + 
     614 + If the disclaimer of warranty and limitation of liability provided
     615 +above cannot be given local legal effect according to their terms,
     616 +reviewing courts shall apply local law that most closely approximates
     617 +an absolute waiver of all civil liability in connection with the
     618 +Program, unless a warranty or assumption of liability accompanies a
     619 +copy of the Program in return for a fee.
     620 + 
     621 + END OF TERMS AND CONDITIONS
     622 + 
     623 + How to Apply These Terms to Your New Programs
     624 + 
     625 + If you develop a new program, and you want it to be of the greatest
     626 +possible use to the public, the best way to achieve this is to make it
     627 +free software which everyone can redistribute and change under these terms.
     628 + 
     629 + To do so, attach the following notices to the program. It is safest
     630 +to attach them to the start of each source file to most effectively
     631 +state the exclusion of warranty; and each file should have at least
     632 +the "copyright" line and a pointer to where the full notice is found.
     633 + 
     634 + <one line to give the program's name and a brief idea of what it does.>
     635 + Copyright (C) <year> <name of author>
     636 + 
     637 + This program is free software: you can redistribute it and/or modify
     638 + it under the terms of the GNU General Public License as published by
     639 + the Free Software Foundation, either version 3 of the License, or
     640 + (at your option) any later version.
     641 + 
     642 + This program is distributed in the hope that it will be useful,
     643 + but WITHOUT ANY WARRANTY; without even the implied warranty of
     644 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     645 + GNU General Public License for more details.
     646 + 
     647 + You should have received a copy of the GNU General Public License
     648 + along with this program. If not, see <https://www.gnu.org/licenses/>.
     649 + 
     650 +Also add information on how to contact you by electronic and paper mail.
     651 + 
     652 + If the program does terminal interaction, make it output a short
     653 +notice like this when it starts in an interactive mode:
     654 + 
     655 + <program> Copyright (C) <year> <name of author>
     656 + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
     657 + This is free software, and you are welcome to redistribute it
     658 + under certain conditions; type `show c' for details.
     659 + 
     660 +The hypothetical commands `show w' and `show c' should show the appropriate
     661 +parts of the General Public License. Of course, your program's commands
     662 +might be different; for a GUI interface, you would use an "about box".
     663 + 
     664 + You should also get your employer (if you work as a programmer) or school,
     665 +if any, to sign a "copyright disclaimer" for the program, if necessary.
     666 +For more information on this, and how to apply and follow the GNU GPL, see
     667 +<https://www.gnu.org/licenses/>.
     668 + 
     669 + The GNU General Public License does not permit incorporating your program
     670 +into proprietary programs. If your program is a subroutine library, you
     671 +may consider it more useful to permit linking proprietary applications with
     672 +the library. If this is what you want to do, use the GNU Lesser General
     673 +Public License instead of this License. But first, please read
     674 +<https://www.gnu.org/licenses/why-not-lgpl.html>.
     675 + 
  • ■ ■ ■ ■ ■ ■
    README.md
     1 +# Zygisk-based reFlutter implementation
     2 + 
     3 +reFlutter is a handy tool for reverse engineering Flutter-based applications for both rooted and non-rooted Android. To use Reflutter, you must obtain your APK and replace libflutter.so, resign the APK and reinstall it.
     4 + 
     5 +The Zygisk module is designed to empower rooted Android users by simplifying the process. With the provided app, you can easily download `libflutter.so` from reFlutter project. The Zygisk module will then seamlessly replace `libflutter.so` at runtime, making the process straightforward and efficient.
     6 + 
     7 +Please note: set up your Proxy IP. Set your Burp Suite like you would set when using reFlutter (listen to `*:8083` and enable "Support invisible proxying").
     8 + 
     9 +## Requirements
     10 + 
     11 +Rooted Android with Magisk installed and Zygisk Enabled
     12 + 
     13 +## Installation
     14 + 
     15 +Download the ZIP file, and install it as Zygisk module. You can also do it from ADB:
     16 + 
     17 + 
     18 +```
     19 +adb push zygiskreflutter_1.0.zip /sdcard/
     20 +adb shell su -c magisk --install-module /sdcard/zygiskreflutter_1.0.zip
     21 +adb reboot
     22 +```
     23 + 
     24 + 
     25 +Install the APK, then setup your proxy IP from "Set Proxy Host" menu.
     26 + 
     27 +Select the app that you want to *reFlutter*, click download library, once it is downloaded, you can enable the proxy feature.
     28 + 
     29 +You can now start the target app normally
     30 + 
     31 + 
     32 +Check adb log and filter it by "ZygiskReflutter" in case you found a problem
     33 + 
  • ■ ■ ■ ■ ■
    app/.gitignore
     1 +/build
  • ■ ■ ■ ■ ■ ■
    app/build.gradle.kts
     1 +plugins {
     2 + alias(libs.plugins.androidApplication)
     3 +}
     4 + 
     5 +android {
     6 + namespace = "com.tinyhack.zygiskreflutter"
     7 + compileSdk = 34
     8 + 
     9 + defaultConfig {
     10 + applicationId = "com.tinyhack.zygiskreflutter"
     11 + minSdk = 27
     12 + targetSdk = 34
     13 + versionCode = 1
     14 + versionName = "1.0"
     15 + 
     16 + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
     17 + externalNativeBuild {
     18 + cmake {
     19 + arguments += "-DCMAKE_BUILD_TYPE=MinSizeRel"
     20 + 
     21 + cppFlags += "-std=c++17"
     22 + cppFlags += "-fno-exceptions"
     23 + cppFlags += "-fno-rtti"
     24 + cppFlags += "-fvisibility=hidden"
     25 + cppFlags += "-fvisibility-inlines-hidden"
     26 + }
     27 + }
     28 + }
     29 + 
     30 + buildTypes {
     31 + release {
     32 + isMinifyEnabled = true
     33 + isShrinkResources = true
     34 + multiDexEnabled = false
     35 + proguardFiles(
     36 + getDefaultProguardFile("proguard-android-optimize.txt"),
     37 + "proguard-rules.pro"
     38 + )
     39 + }
     40 + }
     41 + compileOptions {
     42 + sourceCompatibility = JavaVersion.VERSION_1_8
     43 + targetCompatibility = JavaVersion.VERSION_1_8
     44 + }
     45 + externalNativeBuild {
     46 + cmake {
     47 + path = file("src/main/cpp/CMakeLists.txt")
     48 + version = "3.22.1"
     49 + }
     50 + }
     51 + buildFeatures {
     52 + viewBinding = true
     53 + }
     54 +}
     55 + 
     56 + 
     57 + 
     58 +dependencies {
     59 + 
     60 + implementation(libs.appcompat)
     61 + implementation(libs.material)
     62 + implementation(libs.constraintlayout)
     63 + implementation(libs.activity)
     64 + testImplementation(libs.junit)
     65 + androidTestImplementation(libs.ext.junit)
     66 + androidTestImplementation(libs.espresso.core)
     67 +}
     68 + 
     69 +tasks.register("updateModuleProp") {
     70 + doLast {
     71 + val versionName = project.android.defaultConfig.versionName
     72 + val versionCode = project.android.defaultConfig.versionCode
     73 + 
     74 + val modulePropFile = project.rootDir.resolve("module/module.prop")
     75 + 
     76 + var content = modulePropFile.readText()
     77 + 
     78 + content = content.replace(Regex("version=.*"), "version=$versionName")
     79 + content = content.replace(Regex("versionCode=.*"), "versionCode=$versionCode")
     80 + 
     81 + modulePropFile.writeText(content)
     82 + }
     83 +}
     84 + 
     85 + 
     86 +tasks.register("copyFiles") {
     87 + dependsOn("updateModuleProp")
     88 + 
     89 + doLast {
     90 + val moduleFolder = project.rootDir.resolve("module")
     91 + val dexFile = project.layout.buildDirectory.get().asFile.resolve("intermediates/dex/release/minifyReleaseWithR8/classes.dex")
     92 + val soDir = project.layout.buildDirectory.get().asFile.resolve("intermediates/stripped_native_libs/release/stripReleaseDebugSymbols/out/lib")
     93 + 
     94 + //dexFile.copyTo(moduleFolder.resolve("classes.dex"), overwrite = true)
     95 + 
     96 + soDir.walk().filter { it.isFile && it.extension == "so" }.forEach { soFile ->
     97 + val abiFolder = soFile.parentFile.name
     98 + val destination = moduleFolder.resolve("zygisk/$abiFolder.so")
     99 + soFile.copyTo(destination, overwrite = true)
     100 + }
     101 + }
     102 +}
     103 + 
     104 +tasks.register<Zip>("zip") {
     105 + dependsOn("copyFiles")
     106 + 
     107 + archiveFileName.set("zygiskreflutter_${project.android.defaultConfig.versionName}.zip")
     108 + destinationDirectory.set(project.rootDir.resolve("out"))
     109 + 
     110 + from(project.rootDir.resolve("module"))
     111 +}
     112 + 
     113 +afterEvaluate {
     114 + tasks["assembleRelease"].finalizedBy("updateModuleProp", "copyFiles", "zip")
     115 +}
     116 + 
  • ■ ■ ■ ■ ■ ■
    app/proguard-rules.pro
     1 +# Add project specific ProGuard rules here.
     2 +# You can control the set of applied configuration files using the
     3 +# proguardFiles setting in build.gradle.
     4 +#
     5 +# For more details, see
     6 +# http://developer.android.com/guide/developing/tools/proguard.html
     7 + 
     8 +# If your project uses WebView with JS, uncomment the following
     9 +# and specify the fully qualified class name to the JavaScript interface
     10 +# class:
     11 +#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
     12 +# public *;
     13 +#}
     14 + 
     15 +# Uncomment this to preserve the line number information for
     16 +# debugging stack traces.
     17 +#-keepattributes SourceFile,LineNumberTable
     18 + 
     19 +# If you keep the line number information, uncomment this to
     20 +# hide the original source file name.
     21 +#-renamesourcefileattribute SourceFile
  • ■ ■ ■ ■ ■ ■
    app/src/androidTest/java/com/tinyhack/zygiskreflutter/ExampleInstrumentedTest.java
     1 +package com.tinyhack.zygiskreflutter;
     2 + 
     3 +import android.content.Context;
     4 + 
     5 +import androidx.test.platform.app.InstrumentationRegistry;
     6 +import androidx.test.ext.junit.runners.AndroidJUnit4;
     7 + 
     8 +import org.junit.Test;
     9 +import org.junit.runner.RunWith;
     10 + 
     11 +import static org.junit.Assert.*;
     12 + 
     13 +/**
     14 + * Instrumented test, which will execute on an Android device.
     15 + *
     16 + * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
     17 + */
     18 +@RunWith(AndroidJUnit4.class)
     19 +public class ExampleInstrumentedTest {
     20 + @Test
     21 + public void useAppContext() {
     22 + // Context of the app under test.
     23 + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
     24 + assertEquals("com.tinyhack.zygiskreflutter", appContext.getPackageName());
     25 + }
     26 +}
     27 + 
  • ■ ■ ■ ■ ■ ■
    app/src/main/AndroidManifest.xml
     1 +<?xml version="1.0" encoding="utf-8"?>
     2 +<manifest xmlns:android="http://schemas.android.com/apk/res/android"
     3 + xmlns:tools="http://schemas.android.com/tools">
     4 + 
     5 + <uses-permission android:name="android.permission.INTERNET" />
     6 + <uses-permission
     7 + android:name="android.permission.QUERY_ALL_PACKAGES"
     8 + tools:ignore="QueryAllPackagesPermission" />
     9 + 
     10 + <application
     11 + android:allowBackup="true"
     12 + android:dataExtractionRules="@xml/data_extraction_rules"
     13 + android:fullBackupContent="@xml/backup_rules"
     14 + android:icon="@mipmap/ic_launcher"
     15 + android:label="@string/app_name"
     16 + android:roundIcon="@mipmap/ic_launcher_round"
     17 + android:supportsRtl="true"
     18 + android:theme="@style/Theme.Zygiskreflutter"
     19 + tools:targetApi="31">
     20 + <activity
     21 + android:name=".SetHostActivity"
     22 + android:exported="false" />
     23 + <activity
     24 + android:name=".AboutActivity"
     25 + android:exported="false" />
     26 + <activity
     27 + android:name=".AppDetailActivity"
     28 + android:exported="false" />
     29 + <activity
     30 + android:name=".MainActivity"
     31 + android:exported="true">
     32 + <intent-filter>
     33 + <action android:name="android.intent.action.MAIN" />
     34 + 
     35 + <category android:name="android.intent.category.LAUNCHER" />
     36 + </intent-filter>
     37 + </activity>
     38 + </application>
     39 + 
     40 +</manifest>
     41 + 
  • ■ ■ ■ ■ ■ ■
    app/src/main/cpp/CMakeLists.txt
     1 +# For more information about using CMake with Android Studio, read the
     2 +# documentation: https://d.android.com/studio/projects/add-native-code.html.
     3 +# For more examples on how to use CMake, see https://github.com/android/ndk-samples.
     4 + 
     5 +# Sets the minimum CMake version required for this project.
     6 +cmake_minimum_required(VERSION 3.22.1)
     7 + 
     8 +# Declares the project name. The project name can be accessed via ${ PROJECT_NAME},
     9 +# Since this is the top level CMakeLists.txt, the project name is also accessible
     10 +# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level
     11 +# build script scope).
     12 +project("zygiskreflutter")
     13 + 
     14 +# Creates and names a library, sets it as either STATIC
     15 +# or SHARED, and provides the relative paths to its source code.
     16 +# You can define multiple libraries, and CMake builds them for you.
     17 +# Gradle automatically packages shared libraries with your APK.
     18 +#
     19 +# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define
     20 +# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME}
     21 +# is preferred for the same purpose.
     22 +#
     23 +# In order to load a library into your app from Java/Kotlin, you must call
     24 +# System.loadLibrary() and pass the name of the library defined here;
     25 +# for GameActivity/NativeActivity derived applications, the same library name must be
     26 +# used in the AndroidManifest.xml file.
     27 +add_library(${CMAKE_PROJECT_NAME} SHARED
     28 + # List C/C++ source files with relative paths to this CMakeLists.txt.
     29 + native-lib.cpp)
     30 + 
     31 +# Specifies libraries CMake should link to your target library. You
     32 +# can link libraries from various origins, such as libraries defined in this
     33 +# build script, prebuilt third-party libraries, or Android system libraries.
     34 +target_link_libraries(${CMAKE_PROJECT_NAME}
     35 + # List libraries link to the target library
     36 + # do not link to android https://github.com/topjohnwu/Magisk/issues/6351
     37 + log)
     38 + 
  • ■ ■ ■ ■ ■ ■
    app/src/main/cpp/native-lib.cpp
     1 +#include <jni.h>
     2 +#include <string>
     3 +#include "zygisk.h"
     4 +#include <android/log.h>
     5 +#include <dlfcn.h>
     6 +#include <android/dlext.h>
     7 +#include <unistd.h>
     8 +#include <sys/types.h>
     9 +#include <sys/stat.h>
     10 +#include <fcntl.h>
     11 +#include <fstream>
     12 +#include <sys/mman.h>
     13 + 
     14 + 
     15 + 
     16 + 
     17 +#define LOG_TAG "ZygiskReflutter"
     18 + 
     19 +void *(*orig__dlopen)(const char *filename, int flags);
     20 + 
     21 +void *my_dlopen(const char *filename, int flags)
     22 +{
     23 + __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "my_dlopen: %s", filename);
     24 + return orig__dlopen(filename, flags);
     25 +}
     26 + 
     27 +static void send_string(int fd, const char *str) {
     28 + int len = strlen(str);
     29 + write(fd, &len, sizeof(len));
     30 + write(fd, str, len);
     31 +}
     32 + 
     33 +static std::string so_replacement;
     34 + 
     35 +void *(*orig_android_dlopen_ext)(const char *_Nullable __filename, int __flags, const android_dlextinfo *_Nullable __info);
     36 + 
     37 +void *android_dlopen_ext(const char *_Nullable __filename, int __flags, const android_dlextinfo *_Nullable __info)
     38 +{
     39 + __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "android_dlopen_ext: %s flags: %08x", __filename, __flags);
     40 + 
     41 + if (!so_replacement.empty() && so_replacement[0]=='/') {
     42 + //if trying to load libflutter.so
     43 + if (strstr(__filename, "libflutter.so")) {
     44 + __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "android_dlopen_ext replace: %s", so_replacement.c_str());
     45 + void *res = orig_android_dlopen_ext(so_replacement.c_str(), __flags, __info);
     46 + if (!res) {
     47 + __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "android_dlopen_ext replace result2: %p %s", res, dlerror());
     48 + if (!res) {
     49 + __android_log_print(ANDROID_LOG_WARN, LOG_TAG, "failed hooking");
     50 + //reopen orig
     51 + res = orig_android_dlopen_ext(__filename, __flags, __info);
     52 + }
     53 + }
     54 + __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "android_dlopen_ext returning %p", res);
     55 + return res;
     56 + }
     57 + }
     58 + 
     59 + return orig_android_dlopen_ext(__filename, __flags, __info);
     60 +}
     61 + 
     62 +class ZygiskReflutter : public zygisk::ModuleBase {
     63 +public:
     64 + void onLoad(zygisk::Api *api, JNIEnv *env) override {
     65 + this->api = api;
     66 + this->env = env;
     67 + do_hook = false;
     68 + }
     69 + 
     70 + void preAppSpecialize(zygisk::AppSpecializeArgs *args) override {
     71 + auto package_name = env->GetStringUTFChars(args->nice_name, nullptr);
     72 + auto app_data_dir = env->GetStringUTFChars(args->app_data_dir, nullptr);
     73 + preSpecialize(package_name, app_data_dir);
     74 + env->ReleaseStringUTFChars(args->nice_name, package_name);
     75 + env->ReleaseStringUTFChars(args->app_data_dir, app_data_dir);
     76 + }
     77 + 
     78 + void postAppSpecialize(const zygisk::AppSpecializeArgs *) override {
     79 + if (do_hook) {
     80 + //hook dlopen
     81 + api->pltHookRegister(".*", "dlopen", (void *) my_dlopen, (void **) &orig__dlopen);
     82 + //hook android_dlopen_ext
     83 + api->pltHookRegister(".*", "android_dlopen_ext", (void *) android_dlopen_ext, (void **) &orig_android_dlopen_ext);
     84 + api->pltHookCommit();
     85 + 
     86 + }
     87 + if (create_installed) {
     88 + //create a file to indicate that we are installed
     89 + std::string installed_file = "/data/data/com.tinyhack.zygiskreflutter/files/installed.txt";
     90 + std::ofstream out_file(installed_file);
     91 + if (out_file.is_open()) {
     92 + out_file << "installed";
     93 + out_file.close();
     94 + } else {
     95 + __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "failed to create installed file");
     96 + }
     97 + }
     98 + }
     99 + 
     100 +private:
     101 + zygisk::Api *api;
     102 + JNIEnv *env;
     103 + bool do_hook;
     104 + bool create_installed;
     105 + 
     106 + void preSpecialize(const char *package_name, const char *app_data_dir) {
     107 + 
     108 + if (strcmp(package_name, "com.tinyhack.zygiskreflutter") == 0) {
     109 + create_installed = true;
     110 + return;
     111 + }
     112 + 
     113 + //__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "preSpecialize: '%s', %s", package_name, app_data_dir);
     114 + 
     115 + int fd = api->connectCompanion();
     116 + send_string(fd, package_name);
     117 + send_string(fd, app_data_dir);
     118 + //read the path length
     119 + int path_len = 0;
     120 + int len = read(fd, &path_len, sizeof(path_len));
     121 + if (len <= 0) {
     122 + __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "read path length failed %d err = %d", len, errno);
     123 + return;
     124 + }
     125 + //read the path
     126 + char buf[1024];
     127 + len = read(fd, buf, path_len);
     128 + if (len <= 0) {
     129 + __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "read path failed");
     130 + return;
     131 + }
     132 + buf[len] = '\0';
     133 + if (strcmp(buf, "error")==0) {
     134 + return;
     135 + }
     136 + 
     137 + so_replacement = buf;
     138 + 
     139 + __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "preSpecialize: ZygiskReflutter: %s", so_replacement.c_str());
     140 + do_hook = true;
     141 + 
     142 + }
     143 +};
     144 + 
     145 +static std::string readFirstLine(const char *filename) {
     146 + std::ifstream in_file(filename);
     147 + std::string firstLine;
     148 + if (in_file.is_open()) {
     149 + std::getline(in_file, firstLine);
     150 + in_file.close();
     151 + }
     152 + return firstLine;
     153 +}
     154 + 
     155 +static void writeLine(const char *filename, const char *line) {
     156 + std::ofstream out_file(filename);
     157 + if (out_file.is_open()) {
     158 + out_file << line;
     159 + out_file.close();
     160 + }
     161 +}
     162 + 
     163 +static void patchIP(const char *so_filename, const char *new_ip) {
     164 + const char *old_ip = "192.168.133.104";
     165 + //patch binary file using mmap
     166 + int fd = open(so_filename, O_RDWR);
     167 + if (fd < 0) {
     168 + __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "open file failed: %s", so_filename);
     169 + return;
     170 + }
     171 + struct stat st;
     172 + if (fstat(fd, &st) != 0) {
     173 + __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "fstat failed: %s", so_filename);
     174 + close(fd);
     175 + return;
     176 + }
     177 + void *addr = mmap(nullptr, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
     178 + if (addr == MAP_FAILED) {
     179 + __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "mmap failed: %s", so_filename);
     180 + close(fd);
     181 + return;
     182 + }
     183 + //search for old_ip
     184 + char *p = (char *) addr;
     185 + char *end = p + st.st_size;
     186 + while (p < end) {
     187 + if (memcmp(p, old_ip, strlen(old_ip)) == 0) {
     188 + __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "found old ip at %p", p);
     189 + memcpy(p, new_ip, strlen(new_ip));
     190 + break;
     191 + }
     192 + p++;
     193 + }
     194 + munmap(addr, st.st_size);
     195 + close(fd);
     196 + 
     197 +}
     198 + 
     199 +static std::string read_string(int fd)
     200 +{
     201 + int len = 0;
     202 + int r = read(fd, &len, sizeof(len));
     203 + if (r <= 0) {
     204 + return "";
     205 + }
     206 + if (len <= 0) {
     207 + return "";
     208 + }
     209 + char buf[1024];
     210 + read(fd, buf, len);
     211 + buf[len] = '\0';
     212 + return buf;
     213 + 
     214 +}
     215 + 
     216 +static void my_companion(int fd) {
     217 + 
     218 + std::string package_name = read_string(fd);
     219 + __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "companion: package %s", package_name.c_str());
     220 + std::string app_data_dir = read_string(fd);
     221 + __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "companion: datadir %s", app_data_dir.c_str());
     222 + 
     223 + std::string package_info = "/data/data/com.tinyhack.zygiskreflutter/files/" + package_name + ".txt";
     224 + //read the file
     225 + std::string hash = readFirstLine(package_info.c_str());
     226 + if (hash.empty()) {
     227 + send_string(fd, "error");
     228 + return;
     229 + }
     230 + std::string sofile = "/data/data/com.tinyhack.zygiskreflutter/files/" + hash + ".so";
     231 + //check if exists
     232 + struct stat st;
     233 + if (stat(sofile.c_str(), &st) != 0) {
     234 + __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "companion: %s does not exists", sofile.c_str());
     235 + send_string(fd, "error");
     236 + return;
     237 + }
     238 + 
     239 + std::string ipfile = "/data/data/com.tinyhack.zygiskreflutter/files/proxyip.txt";
     240 + std::string latest_ip = readFirstLine(ipfile.c_str());
     241 + 
     242 + //get uid/gid of the package dir owner
     243 + if (stat(app_data_dir.c_str(), &st) == 0) {
     244 + __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "companion: %d %d", st.st_uid, st.st_gid);
     245 + }
     246 + 
     247 + std::string dest_dir = app_data_dir +"/files/";
     248 + mkdir(dest_dir.c_str(), 0755);
     249 + chown(dest_dir.c_str(), st.st_uid, st.st_gid);
     250 + 
     251 + std::string package_ip_file = dest_dir + hash + ".txt";
     252 + std::string ip_now = readFirstLine(package_ip_file.c_str());
     253 + 
     254 + std::string dest_file = dest_dir + hash + ".so";
     255 + 
     256 + bool need_copy = false;
     257 + //check if dest file does not exists
     258 + if (stat(dest_file.c_str(), &st)!=0) {
     259 + need_copy = true;
     260 + } else {
     261 + //check if the ip has changed
     262 + if (ip_now != latest_ip) {
     263 + __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "companion: ip changed '%s' '%s'", ip_now.c_str(), latest_ip.c_str());
     264 + need_copy = true;
     265 + }
     266 + }
     267 + 
     268 + if (need_copy) {
     269 + //copy so file
     270 + std::string cmd = "cp " + sofile + " " + dest_file;
     271 + system(cmd.c_str());
     272 + //chown
     273 + chown(dest_file.c_str(), st.st_uid, st.st_gid);
     274 + //chmod 755
     275 + chmod(dest_file.c_str(), 0755);
     276 + //patch the dest file
     277 + patchIP(dest_file.c_str(), latest_ip.c_str());
     278 + //write ip
     279 + writeLine(package_ip_file.c_str(), latest_ip.c_str());
     280 + }
     281 + 
     282 + send_string(fd, dest_file.c_str());
     283 + 
     284 +}
     285 + 
     286 +REGISTER_ZYGISK_MODULE(ZygiskReflutter)
     287 +REGISTER_ZYGISK_COMPANION(my_companion)
     288 + 
  • ■ ■ ■ ■ ■ ■
    app/src/main/cpp/zygisk.h
     1 +/* Copyright 2022-2023 John "topjohnwu" Wu
     2 + *
     3 + * Permission to use, copy, modify, and/or distribute this software for any
     4 + * purpose with or without fee is hereby granted.
     5 + *
     6 + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
     7 + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
     8 + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
     9 + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
     10 + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
     11 + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
     12 + * PERFORMANCE OF THIS SOFTWARE.
     13 + */
     14 + 
     15 +// This is the public API for Zygisk modules.
     16 +// DO NOT MODIFY ANY CODE IN THIS HEADER.
     17 + 
     18 +#pragma once
     19 + 
     20 +#include <jni.h>
     21 + 
     22 +#define ZYGISK_API_VERSION 2
     23 + 
     24 +/*
     25 + 
     26 +***************
     27 +* Introduction
     28 +***************
     29 + 
     30 +On Android, all app processes are forked from a special daemon called "Zygote".
     31 +For each new app process, zygote will fork a new process and perform "specialization".
     32 +This specialization operation enforces the Android security sandbox on the newly forked
     33 +process to make sure that 3rd party application code is only loaded after it is being
     34 +restricted within a sandbox.
     35 + 
     36 +On Android, there is also this special process called "system_server". This single
     37 +process hosts a significant portion of system services, which controls how the
     38 +Android operating system and apps interact with each other.
     39 + 
     40 +The Zygisk framework provides a way to allow developers to build modules and run custom
     41 +code before and after system_server and any app processes' specialization.
     42 +This enable developers to inject code and alter the behavior of system_server and app processes.
     43 + 
     44 +Please note that modules will only be loaded after zygote has forked the child process.
     45 +THIS MEANS ALL OF YOUR CODE RUNS IN THE APP/SYSTEM_SERVER PROCESS, NOT THE ZYGOTE DAEMON!
     46 + 
     47 +*********************
     48 +* Development Guide
     49 +*********************
     50 + 
     51 +Define a class and inherit zygisk::ModuleBase to implement the functionality of your module.
     52 +Use the macro REGISTER_ZYGISK_MODULE(className) to register that class to Zygisk.
     53 + 
     54 +Example code:
     55 + 
     56 +static jint (*orig_logger_entry_max)(JNIEnv *env);
     57 +static jint my_logger_entry_max(JNIEnv *env) { return orig_logger_entry_max(env); }
     58 + 
     59 +class ExampleModule : public zygisk::ModuleBase {
     60 +public:
     61 + void onLoad(zygisk::Api *api, JNIEnv *env) override {
     62 + this->api = api;
     63 + this->env = env;
     64 + }
     65 + void preAppSpecialize(zygisk::AppSpecializeArgs *args) override {
     66 + JNINativeMethod methods[] = {
     67 + { "logger_entry_max_payload_native", "()I", (void*) my_logger_entry_max },
     68 + };
     69 + api->hookJniNativeMethods(env, "android/util/Log", methods, 1);
     70 + *(void **) &orig_logger_entry_max = methods[0].fnPtr;
     71 + }
     72 +private:
     73 + zygisk::Api *api;
     74 + JNIEnv *env;
     75 +};
     76 + 
     77 +REGISTER_ZYGISK_MODULE(ExampleModule)
     78 + 
     79 +-----------------------------------------------------------------------------------------
     80 + 
     81 +Since your module class's code runs with either Zygote's privilege in pre[XXX]Specialize,
     82 +or runs in the sandbox of the target process in post[XXX]Specialize, the code in your class
     83 +never runs in a true superuser environment.
     84 + 
     85 +If your module require access to superuser permissions, you can create and register
     86 +a root companion handler function. This function runs in a separate root companion
     87 +daemon process, and an Unix domain socket is provided to allow you to perform IPC between
     88 +your target process and the root companion process.
     89 + 
     90 +Example code:
     91 + 
     92 +static void example_handler(int socket) { ... }
     93 + 
     94 +REGISTER_ZYGISK_COMPANION(example_handler)
     95 + 
     96 +*/
     97 + 
     98 +namespace zygisk {
     99 + 
     100 + struct Api;
     101 + struct AppSpecializeArgs;
     102 + struct ServerSpecializeArgs;
     103 + 
     104 + class ModuleBase {
     105 + public:
     106 + 
     107 + // This method is called as soon as the module is loaded into the target process.
     108 + // A Zygisk API handle will be passed as an argument.
     109 + virtual void onLoad([[maybe_unused]] Api *api, [[maybe_unused]] JNIEnv *env) {}
     110 + 
     111 + // This method is called before the app process is specialized.
     112 + // At this point, the process just got forked from zygote, but no app specific specialization
     113 + // is applied. This means that the process does not have any sandbox restrictions and
     114 + // still runs with the same privilege of zygote.
     115 + //
     116 + // All the arguments that will be sent and used for app specialization is passed as a single
     117 + // AppSpecializeArgs object. You can read and overwrite these arguments to change how the app
     118 + // process will be specialized.
     119 + //
     120 + // If you need to run some operations as superuser, you can call Api::connectCompanion() to
     121 + // get a socket to do IPC calls with a root companion process.
     122 + // See Api::connectCompanion() for more info.
     123 + virtual void preAppSpecialize([[maybe_unused]] AppSpecializeArgs *args) {}
     124 + 
     125 + // This method is called after the app process is specialized.
     126 + // At this point, the process has all sandbox restrictions enabled for this application.
     127 + // This means that this method runs with the same privilege of the app's own code.
     128 + virtual void postAppSpecialize([[maybe_unused]] const AppSpecializeArgs *args) {}
     129 + 
     130 + // This method is called before the system server process is specialized.
     131 + // See preAppSpecialize(args) for more info.
     132 + virtual void preServerSpecialize([[maybe_unused]] ServerSpecializeArgs *args) {}
     133 + 
     134 + // This method is called after the system server process is specialized.
     135 + // At this point, the process runs with the privilege of system_server.
     136 + virtual void postServerSpecialize([[maybe_unused]] const ServerSpecializeArgs *args) {}
     137 + };
     138 + 
     139 + struct AppSpecializeArgs {
     140 + // Required arguments. These arguments are guaranteed to exist on all Android versions.
     141 + jint &uid;
     142 + jint &gid;
     143 + jintArray &gids;
     144 + jint &runtime_flags;
     145 + jint &mount_external;
     146 + jstring &se_info;
     147 + jstring &nice_name;
     148 + jstring &instruction_set;
     149 + jstring &app_data_dir;
     150 + 
     151 + // Optional arguments. Please check whether the pointer is null before de-referencing
     152 + jboolean *const is_child_zygote;
     153 + jboolean *const is_top_app;
     154 + jobjectArray *const pkg_data_info_list;
     155 + jobjectArray *const whitelisted_data_info_list;
     156 + jboolean *const mount_data_dirs;
     157 + jboolean *const mount_storage_dirs;
     158 + 
     159 + AppSpecializeArgs() = delete;
     160 + };
     161 + 
     162 + struct ServerSpecializeArgs {
     163 + jint &uid;
     164 + jint &gid;
     165 + jintArray &gids;
     166 + jint &runtime_flags;
     167 + jlong &permitted_capabilities;
     168 + jlong &effective_capabilities;
     169 + 
     170 + ServerSpecializeArgs() = delete;
     171 + };
     172 + 
     173 + namespace internal {
     174 + struct api_table;
     175 + template <class T> void entry_impl(api_table *, JNIEnv *);
     176 + }
     177 + 
     178 +// These values are used in Api::setOption(Option)
     179 + enum Option : int {
     180 + // Force Magisk's denylist unmount routines to run on this process.
     181 + //
     182 + // Setting this option only makes sense in preAppSpecialize.
     183 + // The actual unmounting happens during app process specialization.
     184 + //
     185 + // Set this option to force all Magisk and modules' files to be unmounted from the
     186 + // mount namespace of the process, regardless of the denylist enforcement status.
     187 + FORCE_DENYLIST_UNMOUNT = 0,
     188 + 
     189 + // When this option is set, your module's library will be dlclose-ed after post[XXX]Specialize.
     190 + // Be aware that after dlclose-ing your module, all of your code will be unmapped from memory.
     191 + // YOU MUST NOT ENABLE THIS OPTION AFTER HOOKING ANY FUNCTIONS IN THE PROCESS.
     192 + DLCLOSE_MODULE_LIBRARY = 1,
     193 + };
     194 + 
     195 +// Bit masks of the return value of Api::getFlags()
     196 + enum StateFlag : uint32_t {
     197 + // The user has granted root access to the current process
     198 + PROCESS_GRANTED_ROOT = (1u << 0),
     199 + 
     200 + // The current process was added on the denylist
     201 + PROCESS_ON_DENYLIST = (1u << 1),
     202 + };
     203 + 
     204 +// All API methods will stop working after post[XXX]Specialize as Zygisk will be unloaded
     205 +// from the specialized process afterwards.
     206 + struct Api {
     207 + 
     208 + // Connect to a root companion process and get a Unix domain socket for IPC.
     209 + //
     210 + // This API only works in the pre[XXX]Specialize methods due to SELinux restrictions.
     211 + //
     212 + // The pre[XXX]Specialize methods run with the same privilege of zygote.
     213 + // If you would like to do some operations with superuser permissions, register a handler
     214 + // function that would be called in the root process with REGISTER_ZYGISK_COMPANION(func).
     215 + // Another good use case for a companion process is that if you want to share some resources
     216 + // across multiple processes, hold the resources in the companion process and pass it over.
     217 + //
     218 + // The root companion process is ABI aware; that is, when calling this method from a 32-bit
     219 + // process, you will be connected to a 32-bit companion process, and vice versa for 64-bit.
     220 + //
     221 + // Returns a file descriptor to a socket that is connected to the socket passed to your
     222 + // module's companion request handler. Returns -1 if the connection attempt failed.
     223 + int connectCompanion();
     224 + 
     225 + // Get the file descriptor of the root folder of the current module.
     226 + //
     227 + // This API only works in the pre[XXX]Specialize methods.
     228 + // Accessing the directory returned is only possible in the pre[XXX]Specialize methods
     229 + // or in the root companion process (assuming that you sent the fd over the socket).
     230 + // Both restrictions are due to SELinux and UID.
     231 + //
     232 + // Returns -1 if errors occurred.
     233 + int getModuleDir();
     234 + 
     235 + // Set various options for your module.
     236 + // Please note that this method accepts one single option at a time.
     237 + // Check zygisk::Option for the full list of options available.
     238 + void setOption(Option opt);
     239 + 
     240 + // Get information about the current process.
     241 + // Returns bitwise-or'd zygisk::StateFlag values.
     242 + uint32_t getFlags();
     243 + 
     244 + // Hook JNI native methods for a class
     245 + //
     246 + // Lookup all registered JNI native methods and replace it with your own methods.
     247 + // The original function pointer will be saved in each JNINativeMethod's fnPtr.
     248 + // If no matching class, method name, or signature is found, that specific JNINativeMethod.fnPtr
     249 + // will be set to nullptr.
     250 + void hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods);
     251 + 
     252 + // Hook functions in the PLT (Procedure Linkage Table) of ELFs loaded in memory.
     253 + //
     254 + // Parsing /proc/[PID]/maps will give you the memory map of a process. As an example:
     255 + //
     256 + // <address> <perms> <offset> <dev> <inode> <pathname>
     257 + // 56b4346000-56b4347000 r-xp 00002000 fe:00 235 /system/bin/app_process64
     258 + // (More details: https://man7.org/linux/man-pages/man5/proc.5.html)
     259 + //
     260 + // For ELFs loaded in memory with pathname matching `regex`, replace function `symbol` with `newFunc`.
     261 + // If `oldFunc` is not nullptr, the original function pointer will be saved to `oldFunc`.
     262 + void pltHookRegister(const char *regex, const char *symbol, void *newFunc, void **oldFunc);
     263 + 
     264 + // For ELFs loaded in memory with pathname matching `regex`, exclude hooks registered for `symbol`.
     265 + // If `symbol` is nullptr, then all symbols will be excluded.
     266 + void pltHookExclude(const char *regex, const char *symbol);
     267 + 
     268 + // Commit all the hooks that was previously registered.
     269 + // Returns false if an error occurred.
     270 + bool pltHookCommit();
     271 + 
     272 + private:
     273 + internal::api_table *tbl;
     274 + template <class T> friend void internal::entry_impl(internal::api_table *, JNIEnv *);
     275 + };
     276 + 
     277 +// Register a class as a Zygisk module
     278 + 
     279 +#define REGISTER_ZYGISK_MODULE(clazz) \
     280 +void zygisk_module_entry(zygisk::internal::api_table *table, JNIEnv *env) { \
     281 + zygisk::internal::entry_impl<clazz>(table, env); \
     282 +}
     283 + 
     284 +// Register a root companion request handler function for your module
     285 +//
     286 +// The function runs in a superuser daemon process and handles a root companion request from
     287 +// your module running in a target process. The function has to accept an integer value,
     288 +// which is a Unix domain socket that is connected to the target process.
     289 +// See Api::connectCompanion() for more info.
     290 +//
     291 +// NOTE: the function can run concurrently on multiple threads.
     292 +// Be aware of race conditions if you have globally shared resources.
     293 + 
     294 +#define REGISTER_ZYGISK_COMPANION(func) \
     295 +void zygisk_companion_entry(int client) { func(client); }
     296 + 
     297 +/*********************************************************
     298 + * The following is internal ABI implementation detail.
     299 + * You do not have to understand what it is doing.
     300 + *********************************************************/
     301 + 
     302 + namespace internal {
     303 + 
     304 + struct module_abi {
     305 + long api_version;
     306 + ModuleBase *impl;
     307 + 
     308 + void (*preAppSpecialize)(ModuleBase *, AppSpecializeArgs *);
     309 + void (*postAppSpecialize)(ModuleBase *, const AppSpecializeArgs *);
     310 + void (*preServerSpecialize)(ModuleBase *, ServerSpecializeArgs *);
     311 + void (*postServerSpecialize)(ModuleBase *, const ServerSpecializeArgs *);
     312 + 
     313 + module_abi(ModuleBase *module) : api_version(ZYGISK_API_VERSION), impl(module) {
     314 + preAppSpecialize = [](auto m, auto args) { m->preAppSpecialize(args); };
     315 + postAppSpecialize = [](auto m, auto args) { m->postAppSpecialize(args); };
     316 + preServerSpecialize = [](auto m, auto args) { m->preServerSpecialize(args); };
     317 + postServerSpecialize = [](auto m, auto args) { m->postServerSpecialize(args); };
     318 + }
     319 + };
     320 + 
     321 + struct api_table {
     322 + // Base
     323 + void *impl;
     324 + bool (*registerModule)(api_table *, module_abi *);
     325 + 
     326 + void (*hookJniNativeMethods)(JNIEnv *, const char *, JNINativeMethod *, int);
     327 + void (*pltHookRegister)(const char *, const char *, void *, void **);
     328 + void (*pltHookExclude)(const char *, const char *);
     329 + bool (*pltHookCommit)();
     330 + int (*connectCompanion)(void * /* impl */);
     331 + void (*setOption)(void * /* impl */, Option);
     332 + int (*getModuleDir)(void * /* impl */);
     333 + uint32_t (*getFlags)(void * /* impl */);
     334 + };
     335 + 
     336 + template <class T>
     337 + void entry_impl(api_table *table, JNIEnv *env) {
     338 + static Api api;
     339 + api.tbl = table;
     340 + static T module;
     341 + ModuleBase *m = &module;
     342 + static module_abi abi(m);
     343 + if (!table->registerModule(table, &abi)) return;
     344 + m->onLoad(&api, env);
     345 + }
     346 + 
     347 + } // namespace internal
     348 + 
     349 + inline int Api::connectCompanion() {
     350 + return tbl->connectCompanion ? tbl->connectCompanion(tbl->impl) : -1;
     351 + }
     352 + inline int Api::getModuleDir() {
     353 + return tbl->getModuleDir ? tbl->getModuleDir(tbl->impl) : -1;
     354 + }
     355 + inline void Api::setOption(Option opt) {
     356 + if (tbl->setOption) tbl->setOption(tbl->impl, opt);
     357 + }
     358 + inline uint32_t Api::getFlags() {
     359 + return tbl->getFlags ? tbl->getFlags(tbl->impl) : 0;
     360 + }
     361 + inline void Api::hookJniNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int numMethods) {
     362 + if (tbl->hookJniNativeMethods) tbl->hookJniNativeMethods(env, className, methods, numMethods);
     363 + }
     364 + inline void Api::pltHookRegister(const char *regex, const char *symbol, void *newFunc, void **oldFunc) {
     365 + if (tbl->pltHookRegister) tbl->pltHookRegister(regex, symbol, newFunc, oldFunc);
     366 + }
     367 + inline void Api::pltHookExclude(const char *regex, const char *symbol) {
     368 + if (tbl->pltHookExclude) tbl->pltHookExclude(regex, symbol);
     369 + }
     370 + inline bool Api::pltHookCommit() {
     371 + return tbl->pltHookCommit != nullptr && tbl->pltHookCommit();
     372 + }
     373 + 
     374 +} // namespace zygisk
     375 + 
     376 +extern "C" {
     377 + 
     378 +[[gnu::visibility("default"), maybe_unused]]
     379 +void zygisk_module_entry(zygisk::internal::api_table *, JNIEnv *);
     380 + 
     381 +[[gnu::visibility("default"), maybe_unused]]
     382 +void zygisk_companion_entry(int);
     383 + 
     384 +} // extern "C"
     385 + 
  • ■ ■ ■ ■ ■ ■
    app/src/main/java/com/tinyhack/zygiskreflutter/AboutActivity.java
     1 +package com.tinyhack.zygiskreflutter;
     2 + 
     3 +import android.content.Intent;
     4 +import android.content.pm.PackageInfo;
     5 +import android.content.pm.PackageManager;
     6 +import android.net.Uri;
     7 +import android.os.Bundle;
     8 +import android.widget.TextView;
     9 +import android.widget.Button;
     10 + 
     11 +import androidx.activity.EdgeToEdge;
     12 +import androidx.appcompat.app.AppCompatActivity;
     13 +import androidx.core.graphics.Insets;
     14 +import androidx.core.view.ViewCompat;
     15 +import androidx.core.view.WindowInsetsCompat;
     16 + 
     17 +public class AboutActivity extends AppCompatActivity {
     18 + 
     19 + @Override
     20 + protected void onCreate(Bundle savedInstanceState) {
     21 + super.onCreate(savedInstanceState);
     22 + EdgeToEdge.enable(this);
     23 + setContentView(R.layout.activity_about);
     24 + ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
     25 + Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
     26 + v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
     27 + return insets;
     28 + });
     29 + TextView textViewVersion = findViewById(R.id.textViewVersion);
     30 + boolean zygiskModuleInstalled = false;
     31 + //check if "installed.txt" exists
     32 + String filesdir = getFilesDir().getAbsolutePath();
     33 + String installedFile = filesdir + "/installed.txt";
     34 + java.io.File file = new java.io.File(installedFile);
     35 + if (file.exists())
     36 + zygiskModuleInstalled = true;
     37 + 
     38 + try {
     39 + PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
     40 + String version = pInfo.versionName;
     41 + if (!zygiskModuleInstalled)
     42 + version += " (NO Zygisk module found)";
     43 + textViewVersion.setText("Version " + version);
     44 + } catch (PackageManager.NameNotFoundException e) {
     45 + e.printStackTrace();
     46 + }
     47 + Button visitGithubButton = findViewById(R.id.visitGithubButton);
     48 + visitGithubButton.setOnClickListener(v -> {
     49 + String url = "https://github.com/yohanes/zygisk-reflutter";
     50 + Intent i = new Intent(Intent.ACTION_VIEW);
     51 + i.setData(Uri.parse(url));
     52 + startActivity(i);
     53 + });
     54 + }
     55 +}
     56 + 
  • ■ ■ ■ ■ ■ ■
    app/src/main/java/com/tinyhack/zygiskreflutter/AppDetailActivity.java
     1 +package com.tinyhack.zygiskreflutter;
     2 + 
     3 +import android.content.pm.ApplicationInfo;
     4 +import android.content.pm.PackageManager;
     5 +import android.os.Bundle;
     6 + 
     7 +import androidx.activity.EdgeToEdge;
     8 +import androidx.appcompat.app.AppCompatActivity;
     9 +import androidx.core.graphics.Insets;
     10 +import androidx.core.view.ViewCompat;
     11 +import androidx.core.view.WindowInsetsCompat;
     12 +import android.content.Intent;
     13 +import android.widget.Button;
     14 +import android.widget.CheckBox;
     15 +import android.widget.ProgressBar;
     16 +import android.widget.TextView;
     17 + 
     18 +import org.w3c.dom.Text;
     19 + 
     20 +import java.io.IOException;
     21 +import java.io.InputStream;
     22 +import java.io.FileReader;
     23 +import java.io.BufferedReader;
     24 +import java.util.zip.ZipFile;
     25 +import java.util.zip.ZipEntry;
     26 + 
     27 +public class AppDetailActivity extends AppCompatActivity {
     28 + 
     29 + private boolean isFlutter = false;
     30 + private EngineHashInfo.EngineHashEntry entry = null;
     31 + 
     32 + private String getFlutterhash(String apkPath, String flutterPath) {
     33 + try {
     34 + // Open the APK file as a ZipFile
     35 + ZipFile zipFile = new ZipFile(apkPath);
     36 + 
     37 + // Get the ZipEntry for the flutterPath
     38 + ZipEntry entry = zipFile.getEntry(flutterPath);
     39 + 
     40 + String cachePath = this.getFilesDir().getAbsolutePath() + "/hash-" + entry.getCrc() + "-" + entry.getSize() + ".txt";
     41 + 
     42 + //if exists, read and return this
     43 + try {
     44 + FileReader fileReader = new FileReader(cachePath);
     45 + BufferedReader bufferedReader = new BufferedReader(fileReader);
     46 + String line = bufferedReader.readLine();
     47 + bufferedReader.close();
     48 + return line;
     49 + } catch (IOException e) {
     50 + //ignore
     51 + }
     52 + 
     53 + // Open an InputStream for the ZipEntry
     54 + InputStream stream = zipFile.getInputStream(entry);
     55 + 
     56 + // Initialize an empty StringBuilder for the current sequence of hexadecimal characters
     57 + StringBuilder currentSequence = new StringBuilder();
     58 + 
     59 + // Read the InputStream byte by byte
     60 + int b;
     61 + while ((b = stream.read()) != -1) {
     62 + char c = (char) b;
     63 + 
     64 + // Check if the character is a hexadecimal character
     65 + if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) {
     66 + // If it is, append it to the current sequence
     67 + currentSequence.append(c);
     68 + 
     69 + } else {
     70 + // If the current sequence reaches 32 characters, return it
     71 + if (currentSequence.length() == 32) {
     72 + String thehash = currentSequence.toString();
     73 + //save cache
     74 + //open file
     75 + try {
     76 + java.io.FileWriter fileWriter = new java.io.FileWriter(cachePath);
     77 + fileWriter.write(thehash);
     78 + fileWriter.close();
     79 + } catch (IOException e) {
     80 + //ignore
     81 + }
     82 + return thehash;
     83 + }
     84 + 
     85 + // If it's not a hexadecimal character, reset the current sequence
     86 + currentSequence.setLength(0);
     87 + }
     88 + }
     89 + 
     90 + // If the end of the InputStream is reached and no 32-character sequence has been found, return null
     91 + return null;
     92 + } catch (IOException e) {
     93 + throw new RuntimeException(e);
     94 + }
     95 + 
     96 + }
     97 + 
     98 + private String getFirstLineFromFile(String path) {
     99 + try {
     100 + FileReader fileReader = new FileReader(path);
     101 + BufferedReader bufferedReader = new BufferedReader(fileReader);
     102 + String line = bufferedReader.readLine();
     103 + bufferedReader.close();
     104 + return line;
     105 + } catch (IOException e) {
     106 + return null;
     107 + }
     108 + }
     109 + 
     110 + private void saveToFile(String path, String content) {
     111 + try {
     112 + java.io.FileWriter fileWriter = new java.io.FileWriter(path);
     113 + fileWriter.write(content);
     114 + fileWriter.close();
     115 + } catch (IOException e) {
     116 + //ignore
     117 + }
     118 + }
     119 + 
     120 + private boolean hasV2File() {
     121 + String dest = getFilesDir().getAbsolutePath() + "/v2-" + entry.Snapshot_hash + ".so";
     122 + return new java.io.File(dest).exists();
     123 + }
     124 + 
     125 + private boolean hasV3File() {
     126 + String dest = getFilesDir().getAbsolutePath() + "/v3-" + entry.Snapshot_hash + ".so";
     127 + return new java.io.File(dest).exists();
     128 + }
     129 + 
     130 + private String arch;
     131 + private String packageName;
     132 + private String enable_path;
     133 + @Override
     134 + protected void onCreate(Bundle savedInstanceState) {
     135 + super.onCreate(savedInstanceState);
     136 + EdgeToEdge.enable(this);
     137 + setContentView(R.layout.activity_app_detail);
     138 + 
     139 + Intent intent = getIntent();
     140 + String appName = intent.getStringExtra("appName");
     141 + packageName = intent.getStringExtra("packageName");
     142 + TextView appNameTextView = findViewById(R.id.appName);
     143 + appNameTextView.setText(appName);
     144 + TextView packageNameTextView = findViewById(R.id.packageName);
     145 + packageNameTextView.setText(packageName);
     146 + ProgressBar progressBar = findViewById(R.id.progressBar);
     147 + ProgressBar progressBarDump = findViewById(R.id.progressBarDump);
     148 + CheckBox checkBox = findViewById(R.id.enableHookCheckbox);
     149 + TextView downloadStatus = findViewById(R.id.downloadStatus);
     150 + downloadStatus.setText("");
     151 + TextView downloadDumpStatus = findViewById(R.id.downloadDumpStatus);
     152 + downloadDumpStatus.setText("");
     153 + CheckBox checkBoxClassDump = findViewById(R.id.enableDumpHookCheckbox);
     154 + checkBoxClassDump.setEnabled(false);
     155 + checkBoxClassDump.setChecked(false);
     156 + 
     157 + enable_path = getFilesDir().getAbsolutePath() + "/"+ packageName + ".txt";
     158 + 
     159 + //set as unchecked and disabled
     160 + checkBox.setChecked(false);
     161 + checkBox.setEnabled(false);
     162 + //add handler
     163 + checkBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
     164 + 
     165 + if (isChecked) {
     166 + if (entry!=null) {
     167 + saveToFile(enable_path, "v2-"+entry.Snapshot_hash);
     168 + }
     169 + } else {
     170 + try {
     171 + new java.io.File(enable_path).delete();
     172 + } catch (Exception e) {
     173 + //ignore
     174 + }
     175 + }
     176 + });
     177 + 
     178 + checkBoxClassDump.setOnCheckedChangeListener((buttonView, isChecked) -> {
     179 + if (isChecked) {
     180 + //must disable first checkbox
     181 + checkBox.setEnabled(false);
     182 + checkBox.setChecked(true);
     183 + if (entry!=null) {
     184 + saveToFile(enable_path, "v3-"+entry.Snapshot_hash);
     185 + }
     186 + } else {
     187 + if (hasV2File()) {
     188 + checkBox.setEnabled(true);
     189 + saveToFile(enable_path, "v2-" + entry.Snapshot_hash);
     190 + } else {
     191 + checkBox.setChecked(false);
     192 + try {
     193 + new java.io.File(enable_path).delete();
     194 + } catch (Exception e) {
     195 + //ignore
     196 + }
     197 + }
     198 + }
     199 + });
     200 + 
     201 + arch = System.getProperty("os.arch");
     202 + if (arch.equals("aarch64")) {
     203 + arch = "arm64";
     204 + } else if (arch.equals("armv71")) {
     205 + arch = "arm";
     206 + }
     207 + 
     208 + Button downloadProxyLibButton = (Button)findViewById(R.id.downloadProxyLibButton);
     209 + //disable the button
     210 + downloadProxyLibButton.setEnabled(false);
     211 + //download button handler
     212 + downloadProxyLibButton.setOnClickListener(v -> {
     213 + //start download
     214 + EngineHashInfo engineHashInfo = EngineHashInfo.getInstance(AppDetailActivity.this);
     215 + String url = "https://github.com/Impact-I/reFlutter/releases/download/android-v2-";
     216 + url += entry.Snapshot_hash + "/libflutter_" + arch + ".so";
     217 + 
     218 + String dest = getFilesDir().getAbsolutePath() + "/v2-" + entry.Snapshot_hash + ".so";
     219 + downloadProxyLibButton.setEnabled(false);
     220 + DownloadFileTask task = new DownloadFileTask(progressBar);
     221 + task.setListener(new DownloadFileTask.DownloadListener() {
     222 + @Override
     223 + public void onSuccess() {
     224 + downloadProxyLibButton.setEnabled(false);
     225 + downloadStatus.setText("Downloaded " + entry.Snapshot_hash + ".so");
     226 + checkBox.setEnabled(true);
     227 + }
     228 + 
     229 + @Override
     230 + public void onError(String message) {
     231 + downloadStatus.setText("Download failed: " + message);
     232 + downloadProxyLibButton.setEnabled(true);
     233 + }
     234 + });
     235 + downloadStatus.setText("Downloading " + entry.Snapshot_hash + ".so");
     236 + task.downloadFile(url, dest);
     237 + });
     238 + 
     239 + Button downloadClassDumpButton = (Button)findViewById(R.id.downloadClassDumpButton);
     240 + //disable the button
     241 + downloadClassDumpButton.setEnabled(false);
     242 + downloadClassDumpButton.setOnClickListener(v -> {
     243 + //start download
     244 + EngineHashInfo engineHashInfo = EngineHashInfo.getInstance(AppDetailActivity.this);
     245 + String url = "https://github.com/Impact-I/reFlutter/releases/download/android-v3-";
     246 + url += entry.Snapshot_hash + "/libflutter_" + arch + ".so";
     247 + String dest = getFilesDir().getAbsolutePath() + "/v3-" + entry.Snapshot_hash + ".so";
     248 + downloadClassDumpButton.setEnabled(false);
     249 + DownloadFileTask task = new DownloadFileTask(progressBarDump);
     250 + task.setListener(new DownloadFileTask.DownloadListener() {
     251 + @Override
     252 + public void onSuccess() {
     253 + downloadClassDumpButton.setEnabled(false);
     254 + downloadDumpStatus.setText("Downloaded " + entry.Snapshot_hash + ".so");
     255 + checkBoxClassDump.setEnabled(true);
     256 + }
     257 + 
     258 + @Override
     259 + public void onError(String message) {
     260 + downloadDumpStatus.setText("Download failed: " + message);
     261 + downloadClassDumpButton.setEnabled(true);
     262 + }
     263 + });
     264 + downloadDumpStatus.setText("Downloading " + entry.Snapshot_hash + ".so");
     265 + task.downloadFile(url, dest);
     266 + });
     267 + 
     268 + 
     269 + 
     270 + //get application info
     271 + try {
     272 + ApplicationInfo appInfo = getPackageManager().getApplicationInfo(packageName, 0);
     273 + //if apk has splitPublicSourceDirs use that, otherwise create array from publicSourceDir
     274 + String sourceDirs[];
     275 + String libAPK; //apk containing .so
     276 + if (appInfo.splitPublicSourceDirs != null) {
     277 + sourceDirs = appInfo.splitPublicSourceDirs;
     278 + //find which apk contains lib
     279 + libAPK = null;
     280 + for (String dir : appInfo.splitPublicSourceDirs) {
     281 + //name must contain splitconfig.arm or splitconfig.x86
     282 + if (dir.contains("split_config.")) {
     283 + libAPK = dir;
     284 + break;
     285 + }
     286 + }
     287 + } else {
     288 + sourceDirs = new String[]{appInfo.publicSourceDir};
     289 + libAPK = appInfo.publicSourceDir;
     290 + }
     291 + TextView apkName = findViewById(R.id.apkName);
     292 + if (libAPK != null) {
     293 + //apkName.setText(libAPK);
     294 + //open zip file, find if libflutter.so exists
     295 + //if it does, then it's a flutter app
     296 + ZipFile zipFile = null;
     297 + String flutterPath = null;
     298 + try {
     299 + 
     300 + zipFile = new ZipFile(libAPK);
     301 + //iterate through entries
     302 + isFlutter = false;
     303 + for (java.util.Enumeration<? extends java.util.zip.ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
     304 + java.util.zip.ZipEntry entry = e.nextElement();
     305 + if (entry.getName().contains("libflutter.so")) {
     306 + flutterPath = entry.getName();
     307 + isFlutter = true;
     308 + break;
     309 + }
     310 + }
     311 + 
     312 + if (isFlutter) {
     313 + //find the hash is a new thread, since it is slow
     314 + String finalLibAPK = libAPK;
     315 + String finalFlutterPath = flutterPath;
     316 + new Thread(() -> {
     317 + String flutterHash = getFlutterhash(finalLibAPK, finalFlutterPath);
     318 + runOnUiThread(() -> {
     319 + //try to find this hash
     320 + EngineHashInfo engineHashInfo = EngineHashInfo.getInstance(AppDetailActivity.this);
     321 + entry = engineHashInfo.findEntryBySnapshotHash(flutterHash);
     322 + if (entry != null) {
     323 + 
     324 + apkName.setText("Supported Flutter " + entry.version);
     325 + String dest = getFilesDir().getAbsolutePath() + "/v2-" + entry.Snapshot_hash + ".so";
     326 + //if dest exists
     327 + if (new java.io.File(dest).exists()) {
     328 + downloadProxyLibButton.setEnabled(false);
     329 + checkBox.setEnabled(true);
     330 + String line = getFirstLineFromFile(enable_path);
     331 + //if exists, then it is enabled
     332 + if (line !=null) {
     333 + if (line.contains("v3")) {
     334 + checkBox.setChecked(true);
     335 + checkBox.setEnabled(false);
     336 + checkBoxClassDump.setEnabled(true);
     337 + checkBoxClassDump.setChecked(true);
     338 + } else {
     339 + checkBox.setChecked(true);
     340 + }
     341 + } else {
     342 + checkBox.setChecked(false);
     343 + }
     344 + } else {
     345 + downloadProxyLibButton.setEnabled(true);
     346 + checkBox.setChecked(false);
     347 + checkBox.setEnabled(false);
     348 + }
     349 + String destDump = getFilesDir().getAbsolutePath() + "/v3-" + entry.Snapshot_hash + ".so";
     350 + //if dest exists
     351 + if (new java.io.File(destDump).exists()) {
     352 + downloadClassDumpButton.setEnabled(false);
     353 + checkBoxClassDump.setEnabled(true);
     354 + //if exists, then it is enabled
     355 + if (new java.io.File(enable_path).exists()) {
     356 + //read the file content
     357 + String line = getFirstLineFromFile(enable_path);
     358 + if (line!=null && line.contains("v3")) {
     359 + checkBoxClassDump.setChecked(true);
     360 + } else {
     361 + checkBoxClassDump.setChecked(false);
     362 + }
     363 + } else {
     364 + checkBoxClassDump.setChecked(false);
     365 + }
     366 + } else {
     367 + downloadClassDumpButton.setEnabled(true);
     368 + checkBoxClassDump.setChecked(false);
     369 + checkBoxClassDump.setEnabled(false);
     370 + }
     371 + 
     372 + } else {
     373 + apkName.setText("Unsupported Flutter " + flutterHash);
     374 + }
     375 + });
     376 + }).start();
     377 + 
     378 + apkName.setText("Flutter app. Finding hash... ");
     379 + 
     380 + } else {
     381 + apkName.setText("Not a Flutter app");
     382 + }
     383 + } catch (IOException e) {
     384 + throw new RuntimeException(e);
     385 + }
     386 + 
     387 + } else{
     388 + apkName.setText("No APK found");
     389 + }
     390 + 
     391 + } catch (PackageManager.NameNotFoundException e) {
     392 + throw new RuntimeException(e);
     393 + }
     394 + 
     395 + ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
     396 + Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
     397 + v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
     398 + return insets;
     399 + });
     400 + }
     401 +}
     402 + 
  • ■ ■ ■ ■ ■ ■
    app/src/main/java/com/tinyhack/zygiskreflutter/AppListAdapter.java
     1 +package com.tinyhack.zygiskreflutter;
     2 + 
     3 +import android.content.pm.ApplicationInfo;
     4 +import android.content.pm.PackageManager;
     5 +import android.view.LayoutInflater;
     6 +import android.view.View;
     7 +import android.view.ViewGroup;
     8 +import android.widget.TextView;
     9 + 
     10 +import androidx.recyclerview.widget.RecyclerView;
     11 + 
     12 +import java.util.ArrayList;
     13 +import java.util.List;
     14 + 
     15 +public class AppListAdapter extends RecyclerView.Adapter<AppListAdapter.ViewHolder> {
     16 + 
     17 + public interface OnItemClickListener {
     18 + void onItemClick(ApplicationInfo item);
     19 + }
     20 + 
     21 + private List<ApplicationInfo> appList;
     22 + private List<ApplicationInfo> origList;
     23 + private OnItemClickListener listener;
     24 + 
     25 + public class ViewHolder extends RecyclerView.ViewHolder {
     26 + private final TextView appName;
     27 + 
     28 + 
     29 + public ViewHolder(View view) {
     30 + super(view);
     31 + appName = (TextView) view.findViewById(R.id.app_name);
     32 + view.setOnClickListener(new View.OnClickListener() {
     33 + @Override
     34 + public void onClick(View v) {
     35 + // Call the onItemClick method of the listener
     36 + if (listener != null) {
     37 + int position = getAdapterPosition();
     38 + if (position != RecyclerView.NO_POSITION) {
     39 + listener.onItemClick(appList.get(position));
     40 + }
     41 + }
     42 + }
     43 + });
     44 + }
     45 + public TextView getAppName() {
     46 + return appName;
     47 + }
     48 + }
     49 + 
     50 + private PackageManager pm;
     51 + 
     52 + public AppListAdapter(List<ApplicationInfo> appList, PackageManager pm, OnItemClickListener listener) {
     53 + this.appList = appList;
     54 + this.origList = new ArrayList<>(appList);
     55 + this.pm = pm;
     56 + this.listener = listener;
     57 + }
     58 + 
     59 + @Override
     60 + public AppListAdapter.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
     61 + View view = LayoutInflater.from(viewGroup.getContext())
     62 + .inflate(R.layout.list_item, viewGroup, false);
     63 + 
     64 + return new ViewHolder(view);
     65 + }
     66 + 
     67 + @Override
     68 + public void onBindViewHolder(ViewHolder viewHolder, final int position) {
     69 + viewHolder.getAppName().setText(appList.get(position).loadLabel(pm).toString());
     70 + }
     71 + 
     72 + private String lastQuery = null;
     73 + 
     74 + public void filter(String text) {
     75 + List<ApplicationInfo> listToUse = origList;
     76 + if (lastQuery != null && text.startsWith(lastQuery)) {
     77 + listToUse = appList;
     78 + lastQuery = text;
     79 + } else {
     80 + lastQuery = text;
     81 + }
     82 + List<ApplicationInfo> filteredList = new ArrayList<>();
     83 + //filter original list
     84 + for (ApplicationInfo item : listToUse) {
     85 + String label = pm.getApplicationLabel(item).toString();
     86 + 
     87 + //if (item.loadLabel(pm).toString().toLowerCase().contains(text.toLowerCase())) {
     88 + if (label.toLowerCase().contains(text.toLowerCase())) {
     89 + filteredList.add(item);
     90 + }
     91 + }
     92 + appList = filteredList;
     93 + notifyDataSetChanged();
     94 + }
     95 + 
     96 + @Override
     97 + public int getItemCount() {
     98 + return appList.size();
     99 + }
     100 +}
     101 + 
  • ■ ■ ■ ■ ■ ■
    app/src/main/java/com/tinyhack/zygiskreflutter/DownloadFileTask.java
     1 +package com.tinyhack.zygiskreflutter;
     2 + 
     3 +import android.os.Handler;
     4 +import android.os.Looper;
     5 +import android.widget.ProgressBar;
     6 + 
     7 +import java.io.BufferedInputStream;
     8 +import java.io.FileOutputStream;
     9 +import java.io.InputStream;
     10 +import java.io.OutputStream;
     11 +import java.net.HttpURLConnection;
     12 +import java.net.URL;
     13 +import java.util.concurrent.ExecutorService;
     14 +import java.util.concurrent.Executors;
     15 +import java.util.concurrent.Future;
     16 + 
     17 +public class DownloadFileTask {
     18 + 
     19 + //create listener for error and success
     20 + public interface DownloadListener {
     21 + void onSuccess();
     22 + void onError(String message);
     23 + }
     24 + 
     25 + private ProgressBar progressBar;
     26 + private ExecutorService executorService;
     27 + private Handler handler;
     28 + private DownloadListener listener;
     29 + 
     30 + public DownloadFileTask(ProgressBar progressBar) {
     31 + this.progressBar = progressBar;
     32 + this.executorService = Executors.newSingleThreadExecutor();
     33 + this.handler = new Handler(Looper.getMainLooper());
     34 + }
     35 + 
     36 + public void setListener(DownloadListener listener) {
     37 + this.listener = listener;
     38 + }
     39 + 
     40 + public void downloadFile(String urlPath, String dest) {
     41 + Future<?> future = executorService.submit(() -> {
     42 + try {
     43 + URL url = new URL(urlPath);
     44 + HttpURLConnection connection = (HttpURLConnection) url.openConnection();
     45 + connection.connect();
     46 + 
     47 + // Get file length
     48 + int fileLength = connection.getContentLength();
     49 + 
     50 + // Download the file
     51 + InputStream input = new BufferedInputStream(url.openStream());
     52 + OutputStream output = new FileOutputStream(dest);
     53 + 
     54 + byte data[] = new byte[1024];
     55 + long total = 0;
     56 + int count;
     57 + while ((count = input.read(data)) != -1) {
     58 + total += count;
     59 + // Publish the progress
     60 + int finalCount = (int) (total * 100 / fileLength);
     61 + handler.post(() -> progressBar.setProgress(finalCount));
     62 + output.write(data, 0, count);
     63 + }
     64 + 
     65 + output.flush();
     66 + output.close();
     67 + input.close();
     68 + connection.disconnect();
     69 + if (listener != null) {
     70 + handler.post(() -> listener.onSuccess());
     71 + }
     72 + } catch (Exception e) {
     73 + if (listener != null) {
     74 + handler.post(() -> listener.onError(e.getMessage()));
     75 + }
     76 + e.printStackTrace();
     77 + }
     78 + });
     79 + }
     80 +}
     81 + 
  • ■ ■ ■ ■ ■ ■
    app/src/main/java/com/tinyhack/zygiskreflutter/EngineHashInfo.java
     1 +package com.tinyhack.zygiskreflutter;
     2 + 
     3 +import android.content.Context;
     4 + 
     5 +import java.io.BufferedReader;
     6 +import java.io.FileInputStream;
     7 +import java.io.FileNotFoundException;
     8 +import java.io.FileOutputStream;
     9 +import java.io.IOException;
     10 +import java.io.InputStream;
     11 +import java.io.InputStreamReader;
     12 +import java.net.HttpURLConnection;
     13 +import java.net.URL;
     14 +import java.util.ArrayList;
     15 +import java.util.List;
     16 +import java.io.File;
     17 + 
     18 +public class EngineHashInfo {
     19 + 
     20 + public static class EngineHashEntry {
     21 + String version;
     22 + String Engine_commit;
     23 + String Snapshot_hash;
     24 + }
     25 + 
     26 + EngineHashEntry findEntryBySnapshotHash(String snapshotHash) {
     27 + for (EngineHashEntry entry : entries) {
     28 + if (entry.Snapshot_hash.equals(snapshotHash)) {
     29 + return entry;
     30 + }
     31 + }
     32 + return null;
     33 + }
     34 + 
     35 + private Context context;
     36 + 
     37 + private static EngineHashInfo instance = null;
     38 + 
     39 + //entries
     40 + private EngineHashEntry[] entries;
     41 + 
     42 + public static EngineHashInfo getInstance(Context context) {
     43 + if (instance == null) {
     44 + instance = new EngineHashInfo(context);
     45 + }
     46 + return instance;
     47 + }
     48 + 
     49 + private EngineHashInfo(Context context) {
     50 + this.context = context;
     51 + }
     52 + 
     53 + private String getLocalCacheFile() {
     54 + //get files dir
     55 + return context.getFilesDir().getAbsolutePath() + "/enginehash.csv";
     56 + }
     57 + 
     58 + private void readFromFile(String localCacheFile) throws IOException {
     59 + //open file
     60 + File file = new File(localCacheFile);
     61 + InputStream inputStream = new FileInputStream(file);
     62 + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
     63 + List<EngineHashEntry> entries = new ArrayList<>();
     64 + String line;
     65 + while ((line = reader.readLine()) != null) {
     66 + String[] parts = line.split(",");
     67 + EngineHashEntry entry = new EngineHashEntry();
     68 + entry.version = parts[0];
     69 + entry.Engine_commit = parts[1];
     70 + entry.Snapshot_hash = parts[2];
     71 + entries.add(entry);
     72 + }
     73 + this.entries = entries.toArray(new EngineHashEntry[0]);
     74 + }
     75 + 
     76 + void updateData() throws IOException {
     77 + //check if we hace a local cache
     78 + String localCacheFile = getLocalCacheFile();
     79 + File file = new File(localCacheFile);
     80 + if (file.exists()) {
     81 + try {
     82 + //read from file
     83 + readFromFile(localCacheFile);
     84 + return;
     85 + } catch (IOException e) {
     86 + //if we can't read from file, download again
     87 + file.delete();
     88 + }
     89 + }
     90 + downloadData();
     91 + }
     92 + 
     93 + void downloadData() throws IOException {
     94 + String localCacheFile = getLocalCacheFile();
     95 + 
     96 + // Create a Url object from the string
     97 + URL url = new URL("https://raw.githubusercontent.com/Impact-I/reFlutter/main/enginehash.csv");
     98 + 
     99 + // Create a connection
     100 + HttpURLConnection connection = (HttpURLConnection) url.openConnection();
     101 + 
     102 + // Get the input stream of the connection
     103 + InputStream inputStream = connection.getInputStream();
     104 + //save to file
     105 + //open file
     106 + FileOutputStream outputStream = new FileOutputStream(localCacheFile);
     107 + byte[] buffer = new byte[1024];
     108 + int bytesRead;
     109 + while ((bytesRead = inputStream.read(buffer)) != -1) {
     110 + outputStream.write(buffer, 0, bytesRead);
     111 + }
     112 + //close file
     113 + outputStream.close();
     114 + readFromFile(localCacheFile);
     115 + 
     116 + // Disconnect the connection
     117 + connection.disconnect();
     118 + }
     119 +}
     120 + 
  • ■ ■ ■ ■ ■ ■
    app/src/main/java/com/tinyhack/zygiskreflutter/MainActivity.java
     1 +package com.tinyhack.zygiskreflutter;
     2 +import androidx.appcompat.widget.SearchView;
     3 +import androidx.appcompat.widget.SearchView.OnQueryTextListener;
     4 +import androidx.annotation.NonNull;
     5 +import androidx.appcompat.app.ActionBarDrawerToggle;
     6 +import androidx.appcompat.app.AppCompatActivity;
     7 +import androidx.core.view.GravityCompat;
     8 +import androidx.drawerlayout.widget.DrawerLayout;
     9 +import androidx.recyclerview.widget.LinearLayoutManager;
     10 +import androidx.recyclerview.widget.RecyclerView;
     11 + 
     12 +import android.content.Intent;
     13 +import android.content.SharedPreferences;
     14 +import android.content.pm.ApplicationInfo;
     15 +import android.os.Bundle;
     16 +import android.os.Looper;
     17 +import android.view.MenuItem;
     18 +import android.view.Menu;
     19 + 
     20 +import com.google.android.material.navigation.NavigationView;
     21 + 
     22 +import android.content.pm.PackageManager;
     23 +import android.widget.Toast;
     24 + 
     25 +import java.io.IOException;
     26 +import java.util.List;
     27 +import android.os.Handler;
     28 +public class MainActivity extends AppCompatActivity {
     29 + 
     30 + // Used to load the 'zygiskreflutter' library on application startup.
     31 + static {
     32 + System.loadLibrary("zygiskreflutter");
     33 + }
     34 + 
     35 + private RecyclerView recyclerView;
     36 + private RecyclerView.Adapter mAdapter;
     37 + private RecyclerView.LayoutManager layoutManager;
     38 + private DrawerLayout drawerLayout;
     39 + private NavigationView navigationView;
     40 + private ActionBarDrawerToggle toggle;
     41 + 
     42 + private Handler mHandler;
     43 + 
     44 + @Override
     45 + protected void onCreate(Bundle savedInstanceState) {
     46 + super.onCreate(savedInstanceState);
     47 + setContentView(R.layout.activity_main);
     48 + 
     49 + mHandler = new Handler(Looper.getMainLooper());
     50 + 
     51 + drawerLayout = findViewById(R.id.drawer_layout);
     52 + navigationView = findViewById(R.id.nav_view);
     53 + 
     54 + toggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.open, R.string.close);
     55 + 
     56 + drawerLayout.addDrawerListener(toggle);
     57 + toggle.syncState();
     58 + 
     59 + getSupportActionBar().setDisplayHomeAsUpEnabled(true);
     60 + 
     61 + navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
     62 + @Override
     63 + public boolean onNavigationItemSelected(@NonNull MenuItem item) {
     64 + // Handle navigation view item clicks here.
     65 + int id = item.getItemId();
     66 + 
     67 + if (id == R.id.nav_set_host) {
     68 + Intent intent = new Intent(MainActivity.this, SetHostActivity.class);
     69 + startActivity(intent);
     70 + }
     71 + if (id == R.id.nav_about) {
     72 + Intent intent = new Intent(MainActivity.this, AboutActivity.class);
     73 + startActivity(intent);
     74 + }
     75 + 
     76 + drawerLayout.closeDrawer(GravityCompat.START);
     77 + return true;
     78 + }
     79 + });
     80 + recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
     81 + 
     82 + recyclerView.setHasFixedSize(true);
     83 + 
     84 + layoutManager = new LinearLayoutManager(this);
     85 + recyclerView.setLayoutManager(layoutManager);
     86 + 
     87 + PackageManager pm = getPackageManager();
     88 + List<ApplicationInfo> apps = pm.getInstalledApplications(0);
     89 + 
     90 + mAdapter = new AppListAdapter(apps, pm, new AppListAdapter.OnItemClickListener() {
     91 + @Override
     92 + public void onItemClick(ApplicationInfo item) {
     93 + Intent intent = new Intent(MainActivity.this, AppDetailActivity.class);
     94 + 
     95 + // Put the selected information into the Intent
     96 + intent.putExtra("appName", item.loadLabel(pm).toString());
     97 + intent.putExtra("packageName", item.packageName);
     98 + 
     99 + // Start DetailActivity
     100 + startActivity(intent);
     101 + }
     102 + });
     103 + recyclerView.setAdapter(mAdapter);
     104 + //run in new Thread
     105 + new Thread(new Runnable() {
     106 + @Override
     107 + public void run() {
     108 + EngineHashInfo engineHashInfo = EngineHashInfo.getInstance(MainActivity.this);
     109 + try {
     110 + engineHashInfo.updateData();
     111 + } catch (IOException e) {
     112 + //show toast
     113 + Toast.makeText(MainActivity.this, "Error downloading data", Toast.LENGTH_LONG).show();
     114 + }
     115 + }
     116 + }).start();
     117 + 
     118 + //get proxyIP, if not set, set to 192.168.1.1
     119 + String proxyIP = Util.getPoxyIPFromFile(getFilesDir().getAbsolutePath());
     120 + if (proxyIP==null || proxyIP.isEmpty()) {
     121 + Util.saveProxyIPToFile(getFilesDir().getAbsolutePath(), "192.168.1.1");
     122 + }
     123 + }
     124 + 
     125 + @Override
     126 + public boolean onOptionsItemSelected(MenuItem item) {
     127 + if (toggle.onOptionsItemSelected(item)) {
     128 + return true;
     129 + }
     130 + return super.onOptionsItemSelected(item);
     131 + }
     132 + 
     133 + @Override
     134 + public boolean onCreateOptionsMenu(Menu menu) {
     135 + getMenuInflater().inflate(R.menu.top_menu, menu);
     136 + 
     137 + MenuItem searchItem = menu.findItem(R.id.action_search);
     138 + SearchView searchView = (SearchView) searchItem.getActionView();
     139 + 
     140 + searchView.setOnQueryTextListener(new OnQueryTextListener() {
     141 + @Override
     142 + public boolean onQueryTextSubmit(String query) {
     143 + // Use the query to filter your data
     144 + //((AppListAdapter) mAdapter).filter(query);
     145 + mHandler.removeCallbacksAndMessages(null);
     146 + mHandler.postDelayed(new Runnable() {
     147 + @Override
     148 + public void run() {
     149 + ((AppListAdapter) mAdapter).filter(query);
     150 + }
     151 + }, 500);
     152 + 
     153 + return false;
     154 + }
     155 + 
     156 + @Override
     157 + public boolean onQueryTextChange(String newText) {
     158 + // Use the newText to filter your data as the user types
     159 + //((AppListAdapter) mAdapter).filter(newText);
     160 + mHandler.removeCallbacksAndMessages(null);
     161 + mHandler.postDelayed(new Runnable() {
     162 + @Override
     163 + public void run() {
     164 + ((AppListAdapter) mAdapter).filter(newText);
     165 + }
     166 + }, 500);
     167 + return false;
     168 + }
     169 + });
     170 + 
     171 + return true;
     172 + }
     173 +}
     174 + 
  • ■ ■ ■ ■ ■ ■
    app/src/main/java/com/tinyhack/zygiskreflutter/SetHostActivity.java
     1 +package com.tinyhack.zygiskreflutter;
     2 + 
     3 +import android.content.SharedPreferences;
     4 +import android.os.Bundle;
     5 +import android.text.Editable;
     6 +import android.widget.EditText;
     7 + 
     8 +import androidx.activity.EdgeToEdge;
     9 +import androidx.appcompat.app.AppCompatActivity;
     10 +import androidx.core.graphics.Insets;
     11 +import androidx.core.view.ViewCompat;
     12 +import androidx.core.view.WindowInsetsCompat;
     13 +import android.text.TextWatcher;
     14 +import android.widget.TextView;
     15 + 
     16 +import java.io.FileInputStream;
     17 +import java.io.FileOutputStream;
     18 + 
     19 +public class SetHostActivity extends AppCompatActivity {
     20 + 
     21 + 
     22 + @Override
     23 + protected void onCreate(Bundle savedInstanceState) {
     24 + super.onCreate(savedInstanceState);
     25 + EdgeToEdge.enable(this);
     26 + setContentView(R.layout.activity_set_host);
     27 + ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
     28 + Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
     29 + v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
     30 + return insets;
     31 + });
     32 + EditText editTextProxyIP = findViewById(R.id.proxyIP);
     33 + //get the current value
     34 + String proxyIP = Util.getPoxyIPFromFile(getFilesDir().getAbsolutePath());
     35 + editTextProxyIP.setText(proxyIP);
     36 + 
     37 + TextView IPStatus = findViewById(R.id.IPStatus);
     38 + IPStatus.setText("Valid IP");
     39 + 
     40 + //monitor changes
     41 + editTextProxyIP.addTextChangedListener(new TextWatcher() {
     42 + @Override
     43 + public void beforeTextChanged(CharSequence s, int start, int count, int after) {
     44 + }
     45 + @Override
     46 + public void onTextChanged(CharSequence s, int start, int before, int count) {
     47 + }
     48 + @Override
     49 + public void afterTextChanged(Editable s) {
     50 + //check if entry is valid IP
     51 + String ip = s.toString();
     52 + if (!ip.matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}")) {
     53 + IPStatus.setText("Invalid IP");
     54 + return;
     55 + }
     56 + IPStatus.setText("Valid IP");
     57 + Util.saveProxyIPToFile(getFilesDir().getAbsolutePath(),s.toString());
     58 + }
     59 + });
     60 + }
     61 +}
     62 + 
  • ■ ■ ■ ■ ■ ■
    app/src/main/java/com/tinyhack/zygiskreflutter/Util.java
     1 +package com.tinyhack.zygiskreflutter;
     2 + 
     3 +import java.io.FileInputStream;
     4 +import java.io.FileOutputStream;
     5 + 
     6 +public class Util {
     7 + public static void saveProxyIPToFile(String filesdir, String ip) {
     8 + String path = filesdir + "/proxyip.txt";
     9 + try {
     10 + FileOutputStream fos = new FileOutputStream(path);
     11 + //reformat IP so that it will have 3 digits for each part
     12 + String[] parts = ip.split("\\.");
     13 + for (int i = 0; i < parts.length; i++) {
     14 + int val = Integer.parseInt(parts[i]);
     15 + if (val < 100) {
     16 + //represent as octal
     17 + parts[i] = String.format("%03o", val);
     18 + }
     19 + }
     20 + ip = String.join(".", parts);
     21 + fos.write(ip.getBytes());
     22 + fos.close();
     23 + } catch (Exception e) {
     24 + e.printStackTrace();
     25 + }
     26 + }
     27 + 
     28 + public static String getPoxyIPFromFile(String filesdir) {
     29 + String path = filesdir + "/proxyip.txt";
     30 + try {
     31 + FileInputStream fis = new FileInputStream(path);
     32 + byte[] data = new byte[(int) fis.available()];
     33 + fis.read(data);
     34 + fis.close();
     35 + String ip = new String(data);
     36 + //reformat IP so that it will have normal decimal
     37 + String[] parts = ip.split("\\.");
     38 + for (int i = 0; i < parts.length; i++) {
     39 + if (parts[i].startsWith("0")) {
     40 + //represent as octal
     41 + parts[i] = String.valueOf(Integer.parseInt(parts[i], 8));
     42 + }
     43 + }
     44 + ip = String.join(".", parts);
     45 + return ip;
     46 + } catch (Exception e) {
     47 + e.printStackTrace();
     48 + }
     49 + return null;
     50 + }
     51 + 
     52 +}
     53 + 
  • ■ ■ ■ ■ ■ ■
    app/src/main/res/drawable/butterfly_svgrepo_com.xml
     1 +<vector xmlns:android="http://schemas.android.com/apk/res/android"
     2 + android:width="800dp"
     3 + android:height="800dp"
     4 + android:viewportWidth="32.002"
     5 + android:viewportHeight="32.002">
     6 + <path
     7 + android:pathData="M31.511,15.142c-2.351,-2.399 -13.22,-1.883 -16.027,0.2c-0.091,0.067 -0.17,0.125 -0.254,0.188c-0.034,-0.037 -0.052,-0.071 -0.042,-0.107c0.071,-0.26 0.721,-0.47 0.727,-0.472c0.283,-0.09 0.532,-0.287 0.679,-0.571c0.18,-0.349 0.164,-0.746 -0.006,-1.069c1.031,-1.502 2.554,-3.428 3.723,-3.669c0.195,-0.04 0.368,-0.026 0.527,0.031c-0.06,0.034 -0.113,0.081 -0.145,0.148c-0.079,0.17 -0.004,0.373 0.166,0.452c0.17,0.078 0.372,0.005 0.452,-0.165c0.077,-0.166 0.008,-0.358 -0.153,-0.441l0.002,-0.001c-0.006,-0.006 -0.011,-0.007 -0.015,-0.011c-0.249,-0.18 -0.539,-0.238 -0.868,-0.17c-1.217,0.251 -2.746,2.162 -3.788,3.676c-0.102,-0.136 -0.23,-0.254 -0.392,-0.337c-0.158,-0.082 -0.326,-0.119 -0.492,-0.125c0.599,-2.082 1.144,-5.051 -0.25,-6.016c-0.028,-0.028 -0.06,-0.054 -0.097,-0.072c-0.008,-0.003 -0.016,-0.002 -0.023,-0.004c-0.01,-0.005 -0.018,-0.013 -0.027,-0.018l-0.006,0.01c-0.156,-0.042 -0.324,0.026 -0.396,0.179c-0.078,0.169 -0.004,0.373 0.166,0.453c0.17,0.079 0.373,0.003 0.452,-0.166c0.014,-0.032 0.02,-0.064 0.023,-0.097c1.073,1.048 0.544,3.793 -0.016,5.736c-0.367,0.044 -0.707,0.263 -0.89,0.616c-0.146,0.284 -0.163,0.6 -0.072,0.883c0,0.002 0.084,0.453 0.001,0.727c-0.018,-0.122 -0.035,-0.247 -0.054,-0.382C13.94,11.084 6.703,2.959 3.345,2.959c-0.378,0 -0.709,0.095 -1,0.297c-1.455,1.283 -1.132,7.088 -0.642,8.556c0.277,0.831 0.822,1.319 1.286,1.599c-0.768,0.08 -1.869,0.364 -2.502,1.275c-0.025,0 -0.047,0 -0.064,0l-0.001,0.1c-0.222,0.355 -0.354,0.771 -0.401,1.241c-0.005,0.01 -0.012,0.019 -0.015,0.029l0.01,0.003c-0.084,0.923 0.159,2.059 0.739,3.402c1.341,3.107 3.674,3.76 5.396,3.76l0,0c1.752,0 3.56,-0.683 4.949,-1.593c-0.499,1.372 -1.038,2.681 -1.382,3.547c0.432,-0.836 1.602,-2.6 2.864,-4.259c-0.033,1.97 0.802,4.675 2.492,6.4l0,0c1.205,1.229 3.303,2.438 6.462,1.223c1.365,-0.525 2.346,-1.146 2.947,-1.854l0.01,0.006c0.005,-0.01 0.007,-0.021 0.01,-0.031c0.304,-0.363 0.508,-0.748 0.606,-1.156l0.071,-0.07c-0.013,-0.012 -0.027,-0.026 -0.047,-0.045c0.208,-1.09 -0.358,-2.076 -0.839,-2.681c0.524,0.136 1.254,0.185 2.042,-0.2c1.392,-0.678 5.765,-4.509 5.663,-6.447C31.938,15.715 31.775,15.412 31.511,15.142zM29.263,16.212c-2.22,0.208 -5.314,0.45 -5.982,0.317c-0.712,-0.141 -4.202,-0.154 -6.401,-0.147c-0.017,-0.046 -0.036,-0.091 -0.06,-0.136c1.564,-0.447 3.794,-1.046 4.854,-1.142c1.487,-0.135 5.88,0.306 7.057,0.43C28.913,15.749 29.094,15.975 29.263,16.212zM27.149,14.052c0.325,0.23 0.766,0.607 1.215,1.08c-1.569,-0.161 -5.324,-0.516 -6.724,-0.389c-1.127,0.102 -3.467,0.734 -5.042,1.188c-0.192,-0.216 -0.422,-0.396 -0.577,-0.503C18.104,14.218 23.313,13.64 27.149,14.052zM8.986,18.379c-0.811,1.044 -3.11,3.846 -4.666,4.18c-0.458,-0.158 -0.919,-0.395 -1.361,-0.73c1.466,-0.285 3.862,-2.83 4.336,-3.348C7.882,18.486 8.465,18.466 8.986,18.379zM6.236,18.093c-0.427,-0.015 -0.83,-0.028 -1.183,-0.022c-1.148,0.021 -3.202,1.205 -3.811,1.572c-0.052,-0.106 -0.105,-0.211 -0.155,-0.326c-0.076,-0.176 -0.139,-0.344 -0.204,-0.514c0.52,-0.347 2.641,-1.685 4.272,-1.551c0.439,0.035 0.872,-0.012 1.282,-0.104c0.452,0.078 0.898,0.115 1.332,0.115c1.827,0 3.419,-0.606 4.263,-1.002c-0.055,0.097 -0.115,0.195 -0.177,0.293c-0.688,0.47 -1.609,1.054 -2.272,1.298C8.718,18.176 7.4,18.132 6.236,18.093zM0.365,16.189c0.245,-0.135 1.285,-0.221 2.369,-0.279c-0.9,0.349 -1.909,0.809 -2.312,1.236C0.369,16.804 0.347,16.482 0.365,16.189zM6.009,15.398c-0.435,0.029 -1.04,0.053 -1.603,0.074c-2.314,0.09 -3.456,0.172 -3.99,0.32c0.053,-0.273 0.147,-0.518 0.275,-0.74C2.101,15.072 4.978,15.162 6.009,15.398zM0.602,17.493c0.014,-0.274 1.578,-1.039 3.354,-1.639c0.162,-0.007 0.319,-0.013 0.464,-0.019c1.593,-0.062 2.127,-0.093 2.323,-0.171c0.526,0.179 1.113,0.27 1.607,0.278c-0.683,0.426 -1.917,1.049 -3.166,0.948c-1.707,-0.137 -3.785,1.135 -4.43,1.562c-0.115,-0.334 -0.203,-0.651 -0.268,-0.952L0.602,17.493zM1.728,6.246c0.545,-0.431 1.266,-0.7 2.02,-0.857C4.443,6.248 5.627,7.692 6.584,8.77C5.046,8.355 2.251,6.643 1.722,6.313C1.724,6.291 1.726,6.268 1.728,6.246zM6.985,9.215c0.399,0.434 0.73,0.763 0.923,0.894c0.145,0.1 0.413,0.336 0.754,0.656c-2.609,-0.454 -6.363,-2.188 -7.006,-2.492C1.648,7.757 1.659,7.23 1.691,6.722C2.59,7.276 5.613,9.084 6.985,9.215zM1.665,8.68c1.051,0.491 4.923,2.233 7.451,2.517c0.287,0.277 0.603,0.588 0.931,0.915c-2.23,0.555 -7.388,-1.679 -8.295,-2.084C1.71,9.61 1.68,9.153 1.665,8.68zM8.113,9.809C7.551,9.425 5.558,7.044 4.154,5.314c0.287,-0.045 0.575,-0.075 0.857,-0.093c0.911,0.754 4.3,3.583 5.245,4.739c0.672,0.824 1.806,2.836 2.58,4.266c-0.048,0.015 -0.094,0.032 -0.138,0.053C11.164,12.704 8.712,10.219 8.113,9.809zM9.087,15.879c0.337,-0.061 1.532,-0.028 2.81,0.041c-0.877,0.408 -2.64,1.072 -4.587,0.96C8.232,16.517 8.919,16.009 9.087,15.879zM13.98,14.225c-0.185,-0.035 -0.474,-0.075 -0.763,-0.06c-0.779,-1.444 -1.963,-3.557 -2.68,-4.434c-0.889,-1.089 -3.77,-3.523 -4.983,-4.532C6.205,5.19 6.783,5.241 7.175,5.312C10.154,7.764 13.387,11.89 13.98,14.225zM1.799,10.447c1.163,0.513 4.987,2.113 7.429,2.113c0.418,0 0.793,-0.05 1.109,-0.158c0.719,0.719 1.468,1.483 2.078,2.109c-0.063,0.094 -0.104,0.21 -0.11,0.356c0.066,0.124 0.089,0.261 0.083,0.408l-4.395,-1.938c-2.229,-0.982 -5.01,-0.917 -5.55,-0.893c-0.157,-0.205 -0.296,-0.45 -0.395,-0.747C1.951,11.406 1.866,10.97 1.799,10.447zM3.549,13.745c0.162,0 0.26,0.008 0.264,0.009l0.055,-0.359c-0.035,-0.008 -0.584,-0.139 -1.096,-0.594c0.914,-0.01 3.225,0.054 5.074,0.87l4.308,1.899c-1.074,-0.062 -2.771,-0.138 -3.186,-0.039c-0.552,0.133 -1.745,0.001 -2.514,-0.373c-0.72,-0.351 -4.143,-0.443 -5.512,-0.466C1.697,13.845 2.993,13.745 3.549,13.745zM1.408,19.968c0.557,-0.335 2.591,-1.514 3.652,-1.532c0.341,-0.007 0.741,0.006 1.165,0.021c0.19,0.006 0.386,0.012 0.582,0.019c-1.366,1.445 -3.516,3.319 -4.283,2.983C2.119,21.07 1.741,20.579 1.408,19.968zM6.149,22.857c-0.38,0 -0.792,-0.035 -1.217,-0.127c1.906,-0.879 4.3,-4.062 4.612,-4.483c0.055,-0.017 0.113,-0.031 0.165,-0.05c0.48,-0.178 1.074,-0.514 1.626,-0.862c-0.405,0.601 -0.736,1.142 -0.571,1.462c-1.214,1.359 -2.759,2.893 -3.982,4.028C6.572,22.844 6.361,22.857 6.149,22.857C6.15,22.857 6.15,22.857 6.149,22.857zM7.407,22.739c0.835,-0.788 2.375,-2.276 3.645,-3.705c0.271,0.132 0.575,0.14 0.864,0.089c-0.053,0.191 -0.109,0.387 -0.17,0.585c-0.134,0.438 -0.284,0.89 -0.441,1.34C10.243,21.832 8.848,22.484 7.407,22.739zM14.537,26.082c-1.215,-1.779 -1.743,-4.091 -1.564,-5.674c0.019,-0.024 0.038,-0.049 0.056,-0.072c0.196,-0.249 0.392,-0.49 0.585,-0.725c0.153,0.451 0.459,1.074 1.02,1.274C14.501,22.792 14.516,24.934 14.537,26.082zM15.332,27.062L15.332,27.062c-0.147,-0.15 -0.286,-0.312 -0.419,-0.476c-0.045,-1.668 -0.03,-3.845 0.091,-5.663c0.345,-0.107 0.5,-0.722 0.645,-1.431c0.138,0.638 0.313,1.297 0.522,1.765c0.022,0.051 0.053,0.102 0.08,0.153c-0.083,0.519 -0.681,4.456 0.024,6.433C15.913,27.604 15.599,27.334 15.332,27.062zM16.826,28.161c-0.849,-1.347 -0.457,-4.948 -0.278,-6.259c0.302,0.434 0.695,0.863 1.109,1.279c-0.038,0.701 -0.179,4.193 0.644,5.439C17.751,28.542 17.261,28.377 16.826,28.161zM18.87,28.674c-0.778,-0.312 -0.941,-3.16 -0.865,-5.148c0.142,0.138 0.282,0.272 0.42,0.404c0.307,0.293 0.597,0.568 0.83,0.817c0.729,0.771 1.311,3.049 1.46,3.682C20.045,28.618 19.431,28.691 18.87,28.674zM21.404,28.201c-0.116,0.045 -0.229,0.08 -0.341,0.117c-0.164,-0.691 -0.755,-2.987 -1.544,-3.822c-0.243,-0.257 -0.534,-0.535 -0.844,-0.83c-0.842,-0.805 -1.796,-1.714 -2.173,-2.557c-0.289,-0.646 -0.516,-1.711 -0.662,-2.533c0.027,-0.113 0.055,-0.225 0.086,-0.331c0.309,0.879 0.99,2.44 2.269,3.746c0.302,0.309 0.64,0.603 1.013,0.87c0.223,0.357 0.491,0.701 0.824,0.99c1.238,1.069 1.766,3.521 1.881,4.137C21.748,28.061 21.583,28.132 21.404,28.201zM16.267,18.102c0.944,0.864 1.803,1.696 1.997,1.979c0.024,0.211 0.143,1.058 0.527,1.969C17.349,20.736 16.589,19.014 16.267,18.102zM22.255,27.833c-0.146,-0.759 -0.688,-3.134 -1.983,-4.257c-0.948,-0.821 -1.365,-2.139 -1.539,-2.926c0.352,0.348 0.828,0.703 1.324,0.953c0.082,0.195 0.433,0.599 1.503,1.78c0.098,0.107 0.203,0.224 0.312,0.345c0.814,1.688 1.363,3.34 1.176,3.543l0.075,0.086C22.862,27.521 22.574,27.682 22.255,27.833zM23.42,27.158c0.023,-0.588 -0.354,-1.631 -0.733,-2.518c0.717,0.815 1.383,1.618 1.458,1.887C23.948,26.747 23.703,26.956 23.42,27.158zM24.393,26.214c-0.27,-0.485 -1.009,-1.358 -2.563,-3.073c-0.378,-0.418 -0.785,-0.867 -1.068,-1.197c0.89,0.57 2.967,2.562 3.969,3.557C24.659,25.746 24.55,25.984 24.393,26.214zM23.689,22.07l-0.218,0.291c0.003,0.002 0.077,0.066 0.19,0.183c0.391,0.397 1.226,1.394 1.148,2.526c-0.976,-0.961 -3.438,-3.342 -4.19,-3.612c-0.806,-0.288 -1.734,-1.047 -2.025,-1.535c-0.22,-0.366 -1.462,-1.524 -2.258,-2.25l4.371,1.749c1.876,0.75 3.54,2.355 4.172,3.016C24.197,22.391 23.72,22.09 23.689,22.07zM26.176,22.184c-0.281,0.137 -0.554,0.209 -0.811,0.24c-0.36,-0.402 -2.26,-2.435 -4.521,-3.34L16.384,17.3c0.101,-0.106 0.215,-0.187 0.35,-0.226c0.1,-0.107 0.153,-0.217 0.177,-0.328c0.873,-0.002 1.944,-0.002 2.96,0.008c0.146,0.303 0.371,0.604 0.664,0.903c1.708,1.743 5.528,3.356 6.708,3.829C26.823,21.804 26.452,22.049 26.176,22.184zM27.575,21.227c-0.924,-0.365 -6.128,-2.486 -7.293,-4.468c0.463,0.006 0.907,0.014 1.305,0.025c1.973,1.606 5.927,3.153 7.013,3.561C28.25,20.666 27.903,20.964 27.575,21.227zM28.896,20.066c-0.667,-0.248 -4.532,-1.715 -6.683,-3.261c0.469,0.02 0.824,0.046 0.996,0.08c0.229,0.045 0.695,0.052 1.284,0.034c1.054,0.888 4.461,1.783 5.486,2.037C29.64,19.335 29.271,19.711 28.896,20.066zM30.249,18.648c-0.604,-0.147 -3.785,-0.946 -5.157,-1.754c1.44,-0.071 3.301,-0.235 4.4,-0.34c0.415,0.648 0.729,1.353 0.801,2.042C30.279,18.614 30.266,18.631 30.249,18.648z"
     8 + android:fillColor="#000000"/>
     9 +</vector>
     10 + 
  • ■ ■ ■ ■ ■ ■
    app/src/main/res/drawable/ic_launcher_background.xml
     1 +<?xml version="1.0" encoding="utf-8"?>
     2 +<vector xmlns:android="http://schemas.android.com/apk/res/android"
     3 + android:width="108dp"
     4 + android:height="108dp"
     5 + android:viewportWidth="108"
     6 + android:viewportHeight="108">
     7 + <path
     8 + android:fillColor="#3DDC84"
     9 + android:pathData="M0,0h108v108h-108z" />
     10 + <path
     11 + android:fillColor="#00000000"
     12 + android:pathData="M9,0L9,108"
     13 + android:strokeWidth="0.8"
     14 + android:strokeColor="#33FFFFFF" />
     15 + <path
     16 + android:fillColor="#00000000"
     17 + android:pathData="M19,0L19,108"
     18 + android:strokeWidth="0.8"
     19 + android:strokeColor="#33FFFFFF" />
     20 + <path
     21 + android:fillColor="#00000000"
     22 + android:pathData="M29,0L29,108"
     23 + android:strokeWidth="0.8"
     24 + android:strokeColor="#33FFFFFF" />
     25 + <path
     26 + android:fillColor="#00000000"
     27 + android:pathData="M39,0L39,108"
     28 + android:strokeWidth="0.8"
     29 + android:strokeColor="#33FFFFFF" />
     30 + <path
     31 + android:fillColor="#00000000"
     32 + android:pathData="M49,0L49,108"
     33 + android:strokeWidth="0.8"
     34 + android:strokeColor="#33FFFFFF" />
     35 + <path
     36 + android:fillColor="#00000000"
     37 + android:pathData="M59,0L59,108"
     38 + android:strokeWidth="0.8"
     39 + android:strokeColor="#33FFFFFF" />
     40 + <path
     41 + android:fillColor="#00000000"
     42 + android:pathData="M69,0L69,108"
     43 + android:strokeWidth="0.8"
     44 + android:strokeColor="#33FFFFFF" />
     45 + <path
     46 + android:fillColor="#00000000"
     47 + android:pathData="M79,0L79,108"
     48 + android:strokeWidth="0.8"
     49 + android:strokeColor="#33FFFFFF" />
     50 + <path
     51 + android:fillColor="#00000000"
     52 + android:pathData="M89,0L89,108"
     53 + android:strokeWidth="0.8"
     54 + android:strokeColor="#33FFFFFF" />
     55 + <path
     56 + android:fillColor="#00000000"
     57 + android:pathData="M99,0L99,108"
     58 + android:strokeWidth="0.8"
     59 + android:strokeColor="#33FFFFFF" />
     60 + <path
     61 + android:fillColor="#00000000"
     62 + android:pathData="M0,9L108,9"
     63 + android:strokeWidth="0.8"
     64 + android:strokeColor="#33FFFFFF" />
     65 + <path
     66 + android:fillColor="#00000000"
     67 + android:pathData="M0,19L108,19"
     68 + android:strokeWidth="0.8"
     69 + android:strokeColor="#33FFFFFF" />
     70 + <path
     71 + android:fillColor="#00000000"
     72 + android:pathData="M0,29L108,29"
     73 + android:strokeWidth="0.8"
     74 + android:strokeColor="#33FFFFFF" />
     75 + <path
     76 + android:fillColor="#00000000"
     77 + android:pathData="M0,39L108,39"
     78 + android:strokeWidth="0.8"
     79 + android:strokeColor="#33FFFFFF" />
     80 + <path
     81 + android:fillColor="#00000000"
     82 + android:pathData="M0,49L108,49"
     83 + android:strokeWidth="0.8"
     84 + android:strokeColor="#33FFFFFF" />
     85 + <path
     86 + android:fillColor="#00000000"
     87 + android:pathData="M0,59L108,59"
     88 + android:strokeWidth="0.8"
     89 + android:strokeColor="#33FFFFFF" />
     90 + <path
     91 + android:fillColor="#00000000"
     92 + android:pathData="M0,69L108,69"
     93 + android:strokeWidth="0.8"
     94 + android:strokeColor="#33FFFFFF" />
     95 + <path
     96 + android:fillColor="#00000000"
     97 + android:pathData="M0,79L108,79"
     98 + android:strokeWidth="0.8"
     99 + android:strokeColor="#33FFFFFF" />
     100 + <path
     101 + android:fillColor="#00000000"
     102 + android:pathData="M0,89L108,89"
     103 + android:strokeWidth="0.8"
     104 + android:strokeColor="#33FFFFFF" />
     105 + <path
     106 + android:fillColor="#00000000"
     107 + android:pathData="M0,99L108,99"
     108 + android:strokeWidth="0.8"
     109 + android:strokeColor="#33FFFFFF" />
     110 + <path
     111 + android:fillColor="#00000000"
     112 + android:pathData="M19,29L89,29"
     113 + android:strokeWidth="0.8"
     114 + android:strokeColor="#33FFFFFF" />
     115 + <path
     116 + android:fillColor="#00000000"
     117 + android:pathData="M19,39L89,39"
     118 + android:strokeWidth="0.8"
     119 + android:strokeColor="#33FFFFFF" />
     120 + <path
     121 + android:fillColor="#00000000"
     122 + android:pathData="M19,49L89,49"
     123 + android:strokeWidth="0.8"
     124 + android:strokeColor="#33FFFFFF" />
     125 + <path
     126 + android:fillColor="#00000000"
     127 + android:pathData="M19,59L89,59"
     128 + android:strokeWidth="0.8"
     129 + android:strokeColor="#33FFFFFF" />
     130 + <path
     131 + android:fillColor="#00000000"
     132 + android:pathData="M19,69L89,69"
     133 + android:strokeWidth="0.8"
     134 + android:strokeColor="#33FFFFFF" />
     135 + <path
     136 + android:fillColor="#00000000"
     137 + android:pathData="M19,79L89,79"
     138 + android:strokeWidth="0.8"
     139 + android:strokeColor="#33FFFFFF" />
     140 + <path
     141 + android:fillColor="#00000000"
     142 + android:pathData="M29,19L29,89"
     143 + android:strokeWidth="0.8"
     144 + android:strokeColor="#33FFFFFF" />
     145 + <path
     146 + android:fillColor="#00000000"
     147 + android:pathData="M39,19L39,89"
     148 + android:strokeWidth="0.8"
     149 + android:strokeColor="#33FFFFFF" />
     150 + <path
     151 + android:fillColor="#00000000"
     152 + android:pathData="M49,19L49,89"
     153 + android:strokeWidth="0.8"
     154 + android:strokeColor="#33FFFFFF" />
     155 + <path
     156 + android:fillColor="#00000000"
     157 + android:pathData="M59,19L59,89"
     158 + android:strokeWidth="0.8"
     159 + android:strokeColor="#33FFFFFF" />
     160 + <path
     161 + android:fillColor="#00000000"
     162 + android:pathData="M69,19L69,89"
     163 + android:strokeWidth="0.8"
     164 + android:strokeColor="#33FFFFFF" />
     165 + <path
     166 + android:fillColor="#00000000"
     167 + android:pathData="M79,19L79,89"
     168 + android:strokeWidth="0.8"
     169 + android:strokeColor="#33FFFFFF" />
     170 +</vector>
     171 + 
  • ■ ■ ■ ■ ■ ■
    app/src/main/res/drawable/ic_launcher_foreground.xml
     1 +<vector xmlns:android="http://schemas.android.com/apk/res/android"
     2 + android:width="800dp"
     3 + android:height="800dp"
     4 + android:viewportWidth="32.002"
     5 + android:viewportHeight="32.002">
     6 + <path
     7 + android:pathData="M31.511,15.142c-2.351,-2.399 -13.22,-1.883 -16.027,0.2c-0.091,0.067 -0.17,0.125 -0.254,0.188c-0.034,-0.037 -0.052,-0.071 -0.042,-0.107c0.071,-0.26 0.721,-0.47 0.727,-0.472c0.283,-0.09 0.532,-0.287 0.679,-0.571c0.18,-0.349 0.164,-0.746 -0.006,-1.069c1.031,-1.502 2.554,-3.428 3.723,-3.669c0.195,-0.04 0.368,-0.026 0.527,0.031c-0.06,0.034 -0.113,0.081 -0.145,0.148c-0.079,0.17 -0.004,0.373 0.166,0.452c0.17,0.078 0.372,0.005 0.452,-0.165c0.077,-0.166 0.008,-0.358 -0.153,-0.441l0.002,-0.001c-0.006,-0.006 -0.011,-0.007 -0.015,-0.011c-0.249,-0.18 -0.539,-0.238 -0.868,-0.17c-1.217,0.251 -2.746,2.162 -3.788,3.676c-0.102,-0.136 -0.23,-0.254 -0.392,-0.337c-0.158,-0.082 -0.326,-0.119 -0.492,-0.125c0.599,-2.082 1.144,-5.051 -0.25,-6.016c-0.028,-0.028 -0.06,-0.054 -0.097,-0.072c-0.008,-0.003 -0.016,-0.002 -0.023,-0.004c-0.01,-0.005 -0.018,-0.013 -0.027,-0.018l-0.006,0.01c-0.156,-0.042 -0.324,0.026 -0.396,0.179c-0.078,0.169 -0.004,0.373 0.166,0.453c0.17,0.079 0.373,0.003 0.452,-0.166c0.014,-0.032 0.02,-0.064 0.023,-0.097c1.073,1.048 0.544,3.793 -0.016,5.736c-0.367,0.044 -0.707,0.263 -0.89,0.616c-0.146,0.284 -0.163,0.6 -0.072,0.883c0,0.002 0.084,0.453 0.001,0.727c-0.018,-0.122 -0.035,-0.247 -0.054,-0.382C13.94,11.084 6.703,2.959 3.345,2.959c-0.378,0 -0.709,0.095 -1,0.297c-1.455,1.283 -1.132,7.088 -0.642,8.556c0.277,0.831 0.822,1.319 1.286,1.599c-0.768,0.08 -1.869,0.364 -2.502,1.275c-0.025,0 -0.047,0 -0.064,0l-0.001,0.1c-0.222,0.355 -0.354,0.771 -0.401,1.241c-0.005,0.01 -0.012,0.019 -0.015,0.029l0.01,0.003c-0.084,0.923 0.159,2.059 0.739,3.402c1.341,3.107 3.674,3.76 5.396,3.76l0,0c1.752,0 3.56,-0.683 4.949,-1.593c-0.499,1.372 -1.038,2.681 -1.382,3.547c0.432,-0.836 1.602,-2.6 2.864,-4.259c-0.033,1.97 0.802,4.675 2.492,6.4l0,0c1.205,1.229 3.303,2.438 6.462,1.223c1.365,-0.525 2.346,-1.146 2.947,-1.854l0.01,0.006c0.005,-0.01 0.007,-0.021 0.01,-0.031c0.304,-0.363 0.508,-0.748 0.606,-1.156l0.071,-0.07c-0.013,-0.012 -0.027,-0.026 -0.047,-0.045c0.208,-1.09 -0.358,-2.076 -0.839,-2.681c0.524,0.136 1.254,0.185 2.042,-0.2c1.392,-0.678 5.765,-4.509 5.663,-6.447C31.938,15.715 31.775,15.412 31.511,15.142zM29.263,16.212c-2.22,0.208 -5.314,0.45 -5.982,0.317c-0.712,-0.141 -4.202,-0.154 -6.401,-0.147c-0.017,-0.046 -0.036,-0.091 -0.06,-0.136c1.564,-0.447 3.794,-1.046 4.854,-1.142c1.487,-0.135 5.88,0.306 7.057,0.43C28.913,15.749 29.094,15.975 29.263,16.212zM27.149,14.052c0.325,0.23 0.766,0.607 1.215,1.08c-1.569,-0.161 -5.324,-0.516 -6.724,-0.389c-1.127,0.102 -3.467,0.734 -5.042,1.188c-0.192,-0.216 -0.422,-0.396 -0.577,-0.503C18.104,14.218 23.313,13.64 27.149,14.052zM8.986,18.379c-0.811,1.044 -3.11,3.846 -4.666,4.18c-0.458,-0.158 -0.919,-0.395 -1.361,-0.73c1.466,-0.285 3.862,-2.83 4.336,-3.348C7.882,18.486 8.465,18.466 8.986,18.379zM6.236,18.093c-0.427,-0.015 -0.83,-0.028 -1.183,-0.022c-1.148,0.021 -3.202,1.205 -3.811,1.572c-0.052,-0.106 -0.105,-0.211 -0.155,-0.326c-0.076,-0.176 -0.139,-0.344 -0.204,-0.514c0.52,-0.347 2.641,-1.685 4.272,-1.551c0.439,0.035 0.872,-0.012 1.282,-0.104c0.452,0.078 0.898,0.115 1.332,0.115c1.827,0 3.419,-0.606 4.263,-1.002c-0.055,0.097 -0.115,0.195 -0.177,0.293c-0.688,0.47 -1.609,1.054 -2.272,1.298C8.718,18.176 7.4,18.132 6.236,18.093zM0.365,16.189c0.245,-0.135 1.285,-0.221 2.369,-0.279c-0.9,0.349 -1.909,0.809 -2.312,1.236C0.369,16.804 0.347,16.482 0.365,16.189zM6.009,15.398c-0.435,0.029 -1.04,0.053 -1.603,0.074c-2.314,0.09 -3.456,0.172 -3.99,0.32c0.053,-0.273 0.147,-0.518 0.275,-0.74C2.101,15.072 4.978,15.162 6.009,15.398zM0.602,17.493c0.014,-0.274 1.578,-1.039 3.354,-1.639c0.162,-0.007 0.319,-0.013 0.464,-0.019c1.593,-0.062 2.127,-0.093 2.323,-0.171c0.526,0.179 1.113,0.27 1.607,0.278c-0.683,0.426 -1.917,1.049 -3.166,0.948c-1.707,-0.137 -3.785,1.135 -4.43,1.562c-0.115,-0.334 -0.203,-0.651 -0.268,-0.952L0.602,17.493zM1.728,6.246c0.545,-0.431 1.266,-0.7 2.02,-0.857C4.443,6.248 5.627,7.692 6.584,8.77C5.046,8.355 2.251,6.643 1.722,6.313C1.724,6.291 1.726,6.268 1.728,6.246zM6.985,9.215c0.399,0.434 0.73,0.763 0.923,0.894c0.145,0.1 0.413,0.336 0.754,0.656c-2.609,-0.454 -6.363,-2.188 -7.006,-2.492C1.648,7.757 1.659,7.23 1.691,6.722C2.59,7.276 5.613,9.084 6.985,9.215zM1.665,8.68c1.051,0.491 4.923,2.233 7.451,2.517c0.287,0.277 0.603,0.588 0.931,0.915c-2.23,0.555 -7.388,-1.679 -8.295,-2.084C1.71,9.61 1.68,9.153 1.665,8.68zM8.113,9.809C7.551,9.425 5.558,7.044 4.154,5.314c0.287,-0.045 0.575,-0.075 0.857,-0.093c0.911,0.754 4.3,3.583 5.245,4.739c0.672,0.824 1.806,2.836 2.58,4.266c-0.048,0.015 -0.094,0.032 -0.138,0.053C11.164,12.704 8.712,10.219 8.113,9.809zM9.087,15.879c0.337,-0.061 1.532,-0.028 2.81,0.041c-0.877,0.408 -2.64,1.072 -4.587,0.96C8.232,16.517 8.919,16.009 9.087,15.879zM13.98,14.225c-0.185,-0.035 -0.474,-0.075 -0.763,-0.06c-0.779,-1.444 -1.963,-3.557 -2.68,-4.434c-0.889,-1.089 -3.77,-3.523 -4.983,-4.532C6.205,5.19 6.783,5.241 7.175,5.312C10.154,7.764 13.387,11.89 13.98,14.225zM1.799,10.447c1.163,0.513 4.987,2.113 7.429,2.113c0.418,0 0.793,-0.05 1.109,-0.158c0.719,0.719 1.468,1.483 2.078,2.109c-0.063,0.094 -0.104,0.21 -0.11,0.356c0.066,0.124 0.089,0.261 0.083,0.408l-4.395,-1.938c-2.229,-0.982 -5.01,-0.917 -5.55,-0.893c-0.157,-0.205 -0.296,-0.45 -0.395,-0.747C1.951,11.406 1.866,10.97 1.799,10.447zM3.549,13.745c0.162,0 0.26,0.008 0.264,0.009l0.055,-0.359c-0.035,-0.008 -0.584,-0.139 -1.096,-0.594c0.914,-0.01 3.225,0.054 5.074,0.87l4.308,1.899c-1.074,-0.062 -2.771,-0.138 -3.186,-0.039c-0.552,0.133 -1.745,0.001 -2.514,-0.373c-0.72,-0.351 -4.143,-0.443 -5.512,-0.466C1.697,13.845 2.993,13.745 3.549,13.745zM1.408,19.968c0.557,-0.335 2.591,-1.514 3.652,-1.532c0.341,-0.007 0.741,0.006 1.165,0.021c0.19,0.006 0.386,0.012 0.582,0.019c-1.366,1.445 -3.516,3.319 -4.283,2.983C2.119,21.07 1.741,20.579 1.408,19.968zM6.149,22.857c-0.38,0 -0.792,-0.035 -1.217,-0.127c1.906,-0.879 4.3,-4.062 4.612,-4.483c0.055,-0.017 0.113,-0.031 0.165,-0.05c0.48,-0.178 1.074,-0.514 1.626,-0.862c-0.405,0.601 -0.736,1.142 -0.571,1.462c-1.214,1.359 -2.759,2.893 -3.982,4.028C6.572,22.844 6.361,22.857 6.149,22.857C6.15,22.857 6.15,22.857 6.149,22.857zM7.407,22.739c0.835,-0.788 2.375,-2.276 3.645,-3.705c0.271,0.132 0.575,0.14 0.864,0.089c-0.053,0.191 -0.109,0.387 -0.17,0.585c-0.134,0.438 -0.284,0.89 -0.441,1.34C10.243,21.832 8.848,22.484 7.407,22.739zM14.537,26.082c-1.215,-1.779 -1.743,-4.091 -1.564,-5.674c0.019,-0.024 0.038,-0.049 0.056,-0.072c0.196,-0.249 0.392,-0.49 0.585,-0.725c0.153,0.451 0.459,1.074 1.02,1.274C14.501,22.792 14.516,24.934 14.537,26.082zM15.332,27.062L15.332,27.062c-0.147,-0.15 -0.286,-0.312 -0.419,-0.476c-0.045,-1.668 -0.03,-3.845 0.091,-5.663c0.345,-0.107 0.5,-0.722 0.645,-1.431c0.138,0.638 0.313,1.297 0.522,1.765c0.022,0.051 0.053,0.102 0.08,0.153c-0.083,0.519 -0.681,4.456 0.024,6.433C15.913,27.604 15.599,27.334 15.332,27.062zM16.826,28.161c-0.849,-1.347 -0.457,-4.948 -0.278,-6.259c0.302,0.434 0.695,0.863 1.109,1.279c-0.038,0.701 -0.179,4.193 0.644,5.439C17.751,28.542 17.261,28.377 16.826,28.161zM18.87,28.674c-0.778,-0.312 -0.941,-3.16 -0.865,-5.148c0.142,0.138 0.282,0.272 0.42,0.404c0.307,0.293 0.597,0.568 0.83,0.817c0.729,0.771 1.311,3.049 1.46,3.682C20.045,28.618 19.431,28.691 18.87,28.674zM21.404,28.201c-0.116,0.045 -0.229,0.08 -0.341,0.117c-0.164,-0.691 -0.755,-2.987 -1.544,-3.822c-0.243,-0.257 -0.534,-0.535 -0.844,-0.83c-0.842,-0.805 -1.796,-1.714 -2.173,-2.557c-0.289,-0.646 -0.516,-1.711 -0.662,-2.533c0.027,-0.113 0.055,-0.225 0.086,-0.331c0.309,0.879 0.99,2.44 2.269,3.746c0.302,0.309 0.64,0.603 1.013,0.87c0.223,0.357 0.491,0.701 0.824,0.99c1.238,1.069 1.766,3.521 1.881,4.137C21.748,28.061 21.583,28.132 21.404,28.201zM16.267,18.102c0.944,0.864 1.803,1.696 1.997,1.979c0.024,0.211 0.143,1.058 0.527,1.969C17.349,20.736 16.589,19.014 16.267,18.102zM22.255,27.833c-0.146,-0.759 -0.688,-3.134 -1.983,-4.257c-0.948,-0.821 -1.365,-2.139 -1.539,-2.926c0.352,0.348 0.828,0.703 1.324,0.953c0.082,0.195 0.433,0.599 1.503,1.78c0.098,0.107 0.203,0.224 0.312,0.345c0.814,1.688 1.363,3.34 1.176,3.543l0.075,0.086C22.862,27.521 22.574,27.682 22.255,27.833zM23.42,27.158c0.023,-0.588 -0.354,-1.631 -0.733,-2.518c0.717,0.815 1.383,1.618 1.458,1.887C23.948,26.747 23.703,26.956 23.42,27.158zM24.393,26.214c-0.27,-0.485 -1.009,-1.358 -2.563,-3.073c-0.378,-0.418 -0.785,-0.867 -1.068,-1.197c0.89,0.57 2.967,2.562 3.969,3.557C24.659,25.746 24.55,25.984 24.393,26.214zM23.689,22.07l-0.218,0.291c0.003,0.002 0.077,0.066 0.19,0.183c0.391,0.397 1.226,1.394 1.148,2.526c-0.976,-0.961 -3.438,-3.342 -4.19,-3.612c-0.806,-0.288 -1.734,-1.047 -2.025,-1.535c-0.22,-0.366 -1.462,-1.524 -2.258,-2.25l4.371,1.749c1.876,0.75 3.54,2.355 4.172,3.016C24.197,22.391 23.72,22.09 23.689,22.07zM26.176,22.184c-0.281,0.137 -0.554,0.209 -0.811,0.24c-0.36,-0.402 -2.26,-2.435 -4.521,-3.34L16.384,17.3c0.101,-0.106 0.215,-0.187 0.35,-0.226c0.1,-0.107 0.153,-0.217 0.177,-0.328c0.873,-0.002 1.944,-0.002 2.96,0.008c0.146,0.303 0.371,0.604 0.664,0.903c1.708,1.743 5.528,3.356 6.708,3.829C26.823,21.804 26.452,22.049 26.176,22.184zM27.575,21.227c-0.924,-0.365 -6.128,-2.486 -7.293,-4.468c0.463,0.006 0.907,0.014 1.305,0.025c1.973,1.606 5.927,3.153 7.013,3.561C28.25,20.666 27.903,20.964 27.575,21.227zM28.896,20.066c-0.667,-0.248 -4.532,-1.715 -6.683,-3.261c0.469,0.02 0.824,0.046 0.996,0.08c0.229,0.045 0.695,0.052 1.284,0.034c1.054,0.888 4.461,1.783 5.486,2.037C29.64,19.335 29.271,19.711 28.896,20.066zM30.249,18.648c-0.604,-0.147 -3.785,-0.946 -5.157,-1.754c1.44,-0.071 3.301,-0.235 4.4,-0.34c0.415,0.648 0.729,1.353 0.801,2.042C30.279,18.614 30.266,18.631 30.249,18.648z"
     8 + android:fillColor="#000000"/>
     9 +</vector>
     10 + 
  • ■ ■ ■ ■ ■ ■
    app/src/main/res/layout/activity_about.xml
     1 +<?xml version="1.0" encoding="utf-8"?>
     2 +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
     3 + xmlns:app="http://schemas.android.com/apk/res-auto"
     4 + xmlns:tools="http://schemas.android.com/tools"
     5 + android:id="@+id/main"
     6 + android:layout_width="match_parent"
     7 + android:layout_height="match_parent"
     8 + tools:context=".AboutActivity">
     9 + 
     10 + <TextView
     11 + android:id="@+id/textView"
     12 + android:layout_width="wrap_content"
     13 + android:layout_height="wrap_content"
     14 + android:layout_marginTop="100dp"
     15 + android:text="ZygiskReflutter"
     16 + android:textSize="24sp"
     17 + app:layout_constraintEnd_toEndOf="parent"
     18 + app:layout_constraintStart_toStartOf="parent"
     19 + app:layout_constraintTop_toTopOf="parent" />
     20 + 
     21 + <TextView
     22 + android:id="@+id/textViewVersion"
     23 + android:layout_width="wrap_content"
     24 + android:layout_height="wrap_content"
     25 + android:layout_marginTop="100dp"
     26 + android:text="TextView"
     27 + app:layout_constraintEnd_toEndOf="parent"
     28 + app:layout_constraintStart_toStartOf="parent"
     29 + app:layout_constraintTop_toBottomOf="@+id/textView" />
     30 + 
     31 + <TextView
     32 + android:id="@+id/textView3"
     33 + android:layout_width="wrap_content"
     34 + android:layout_height="wrap_content"
     35 + android:layout_marginTop="50dp"
     36 + android:text="https://github.com/yohanes/zygisk-reflutter"
     37 + app:layout_constraintEnd_toEndOf="parent"
     38 + app:layout_constraintStart_toStartOf="parent"
     39 + app:layout_constraintTop_toBottomOf="@+id/textViewVersion" />
     40 + 
     41 + <Button
     42 + android:id="@+id/visitGithubButton"
     43 + android:layout_width="wrap_content"
     44 + android:layout_height="wrap_content"
     45 + android:layout_marginTop="10dp"
     46 + android:text="Visit Github"
     47 + app:layout_constraintEnd_toEndOf="parent"
     48 + app:layout_constraintStart_toStartOf="parent"
     49 + app:layout_constraintTop_toBottomOf="@+id/textView3" />
     50 + 
     51 + <TextView
     52 + android:id="@+id/explanation"
     53 + android:layout_width="0dp"
     54 + android:layout_height="wrap_content"
     55 + android:layout_marginStart="10dp"
     56 + android:layout_marginTop="20dp"
     57 + android:layout_marginEnd="10dp"
     58 + android:ems="10"
     59 + android:gravity="start|top"
     60 + android:inputType="textMultiLine"
     61 + android:text="This app is based on reFlutter Project (https://github.com/Impact-I/reFlutter/)."
     62 + app:layout_constraintEnd_toEndOf="parent"
     63 + app:layout_constraintStart_toStartOf="parent"
     64 + app:layout_constraintTop_toBottomOf="@+id/visitGithubButton" />
     65 +</androidx.constraintlayout.widget.ConstraintLayout>
  • ■ ■ ■ ■ ■ ■
    app/src/main/res/layout/activity_app_detail.xml
     1 +<?xml version="1.0" encoding="utf-8"?>
     2 +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
     3 + xmlns:app="http://schemas.android.com/apk/res-auto"
     4 + xmlns:tools="http://schemas.android.com/tools"
     5 + android:id="@+id/main"
     6 + android:layout_width="match_parent"
     7 + android:layout_height="match_parent"
     8 + tools:context=".AppDetailActivity">
     9 + 
     10 + <TextView
     11 + android:id="@+id/packageName"
     12 + android:layout_width="wrap_content"
     13 + android:layout_height="wrap_content"
     14 + android:layout_marginTop="100dp"
     15 + android:text="TextView"
     16 + android:textSize="14sp"
     17 + app:layout_constraintEnd_toEndOf="parent"
     18 + app:layout_constraintStart_toStartOf="parent"
     19 + app:layout_constraintTop_toTopOf="parent" />
     20 + 
     21 + <TextView
     22 + android:id="@+id/appName"
     23 + android:layout_width="wrap_content"
     24 + android:layout_height="wrap_content"
     25 + android:layout_marginTop="7dp"
     26 + android:text="TextView"
     27 + android:textSize="24sp"
     28 + app:layout_constraintEnd_toEndOf="parent"
     29 + app:layout_constraintStart_toStartOf="parent"
     30 + app:layout_constraintTop_toBottomOf="@+id/packageName" />
     31 + 
     32 + <TextView
     33 + android:id="@+id/apkName"
     34 + android:layout_width="wrap_content"
     35 + android:layout_height="wrap_content"
     36 + android:layout_marginTop="20dp"
     37 + android:text="TextView"
     38 + app:layout_constraintEnd_toEndOf="parent"
     39 + app:layout_constraintStart_toStartOf="parent"
     40 + app:layout_constraintTop_toBottomOf="@+id/appName" />
     41 + 
     42 + <ProgressBar
     43 + android:id="@+id/progressBar"
     44 + style="?android:attr/progressBarStyleHorizontal"
     45 + android:layout_width="match_parent"
     46 + android:layout_height="wrap_content"
     47 + android:layout_marginStart="5dp"
     48 + android:layout_marginEnd="5dp"
     49 + app:layout_constraintEnd_toEndOf="parent"
     50 + app:layout_constraintStart_toStartOf="parent"
     51 + app:layout_constraintTop_toBottomOf="@+id/apkName" />
     52 + 
     53 + <TextView
     54 + android:id="@+id/downloadStatus"
     55 + android:layout_width="wrap_content"
     56 + android:layout_height="wrap_content"
     57 + android:text="TextView"
     58 + app:layout_constraintEnd_toEndOf="parent"
     59 + app:layout_constraintStart_toStartOf="parent"
     60 + app:layout_constraintTop_toBottomOf="@+id/progressBar" />
     61 + 
     62 + 
     63 + <Button
     64 + android:id="@+id/downloadProxyLibButton"
     65 + android:layout_width="wrap_content"
     66 + android:layout_height="wrap_content"
     67 + android:text="Download Proxy Lib"
     68 + app:layout_constraintEnd_toEndOf="parent"
     69 + app:layout_constraintStart_toStartOf="parent"
     70 + app:layout_constraintTop_toBottomOf="@+id/downloadStatus" />
     71 + 
     72 + <CheckBox
     73 + android:id="@+id/enableHookCheckbox"
     74 + android:layout_width="wrap_content"
     75 + android:layout_height="wrap_content"
     76 + android:text="Enable Proxy"
     77 + app:layout_constraintEnd_toEndOf="parent"
     78 + app:layout_constraintStart_toStartOf="parent"
     79 + app:layout_constraintTop_toBottomOf="@+id/downloadProxyLibButton" />
     80 + 
     81 + <ProgressBar
     82 + android:id="@+id/progressBarDump"
     83 + style="?android:attr/progressBarStyleHorizontal"
     84 + android:layout_width="match_parent"
     85 + android:layout_height="wrap_content"
     86 + android:layout_marginStart="5dp"
     87 + android:layout_marginEnd="5dp"
     88 + app:layout_constraintEnd_toEndOf="parent"
     89 + app:layout_constraintStart_toStartOf="parent"
     90 + app:layout_constraintTop_toBottomOf="@+id/enableHookCheckbox" />
     91 + 
     92 + <TextView
     93 + android:id="@+id/downloadDumpStatus"
     94 + android:layout_width="wrap_content"
     95 + android:layout_height="wrap_content"
     96 + android:text="TextView"
     97 + app:layout_constraintEnd_toEndOf="parent"
     98 + app:layout_constraintStart_toStartOf="parent"
     99 + app:layout_constraintTop_toBottomOf="@+id/progressBarDump" />
     100 + 
     101 + <Button
     102 + android:id="@+id/downloadClassDumpButton"
     103 + android:layout_width="wrap_content"
     104 + android:layout_height="wrap_content"
     105 + android:text="Download Class Dump Lib"
     106 + app:layout_constraintEnd_toEndOf="parent"
     107 + app:layout_constraintStart_toStartOf="parent"
     108 + app:layout_constraintTop_toBottomOf="@+id/downloadDumpStatus" />
     109 + 
     110 + <CheckBox
     111 + android:id="@+id/enableDumpHookCheckbox"
     112 + android:layout_width="wrap_content"
     113 + android:layout_height="wrap_content"
     114 + android:text="Enable Class Dump"
     115 + app:layout_constraintEnd_toEndOf="parent"
     116 + app:layout_constraintStart_toStartOf="parent"
     117 + app:layout_constraintTop_toBottomOf="@+id/downloadClassDumpButton" />
     118 + 
     119 + 
     120 + 
     121 +</androidx.constraintlayout.widget.ConstraintLayout>
  • ■ ■ ■ ■ ■ ■
    app/src/main/res/layout/activity_main.xml
     1 +<?xml version="1.0" encoding="utf-8"?>
     2 +<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
     3 + xmlns:app="http://schemas.android.com/apk/res-auto"
     4 + android:id="@+id/drawer_layout"
     5 + android:layout_width="match_parent"
     6 + android:layout_height="match_parent">
     7 + 
     8 + <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
     9 + xmlns:app="http://schemas.android.com/apk/res-auto"
     10 + xmlns:tools="http://schemas.android.com/tools"
     11 + android:layout_width="match_parent"
     12 + android:layout_height="wrap_content"
     13 + tools:context=".MainActivity">
     14 + <androidx.recyclerview.widget.RecyclerView
     15 + android:id="@+id/recyclerView"
     16 + android:layout_width="match_parent"
     17 + android:layout_height="wrap_content"
     18 + android:padding="8dp"
     19 + app:layout_constraintTop_toTopOf="parent"
     20 + />
     21 + </androidx.constraintlayout.widget.ConstraintLayout>
     22 + 
     23 + <com.google.android.material.navigation.NavigationView
     24 + android:id="@+id/nav_view"
     25 + android:layout_width="wrap_content"
     26 + android:layout_height="match_parent"
     27 + android:layout_gravity="start"
     28 + app:menu="@menu/drawer_menu" />
     29 + 
     30 +</androidx.drawerlayout.widget.DrawerLayout>
  • ■ ■ ■ ■ ■ ■
    app/src/main/res/layout/activity_set_host.xml
     1 +<?xml version="1.0" encoding="utf-8"?>
     2 +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
     3 + xmlns:app="http://schemas.android.com/apk/res-auto"
     4 + xmlns:tools="http://schemas.android.com/tools"
     5 + android:id="@+id/main"
     6 + android:layout_width="match_parent"
     7 + android:layout_height="match_parent"
     8 + tools:context=".SetHostActivity">
     9 + 
     10 + <TextView
     11 + android:id="@+id/textView2"
     12 + android:layout_width="wrap_content"
     13 + android:layout_height="wrap_content"
     14 + android:layout_marginTop="100dp"
     15 + android:text="Proxy IP"
     16 + app:layout_constraintEnd_toEndOf="parent"
     17 + app:layout_constraintStart_toStartOf="parent"
     18 + app:layout_constraintTop_toTopOf="parent" />
     19 + 
     20 + <EditText
     21 + android:id="@+id/proxyIP"
     22 + android:layout_width="wrap_content"
     23 + android:layout_height="wrap_content"
     24 + android:layout_marginTop="50dp"
     25 + android:ems="10"
     26 + android:inputType="text"
     27 + android:text="192.168.1.1"
     28 + app:layout_constraintEnd_toEndOf="parent"
     29 + app:layout_constraintStart_toStartOf="parent"
     30 + app:layout_constraintTop_toBottomOf="@+id/textView2" />
     31 + 
     32 + <TextView
     33 + android:id="@+id/IPStatus"
     34 + android:layout_width="wrap_content"
     35 + android:layout_height="wrap_content"
     36 + android:layout_marginTop="20dp"
     37 + android:text="TextView"
     38 + app:layout_constraintEnd_toEndOf="parent"
     39 + app:layout_constraintStart_toStartOf="parent"
     40 + app:layout_constraintTop_toBottomOf="@+id/proxyIP" />
     41 +</androidx.constraintlayout.widget.ConstraintLayout>
  • ■ ■ ■ ■ ■ ■
    app/src/main/res/layout/list_item.xml
     1 +<?xml version="1.0" encoding="utf-8"?>
     2 +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     3 + android:layout_width="match_parent"
     4 + android:layout_height="wrap_content">
     5 + <TextView
     6 + android:id="@+id/app_name"
     7 + android:layout_width="match_parent"
     8 + android:layout_height="wrap_content"
     9 + android:padding="8dp"
     10 + android:textSize="18sp"/>
     11 +</LinearLayout>
  • ■ ■ ■ ■ ■ ■
    app/src/main/res/menu/drawer_menu.xml
     1 +<?xml version="1.0" encoding="utf-8"?>
     2 +<menu xmlns:android="http://schemas.android.com/apk/res/android"
     3 + xmlns:tools="http://schemas.android.com/tools"
     4 + xmlns:app="http://schemas.android.com/apk/res-auto">
     5 + <group android:checkableBehavior="single" >
     6 + <item
     7 + android:id="@+id/nav_set_host"
     8 + android:title="Set Proxy Host" />
     9 + <item
     10 + android:id="@+id/nav_about"
     11 + android:title="About" />
     12 + <!-- Add more items here -->
     13 + </group>
     14 +</menu>
  • ■ ■ ■ ■ ■ ■
    app/src/main/res/menu/top_menu.xml
     1 +<?xml version="1.0" encoding="utf-8"?>
     2 +<menu xmlns:android="http://schemas.android.com/apk/res/android"
     3 + xmlns:tools="http://schemas.android.com/tools"
     4 + xmlns:app="http://schemas.android.com/apk/res-auto">
     5 + <group android:checkableBehavior="single" >
     6 + <item
     7 + android:id="@+id/action_search"
     8 + android:icon="@android:drawable/ic_menu_search"
     9 + android:title="Search"
     10 + app:actionViewClass="androidx.appcompat.widget.SearchView"
     11 + app:showAsAction="always|collapseActionView" />
     12 + </group>
     13 +</menu>
  • ■ ■ ■ ■ ■ ■
    app/src/main/res/mipmap-anydpi/ic_launcher.xml
     1 +<?xml version="1.0" encoding="utf-8"?>
     2 +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
     3 + <background android:drawable="@drawable/ic_launcher_background" />
     4 + <foreground android:drawable="@drawable/ic_launcher_foreground" />
     5 + <monochrome android:drawable="@drawable/ic_launcher_foreground" />
     6 +</adaptive-icon>
  • ■ ■ ■ ■ ■ ■
    app/src/main/res/mipmap-anydpi/ic_launcher_round.xml
     1 +<?xml version="1.0" encoding="utf-8"?>
     2 +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
     3 + <background android:drawable="@drawable/ic_launcher_background" />
     4 + <foreground android:drawable="@drawable/ic_launcher_foreground" />
     5 + <monochrome android:drawable="@drawable/ic_launcher_foreground" />
     6 +</adaptive-icon>
  • app/src/main/res/mipmap-hdpi/ic_launcher.webp
  • app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
  • app/src/main/res/mipmap-mdpi/ic_launcher.webp
  • app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
  • app/src/main/res/mipmap-xhdpi/ic_launcher.webp
  • app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
  • app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
  • app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
  • app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
  • app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
  • ■ ■ ■ ■ ■ ■
    app/src/main/res/values/colors.xml
     1 +<?xml version="1.0" encoding="utf-8"?>
     2 +<resources>
     3 + <color name="purple_200">#FFBB86FC</color>
     4 + <color name="purple_500">#FF6200EE</color>
     5 + <color name="purple_700">#FF3700B3</color>
     6 + <color name="teal_200">#FF03DAC5</color>
     7 + <color name="teal_700">#FF018786</color>
     8 + <color name="black">#FF000000</color>
     9 + <color name="white">#FFFFFFFF</color>
     10 +</resources>
  • ■ ■ ■ ■ ■ ■
    app/src/main/res/values/ids.xml
     1 +<?xml version="1.0" encoding="utf-8"?>
     2 +<resources>
     3 + 
     4 +</resources>
  • ■ ■ ■ ■ ■ ■
    app/src/main/res/values/strings.xml
     1 +<resources>
     2 + <string name="app_name">ZygiskReflutter</string>
     3 + <string name="open">open</string>
     4 + <string name="close">close</string>
     5 +</resources>
     6 + 
  • ■ ■ ■ ■ ■ ■
    app/src/main/res/values/themes.xml
     1 +<resources xmlns:tools="http://schemas.android.com/tools">
     2 + <!-- Base application theme. -->
     3 + <style name="Theme.Zygiskreflutter" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
     4 + <!-- Primary brand color. -->
     5 + <item name="colorPrimary">@color/purple_500</item>
     6 + <item name="colorPrimaryVariant">@color/purple_700</item>
     7 + <item name="colorOnPrimary">@color/white</item>
     8 + <!-- Secondary brand color. -->
     9 + <item name="colorSecondary">@color/teal_200</item>
     10 + <item name="colorSecondaryVariant">@color/teal_700</item>
     11 + <item name="colorOnSecondary">@color/black</item>
     12 + <!-- Status bar color. -->
     13 + <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
     14 + <!-- Customize your theme here. -->
     15 + </style>
     16 +</resources>
     17 + 
  • ■ ■ ■ ■ ■ ■
    app/src/main/res/values-night/themes.xml
     1 +<resources xmlns:tools="http://schemas.android.com/tools">
     2 + <!-- Base application theme. -->
     3 + <style name="Theme.Zygiskreflutter" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
     4 + <!-- Primary brand color. -->
     5 + <item name="colorPrimary">@color/purple_200</item>
     6 + <item name="colorPrimaryVariant">@color/purple_700</item>
     7 + <item name="colorOnPrimary">@color/black</item>
     8 + <!-- Secondary brand color. -->
     9 + <item name="colorSecondary">@color/teal_200</item>
     10 + <item name="colorSecondaryVariant">@color/teal_200</item>
     11 + <item name="colorOnSecondary">@color/black</item>
     12 + <!-- Status bar color. -->
     13 + <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
     14 + <!-- Customize your theme here. -->
     15 + </style>
     16 +</resources>
     17 + 
  • ■ ■ ■ ■ ■ ■
    app/src/main/res/xml/backup_rules.xml
     1 +<?xml version="1.0" encoding="utf-8"?><!--
     2 + Sample backup rules file; uncomment and customize as necessary.
     3 + See https://developer.android.com/guide/topics/data/autobackup
     4 + for details.
     5 + Note: This file is ignored for devices older that API 31
     6 + See https://developer.android.com/about/versions/12/backup-restore
     7 +-->
     8 +<full-backup-content>
     9 + <!--
     10 + <include domain="sharedpref" path="."/>
     11 + <exclude domain="sharedpref" path="device.xml"/>
     12 +-->
     13 +</full-backup-content>
  • ■ ■ ■ ■ ■ ■
    app/src/main/res/xml/data_extraction_rules.xml
     1 +<?xml version="1.0" encoding="utf-8"?><!--
     2 + Sample data extraction rules file; uncomment and customize as necessary.
     3 + See https://developer.android.com/about/versions/12/backup-restore#xml-changes
     4 + for details.
     5 +-->
     6 +<data-extraction-rules>
     7 + <cloud-backup>
     8 + <!-- TODO: Use <include> and <exclude> to control what is backed up.
     9 + <include .../>
     10 + <exclude .../>
     11 + -->
     12 + </cloud-backup>
     13 + <!--
     14 + <device-transfer>
     15 + <include .../>
     16 + <exclude .../>
     17 + </device-transfer>
     18 + -->
     19 +</data-extraction-rules>
  • ■ ■ ■ ■ ■ ■
    app/src/test/java/com/tinyhack/zygiskreflutter/ExampleUnitTest.java
     1 +package com.tinyhack.zygiskreflutter;
     2 + 
     3 +import org.junit.Test;
     4 + 
     5 +import static org.junit.Assert.*;
     6 + 
     7 +/**
     8 + * Example local unit test, which will execute on the development machine (host).
     9 + *
     10 + * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
     11 + */
     12 +public class ExampleUnitTest {
     13 + @Test
     14 + public void addition_isCorrect() {
     15 + assertEquals(4, 2 + 2);
     16 + }
     17 +}
     18 + 
  • ■ ■ ■ ■ ■ ■
    build.gradle.kts
     1 +// Top-level build file where you can add configuration options common to all sub-projects/modules.
     2 +plugins {
     3 + alias(libs.plugins.androidApplication) apply false
     4 +}
  • ■ ■ ■ ■ ■ ■
    gradle/libs.versions.toml
     1 +[versions]
     2 +agp = "8.3.1"
     3 +junit = "4.13.2"
     4 +junitVersion = "1.1.5"
     5 +espressoCore = "3.5.1"
     6 +appcompat = "1.6.1"
     7 +material = "1.10.0"
     8 +constraintlayout = "2.1.4"
     9 +activity = "1.8.0"
     10 + 
     11 +[libraries]
     12 +junit = { group = "junit", name = "junit", version.ref = "junit" }
     13 +ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
     14 +espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
     15 +appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
     16 +material = { group = "com.google.android.material", name = "material", version.ref = "material" }
     17 +constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
     18 +activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
     19 + 
     20 +[plugins]
     21 +androidApplication = { id = "com.android.application", version.ref = "agp" }
     22 + 
     23 + 
  • gradle/wrapper/gradle-wrapper.jar
    Binary file.
  • ■ ■ ■ ■ ■ ■
    gradle/wrapper/gradle-wrapper.properties
     1 +#Wed Apr 03 21:36:45 ICT 2024
     2 +distributionBase=GRADLE_USER_HOME
     3 +distributionPath=wrapper/dists
     4 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
     5 +zipStoreBase=GRADLE_USER_HOME
     6 +zipStorePath=wrapper/dists
     7 + 
  • ■ ■ ■ ■ ■ ■
    gradle.properties
     1 +# Project-wide Gradle settings.
     2 +# IDE (e.g. Android Studio) users:
     3 +# Gradle settings configured through the IDE *will override*
     4 +# any settings specified in this file.
     5 +# For more details on how to configure your build environment visit
     6 +# http://www.gradle.org/docs/current/userguide/build_environment.html
     7 +# Specifies the JVM arguments used for the daemon process.
     8 +# The setting is particularly useful for tweaking memory settings.
     9 +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
     10 +# When configured, Gradle will run in incubating parallel mode.
     11 +# This option should only be used with decoupled projects. For more details, visit
     12 +# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
     13 +# org.gradle.parallel=true
     14 +# AndroidX package structure to make it clearer which packages are bundled with the
     15 +# Android operating system, and which are packaged with your app's APK
     16 +# https://developer.android.com/topic/libraries/support-library/androidx-rn
     17 +android.useAndroidX=true
     18 +# Enables namespacing of each library's R class so that its R class includes only the
     19 +# resources declared in the library itself and none from the library's dependencies,
     20 +# thereby reducing the size of the R class for that library
     21 +android.nonTransitiveRClass=true
  • ■ ■ ■ ■ ■ ■
    gradlew
     1 +#!/usr/bin/env sh
     2 + 
     3 +#
     4 +# Copyright 2015 the original author or authors.
     5 +#
     6 +# Licensed under the Apache License, Version 2.0 (the "License");
     7 +# you may not use this file except in compliance with the License.
     8 +# You may obtain a copy of the License at
     9 +#
     10 +# https://www.apache.org/licenses/LICENSE-2.0
     11 +#
     12 +# Unless required by applicable law or agreed to in writing, software
     13 +# distributed under the License is distributed on an "AS IS" BASIS,
     14 +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15 +# See the License for the specific language governing permissions and
     16 +# limitations under the License.
     17 +#
     18 + 
     19 +##############################################################################
     20 +##
     21 +## Gradle start up script for UN*X
     22 +##
     23 +##############################################################################
     24 + 
     25 +# Attempt to set APP_HOME
     26 +# Resolve links: $0 may be a link
     27 +PRG="$0"
     28 +# Need this for relative symlinks.
     29 +while [ -h "$PRG" ] ; do
     30 + ls=`ls -ld "$PRG"`
     31 + link=`expr "$ls" : '.*-> \(.*\)$'`
     32 + if expr "$link" : '/.*' > /dev/null; then
     33 + PRG="$link"
     34 + else
     35 + PRG=`dirname "$PRG"`"/$link"
     36 + fi
     37 +done
     38 +SAVED="`pwd`"
     39 +cd "`dirname \"$PRG\"`/" >/dev/null
     40 +APP_HOME="`pwd -P`"
     41 +cd "$SAVED" >/dev/null
     42 + 
     43 +APP_NAME="Gradle"
     44 +APP_BASE_NAME=`basename "$0"`
     45 + 
     46 +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
     47 +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
     48 + 
     49 +# Use the maximum available, or set MAX_FD != -1 to use that value.
     50 +MAX_FD="maximum"
     51 + 
     52 +warn () {
     53 + echo "$*"
     54 +}
     55 + 
     56 +die () {
     57 + echo
     58 + echo "$*"
     59 + echo
     60 + exit 1
     61 +}
     62 + 
     63 +# OS specific support (must be 'true' or 'false').
     64 +cygwin=false
     65 +msys=false
     66 +darwin=false
     67 +nonstop=false
     68 +case "`uname`" in
     69 + CYGWIN* )
     70 + cygwin=true
     71 + ;;
     72 + Darwin* )
     73 + darwin=true
     74 + ;;
     75 + MINGW* )
     76 + msys=true
     77 + ;;
     78 + NONSTOP* )
     79 + nonstop=true
     80 + ;;
     81 +esac
     82 + 
     83 +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
     84 + 
     85 + 
     86 +# Determine the Java command to use to start the JVM.
     87 +if [ -n "$JAVA_HOME" ] ; then
     88 + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
     89 + # IBM's JDK on AIX uses strange locations for the executables
     90 + JAVACMD="$JAVA_HOME/jre/sh/java"
     91 + else
     92 + JAVACMD="$JAVA_HOME/bin/java"
     93 + fi
     94 + if [ ! -x "$JAVACMD" ] ; then
     95 + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
     96 + 
     97 +Please set the JAVA_HOME variable in your environment to match the
     98 +location of your Java installation."
     99 + fi
     100 +else
     101 + JAVACMD="java"
     102 + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
     103 + 
     104 +Please set the JAVA_HOME variable in your environment to match the
     105 +location of your Java installation."
     106 +fi
     107 + 
     108 +# Increase the maximum file descriptors if we can.
     109 +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
     110 + MAX_FD_LIMIT=`ulimit -H -n`
     111 + if [ $? -eq 0 ] ; then
     112 + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
     113 + MAX_FD="$MAX_FD_LIMIT"
     114 + fi
     115 + ulimit -n $MAX_FD
     116 + if [ $? -ne 0 ] ; then
     117 + warn "Could not set maximum file descriptor limit: $MAX_FD"
     118 + fi
     119 + else
     120 + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
     121 + fi
     122 +fi
     123 + 
     124 +# For Darwin, add options to specify how the application appears in the dock
     125 +if $darwin; then
     126 + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
     127 +fi
     128 + 
     129 +# For Cygwin or MSYS, switch paths to Windows format before running java
     130 +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
     131 + APP_HOME=`cygpath --path --mixed "$APP_HOME"`
     132 + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
     133 + 
     134 + JAVACMD=`cygpath --unix "$JAVACMD"`
     135 + 
     136 + # We build the pattern for arguments to be converted via cygpath
     137 + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
     138 + SEP=""
     139 + for dir in $ROOTDIRSRAW ; do
     140 + ROOTDIRS="$ROOTDIRS$SEP$dir"
     141 + SEP="|"
     142 + done
     143 + OURCYGPATTERN="(^($ROOTDIRS))"
     144 + # Add a user-defined pattern to the cygpath arguments
     145 + if [ "$GRADLE_CYGPATTERN" != "" ] ; then
     146 + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
     147 + fi
     148 + # Now convert the arguments - kludge to limit ourselves to /bin/sh
     149 + i=0
     150 + for arg in "$@" ; do
     151 + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
     152 + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
     153 + 
     154 + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
     155 + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
     156 + else
     157 + eval `echo args$i`="\"$arg\""
     158 + fi
     159 + i=`expr $i + 1`
     160 + done
     161 + case $i in
     162 + 0) set -- ;;
     163 + 1) set -- "$args0" ;;
     164 + 2) set -- "$args0" "$args1" ;;
     165 + 3) set -- "$args0" "$args1" "$args2" ;;
     166 + 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
     167 + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
     168 + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
     169 + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
     170 + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
     171 + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
     172 + esac
     173 +fi
     174 + 
     175 +# Escape application args
     176 +save () {
     177 + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
     178 + echo " "
     179 +}
     180 +APP_ARGS=`save "$@"`
     181 + 
     182 +# Collect all arguments for the java command, following the shell quoting and substitution rules
     183 +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
     184 + 
     185 +exec "$JAVACMD" "$@"
     186 + 
  • ■ ■ ■ ■ ■ ■
    gradlew.bat
     1 +@rem
     2 +@rem Copyright 2015 the original author or authors.
     3 +@rem
     4 +@rem Licensed under the Apache License, Version 2.0 (the "License");
     5 +@rem you may not use this file except in compliance with the License.
     6 +@rem You may obtain a copy of the License at
     7 +@rem
     8 +@rem https://www.apache.org/licenses/LICENSE-2.0
     9 +@rem
     10 +@rem Unless required by applicable law or agreed to in writing, software
     11 +@rem distributed under the License is distributed on an "AS IS" BASIS,
     12 +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 +@rem See the License for the specific language governing permissions and
     14 +@rem limitations under the License.
     15 +@rem
     16 + 
     17 +@if "%DEBUG%" == "" @echo off
     18 +@rem ##########################################################################
     19 +@rem
     20 +@rem Gradle startup script for Windows
     21 +@rem
     22 +@rem ##########################################################################
     23 + 
     24 +@rem Set local scope for the variables with windows NT shell
     25 +if "%OS%"=="Windows_NT" setlocal
     26 + 
     27 +set DIRNAME=%~dp0
     28 +if "%DIRNAME%" == "" set DIRNAME=.
     29 +set APP_BASE_NAME=%~n0
     30 +set APP_HOME=%DIRNAME%
     31 + 
     32 +@rem Resolve any "." and ".." in APP_HOME to make it shorter.
     33 +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
     34 + 
     35 +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
     36 +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
     37 + 
     38 +@rem Find java.exe
     39 +if defined JAVA_HOME goto findJavaFromJavaHome
     40 + 
     41 +set JAVA_EXE=java.exe
     42 +%JAVA_EXE% -version >NUL 2>&1
     43 +if "%ERRORLEVEL%" == "0" goto execute
     44 + 
     45 +echo.
     46 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
     47 +echo.
     48 +echo Please set the JAVA_HOME variable in your environment to match the
     49 +echo location of your Java installation.
     50 + 
     51 +goto fail
     52 + 
     53 +:findJavaFromJavaHome
     54 +set JAVA_HOME=%JAVA_HOME:"=%
     55 +set JAVA_EXE=%JAVA_HOME%/bin/java.exe
     56 + 
     57 +if exist "%JAVA_EXE%" goto execute
     58 + 
     59 +echo.
     60 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
     61 +echo.
     62 +echo Please set the JAVA_HOME variable in your environment to match the
     63 +echo location of your Java installation.
     64 + 
     65 +goto fail
     66 + 
     67 +:execute
     68 +@rem Setup the command line
     69 + 
     70 +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
     71 + 
     72 + 
     73 +@rem Execute Gradle
     74 +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
     75 + 
     76 +:end
     77 +@rem End local scope for the variables with windows NT shell
     78 +if "%ERRORLEVEL%"=="0" goto mainEnd
     79 + 
     80 +:fail
     81 +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
     82 +rem the _cmd.exe /c_ return code!
     83 +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
     84 +exit /b 1
     85 + 
     86 +:mainEnd
     87 +if "%OS%"=="Windows_NT" endlocal
     88 + 
     89 +:omega
     90 + 
  • ■ ■ ■ ■ ■ ■
    module/META-INF/com/google/android/update-binary
     1 +#!/sbin/sh
     2 + 
     3 +#################
     4 +# Initialization
     5 +#################
     6 + 
     7 +umask 022
     8 + 
     9 +# echo before loading util_functions
     10 +ui_print() { echo "$1"; }
     11 + 
     12 +require_new_magisk() {
     13 + ui_print "*******************************"
     14 + ui_print " Please install Magisk v20.4+! "
     15 + ui_print "*******************************"
     16 + exit 1
     17 +}
     18 + 
     19 +#########################
     20 +# Load util_functions.sh
     21 +#########################
     22 + 
     23 +OUTFD=$2
     24 +ZIPFILE=$3
     25 + 
     26 +mount /data 2>/dev/null
     27 + 
     28 +[ -f /data/adb/magisk/util_functions.sh ] || require_new_magisk
     29 +. /data/adb/magisk/util_functions.sh
     30 +[ $MAGISK_VER_CODE -lt 20400 ] && require_new_magisk
     31 + 
     32 +install_module
     33 +exit 0
     34 + 
  • ■ ■ ■ ■ ■
    module/META-INF/com/google/android/updater-script
     1 +#MAGISK
  • ■ ■ ■ ■ ■ ■
    module/module.prop
     1 +id=zygiskreflutter
     2 +name=ZygiskReflutter
     3 +version=1.0
     4 +versionCode=1
     5 +author=Yohanes Nugroho
     6 +description=Zygisk module for flutter Reverse Engineering
     7 +updateJson=
     8 + 
  • ■ ■ ■ ■ ■ ■
    settings.gradle.kts
     1 +pluginManagement {
     2 + repositories {
     3 + google {
     4 + content {
     5 + includeGroupByRegex("com\\.android.*")
     6 + includeGroupByRegex("com\\.google.*")
     7 + includeGroupByRegex("androidx.*")
     8 + }
     9 + }
     10 + mavenCentral()
     11 + gradlePluginPortal()
     12 + }
     13 +}
     14 +dependencyResolutionManagement {
     15 + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
     16 + repositories {
     17 + google()
     18 + mavenCentral()
     19 + }
     20 +}
     21 + 
     22 +rootProject.name = "joezygisk"
     23 +include(":app")
     24 +
Please wait...
Page is in error, reload to recover