diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..92f49bf5d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,25 @@ +* text=auto + +*.txt text eol=lf +*.xml text eol=lf +*.json text eol=lf +*.java text eol=lf +*.kt text eol=lf +*.c text eol=lf +*.h text eol=lf +*.cpp text eol=lf +*.py text eol=lf +*.mk text eol=lf +*.pro text eol=lf +*.properties text eol=lf +*.conf text eol=lf +*.awk text eol=lf +*.sed text eol=lf +*.sh text eol=lf + +*.attheme text eol=lf + +*.png binary +*.jpg binary + +*.p12 binary \ No newline at end of file diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 6c1a7939e..77eca3a48 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1 @@ -patreon: NekoWorkshop +patreon: NekoXDev diff --git a/.github/workflows/canary.yml b/.github/workflows/canary.yml new file mode 100644 index 000000000..782bb6927 --- /dev/null +++ b/.github/workflows/canary.yml @@ -0,0 +1,30 @@ +name: Canary Build + +on: + push: + branches: + - master + +jobs: + canaryBuild: + name: Canary Build + runs-on: ubuntu-latest + if: "!endsWith(github.event.head_commit.message, '~')" + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-java@v1 + with: + java-version: 1.8 + - name: Run Gradle Build + run: | + git submodule update --init --recursive + sudo bash < $HOME/.gnupg/sshcontrol + + export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket) + gpgconf --launch gpg-agent + + git config --global user.name "世界" + git config --global user.email "i@nekox.me" + git config --global user.signingkey "DA55ACBFDDA40853BFC5496ECD109927C34A63C4" + git config --global commit.gpgsign true + + git init + + git remote add github git@github.com:NekogramX/NekoX-Resources.git + git remote add gitlab git@gitlab.com:NekohaSekai/nekox-resources.git + git remote add gitee git@gitee.com:nekoshizuku/nekox-resources.git + + git add . --all + git commit -m "${{ github.event.head_commit.message }}" + + git push github master -f + git push gitlab master -f + + git checkout -b release + git push gitee release -f + with: + serviceAccountJsonPlainText: ${{ secrets.GOOGLE_ACCOUNT_SERVICE }} + packageName: nekox.messenger + releaseFile: ${{ env.AAB_FILE }} + track: production \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 213b00ee2..41786982b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,45 @@ [submodule "TMessagesProj/jni/boringssl"] path = TMessagesProj/jni/boringssl url = https://github.com/google/boringssl +[submodule "ss-libev/src/main/jni/shadowsocks-libev"] + path = ss-libev/src/main/jni/shadowsocks-libev + url = https://github.com/shadowsocks/shadowsocks-libev +[submodule "ss-libev/src/main/jni/libancillary"] + path = ss-libev/src/main/jni/libancillary + url = https://github.com/shadowsocks/libancillary.git + branch = shadowsocks-android +[submodule "ss-libev/src/main/jni/mbedtls"] + path = ss-libev/src/main/jni/mbedtls + url = https://github.com/ARMmbed/mbedtls +[submodule "ss-libev/src/main/jni/pcre"] + path = ss-libev/src/main/jni/pcre + url = https://android.googlesource.com/platform/external/pcre +[submodule "ss-libev/src/main/jni/libsodium"] + path = ss-libev/src/main/jni/libsodium + url = https://github.com/jedisct1/libsodium.git +[submodule "ss-libev/src/main/jni/libev"] + path = ss-libev/src/main/jni/libev + url = https://git.lighttpd.net/libev.git +[submodule "ss-libev/src/main/jni/re2"] + path = ss-libev/src/main/jni/re2 + url = https://github.com/google/re2.git + +[submodule "shadowsocksr-libev"] + path = ssr-libev/src/main/jni/shadowsocks-libev + url = https://github.com/shadowsocksRb/shadowsocksr-libev.git +[submodule "ssr-libev/src/main/jni/libancillary"] + path = ssr-libev/src/main/jni/libancillary + url = https://github.com/shadowsocks/libancillary.git +[submodule "ssr-libev/src/main/jni/mbedtls"] + path = ssr-libev/src/main/jni/mbedtls + url = https://github.com/ARMmbed/mbedtls +[submodule "ssr-libev/src/main/jni/pcre"] + path = ssr-libev/src/main/jni/pcre + url = https://android.googlesource.com/platform/external/pcre +[submodule "ssr-libev/src/main/jni/libsodium"] + path = ssr-libev/src/main/jni/libsodium + url = https://github.com/jedisct1/libsodium.git + branch = stable +[submodule "ssr-libev/src/main/jni/re2"] + path = ssr-libev/src/main/jni/re2 + url = https://github.com/google/re2.git diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c3adedcd1..000000000 --- a/.travis.yml +++ /dev/null @@ -1,33 +0,0 @@ -language: android -dist: trusty -jdk: - - openjdk8 -android: - components: - - build-tools-29.0.2 - - android-29 -addons: - apt_packages: - - ninja-build - -install: - - set -e - - echo y | sdkmanager "ndk-bundle" - - echo y | sdkmanager "cmake;3.6.4111459" - - echo y | sdkmanager "lldb;3.1" -before_script: - - export NDK=$ANDROID_HOME/ndk-bundle - - export NINJA_PATH=/usr/bin/ninja - - export PATH=`echo $ANDROID_HOME/cmake/*/bin`:$PATH -script: - # If some stage fails, exit inmediatly - - set -e - # Prebuild - - "cd TMessagesProj/jni" - - "./build_ffmpeg_clang.sh" - - "./patch_ffmpeg.sh" - - "./patch_boringssl.sh" - - "./build_boringssl.sh" - - "cd ../.." - # Build - - "./gradlew assembleAfatRelease" \ No newline at end of file diff --git a/LICENSE b/LICENSE index d159169d1..f288702d2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,281 +1,622 @@ GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 + Version 3, 29 June 2007 - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + TERMS AND CONDITIONS - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". + 0. Definitions. -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. + "This License" refers to version 3 of the GNU General Public License. - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. + A "covered work" means either the unmodified Program or a work based +on the Program. - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. + 1. Source Code. -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. + The Corresponding Source for a work in source code form is that +same work. -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. + 2. Basic Permissions. - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of this License. - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. + 13. Use with the GNU Affero General Public License. -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. + 14. Revised Versions of this License. - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. - NO WARRANTY + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. + 15. Disclaimer of Warranty. - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. END OF TERMS AND CONDITIONS @@ -287,15 +628,15 @@ free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least +state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) - This program is free software; you can redistribute it and/or modify + This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or + the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, @@ -303,37 +644,31 @@ the "copyright" line and a pointer to where the full notice is found. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + You should have received a copy of the GNU General Public License + along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md index 398f1aa7d..4b29d9284 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,12 @@ -# Nekogram -![Logo](https://raw.githubusercontent.com/Nekogram/Nekogram/master/TMessagesProj/src/main/res/mipmap-xxxhdpi/ic_launcher.png) -Nekogram is an UNOFFICIAL app that uses Telegram's API. +# NekoX +![Logo](https://raw.githubusercontent.com/NekogramX/NekoX/master/TMessagesProj/src/main/res/mipmap-xxxhdpi/ic_launcher.png) -- Google play store: https://play.google.com/store/apps/details?id=tw.nekomimi.nekogram -- Update news (English): https://t.me/nekoupdates -- Update news (Chinese): https://t.me/zuragram -- APKs: https://github.com/Nekogram/Nekogram/releases -- Feedback: https://t.me/nekosupportbot -- Feedback: https://github.com/Nekogram/Nekogram/issues +NekogramX is an UNOFFICIAL app that uses Telegram's API. + +- Google play store: (unavailable now) +- Update news : https://t.me/NekogramX +- Feedback: https://t.me/NekoXChat +- Feedback: https://github.com/NekogramX/NekoX/issues ## API, Protocol documentation @@ -17,26 +16,67 @@ MTproto protocol manuals: https://core.telegram.org/mtproto ## Compilation Guide -1. Download the Nekogram source code from https://github.com/Nekogram/Nekogram-FOSS ( git clone https://github.com/Nekogram/Nekogram-FOSS.git ) -2. Copy your release.keystore into TMessagesProj/config -3. Fill out RELEASE_KEY_PASSWORD, RELEASE_KEY_ALIAS, RELEASE_STORE_PASSWORD in local.properties to access your release.keystore -4. Open the project in the Studio (note that it should be opened, NOT imported). -5. If you're compiling DEBUG version, make sure your build variants is set to afatDebugMultidex. -6. You are ready to compile Nekogram. +### Specify APP_ID and APP_HASH + +Just fill out TELEGRAM_APP_ID and TELEGRAM_APP_HASH in local.properties + +### Build Types + +#### Debug + +`./gradlew assembleDebug` + +The default debug key is used, and placing yours is not needed. + +#### Release + +`./gradlew assembleRelease` + +The difference between release and other build types is that it adds fcm and firebase crash analysis, if you don't like them, use releaseNoGcm. + +To compile the release version, please place your keysotre at TMessageProj/release.jks, and fill in KEYSTORE_PASS, ALIAS_NAME, ALIAS_PASS in local.properties, environment variables are also recommended + +If you don't use NekoX's APP_ID and APP_HASH, you need to register a physical firebase app and replace google-services.json to ensure fcm works + +#### Foss + +`./gradlew assembleFoss` + +OK, a version without firebase cloud messaging and precompiled native libraries, maybe this makes you feel more free, or your phone does not have Google services. + +To compile the foss version, please refer to [this script](.github/workflows/release.yml). + +### Build Variants + +Available variant list: + +`Afat`, `Legacy`, `MinApi21` + ## Localization -Nekogram is forked from Telegram, thus most locales follows the translations of Telegram for Android, checkout https://translations.telegram.org/en/android/. +NekoX is forked from Nekogram-FOSS, thus most locales follows the translations of Telegram for Android, checkout https://translations.telegram.org/en/android/. -As for the Nekogram specialized strings, we use Crowdin to translate Nekogram. Join project at https://neko.crowdin.com/nekogram. Help us bring Nekogram to the world! +As for the specialized strings, we use Crowdin to translate Nekogram. Join project at https://neko.crowdin.com/nekogram and https://nekox.crowdin.com/nekox. Help us bring Nekogram to the world! + +## Credits + + ## Contributors Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): -| [
猫耳逆变器](https://github.com/NekoInverter)
[💻](https://github.com/Nekogram/Nekogram/commits?author=NekoInverter "Code") | [
梨子](https://github.com/rikakomoe)
[💻](https://github.com/Nekogram/Nekogram/commits?author=rikakomoe "Code") | [
呆瓜](https://t.me/Duang)
[🎨](#design-duang "Design") | -| :---: | :---: | :---: | +| [
世界](https://github.com/nekohasekai)
[💻](https://github.com/NekogramX/NekoX/commits?author=nekohasekai "Code") | [
猫耳逆变器](https://github.com/NekoInverter)
[💻](https://github.com/NekogramX/NekoX/commits?author=NekoInverter "Code") | [
梨子](https://github.com/rikakomoe)
[💻](https://github.com/NekogramX/NekoX/commits?author=rikakomoe "Code") | [
呆瓜](https://t.me/Duang)
[🎨](#design-duang "Design") | +| :---: | :---: | :---: | :---: | -This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome! +This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome! \ No newline at end of file diff --git a/TMessagesProj/appcenter-post-build.sh b/TMessagesProj/appcenter-post-build.sh deleted file mode 100644 index 24f8f209a..000000000 --- a/TMessagesProj/appcenter-post-build.sh +++ /dev/null @@ -1,4 +0,0 @@ -if [ "$AGENT_JOBSTATUS" == "Succeeded" ]; then - export name=$(find $APPCENTER_OUTPUT_DIRECTORY -name '*.apk') - curl https://api.telegram.org/bot${BOT_TOKEN}/sendDocument -X POST -F chat_id="$CHANNEL_ID" -F document="@$name" -fi \ No newline at end of file diff --git a/TMessagesProj/build.gradle b/TMessagesProj/build.gradle index a0c820da6..bd26f282d 100644 --- a/TMessagesProj/build.gradle +++ b/TMessagesProj/build.gradle @@ -1,37 +1,47 @@ apply plugin: 'com.android.application' -repositories { - mavenCentral() - google() - jcenter() -} +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' configurations { compile.exclude module: 'support-v4' } dependencies { - implementation 'androidx.core:core:1.1.0-beta01' + + implementation 'androidx.core:core:1.3.0-alpha02' implementation 'androidx.palette:palette:1.0.0' implementation 'androidx.viewpager:viewpager:1.0.0' implementation 'androidx.exifinterface:exifinterface:1.1.0' - compileOnly 'org.checkerframework:checker-qual:2.5.2' - compileOnly 'org.checkerframework:checker-compat-qual:2.5.0' - implementation 'com.googlecode.mp4parser:isoparser:1.0.6' - implementation 'com.stripe:stripe-android:2.0.2' + compileOnly 'org.checkerframework:checker-qual:3.2.0' + compileOnly 'org.checkerframework:checker-compat-qual:2.5.5' + implementation 'com.googlecode.mp4parser:isoparser:1.1.22' implementation 'com.google.code.gson:gson:2.8.6' - implementation 'org.osmdroid:osmdroid-android:6.1.5' + implementation 'org.osmdroid:osmdroid-android:6.1.6' + implementation 'com.google.zxing:core:3.4.0' + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.71" + + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.5" + + def okHttpVersion = '4.4.1' + + implementation "com.squareup.okhttp3:okhttp:$okHttpVersion" + implementation "com.squareup.okhttp3:okhttp-dnsoverhttps:$okHttpVersion" + + implementation files('libs/libv2ray.aar') + + releaseImplementation 'com.google.firebase:firebase-messaging:20.1.3' + releaseImplementation 'com.google.firebase:firebase-crashlytics:17.0.0-beta02' + } android { compileSdkVersion 29 - buildToolsVersion '29.0.2' - //ndkVersion "20.0.5594570" + buildToolsVersion '29.0.3' + ndkVersion "21.0.6113669" - defaultConfig.applicationId = "tw.nekomimi.nekogram" - - sourceSets.main.jniLibs.srcDirs = ['./jni/'] + defaultConfig.applicationId = "nekox.messenger" externalNativeBuild { ndkBuild { @@ -45,6 +55,17 @@ android { disable 'BlockedPrivateApi' } + packagingOptions { + + exclude '/fabric/**' + exclude '/kotlin/**' + exclude '/META-INF/*.version' + exclude '/META-INF/*.kotlin_module' + exclude '/builddef.lst' + exclude '/*.txt' + + } + dexOptions { jumboMode = true } @@ -54,248 +75,188 @@ android { targetCompatibility JavaVersion.VERSION_1_8 } + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8.toString() + } + + signingConfigs { + def keystorePwd = null + def alias = null + def pwd = null + Properties properties; + + if (project.rootProject.file('local.properties').exists()) { + properties = new Properties() + properties.load(project.rootProject.file('local.properties').newDataInputStream()) + } else { + def base64 = System.getenv("LOCAL_PROPERTIES") + if (base64 != null && !base64.isBlank()) { + properties = new Properties() + properties.load(new ByteArrayInputStream(Base64.decoder.decode(base64))) + } + } + + if (properties != null) { + keystorePwd = properties.getProperty("KEYSTORE_PASS") + alias = properties.getProperty("ALIAS_NAME") + pwd = properties.getProperty("ALIAS_PASS") + } + + keystorePwd = keystorePwd ?: System.getenv("KEYSTORE_PASS") + alias = alias ?: System.getenv("ALIAS_NAME") + pwd = pwd ?: System.getenv("ALIAS_PASS") + + release { + + storeFile project.file('release.keystore') + storePassword keystorePwd + keyAlias alias + keyPassword pwd + } + + debug { + storeFile project.file('debug.keystore') + storePassword "android" + keyAlias "androiddebugkey" + keyPassword "android" + } + } + buildTypes { debug { debuggable true jniDebuggable true - applicationIdSuffix ".beta" + signingConfig signingConfigs.debug minifyEnabled true - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - - /*debugAsan { - debuggable true - jniDebuggable true - applicationIdSuffix ".beta" - minifyEnabled true - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - - packagingOptions { - doNotStrip "**.so" - } - - sourceSets { - main { - jniLibs { - srcDir { - 'jniLibs' - } - } - resources { - srcDir { - 'jniRes' - } - } - } - } - }*/ - - debugMultidex { - initWith debug - minifyEnabled false + shrinkResources true multiDexEnabled true - dependencies { - implementation 'com.android.support:multidex:1.0.3' - } - manifestPlaceholders = [applicationClassName: "MultiDexApplicationLoader"] + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } - HA { + releaseNoGcm { + initWith debug debuggable false jniDebuggable false - applicationIdSuffix ".beta" - minifyEnabled true - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + signingConfig signingConfigs.release } release { - debuggable false - jniDebuggable false - minifyEnabled true - shrinkResources false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - multiDexEnabled true - dependencies { - implementation 'com.android.support:multidex:1.0.3' - } - manifestPlaceholders = [applicationClassName: "MultiDexApplicationLoader"] + initWith releaseNoGcm + } + + foss { + initWith release } } + sourceSets.main { + java.srcDirs = ['src/main/java', 'src/multidex/java'] + } + sourceSets.debug { - manifest.srcFile 'config/debug/AndroidManifest.xml' + jniLibs.srcDir 'src/main/libs' + jni.srcDirs = [] } - /*sourceSets.debugAsan { - manifest.srcFile 'config/debug/AndroidManifest.xml' - }*/ - - sourceSets.debugMultidex { - manifest.srcFile 'config/debug/AndroidManifest.xml' - java.srcDirs = ['src/multidex/java'] - } - - sourceSets.HA { - manifest.srcFile 'config/debug/AndroidManifest.xml' + sourceSets.releaseNoGcm { + jniLibs.srcDir 'src/main/libs' + jni.srcDirs = [] } sourceSets.release { - manifest.srcFile 'config/release/AndroidManifest.xml' - java.srcDirs = ['src/multidex/java'] + jniLibs.srcDir 'src/main/libs' + jni.srcDirs = [] + java.srcDirs = ['/src/gservcies/java'] + manifest.srcFile '/src/gservcies/AndroidManifest.xml' } + sourceSets.foss { + jni.srcDirs = ['./jni/'] + } + + flavorDimensions "version" + + splits { + + abi { + + enable true + + reset() + include "armeabi-v7a", "arm64-v8a", "x86", "x86_64" + + universalApk true + + } + + } + + defaultConfig.versionCode = 2 + flavorDimensions "minApi" productFlavors { - armv7 { - ndk { - abiFilters "armeabi-v7a" - } - ext { - abiVersionCode = 1 - } - } - x86 { - ndk { - abiFilters "x86" - } - ext { - abiVersionCode = 2 - } - } - armv7_SDK23 { - ndk { - abiFilters "armeabi-v7a" - } - sourceSets.debug { - manifest.srcFile 'config/debug/AndroidManifest_SDK23.xml' - } - sourceSets.release { - manifest.srcFile 'config/release/AndroidManifest_SDK23.xml' - } - minSdkVersion 23 - ext { - abiVersionCode = 3 - } - } - x86_SDK23 { - ndk { - abiFilters "x86" - } - sourceSets.debug { - manifest.srcFile 'config/debug/AndroidManifest_SDK23.xml' - } - sourceSets.release { - manifest.srcFile 'config/release/AndroidManifest_SDK23.xml' - } - minSdkVersion 23 - ext { - abiVersionCode = 4 - } - } - arm64 { - ndk { - abiFilters "arm64-v8a" - } - ext { - abiVersionCode = 5 - } - } - x64 { - ndk { - abiFilters "x86_64" - } - ext { - abiVersionCode = 6 - } - } - arm64_SDK23 { - ndk { - abiFilters "arm64-v8a" - } - sourceSets.debug { - manifest.srcFile 'config/debug/AndroidManifest_SDK23.xml' - } - /*sourceSets.debugAsan { - manifest.srcFile 'config/debug/AndroidManifest_SDK23.xml' - }*/ - sourceSets.release { - manifest.srcFile 'config/release/AndroidManifest_SDK23.xml' - } - minSdkVersion 23 - ext { - abiVersionCode = 7 - } - } - x64_SDK23 { - ndk { - abiFilters "x86_64" - } - sourceSets.debug { - manifest.srcFile 'config/debug/AndroidManifest_SDK23.xml' - } - sourceSets.release { - manifest.srcFile 'config/release/AndroidManifest_SDK23.xml' - } - minSdkVersion 23 - ext { - abiVersionCode = 8 - } - } afat { - sourceSets.debug { - manifest.srcFile 'config/debug/AndroidManifest_SDK23.xml' + sourceSets.main { + java.srcDirs = ['src/main/java', 'src/multidex/java'] } - /*sourceSets.debugAsan { - manifest.srcFile 'config/debug/AndroidManifest_SDK23.xml' - }*/ - sourceSets.release { - manifest.srcFile 'config/release/AndroidManifest_SDK23.xml' + dependencies { + implementation 'com.android.support:multidex:1.0.3' } - ext { - abiVersionCode = 9 + multiDexEnabled true + manifestPlaceholders = [applicationClassName: "MultiDexApplicationLoader"] + dependencies { + //implementation project(path: ':ss-libev', configuration: 'default') + implementation files('libs/ss-libev-release.aar') + implementation files('libs/ssr-libev-release.aar') } } - } - defaultConfig.versionCode = 10 * 1875 - - applicationVariants.all { variant -> - variant.outputs.all { output -> - output.getProcessManifestProvider().get().doLast { - def abiVersion = variant.productFlavors.get(0).abiVersionCode - - def outputDir = manifestOutputDirectory - File directory - if (outputDir instanceof File) { - directory = outputDir - } else { - directory = outputDir.get().asFile - } - - String manifestPath = directory.toString() + "/AndroidManifest.xml" - def manifestContent = file(manifestPath).getText() - - manifestContent = manifestContent.replace(String.format('android:versionCode="%d"', defaultConfig.versionCode), String.format('android:versionCode="%s"', defaultConfig.versionCode + abiVersion)) - file(manifestPath).write(manifestContent) + legacy { + versionNameSuffix '-legacy' + sourceSets.main { + java.srcDirs = ['src/main/java', 'src/multidex/java'] } + dependencies { + implementation 'com.android.support:multidex:1.0.3' + } + multiDexEnabled true + manifestPlaceholders = [applicationClassName: "MultiDexApplicationLoader"] } - } - variantFilter { variant -> - def names = variant.flavors*.name - if (variant.buildType.name != "release" && !names.contains("afat")) { - setIgnore(true) + minApi21 { + minSdkVersion 21 + versionNameSuffix '-minApi21' + dependencies { + //implementation project(path: ':ss-libev', configuration: 'default') + implementation files('libs/ss-libev-release.aar') + implementation files('libs/ssr-libev-release.aar') + } } } defaultConfig { minSdkVersion 16 - targetSdkVersion 28 - versionName "5.15.0.3" + targetSdkVersion 29 + versionName "5.15.0.28" vectorDrawables.generatedDensities = ['mdpi', 'hdpi', 'xhdpi', 'xxhdpi'] + def appId = null + def appHash = null + + //obtain your own keys at https://core.telegram.org/api/obtaining_api_id + + if (project.rootProject.file('local.properties').exists()) { + Properties properties = new Properties() + properties.load(project.rootProject.file('local.properties').newDataInputStream()) + appId = properties.getProperty("TELEGRAM_APP_ID") ?: System.getenv("TELEGRAM_APP_ID") + appHash = properties.getProperty("TELEGRAM_APP_HASH") ?: System.getenv("TELEGRAM_APP_HASH") + } + + buildConfigField 'int', 'APP_ID', appId != null ? appId : "1391584" + buildConfigField 'String', 'APP_HASH', "\"" + (appHash != null ? appHash : "355c91550b0d658cfb7ff89dcf91a08c") + "\"" + externalNativeBuild { ndkBuild { arguments "NDK_APPLICATION_MK:=jni/Application.mk", "APP_PLATFORM:=android-16", "--jobs=8" @@ -303,6 +264,30 @@ android { } } - manifestPlaceholders = [applicationClassName: "ApplicationLoader"] + manifestPlaceholders = [applicationClassName: "MultiDexApplicationLoader"] + } + + tasks.all { task -> + if (((task.name.endsWith('Ndk') || task.name.startsWith('generateJsonModel') || task.name.startsWith('externalNativeBuild'))) && !task.name.contains("Foss")) { + task.enabled = false + } + } + +} + +apply plugin: 'com.google.gms.google-services' +apply plugin: 'com.google.firebase.crashlytics' + +android { + + tasks.all { task -> + if (((task.name.contains('Crashlytics'))) && !task.name.endsWith("Release")) { + task.enabled = false + } + if (((task.name.endsWith('GoogleServices'))) && !task.name.endsWith("ReleaseGoogleServices")) { + task.enabled = false + } + } + } \ No newline at end of file diff --git a/TMessagesProj/config/debug/AndroidManifest.xml b/TMessagesProj/config/debug/AndroidManifest.xml deleted file mode 100644 index d304dcb1d..000000000 --- a/TMessagesProj/config/debug/AndroidManifest.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/TMessagesProj/config/debug/AndroidManifest_SDK23.xml b/TMessagesProj/config/debug/AndroidManifest_SDK23.xml deleted file mode 100644 index b20da3ea1..000000000 --- a/TMessagesProj/config/debug/AndroidManifest_SDK23.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/TMessagesProj/config/release/AndroidManifest.xml b/TMessagesProj/config/release/AndroidManifest.xml deleted file mode 100644 index 672a25c3a..000000000 --- a/TMessagesProj/config/release/AndroidManifest.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/TMessagesProj/config/release/AndroidManifest_SDK23.xml b/TMessagesProj/config/release/AndroidManifest_SDK23.xml deleted file mode 100644 index 764591e31..000000000 --- a/TMessagesProj/config/release/AndroidManifest_SDK23.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/TMessagesProj/debug.keystore b/TMessagesProj/debug.keystore new file mode 100644 index 000000000..87a727d3f Binary files /dev/null and b/TMessagesProj/debug.keystore differ diff --git a/TMessagesProj/google-services.json b/TMessagesProj/google-services.json new file mode 100644 index 000000000..1dad79539 --- /dev/null +++ b/TMessagesProj/google-services.json @@ -0,0 +1,40 @@ +{ + "project_info": { + "project_number": "124118418489", + "firebase_url": "https://nekox-114.firebaseio.com", + "project_id": "nekox-114", + "storage_bucket": "nekox-114.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:124118418489:android:3dc02bf5a16e92600d39e8", + "android_client_info": { + "package_name": "nekox.messenger" + } + }, + "oauth_client": [ + { + "client_id": "124118418489-lm9tuk7lbedjal3tgr8g68id1ue8gv2m.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyAGMJyrCKui9kUWANoRYbEa3Gt4fsqm1sE" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "124118418489-lm9tuk7lbedjal3tgr8g68id1ue8gv2m.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/TMessagesProj/jni/Android.mk b/TMessagesProj/jni/Android.mk index 63f68d871..9ab37cc4f 100755 --- a/TMessagesProj/jni/Android.mk +++ b/TMessagesProj/jni/Android.mk @@ -580,4 +580,41 @@ LOCAL_SRC_FILES += \ include $(BUILD_SHARED_LIBRARY) +######################################################## +## shadowsocks-libev local +######################################################## + +include $(CLEAR_VARS) + +SHADOWSOCKS_SOURCES := local.c \ + cache.c udprelay.c utils.c netutils.c json.c jconf.c \ + acl.c http.c tls.c rule.c \ + crypto.c aead.c stream.c base64.c \ + plugin.c ppbloom.c \ + android.c + +LOCAL_MODULE := ss-local +LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/src/, $(SHADOWSOCKS_SOURCES)) +LOCAL_CFLAGS := -Wall -fno-strict-aliasing -DMODULE_LOCAL \ + -DUSE_CRYPTO_MBEDTLS -DHAVE_CONFIG_H \ + -DCONNECT_IN_PROGRESS=EINPROGRESS \ + -I$(LOCAL_PATH)/include/shadowsocks-libev \ + -I$(LOCAL_PATH)/include \ + -I$(LOCAL_PATH)/libancillary \ + -I$(LOCAL_PATH)/mbedtls/include \ + -I$(LOCAL_PATH)/pcre \ + -I$(LOCAL_PATH)/libsodium/src/libsodium/include \ + -I$(LOCAL_PATH)/libsodium/src/libsodium/include/sodium \ + -I$(LOCAL_PATH)/shadowsocks-libev/libcork/include \ + -I$(LOCAL_PATH)/shadowsocks-libev/libipset/include \ + -I$(LOCAL_PATH)/shadowsocks-libev/libbloom \ + -I$(LOCAL_PATH)/libev + +LOCAL_STATIC_LIBRARIES := libev libmbedtls libipset libcork libbloom \ + libsodium libancillary libpcre + +LOCAL_LDLIBS := -llog + +include $(BUILD_SHARED_EXECUTABLE) + $(call import-module,android/cpufeatures) \ No newline at end of file diff --git a/TMessagesProj/jni/Application.mk b/TMessagesProj/jni/Application.mk index 8cd479022..a58d64f8f 100644 --- a/TMessagesProj/jni/Application.mk +++ b/TMessagesProj/jni/Application.mk @@ -1,4 +1,4 @@ -APP_PLATFORM := android-16 +APP_PLATFORM := android-18 NDK_TOOLCHAIN_VERSION := clang APP_STL := c++_static APP_SHORT_COMMANDS := true \ No newline at end of file diff --git a/TMessagesProj/jni/TgNetWrapper.cpp b/TMessagesProj/jni/TgNetWrapper.cpp index 008782f22..a5dffb670 100644 --- a/TMessagesProj/jni/TgNetWrapper.cpp +++ b/TMessagesProj/jni/TgNetWrapper.cpp @@ -322,7 +322,7 @@ class Delegate : public ConnectiosManagerDelegate { } }; -void onHostNameResolved(JNIEnv *env, jclass c, jstring host, jlong address, jstring ip) { +void onHostNameResolved(JNIEnv *env, jclass c, jstring host, jlong address, jstring ip,jboolean ipv6) { const char *ipStr = env->GetStringUTFChars(ip, 0); const char *hostStr = env->GetStringUTFChars(host, 0); std::string i = std::string(ipStr); @@ -334,7 +334,7 @@ void onHostNameResolved(JNIEnv *env, jclass c, jstring host, jlong address, jstr env->ReleaseStringUTFChars(host, hostStr); } ConnectionSocket *socket = (ConnectionSocket *) (intptr_t) address; - socket->onHostNameResolved(h, i, false); + socket->onHostNameResolved(h, i, ipv6); } void setLangCode(JNIEnv *env, jclass c, jint instanceNum, jstring langCode) { @@ -445,7 +445,7 @@ static JNINativeMethod ConnectionsManagerMethods[] = { {"native_setJava", "(Z)V", (void *) setJava}, {"native_applyDnsConfig", "(IJLjava/lang/String;I)V", (void *) applyDnsConfig}, {"native_checkProxy", "(ILjava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Lorg/telegram/tgnet/RequestTimeDelegate;)J", (void *) checkProxy}, - {"native_onHostNameResolved", "(Ljava/lang/String;JLjava/lang/String;)V", (void *) onHostNameResolved} + {"native_onHostNameResolved", "(Ljava/lang/String;JLjava/lang/String;Z)V", (void *) onHostNameResolved} }; inline int registerNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int methodsCount) { diff --git a/TMessagesProj/jni/fast-edge.cpp b/TMessagesProj/jni/fast-edge.cpp index 56c17ea74..af8ea6c93 100644 --- a/TMessagesProj/jni/fast-edge.cpp +++ b/TMessagesProj/jni/fast-edge.cpp @@ -1,550 +1,550 @@ -/* - FAST-EDGE - Copyright (c) 2009 Benjamin C. Haynor - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation - files (the "Software"), to deal in the Software without - restriction, including without limitation the rights to use, - copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following - conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include -#include -#include -#include -#include -#include "fast-edge.h" - -#define LOW_THRESHOLD_PERCENTAGE 0.8 // percentage of the high threshold value that the low threshold shall be set at -#define PI 3.14159265 -#define HIGH_THRESHOLD_PERCENTAGE 0.10 // percentage of pixels that meet the high threshold - for example 0.15 will ensure that at least 15% of edge pixels are considered to meet the high threshold - -#define min(X,Y) ((X) < (Y) ? (X) : (Y)) -#define max(X,Y) ((X) < (Y) ? (Y) : (X)) - -namespace ocr{ -/* - CANNY EDGE DETECT - DOES NOT PERFORM NOISE REDUCTION - PERFORM NOISE REDUCTION PRIOR TO USE - Noise reduction omitted, as some applications benefit from morphological operations such as opening or closing as opposed to Gaussian noise reduction - If your application always takes the same size input image, uncomment the definitions of WIDTH and HEIGHT in the header file and define them to the size of your input image, - otherwise the required intermediate arrays will be dynamically allocated. - If WIDTH and HEIGHT are defined, the arrays will be allocated in the compiler directive that follows: -*/ -#ifdef WIDTH -int g[WIDTH * HEIGHT], dir[WIDTH * HEIGHT] = {0}; -unsigned char img_scratch_data[WIDTH * HEIGHT] = {0}; -#endif -void canny_edge_detect(struct image * img_in, struct image * img_out) { - struct image img_scratch; - int high, low; - #ifndef WIDTH - int * g = (int*)calloc(static_cast(img_in->width*img_in->height), sizeof(int)); - int * dir = (int*)calloc(static_cast(img_in->width*img_in->height), sizeof(int)); - unsigned char * img_scratch_data = (unsigned char*)calloc(static_cast(img_in->width*img_in->height), sizeof(char)); - #endif - img_scratch.width = img_in->width; - img_scratch.height = img_in->height; - img_scratch.pixel_data = img_scratch_data; - calc_gradient_sobel(img_in, g, dir); - //printf("*** performing non-maximum suppression ***\n"); - non_max_suppression(&img_scratch, g, dir); - estimate_threshold(&img_scratch, &high, &low); - hysteresis(high, low, &img_scratch, img_out); - #ifndef WIDTH - free(g); - free(dir); - free(img_scratch_data); - #endif -} - -/* - GAUSSIAN_NOISE_ REDUCE - apply 5x5 Gaussian convolution filter, shrinks the image by 4 pixels in each direction, using Gaussian filter found here: - http://en.wikipedia.org/wiki/Canny_edge_detector -*/ -void gaussian_noise_reduce(struct image * img_in, struct image * img_out) -{ - #ifdef CLOCK - clock_t start = clock(); - #endif - int w, h, x, y, max_x, max_y; - w = img_in->width; - h = img_in->height; - img_out->width = w; - img_out->height = h; - max_x = w - 2; - max_y = w * (h - 2); - for (y = w * 2; y < max_y; y += w) { - for (x = 2; x < max_x; x++) { - img_out->pixel_data[x + y] = (2 * img_in->pixel_data[x + y - 2 - w - w] + - 4 * img_in->pixel_data[x + y - 1 - w - w] + - 5 * img_in->pixel_data[x + y - w - w] + - 4 * img_in->pixel_data[x + y + 1 - w - w] + - 2 * img_in->pixel_data[x + y + 2 - w - w] + - 4 * img_in->pixel_data[x + y - 2 - w] + - 9 * img_in->pixel_data[x + y - 1 - w] + - 12 * img_in->pixel_data[x + y - w] + - 9 * img_in->pixel_data[x + y + 1 - w] + - 4 * img_in->pixel_data[x + y + 2 - w] + - 5 * img_in->pixel_data[x + y - 2] + - 12 * img_in->pixel_data[x + y - 1] + - 15 * img_in->pixel_data[x + y] + - 12 * img_in->pixel_data[x + y + 1] + - 5 * img_in->pixel_data[x + y + 2] + - 4 * img_in->pixel_data[x + y - 2 + w] + - 9 * img_in->pixel_data[x + y - 1 + w] + - 12 * img_in->pixel_data[x + y + w] + - 9 * img_in->pixel_data[x + y + 1 + w] + - 4 * img_in->pixel_data[x + y + 2 + w] + - 2 * img_in->pixel_data[x + y - 2 + w + w] + - 4 * img_in->pixel_data[x + y - 1 + w + w] + - 5 * img_in->pixel_data[x + y + w + w] + - 4 * img_in->pixel_data[x + y + 1 + w + w] + - 2 * img_in->pixel_data[x + y + 2 + w + w]) / 159; - } - } - #ifdef CLOCK - printf("Gaussian noise reduction - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); - #endif -} - -/* - CALC_GRADIENT_SOBEL - calculates the result of the Sobel operator - http://en.wikipedia.org/wiki/Sobel_operator - and estimates edge direction angle -*/ -/*void calc_gradient_sobel(struct image * img_in, int g_x[], int g_y[], int g[], int dir[]) {//float theta[]) {*/ -void calc_gradient_sobel(struct image * img_in, int g[], int dir[]) { - #ifdef CLOCK - clock_t start = clock(); - #endif - int w, h, x, y, max_x, max_y, g_x, g_y; - float g_div; - w = img_in->width; - h = img_in->height; - max_x = w - 3; - max_y = w * (h - 3); - for (y = w * 3; y < max_y; y += w) { - for (x = 3; x < max_x; x++) { - g_x = (2 * img_in->pixel_data[x + y + 1] - + img_in->pixel_data[x + y - w + 1] - + img_in->pixel_data[x + y + w + 1] - - 2 * img_in->pixel_data[x + y - 1] - - img_in->pixel_data[x + y - w - 1] - - img_in->pixel_data[x + y + w - 1]); - g_y = 2 * img_in->pixel_data[x + y - w] - + img_in->pixel_data[x + y - w + 1] - + img_in->pixel_data[x + y - w - 1] - - 2 * img_in->pixel_data[x + y + w] - - img_in->pixel_data[x + y + w + 1] - - img_in->pixel_data[x + y + w - 1]; - #ifndef ABS_APPROX - g[x + y] = sqrt(g_x * g_x + g_y * g_y); - #endif - #ifdef ABS_APPROX - g[x + y] = abs(g_x[x + y]) + abs(g_y[x + y]); - #endif - if (g_x == 0) { - dir[x + y] = 2; - } else { - g_div = g_y / (float) g_x; - /* the following commented-out code is slightly faster than the code that follows, but is a slightly worse approximation for determining the edge direction angle - if (g_div < 0) { - if (g_div < -1) { - dir[n] = 0; - } else { - dir[n] = 1; - } - } else { - if (g_div > 1) { - dir[n] = 0; - } else { - dir[n] = 3; - } - } - */ - if (g_div < 0) { - if (g_div < -2.41421356237) { - dir[x + y] = 0; - } else { - if (g_div < -0.414213562373) { - dir[x + y] = 1; - } else { - dir[x + y] = 2; - } - } - } else { - if (g_div > 2.41421356237) { - dir[x + y] = 0; - } else { - if (g_div > 0.414213562373) { - dir[x + y] = 3; - } else { - dir[x + y] = 2; - } - } - } - } - } - - } - #ifdef CLOCK - printf("Calculate gradient Sobel - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); - #endif -} - -/* - CALC_GRADIENT_SCHARR - calculates the result of the Scharr version of the Sobel operator - http://en.wikipedia.org/wiki/Sobel_operator - and estimates edge direction angle - may have better rotational symmetry -*/ -void calc_gradient_scharr(struct image * img_in, int g_x[], int g_y[], int g[], int dir[]) {//float theta[]) { - #ifdef CLOCK - clock_t start = clock(); - #endif - int w, h, x, y, max_x, max_y, n; - float g_div; - w = img_in->width; - h = img_in->height; - max_x = w - 1; - max_y = w * (h - 1); - n = 0; - for (y = w; y < max_y; y += w) { - for (x = 1; x < max_x; x++) { - g_x[n] = (10 * img_in->pixel_data[x + y + 1] - + 3 * img_in->pixel_data[x + y - w + 1] - + 3 * img_in->pixel_data[x + y + w + 1] - - 10 * img_in->pixel_data[x + y - 1] - - 3 * img_in->pixel_data[x + y - w - 1] - - 3 * img_in->pixel_data[x + y + w - 1]); - g_y[n] = 10 * img_in->pixel_data[x + y - w] - + 3 * img_in->pixel_data[x + y - w + 1] - + 3 * img_in->pixel_data[x + y - w - 1] - - 10 * img_in->pixel_data[x + y + w] - - 3 * img_in->pixel_data[x + y + w + 1] - - 3 * img_in->pixel_data[x + y + w - 1]; - #ifndef ABS_APPROX - g[n] = sqrt(g_x[n] * g_x[n] + g_y[n] * g_y[n]); - #endif - #ifdef ABS_APPROX - g[n] = abs(g_x[n]) + abs(g_y[n]); - #endif - if (g_x[n] == 0) { - dir[n] = 2; - } else { - g_div = g_y[n] / (float) g_x[n]; - if (g_div < 0) { - if (g_div < -2.41421356237) { - dir[n] = 0; - } else { - if (g_div < -0.414213562373) { - dir[n] = 1; - } else { - dir[n] = 2; - } - } - } else { - if (g_div > 2.41421356237) { - dir[n] = 0; - } else { - if (g_div > 0.414213562373) { - dir[n] = 3; - } else { - dir[n] = 2; - } - } - } - } - n++; - } - } - #ifdef CLOCK - printf("Calculate gradient Scharr - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); - #endif -} -/* - NON_MAX_SUPPRESSION - using the estimates of the Gx and Gy image gradients and the edge direction angle determines whether the magnitude of the gradient assumes a local maximum in the gradient direction - if the rounded edge direction angle is 0 degrees, checks the north and south directions - if the rounded edge direction angle is 45 degrees, checks the northwest and southeast directions - if the rounded edge direction angle is 90 degrees, checks the east and west directions - if the rounded edge direction angle is 135 degrees, checks the northeast and southwest directions -*/ -void non_max_suppression(struct image * img, int g[], int dir[]) {//float theta[]) { - #ifdef CLOCK - clock_t start = clock(); - #endif - int w, h, x, y, max_x, max_y; - w = img->width; - h = img->height; - max_x = w; - max_y = w * h; - for (y = 0; y < max_y; y += w) { - for (x = 0; x < max_x; x++) { - switch (dir[x + y]) { - case 0: - if(x+y-w-1<0){ - continue; - } - if (g[x + y] > g[x + y - w] && g[x + y] > g[x + y + w]) { - if (g[x + y] > 255) { - img->pixel_data[x + y] = 0xFF; - } else { - img->pixel_data[x + y] = g[x + y]; - } - } else { - img->pixel_data[x + y] = 0x00; - } - break; - case 1: - if(x+y-w-1<0){ - continue; - } - if (g[x + y] > g[x + y - w - 1] && g[x + y] > g[x + y + w + 1]) { - if (g[x + y] > 255) { - img->pixel_data[x + y] = 0xFF; - } else { - img->pixel_data[x + y] = g[x + y]; - } - } else { - img->pixel_data[x + y] = 0x00; - } - break; - case 2: - if (g[x + y] > g[x + y - 1] && g[x + y] > g[x + y + 1]) { - if (g[x + y] > 255) { - img->pixel_data[x + y] = 0xFF; - } else { - img->pixel_data[x + y] = g[x + y]; - } - } else { - img->pixel_data[x + y] = 0x00; - } - break; - case 3: - if(x+y-w-1<0){ - continue; - } - if (g[x + y] > g[x + y - w + 1] && g[x + y] > g[x + y + w - 1]) { - if (g[x + y] > 255) { - img->pixel_data[x + y] = 0xFF; - } else { - img->pixel_data[x + y] = g[x + y]; - } - } else { - img->pixel_data[x + y] = 0x00; - } - break; - default: - printf("ERROR - direction outside range 0 to 3"); - break; - } - } - } - #ifdef CLOCK - printf("Non-maximum suppression - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); - #endif -} -/* - ESTIMATE_THRESHOLD - estimates hysteresis threshold, assuming that the top X% (as defined by the HIGH_THRESHOLD_PERCENTAGE) of edge pixels with the greatest intesity are true edges - and that the low threshold is equal to the quantity of the high threshold plus the total number of 0s at the low end of the histogram divided by 2 -*/ -void estimate_threshold(struct image * img, int * high, int * low) { - #ifdef CLOCK - clock_t start = clock(); - #endif - int i, max, pixels, high_cutoff; - int histogram[256]; - max = img->width * img->height; - for (i = 0; i < 256; i++) { - histogram[i] = 0; - } - for (i = 0; i < max; i++) { - histogram[img->pixel_data[i]]++; - } - pixels = (max - histogram[0]) * HIGH_THRESHOLD_PERCENTAGE; - high_cutoff = 0; - i = 255; - while (high_cutoff < pixels) { - high_cutoff += histogram[i]; - i--; - } - *high = i; - i = 1; - while (histogram[i] == 0) { - i++; - } - *low = (*high + i) * LOW_THRESHOLD_PERCENTAGE; - #ifdef PRINT_HISTOGRAM - for (i = 0; i < 256; i++) { - printf("i %d count %d\n", i, histogram[i]); - } - #endif - - #ifdef CLOCK - printf("Estimate threshold - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); - #endif -} - -void hysteresis (int high, int low, struct image * img_in, struct image * img_out) -{ - #ifdef CLOCK - clock_t start = clock(); - #endif - int x, y, n, max; - max = img_in->width * img_in->height; - for (n = 0; n < max; n++) { - img_out->pixel_data[n] = 0x00; - } - for (y=0; y < img_out->height; y++) { - for (x=0; x < img_out->width; x++) { - if (img_in->pixel_data[y * img_out->width + x] >= high) { - trace (x, y, low, img_in, img_out); - } - } - } - #ifdef CLOCK - printf("Hysteresis - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); - #endif -} - -int trace(int x, int y, int low, struct image * img_in, struct image * img_out) -{ - int y_off, x_off;//, flag; - if (img_out->pixel_data[y * img_out->width + x] == 0) - { - img_out->pixel_data[y * img_out->width + x] = 0xFF; - for (y_off = -1; y_off <=1; y_off++) - { - for(x_off = -1; x_off <= 1; x_off++) - { - if (!(y == 0 && x_off == 0) && range(img_in, x + x_off, y + y_off) && img_in->pixel_data[(y + y_off) * img_out->width + x + x_off] >= low) { - if (trace(x + x_off, y + y_off, low, img_in, img_out)) - { - return(1); - } - } - } - } - return(1); - } - return(0); -} - -int range(struct image * img, int x, int y) -{ - if ((x < 0) || (x >= img->width)) { - return(0); - } - if ((y < 0) || (y >= img->height)) { - return(0); - } - return(1); -} - -void dilate_1d_h(struct image * img, struct image * img_out) { - int x, y, offset, y_max; - y_max = img->height * (img->width - 2); - for (y = 2 * img->width; y < y_max; y += img->width) { - for (x = 2; x < img->width - 2; x++) { - offset = x + y; - img_out->pixel_data[offset] = max(max(max(max(img->pixel_data[offset-2], img->pixel_data[offset-1]), img->pixel_data[offset]), img->pixel_data[offset+1]), img->pixel_data[offset+2]); - } - } -} - -void dilate_1d_v(struct image * img, struct image * img_out) { - int x, y, offset, y_max; - y_max = img->height * (img->width - 2); - for (y = 2 * img->width; y < y_max; y += img->width) { - for (x = 2; x < img->width - 2; x++) { - offset = x + y; - img_out->pixel_data[offset] = max(max(max(max(img->pixel_data[offset-2 * img->width], img->pixel_data[offset-img->width]), img->pixel_data[offset]), img->pixel_data[offset+img->width]), img->pixel_data[offset+2*img->width]); - } - } -} - -void erode_1d_h(struct image * img, struct image * img_out) { - int x, y, offset, y_max; - y_max = img->height * (img->width - 2); - for (y = 2 * img->width; y < y_max; y += img->width) { - for (x = 2; x < img->width - 2; x++) { - offset = x + y; - img_out->pixel_data[offset] = min(min(min(min(img->pixel_data[offset-2], img->pixel_data[offset-1]), img->pixel_data[offset]), img->pixel_data[offset+1]), img->pixel_data[offset+2]); - } - } -} - -void erode_1d_v(struct image * img, struct image * img_out) { - int x, y, offset, y_max; - y_max = img->height * (img->width - 2); - for (y = 2 * img->width; y < y_max; y += img->width) { - for (x = 2; x < img->width - 2; x++) { - offset = x + y; - img_out->pixel_data[offset] = min(min(min(min(img->pixel_data[offset-2 * img->width], img->pixel_data[offset-img->width]), img->pixel_data[offset]), img->pixel_data[offset+img->width]), img->pixel_data[offset+2*img->width]); - } - } -} - -void erode(struct image * img_in, struct image * img_scratch, struct image * img_out) { - #ifdef CLOCK - clock_t start = clock(); - #endif - erode_1d_h(img_in, img_scratch); - erode_1d_v(img_scratch, img_out); - #ifdef CLOCK - printf("Erosion - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); - #endif -} - -void dilate(struct image * img_in, struct image * img_scratch, struct image * img_out) { - #ifdef CLOCK - clock_t start = clock(); - #endif - dilate_1d_h(img_in, img_scratch); - dilate_1d_v(img_scratch, img_out); - #ifdef CLOCK - printf("Dilation - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); - #endif -} - -void morph_open(struct image * img_in, struct image * img_scratch, struct image * img_scratch2, struct image * img_out) { - #ifdef CLOCK - clock_t start = clock(); - #endif - erode(img_in, img_scratch, img_scratch2); - dilate(img_scratch2, img_scratch, img_out); - #ifdef CLOCK - printf("Morphological opening - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); - #endif -} - -void morph_close(struct image * img_in, struct image * img_scratch, struct image * img_scratch2, struct image * img_out) { - #ifdef CLOCK - clock_t start = clock(); - #endif - dilate(img_in, img_scratch, img_scratch2); - erode(img_scratch2, img_scratch, img_out); - #ifdef CLOCK - printf("Morphological closing - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); - #endif -} - -} +/* + FAST-EDGE + Copyright (c) 2009 Benjamin C. Haynor + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include +#include +#include +#include "fast-edge.h" + +#define LOW_THRESHOLD_PERCENTAGE 0.8 // percentage of the high threshold value that the low threshold shall be set at +#define PI 3.14159265 +#define HIGH_THRESHOLD_PERCENTAGE 0.10 // percentage of pixels that meet the high threshold - for example 0.15 will ensure that at least 15% of edge pixels are considered to meet the high threshold + +#define min(X,Y) ((X) < (Y) ? (X) : (Y)) +#define max(X,Y) ((X) < (Y) ? (Y) : (X)) + +namespace ocr{ +/* + CANNY EDGE DETECT + DOES NOT PERFORM NOISE REDUCTION - PERFORM NOISE REDUCTION PRIOR TO USE + Noise reduction omitted, as some applications benefit from morphological operations such as opening or closing as opposed to Gaussian noise reduction + If your application always takes the same size input image, uncomment the definitions of WIDTH and HEIGHT in the header file and define them to the size of your input image, + otherwise the required intermediate arrays will be dynamically allocated. + If WIDTH and HEIGHT are defined, the arrays will be allocated in the compiler directive that follows: +*/ +#ifdef WIDTH +int g[WIDTH * HEIGHT], dir[WIDTH * HEIGHT] = {0}; +unsigned char img_scratch_data[WIDTH * HEIGHT] = {0}; +#endif +void canny_edge_detect(struct image * img_in, struct image * img_out) { + struct image img_scratch; + int high, low; + #ifndef WIDTH + int * g = (int*)calloc(static_cast(img_in->width*img_in->height), sizeof(int)); + int * dir = (int*)calloc(static_cast(img_in->width*img_in->height), sizeof(int)); + unsigned char * img_scratch_data = (unsigned char*)calloc(static_cast(img_in->width*img_in->height), sizeof(char)); + #endif + img_scratch.width = img_in->width; + img_scratch.height = img_in->height; + img_scratch.pixel_data = img_scratch_data; + calc_gradient_sobel(img_in, g, dir); + //printf("*** performing non-maximum suppression ***\n"); + non_max_suppression(&img_scratch, g, dir); + estimate_threshold(&img_scratch, &high, &low); + hysteresis(high, low, &img_scratch, img_out); + #ifndef WIDTH + free(g); + free(dir); + free(img_scratch_data); + #endif +} + +/* + GAUSSIAN_NOISE_ REDUCE + apply 5x5 Gaussian convolution filter, shrinks the image by 4 pixels in each direction, using Gaussian filter found here: + http://en.wikipedia.org/wiki/Canny_edge_detector +*/ +void gaussian_noise_reduce(struct image * img_in, struct image * img_out) +{ + #ifdef CLOCK + clock_t start = clock(); + #endif + int w, h, x, y, max_x, max_y; + w = img_in->width; + h = img_in->height; + img_out->width = w; + img_out->height = h; + max_x = w - 2; + max_y = w * (h - 2); + for (y = w * 2; y < max_y; y += w) { + for (x = 2; x < max_x; x++) { + img_out->pixel_data[x + y] = (2 * img_in->pixel_data[x + y - 2 - w - w] + + 4 * img_in->pixel_data[x + y - 1 - w - w] + + 5 * img_in->pixel_data[x + y - w - w] + + 4 * img_in->pixel_data[x + y + 1 - w - w] + + 2 * img_in->pixel_data[x + y + 2 - w - w] + + 4 * img_in->pixel_data[x + y - 2 - w] + + 9 * img_in->pixel_data[x + y - 1 - w] + + 12 * img_in->pixel_data[x + y - w] + + 9 * img_in->pixel_data[x + y + 1 - w] + + 4 * img_in->pixel_data[x + y + 2 - w] + + 5 * img_in->pixel_data[x + y - 2] + + 12 * img_in->pixel_data[x + y - 1] + + 15 * img_in->pixel_data[x + y] + + 12 * img_in->pixel_data[x + y + 1] + + 5 * img_in->pixel_data[x + y + 2] + + 4 * img_in->pixel_data[x + y - 2 + w] + + 9 * img_in->pixel_data[x + y - 1 + w] + + 12 * img_in->pixel_data[x + y + w] + + 9 * img_in->pixel_data[x + y + 1 + w] + + 4 * img_in->pixel_data[x + y + 2 + w] + + 2 * img_in->pixel_data[x + y - 2 + w + w] + + 4 * img_in->pixel_data[x + y - 1 + w + w] + + 5 * img_in->pixel_data[x + y + w + w] + + 4 * img_in->pixel_data[x + y + 1 + w + w] + + 2 * img_in->pixel_data[x + y + 2 + w + w]) / 159; + } + } + #ifdef CLOCK + printf("Gaussian noise reduction - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); + #endif +} + +/* + CALC_GRADIENT_SOBEL + calculates the result of the Sobel operator - http://en.wikipedia.org/wiki/Sobel_operator - and estimates edge direction angle +*/ +/*void calc_gradient_sobel(struct image * img_in, int g_x[], int g_y[], int g[], int dir[]) {//float theta[]) {*/ +void calc_gradient_sobel(struct image * img_in, int g[], int dir[]) { + #ifdef CLOCK + clock_t start = clock(); + #endif + int w, h, x, y, max_x, max_y, g_x, g_y; + float g_div; + w = img_in->width; + h = img_in->height; + max_x = w - 3; + max_y = w * (h - 3); + for (y = w * 3; y < max_y; y += w) { + for (x = 3; x < max_x; x++) { + g_x = (2 * img_in->pixel_data[x + y + 1] + + img_in->pixel_data[x + y - w + 1] + + img_in->pixel_data[x + y + w + 1] + - 2 * img_in->pixel_data[x + y - 1] + - img_in->pixel_data[x + y - w - 1] + - img_in->pixel_data[x + y + w - 1]); + g_y = 2 * img_in->pixel_data[x + y - w] + + img_in->pixel_data[x + y - w + 1] + + img_in->pixel_data[x + y - w - 1] + - 2 * img_in->pixel_data[x + y + w] + - img_in->pixel_data[x + y + w + 1] + - img_in->pixel_data[x + y + w - 1]; + #ifndef ABS_APPROX + g[x + y] = sqrt(g_x * g_x + g_y * g_y); + #endif + #ifdef ABS_APPROX + g[x + y] = abs(g_x[x + y]) + abs(g_y[x + y]); + #endif + if (g_x == 0) { + dir[x + y] = 2; + } else { + g_div = g_y / (float) g_x; + /* the following commented-out code is slightly faster than the code that follows, but is a slightly worse approximation for determining the edge direction angle + if (g_div < 0) { + if (g_div < -1) { + dir[n] = 0; + } else { + dir[n] = 1; + } + } else { + if (g_div > 1) { + dir[n] = 0; + } else { + dir[n] = 3; + } + } + */ + if (g_div < 0) { + if (g_div < -2.41421356237) { + dir[x + y] = 0; + } else { + if (g_div < -0.414213562373) { + dir[x + y] = 1; + } else { + dir[x + y] = 2; + } + } + } else { + if (g_div > 2.41421356237) { + dir[x + y] = 0; + } else { + if (g_div > 0.414213562373) { + dir[x + y] = 3; + } else { + dir[x + y] = 2; + } + } + } + } + } + + } + #ifdef CLOCK + printf("Calculate gradient Sobel - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); + #endif +} + +/* + CALC_GRADIENT_SCHARR + calculates the result of the Scharr version of the Sobel operator - http://en.wikipedia.org/wiki/Sobel_operator - and estimates edge direction angle + may have better rotational symmetry +*/ +void calc_gradient_scharr(struct image * img_in, int g_x[], int g_y[], int g[], int dir[]) {//float theta[]) { + #ifdef CLOCK + clock_t start = clock(); + #endif + int w, h, x, y, max_x, max_y, n; + float g_div; + w = img_in->width; + h = img_in->height; + max_x = w - 1; + max_y = w * (h - 1); + n = 0; + for (y = w; y < max_y; y += w) { + for (x = 1; x < max_x; x++) { + g_x[n] = (10 * img_in->pixel_data[x + y + 1] + + 3 * img_in->pixel_data[x + y - w + 1] + + 3 * img_in->pixel_data[x + y + w + 1] + - 10 * img_in->pixel_data[x + y - 1] + - 3 * img_in->pixel_data[x + y - w - 1] + - 3 * img_in->pixel_data[x + y + w - 1]); + g_y[n] = 10 * img_in->pixel_data[x + y - w] + + 3 * img_in->pixel_data[x + y - w + 1] + + 3 * img_in->pixel_data[x + y - w - 1] + - 10 * img_in->pixel_data[x + y + w] + - 3 * img_in->pixel_data[x + y + w + 1] + - 3 * img_in->pixel_data[x + y + w - 1]; + #ifndef ABS_APPROX + g[n] = sqrt(g_x[n] * g_x[n] + g_y[n] * g_y[n]); + #endif + #ifdef ABS_APPROX + g[n] = abs(g_x[n]) + abs(g_y[n]); + #endif + if (g_x[n] == 0) { + dir[n] = 2; + } else { + g_div = g_y[n] / (float) g_x[n]; + if (g_div < 0) { + if (g_div < -2.41421356237) { + dir[n] = 0; + } else { + if (g_div < -0.414213562373) { + dir[n] = 1; + } else { + dir[n] = 2; + } + } + } else { + if (g_div > 2.41421356237) { + dir[n] = 0; + } else { + if (g_div > 0.414213562373) { + dir[n] = 3; + } else { + dir[n] = 2; + } + } + } + } + n++; + } + } + #ifdef CLOCK + printf("Calculate gradient Scharr - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); + #endif +} +/* + NON_MAX_SUPPRESSION + using the estimates of the Gx and Gy image gradients and the edge direction angle determines whether the magnitude of the gradient assumes a local maximum in the gradient direction + if the rounded edge direction angle is 0 degrees, checks the north and south directions + if the rounded edge direction angle is 45 degrees, checks the northwest and southeast directions + if the rounded edge direction angle is 90 degrees, checks the east and west directions + if the rounded edge direction angle is 135 degrees, checks the northeast and southwest directions +*/ +void non_max_suppression(struct image * img, int g[], int dir[]) {//float theta[]) { + #ifdef CLOCK + clock_t start = clock(); + #endif + int w, h, x, y, max_x, max_y; + w = img->width; + h = img->height; + max_x = w; + max_y = w * h; + for (y = 0; y < max_y; y += w) { + for (x = 0; x < max_x; x++) { + switch (dir[x + y]) { + case 0: + if(x+y-w-1<0){ + continue; + } + if (g[x + y] > g[x + y - w] && g[x + y] > g[x + y + w]) { + if (g[x + y] > 255) { + img->pixel_data[x + y] = 0xFF; + } else { + img->pixel_data[x + y] = g[x + y]; + } + } else { + img->pixel_data[x + y] = 0x00; + } + break; + case 1: + if(x+y-w-1<0){ + continue; + } + if (g[x + y] > g[x + y - w - 1] && g[x + y] > g[x + y + w + 1]) { + if (g[x + y] > 255) { + img->pixel_data[x + y] = 0xFF; + } else { + img->pixel_data[x + y] = g[x + y]; + } + } else { + img->pixel_data[x + y] = 0x00; + } + break; + case 2: + if (g[x + y] > g[x + y - 1] && g[x + y] > g[x + y + 1]) { + if (g[x + y] > 255) { + img->pixel_data[x + y] = 0xFF; + } else { + img->pixel_data[x + y] = g[x + y]; + } + } else { + img->pixel_data[x + y] = 0x00; + } + break; + case 3: + if(x+y-w-1<0){ + continue; + } + if (g[x + y] > g[x + y - w + 1] && g[x + y] > g[x + y + w - 1]) { + if (g[x + y] > 255) { + img->pixel_data[x + y] = 0xFF; + } else { + img->pixel_data[x + y] = g[x + y]; + } + } else { + img->pixel_data[x + y] = 0x00; + } + break; + default: + printf("ERROR - direction outside range 0 to 3"); + break; + } + } + } + #ifdef CLOCK + printf("Non-maximum suppression - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); + #endif +} +/* + ESTIMATE_THRESHOLD + estimates hysteresis threshold, assuming that the top X% (as defined by the HIGH_THRESHOLD_PERCENTAGE) of edge pixels with the greatest intesity are true edges + and that the low threshold is equal to the quantity of the high threshold plus the total number of 0s at the low end of the histogram divided by 2 +*/ +void estimate_threshold(struct image * img, int * high, int * low) { + #ifdef CLOCK + clock_t start = clock(); + #endif + int i, max, pixels, high_cutoff; + int histogram[256]; + max = img->width * img->height; + for (i = 0; i < 256; i++) { + histogram[i] = 0; + } + for (i = 0; i < max; i++) { + histogram[img->pixel_data[i]]++; + } + pixels = (max - histogram[0]) * HIGH_THRESHOLD_PERCENTAGE; + high_cutoff = 0; + i = 255; + while (high_cutoff < pixels) { + high_cutoff += histogram[i]; + i--; + } + *high = i; + i = 1; + while (histogram[i] == 0) { + i++; + } + *low = (*high + i) * LOW_THRESHOLD_PERCENTAGE; + #ifdef PRINT_HISTOGRAM + for (i = 0; i < 256; i++) { + printf("i %d count %d\n", i, histogram[i]); + } + #endif + + #ifdef CLOCK + printf("Estimate threshold - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); + #endif +} + +void hysteresis (int high, int low, struct image * img_in, struct image * img_out) +{ + #ifdef CLOCK + clock_t start = clock(); + #endif + int x, y, n, max; + max = img_in->width * img_in->height; + for (n = 0; n < max; n++) { + img_out->pixel_data[n] = 0x00; + } + for (y=0; y < img_out->height; y++) { + for (x=0; x < img_out->width; x++) { + if (img_in->pixel_data[y * img_out->width + x] >= high) { + trace (x, y, low, img_in, img_out); + } + } + } + #ifdef CLOCK + printf("Hysteresis - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); + #endif +} + +int trace(int x, int y, int low, struct image * img_in, struct image * img_out) +{ + int y_off, x_off;//, flag; + if (img_out->pixel_data[y * img_out->width + x] == 0) + { + img_out->pixel_data[y * img_out->width + x] = 0xFF; + for (y_off = -1; y_off <=1; y_off++) + { + for(x_off = -1; x_off <= 1; x_off++) + { + if (!(y == 0 && x_off == 0) && range(img_in, x + x_off, y + y_off) && img_in->pixel_data[(y + y_off) * img_out->width + x + x_off] >= low) { + if (trace(x + x_off, y + y_off, low, img_in, img_out)) + { + return(1); + } + } + } + } + return(1); + } + return(0); +} + +int range(struct image * img, int x, int y) +{ + if ((x < 0) || (x >= img->width)) { + return(0); + } + if ((y < 0) || (y >= img->height)) { + return(0); + } + return(1); +} + +void dilate_1d_h(struct image * img, struct image * img_out) { + int x, y, offset, y_max; + y_max = img->height * (img->width - 2); + for (y = 2 * img->width; y < y_max; y += img->width) { + for (x = 2; x < img->width - 2; x++) { + offset = x + y; + img_out->pixel_data[offset] = max(max(max(max(img->pixel_data[offset-2], img->pixel_data[offset-1]), img->pixel_data[offset]), img->pixel_data[offset+1]), img->pixel_data[offset+2]); + } + } +} + +void dilate_1d_v(struct image * img, struct image * img_out) { + int x, y, offset, y_max; + y_max = img->height * (img->width - 2); + for (y = 2 * img->width; y < y_max; y += img->width) { + for (x = 2; x < img->width - 2; x++) { + offset = x + y; + img_out->pixel_data[offset] = max(max(max(max(img->pixel_data[offset-2 * img->width], img->pixel_data[offset-img->width]), img->pixel_data[offset]), img->pixel_data[offset+img->width]), img->pixel_data[offset+2*img->width]); + } + } +} + +void erode_1d_h(struct image * img, struct image * img_out) { + int x, y, offset, y_max; + y_max = img->height * (img->width - 2); + for (y = 2 * img->width; y < y_max; y += img->width) { + for (x = 2; x < img->width - 2; x++) { + offset = x + y; + img_out->pixel_data[offset] = min(min(min(min(img->pixel_data[offset-2], img->pixel_data[offset-1]), img->pixel_data[offset]), img->pixel_data[offset+1]), img->pixel_data[offset+2]); + } + } +} + +void erode_1d_v(struct image * img, struct image * img_out) { + int x, y, offset, y_max; + y_max = img->height * (img->width - 2); + for (y = 2 * img->width; y < y_max; y += img->width) { + for (x = 2; x < img->width - 2; x++) { + offset = x + y; + img_out->pixel_data[offset] = min(min(min(min(img->pixel_data[offset-2 * img->width], img->pixel_data[offset-img->width]), img->pixel_data[offset]), img->pixel_data[offset+img->width]), img->pixel_data[offset+2*img->width]); + } + } +} + +void erode(struct image * img_in, struct image * img_scratch, struct image * img_out) { + #ifdef CLOCK + clock_t start = clock(); + #endif + erode_1d_h(img_in, img_scratch); + erode_1d_v(img_scratch, img_out); + #ifdef CLOCK + printf("Erosion - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); + #endif +} + +void dilate(struct image * img_in, struct image * img_scratch, struct image * img_out) { + #ifdef CLOCK + clock_t start = clock(); + #endif + dilate_1d_h(img_in, img_scratch); + dilate_1d_v(img_scratch, img_out); + #ifdef CLOCK + printf("Dilation - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); + #endif +} + +void morph_open(struct image * img_in, struct image * img_scratch, struct image * img_scratch2, struct image * img_out) { + #ifdef CLOCK + clock_t start = clock(); + #endif + erode(img_in, img_scratch, img_scratch2); + dilate(img_scratch2, img_scratch, img_out); + #ifdef CLOCK + printf("Morphological opening - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); + #endif +} + +void morph_close(struct image * img_in, struct image * img_scratch, struct image * img_scratch2, struct image * img_out) { + #ifdef CLOCK + clock_t start = clock(); + #endif + dilate(img_in, img_scratch, img_scratch2); + erode(img_scratch2, img_scratch, img_out); + #ifdef CLOCK + printf("Morphological closing - time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC); + #endif +} + +} diff --git a/TMessagesProj/jni/fast-edge.h b/TMessagesProj/jni/fast-edge.h index 4ebee6c54..0cd72ccdf 100644 --- a/TMessagesProj/jni/fast-edge.h +++ b/TMessagesProj/jni/fast-edge.h @@ -1,61 +1,61 @@ -/* - FAST-EDGE - Copyright (c) 2009 Benjamin C. Haynor - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation - files (the "Software"), to deal in the Software without - restriction, including without limitation the rights to use, - copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following - conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef _FASTEDGE -#define _FASTEDGE - -namespace ocr{ -//#define WIDTH 640 // uncomment to define width for situations where width is always known -//#define HEIGHT 480 // uncomment to define heigh for situations where height is always known - -//#define CLOCK // uncomment to show running times of image processing functions (in seconds) -//#define ABS_APPROX // uncomment to use the absolute value approximation of sqrt(Gx ^ 2 + Gy ^2) +/* + FAST-EDGE + Copyright (c) 2009 Benjamin C. Haynor + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef _FASTEDGE +#define _FASTEDGE + +namespace ocr{ +//#define WIDTH 640 // uncomment to define width for situations where width is always known +//#define HEIGHT 480 // uncomment to define heigh for situations where height is always known + +//#define CLOCK // uncomment to show running times of image processing functions (in seconds) +//#define ABS_APPROX // uncomment to use the absolute value approximation of sqrt(Gx ^ 2 + Gy ^2) //#define PRINT_HISTOGRAM // uncomment to print the histogram used to estimate the threshold struct image { int width; int height; unsigned char * pixel_data; - }; - -void canny_edge_detect(struct image * img_in, struct image * img_out); -void gaussian_noise_reduce(struct image * img_in, struct image * img_out); -void calc_gradient_sobel(struct image * img_in, int g[], int dir[]); -void calc_gradient_scharr(struct image * img_in, int g_x[], int g_y[], int g[], int dir[]); -void non_max_suppression(struct image * img, int g[], int dir[]); -void estimate_threshold(struct image * img, int * high, int * low); -void hysteresis (int high, int low, struct image * img_in, struct image * img_out); -int trace (int x, int y, int low, struct image * img_in, struct image * img_out); -int range (struct image * img, int x, int y); -void dilate_1d_h(struct image * img, struct image * img_out); -void dilate_1d_v(struct image * img, struct image * img_out); -void erode_1d_h(struct image * img, struct image * img_out); -void erode_1d_v(struct image * img, struct image * img_out); -void erode(struct image * img_in, struct image * img_scratch, struct image * img_out); -void dilate(struct image * img_in, struct image * img_scratch, struct image * img_out); -void morph_open(struct image * img_in, struct image * img_scratch, struct image * img_scratch2, struct image * img_out); + }; + +void canny_edge_detect(struct image * img_in, struct image * img_out); +void gaussian_noise_reduce(struct image * img_in, struct image * img_out); +void calc_gradient_sobel(struct image * img_in, int g[], int dir[]); +void calc_gradient_scharr(struct image * img_in, int g_x[], int g_y[], int g[], int dir[]); +void non_max_suppression(struct image * img, int g[], int dir[]); +void estimate_threshold(struct image * img, int * high, int * low); +void hysteresis (int high, int low, struct image * img_in, struct image * img_out); +int trace (int x, int y, int low, struct image * img_in, struct image * img_out); +int range (struct image * img, int x, int y); +void dilate_1d_h(struct image * img, struct image * img_out); +void dilate_1d_v(struct image * img, struct image * img_out); +void erode_1d_h(struct image * img, struct image * img_out); +void erode_1d_v(struct image * img, struct image * img_out); +void erode(struct image * img_in, struct image * img_scratch, struct image * img_out); +void dilate(struct image * img_in, struct image * img_scratch, struct image * img_out); +void morph_open(struct image * img_in, struct image * img_scratch, struct image * img_scratch2, struct image * img_out); void morph_close(struct image * img_in, struct image * img_scratch, struct image * img_scratch2, struct image * img_out); -} -#endif +} +#endif diff --git a/TMessagesProj/jni/libtgvoip/os/windows/CXWrapper.cpp b/TMessagesProj/jni/libtgvoip/os/windows/CXWrapper.cpp index 4af2d0791..5f1ee8733 100755 --- a/TMessagesProj/jni/libtgvoip/os/windows/CXWrapper.cpp +++ b/TMessagesProj/jni/libtgvoip/os/windows/CXWrapper.cpp @@ -1,469 +1,469 @@ -#include -#include -#include -#include -#include -#include -#include "CXWrapper.h" -#include -#include - -using namespace Windows::Storage::Streams; -using namespace Microsoft::WRL; -using namespace libtgvoip; -using namespace Platform; -using namespace tgvoip; -using namespace Windows::Security::Cryptography; -using namespace Windows::Security::Cryptography::Core; -using namespace Windows::Storage::Streams; -using namespace Windows::Data::Json; -using namespace Windows::Phone::Media::Devices; - -//CryptographicHash^ MicrosoftCryptoImpl::sha1Hash; -//CryptographicHash^ MicrosoftCryptoImpl::sha256Hash; -HashAlgorithmProvider^ MicrosoftCryptoImpl::sha1Provider; -HashAlgorithmProvider^ MicrosoftCryptoImpl::sha256Provider; -SymmetricKeyAlgorithmProvider^ MicrosoftCryptoImpl::aesKeyProvider; - -/*struct tgvoip_cx_data{ - VoIPControllerWrapper^ self; -};*/ - -VoIPControllerWrapper::VoIPControllerWrapper(){ - VoIPController::crypto.aes_ige_decrypt=MicrosoftCryptoImpl::AesIgeDecrypt; - VoIPController::crypto.aes_ige_encrypt=MicrosoftCryptoImpl::AesIgeEncrypt; - VoIPController::crypto.aes_ctr_encrypt = MicrosoftCryptoImpl::AesCtrEncrypt; - VoIPController::crypto.sha1=MicrosoftCryptoImpl::SHA1; - VoIPController::crypto.sha256=MicrosoftCryptoImpl::SHA256; - VoIPController::crypto.rand_bytes=MicrosoftCryptoImpl::RandBytes; - MicrosoftCryptoImpl::Init(); - controller=new VoIPController(); - controller->implData=(void*)this; - VoIPController::Callbacks callbacks={0}; - callbacks.connectionStateChanged=VoIPControllerWrapper::OnStateChanged; - callbacks.signalBarCountChanged=VoIPControllerWrapper::OnSignalBarsChanged; - controller->SetCallbacks(callbacks); -} - -VoIPControllerWrapper::~VoIPControllerWrapper(){ - controller->Stop(); - delete controller; -} - -void VoIPControllerWrapper::Start(){ - controller->Start(); -} - -void VoIPControllerWrapper::Connect(){ - controller->Connect(); -} - -void VoIPControllerWrapper::SetPublicEndpoints(const Platform::Array^ endpoints, bool allowP2P, int32_t connectionMaxLayer){ - std::vector eps; - for (unsigned int i = 0; i < endpoints->Length; i++) - { - libtgvoip::Endpoint^ _ep = endpoints[i]; - tgvoip::Endpoint ep; - ep.id = _ep->id; - ep.type = tgvoip::Endpoint::Type::UDP_RELAY; - char buf[128]; - if (_ep->ipv4){ - WideCharToMultiByte(CP_UTF8, 0, _ep->ipv4->Data(), -1, buf, sizeof(buf), NULL, NULL); - ep.address = IPv4Address(buf); - } - if (_ep->ipv6){ - WideCharToMultiByte(CP_UTF8, 0, _ep->ipv6->Data(), -1, buf, sizeof(buf), NULL, NULL); - ep.v6address = IPv6Address(buf); - } - ep.port = _ep->port; - if (_ep->peerTag->Length != 16) - throw ref new Platform::InvalidArgumentException("Peer tag must be exactly 16 bytes long"); - memcpy(ep.peerTag, _ep->peerTag->Data, 16); - eps.push_back(ep); - } - controller->SetRemoteEndpoints(eps, allowP2P, connectionMaxLayer); -} - -void VoIPControllerWrapper::SetNetworkType(NetworkType type){ - controller->SetNetworkType((int)type); -} - -void VoIPControllerWrapper::SetMicMute(bool mute){ - controller->SetMicMute(mute); -} - -int64 VoIPControllerWrapper::GetPreferredRelayID(){ - return controller->GetPreferredRelayID(); -} - -int32_t VoIPControllerWrapper::GetConnectionMaxLayer(){ - return tgvoip::VoIPController::GetConnectionMaxLayer(); -} - -void VoIPControllerWrapper::SetEncryptionKey(const Platform::Array^ key, bool isOutgoing){ - if(key->Length!=256) - throw ref new Platform::InvalidArgumentException("Encryption key must be exactly 256 bytes long"); - controller->SetEncryptionKey((char*)key->Data, isOutgoing); -} - -int VoIPControllerWrapper::GetSignalBarsCount(){ - return controller->GetSignalBarsCount(); -} - -CallState VoIPControllerWrapper::GetConnectionState(){ - return (CallState)controller->GetConnectionState(); -} - -TrafficStats^ VoIPControllerWrapper::GetStats(){ - tgvoip::VoIPController::TrafficStats _stats; - controller->GetStats(&_stats); - - TrafficStats^ stats = ref new TrafficStats(); - stats->bytesSentWifi = _stats.bytesSentWifi; - stats->bytesSentMobile = _stats.bytesSentMobile; - stats->bytesRecvdWifi = _stats.bytesRecvdWifi; - stats->bytesRecvdMobile = _stats.bytesRecvdMobile; - - return stats; -} - -Platform::String^ VoIPControllerWrapper::GetDebugString(){ - std::string log = controller->GetDebugString(); - size_t len = sizeof(wchar_t)*(log.length() + 1); - wchar_t* wlog = (wchar_t*)malloc(len); - MultiByteToWideChar(CP_UTF8, 0, log.c_str(), -1, wlog, len / sizeof(wchar_t)); - Platform::String^ res = ref new Platform::String(wlog); - free(wlog); - return res; -} - -Platform::String^ VoIPControllerWrapper::GetDebugLog(){ - std::string log=controller->GetDebugLog(); - size_t len=sizeof(wchar_t)*(log.length()+1); - wchar_t* wlog=(wchar_t*)malloc(len); - MultiByteToWideChar(CP_UTF8, 0, log.c_str(), -1, wlog, len/sizeof(wchar_t)); - Platform::String^ res=ref new Platform::String(wlog); - free(wlog); - return res; -} - -Error VoIPControllerWrapper::GetLastError(){ - return (Error)controller->GetLastError(); -} - -Platform::String^ VoIPControllerWrapper::GetVersion(){ - const char* v=VoIPController::GetVersion(); - wchar_t buf[32]; - MultiByteToWideChar(CP_UTF8, 0, v, -1, buf, sizeof(buf)); - return ref new Platform::String(buf); -} - -void VoIPControllerWrapper::OnStateChanged(VoIPController* c, int state){ - reinterpret_cast(c->implData)->OnStateChangedInternal(state); -} - -void VoIPControllerWrapper::OnSignalBarsChanged(VoIPController* c, int count){ - reinterpret_cast(c->implData)->OnSignalBarsChangedInternal(count); -} - -void VoIPControllerWrapper::OnStateChangedInternal(int state){ - CallStateChanged(this, (CallState)state); -} - -void VoIPControllerWrapper::OnSignalBarsChangedInternal(int count){ - SignalBarsChanged(this, count); -} - -void VoIPControllerWrapper::SetConfig(VoIPConfig^ wrapper){ - VoIPController::Config config{0}; - config.initTimeout=wrapper->initTimeout; - config.recvTimeout=wrapper->recvTimeout; - config.dataSaving=(int)wrapper->dataSaving; - config.logFilePath; - config.statsDumpFilePath; - - config.enableAEC=wrapper->enableAEC; - config.enableNS=wrapper->enableNS; - config.enableAGC=wrapper->enableAGC; - - config.enableCallUpgrade=wrapper->enableCallUpgrade; - - config.logPacketStats=wrapper->logPacketStats; - config.enableVolumeControl=wrapper->enableVolumeControl; - - config.enableVideoSend=wrapper->enableVideoSend; - config.enableVideoReceive=wrapper->enableVideoReceive; - - if(wrapper->logFilePath!=nullptr&&!wrapper->logFilePath->IsEmpty()){ - config.logFilePath = wstring(wrapper->logFilePath->Data()); - } - if (wrapper->statsDumpFilePath != nullptr&&!wrapper->statsDumpFilePath->IsEmpty()){ - config.statsDumpFilePath = wstring(wrapper->statsDumpFilePath->Data()); - } - - controller->SetConfig(config); -} - -void VoIPControllerWrapper::SetProxy(ProxyProtocol protocol, Platform::String^ address, uint16_t port, Platform::String^ username, Platform::String^ password){ - char _address[2000]; - char _username[256]; - char _password[256]; - - WideCharToMultiByte(CP_UTF8, 0, address->Data(), -1, _address, sizeof(_address), NULL, NULL); - WideCharToMultiByte(CP_UTF8, 0, username->Data(), -1, _username, sizeof(_username), NULL, NULL); - WideCharToMultiByte(CP_UTF8, 0, password->Data(), -1, _password, sizeof(_password), NULL, NULL); - - controller->SetProxy((int)protocol, _address, port, _username, _password); -} - -void VoIPControllerWrapper::SetAudioOutputGainControlEnabled(bool enabled){ - controller->SetAudioOutputGainControlEnabled(enabled); -} - -void VoIPControllerWrapper::SetInputVolume(float level){ - controller->SetInputVolume(level); -} - -void VoIPControllerWrapper::SetOutputVolume(float level){ - controller->SetOutputVolume(level); -} - -void VoIPControllerWrapper::UpdateServerConfig(Platform::String^ json){ - std::string config=ToUtf8(json->Data(), json->Length()); - ServerConfig::GetSharedInstance()->Update(config); -} - -void VoIPControllerWrapper::SwitchSpeaker(bool external){ - auto routingManager = AudioRoutingManager::GetDefault(); - if (external){ - routingManager->SetAudioEndpoint(AudioRoutingEndpoint::Speakerphone); - } - else{ - if ((routingManager->AvailableAudioEndpoints & AvailableAudioRoutingEndpoints::Bluetooth) == AvailableAudioRoutingEndpoints::Bluetooth){ - routingManager->SetAudioEndpoint(AudioRoutingEndpoint::Bluetooth); - } - else if ((routingManager->AvailableAudioEndpoints & AvailableAudioRoutingEndpoints::Earpiece) == AvailableAudioRoutingEndpoints::Earpiece){ - routingManager->SetAudioEndpoint(AudioRoutingEndpoint::Earpiece); - } - } -} - -void MicrosoftCryptoImpl::AesIgeEncrypt(uint8_t* in, uint8_t* out, size_t len, uint8_t* key, uint8_t* iv){ - IBuffer^ keybuf=IBufferFromPtr(key, 32); - CryptographicKey^ _key=aesKeyProvider->CreateSymmetricKey(keybuf); - uint8_t tmpOut[16]; - uint8_t* xPrev=iv+16; - uint8_t* yPrev=iv; - uint8_t x[16]; - uint8_t y[16]; - for(size_t offset=0;offsetCreateSymmetricKey(keybuf); - uint8_t tmpOut[16]; - uint8_t* xPrev=iv; - uint8_t* yPrev=iv+16; - uint8_t x[16]; - uint8_t y[16]; - for(size_t offset=0;offset> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } - -typedef uint8_t u8; - -#define L_ENDIAN - -/* increment counter (128-bit int) by 2^64 */ -static void AES_ctr128_inc(unsigned char *counter) { - unsigned long c; - - /* Grab 3rd dword of counter and increment */ -#ifdef L_ENDIAN - c = GETU32(counter + 8); - c++; - PUTU32(counter + 8, c); -#else - c = GETU32(counter + 4); - c++; - PUTU32(counter + 4, c); -#endif - - /* if no overflow, we're done */ - if (c) - return; - - /* Grab top dword of counter and increment */ -#ifdef L_ENDIAN - c = GETU32(counter + 12); - c++; - PUTU32(counter + 12, c); -#else - c = GETU32(counter + 0); - c++; - PUTU32(counter + 0, c); -#endif - -} - -void MicrosoftCryptoImpl::AesCtrEncrypt(uint8_t* inout, size_t len, uint8_t* key, uint8_t* counter, uint8_t* ecount_buf, uint32_t* num){ - unsigned int n; - unsigned long l = len; - - //assert(in && out && key && counter && num); - //assert(*num < AES_BLOCK_SIZE); - - IBuffer^ keybuf = IBufferFromPtr(key, 32); - CryptographicKey^ _key = aesKeyProvider->CreateSymmetricKey(keybuf); - - n = *num; - - while (l--) { - if (n == 0) { - IBuffer^ inbuf = IBufferFromPtr(counter, 16); - IBuffer^ outbuf = CryptographicEngine::Encrypt(_key, inbuf, nullptr); - IBufferToPtr(outbuf, 16, ecount_buf); - //AES_encrypt(counter, ecount_buf, key); - AES_ctr128_inc(counter); - } - *inout = *(inout++) ^ ecount_buf[n]; - n = (n + 1) % 16; - } - - *num = n; -} - -void MicrosoftCryptoImpl::SHA1(uint8_t* msg, size_t len, uint8_t* out){ - //EnterCriticalSection(&hashMutex); - - IBuffer^ arr=IBufferFromPtr(msg, len); - CryptographicHash^ hash=sha1Provider->CreateHash(); - hash->Append(arr); - IBuffer^ res=hash->GetValueAndReset(); - IBufferToPtr(res, 20, out); - - //LeaveCriticalSection(&hashMutex); -} - -void MicrosoftCryptoImpl::SHA256(uint8_t* msg, size_t len, uint8_t* out){ - //EnterCriticalSection(&hashMutex); - - IBuffer^ arr=IBufferFromPtr(msg, len); - CryptographicHash^ hash=sha256Provider->CreateHash(); - hash->Append(arr); - IBuffer^ res=hash->GetValueAndReset(); - IBufferToPtr(res, 32, out); - //LeaveCriticalSection(&hashMutex); -} - -void MicrosoftCryptoImpl::RandBytes(uint8_t* buffer, size_t len){ - IBuffer^ res=CryptographicBuffer::GenerateRandom(len); - IBufferToPtr(res, len, buffer); -} - -void MicrosoftCryptoImpl::Init(){ - /*sha1Hash=HashAlgorithmProvider::OpenAlgorithm(HashAlgorithmNames::Sha1)->CreateHash(); - sha256Hash=HashAlgorithmProvider::OpenAlgorithm(HashAlgorithmNames::Sha256)->CreateHash();*/ - sha1Provider=HashAlgorithmProvider::OpenAlgorithm(HashAlgorithmNames::Sha1); - sha256Provider=HashAlgorithmProvider::OpenAlgorithm(HashAlgorithmNames::Sha256); - aesKeyProvider=SymmetricKeyAlgorithmProvider::OpenAlgorithm(SymmetricAlgorithmNames::AesEcb); -} - -void MicrosoftCryptoImpl::XorInt128(uint8_t* a, uint8_t* b, uint8_t* out){ - uint64_t* _a=reinterpret_cast(a); - uint64_t* _b=reinterpret_cast(b); - uint64_t* _out=reinterpret_cast(out); - _out[0]=_a[0]^_b[0]; - _out[1]=_a[1]^_b[1]; -} - -void MicrosoftCryptoImpl::IBufferToPtr(IBuffer^ buffer, size_t len, uint8_t* out) -{ - ComPtr bufferByteAccess; - reinterpret_cast(buffer)->QueryInterface(IID_PPV_ARGS(&bufferByteAccess)); - - byte* hashBuffer; - bufferByteAccess->Buffer(&hashBuffer); - CopyMemory(out, hashBuffer, len); -} - -IBuffer^ MicrosoftCryptoImpl::IBufferFromPtr(uint8_t* msg, size_t len) -{ - ComPtr nativeBuffer=Make((byte *)msg, len); - return reinterpret_cast(nativeBuffer.Get()); -} - -/*Platform::String^ VoIPControllerWrapper::TestAesIge(){ - MicrosoftCryptoImpl::Init(); - Platform::String^ res=""; - Platform::Array^ data=ref new Platform::Array(32); - Platform::Array^ out=ref new Platform::Array(32); - Platform::Array^ key=ref new Platform::Array(16); - Platform::Array^ iv=ref new Platform::Array(32); - - - CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("0000000000000000000000000000000000000000000000000000000000000000"), &data); - CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"), &iv); - CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("000102030405060708090a0b0c0d0e0f"), &key); - MicrosoftCryptoImpl::AesIgeEncrypt(data->Data, out->Data, 32, key->Data, iv->Data); - res+=CryptographicBuffer::EncodeToHexString(CryptographicBuffer::CreateFromByteArray(out)); - res+="\n"; - - CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("1A8519A6557BE652E9DA8E43DA4EF4453CF456B4CA488AA383C79C98B34797CB"), &data); - CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"), &iv); - CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("000102030405060708090a0b0c0d0e0f"), &key); - MicrosoftCryptoImpl::AesIgeDecrypt(data->Data, out->Data, 32, key->Data, iv->Data); - res+=CryptographicBuffer::EncodeToHexString(CryptographicBuffer::CreateFromByteArray(out)); - res+="\n"; - - CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("99706487A1CDE613BC6DE0B6F24B1C7AA448C8B9C3403E3467A8CAD89340F53B"), &data); - CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("6D656E746174696F6E206F6620494745206D6F646520666F72204F70656E5353"), &iv); - CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("5468697320697320616E20696D706C65"), &key); - MicrosoftCryptoImpl::AesIgeEncrypt(data->Data, out->Data, 32, key->Data, iv->Data); - res+=CryptographicBuffer::EncodeToHexString(CryptographicBuffer::CreateFromByteArray(out)); - res+="\n"; - - CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("4C2E204C6574277320686F70652042656E20676F74206974207269676874210A"), &data); - CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("6D656E746174696F6E206F6620494745206D6F646520666F72204F70656E5353"), &iv); - CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("5468697320697320616E20696D706C65"), &key); - MicrosoftCryptoImpl::AesIgeDecrypt(data->Data, out->Data, 32, key->Data, iv->Data); - res+=CryptographicBuffer::EncodeToHexString(CryptographicBuffer::CreateFromByteArray(out)); - return res; +#include +#include +#include +#include +#include +#include +#include "CXWrapper.h" +#include +#include + +using namespace Windows::Storage::Streams; +using namespace Microsoft::WRL; +using namespace libtgvoip; +using namespace Platform; +using namespace tgvoip; +using namespace Windows::Security::Cryptography; +using namespace Windows::Security::Cryptography::Core; +using namespace Windows::Storage::Streams; +using namespace Windows::Data::Json; +using namespace Windows::Phone::Media::Devices; + +//CryptographicHash^ MicrosoftCryptoImpl::sha1Hash; +//CryptographicHash^ MicrosoftCryptoImpl::sha256Hash; +HashAlgorithmProvider^ MicrosoftCryptoImpl::sha1Provider; +HashAlgorithmProvider^ MicrosoftCryptoImpl::sha256Provider; +SymmetricKeyAlgorithmProvider^ MicrosoftCryptoImpl::aesKeyProvider; + +/*struct tgvoip_cx_data{ + VoIPControllerWrapper^ self; +};*/ + +VoIPControllerWrapper::VoIPControllerWrapper(){ + VoIPController::crypto.aes_ige_decrypt=MicrosoftCryptoImpl::AesIgeDecrypt; + VoIPController::crypto.aes_ige_encrypt=MicrosoftCryptoImpl::AesIgeEncrypt; + VoIPController::crypto.aes_ctr_encrypt = MicrosoftCryptoImpl::AesCtrEncrypt; + VoIPController::crypto.sha1=MicrosoftCryptoImpl::SHA1; + VoIPController::crypto.sha256=MicrosoftCryptoImpl::SHA256; + VoIPController::crypto.rand_bytes=MicrosoftCryptoImpl::RandBytes; + MicrosoftCryptoImpl::Init(); + controller=new VoIPController(); + controller->implData=(void*)this; + VoIPController::Callbacks callbacks={0}; + callbacks.connectionStateChanged=VoIPControllerWrapper::OnStateChanged; + callbacks.signalBarCountChanged=VoIPControllerWrapper::OnSignalBarsChanged; + controller->SetCallbacks(callbacks); +} + +VoIPControllerWrapper::~VoIPControllerWrapper(){ + controller->Stop(); + delete controller; +} + +void VoIPControllerWrapper::Start(){ + controller->Start(); +} + +void VoIPControllerWrapper::Connect(){ + controller->Connect(); +} + +void VoIPControllerWrapper::SetPublicEndpoints(const Platform::Array^ endpoints, bool allowP2P, int32_t connectionMaxLayer){ + std::vector eps; + for (unsigned int i = 0; i < endpoints->Length; i++) + { + libtgvoip::Endpoint^ _ep = endpoints[i]; + tgvoip::Endpoint ep; + ep.id = _ep->id; + ep.type = tgvoip::Endpoint::Type::UDP_RELAY; + char buf[128]; + if (_ep->ipv4){ + WideCharToMultiByte(CP_UTF8, 0, _ep->ipv4->Data(), -1, buf, sizeof(buf), NULL, NULL); + ep.address = IPv4Address(buf); + } + if (_ep->ipv6){ + WideCharToMultiByte(CP_UTF8, 0, _ep->ipv6->Data(), -1, buf, sizeof(buf), NULL, NULL); + ep.v6address = IPv6Address(buf); + } + ep.port = _ep->port; + if (_ep->peerTag->Length != 16) + throw ref new Platform::InvalidArgumentException("Peer tag must be exactly 16 bytes long"); + memcpy(ep.peerTag, _ep->peerTag->Data, 16); + eps.push_back(ep); + } + controller->SetRemoteEndpoints(eps, allowP2P, connectionMaxLayer); +} + +void VoIPControllerWrapper::SetNetworkType(NetworkType type){ + controller->SetNetworkType((int)type); +} + +void VoIPControllerWrapper::SetMicMute(bool mute){ + controller->SetMicMute(mute); +} + +int64 VoIPControllerWrapper::GetPreferredRelayID(){ + return controller->GetPreferredRelayID(); +} + +int32_t VoIPControllerWrapper::GetConnectionMaxLayer(){ + return tgvoip::VoIPController::GetConnectionMaxLayer(); +} + +void VoIPControllerWrapper::SetEncryptionKey(const Platform::Array^ key, bool isOutgoing){ + if(key->Length!=256) + throw ref new Platform::InvalidArgumentException("Encryption key must be exactly 256 bytes long"); + controller->SetEncryptionKey((char*)key->Data, isOutgoing); +} + +int VoIPControllerWrapper::GetSignalBarsCount(){ + return controller->GetSignalBarsCount(); +} + +CallState VoIPControllerWrapper::GetConnectionState(){ + return (CallState)controller->GetConnectionState(); +} + +TrafficStats^ VoIPControllerWrapper::GetStats(){ + tgvoip::VoIPController::TrafficStats _stats; + controller->GetStats(&_stats); + + TrafficStats^ stats = ref new TrafficStats(); + stats->bytesSentWifi = _stats.bytesSentWifi; + stats->bytesSentMobile = _stats.bytesSentMobile; + stats->bytesRecvdWifi = _stats.bytesRecvdWifi; + stats->bytesRecvdMobile = _stats.bytesRecvdMobile; + + return stats; +} + +Platform::String^ VoIPControllerWrapper::GetDebugString(){ + std::string log = controller->GetDebugString(); + size_t len = sizeof(wchar_t)*(log.length() + 1); + wchar_t* wlog = (wchar_t*)malloc(len); + MultiByteToWideChar(CP_UTF8, 0, log.c_str(), -1, wlog, len / sizeof(wchar_t)); + Platform::String^ res = ref new Platform::String(wlog); + free(wlog); + return res; +} + +Platform::String^ VoIPControllerWrapper::GetDebugLog(){ + std::string log=controller->GetDebugLog(); + size_t len=sizeof(wchar_t)*(log.length()+1); + wchar_t* wlog=(wchar_t*)malloc(len); + MultiByteToWideChar(CP_UTF8, 0, log.c_str(), -1, wlog, len/sizeof(wchar_t)); + Platform::String^ res=ref new Platform::String(wlog); + free(wlog); + return res; +} + +Error VoIPControllerWrapper::GetLastError(){ + return (Error)controller->GetLastError(); +} + +Platform::String^ VoIPControllerWrapper::GetVersion(){ + const char* v=VoIPController::GetVersion(); + wchar_t buf[32]; + MultiByteToWideChar(CP_UTF8, 0, v, -1, buf, sizeof(buf)); + return ref new Platform::String(buf); +} + +void VoIPControllerWrapper::OnStateChanged(VoIPController* c, int state){ + reinterpret_cast(c->implData)->OnStateChangedInternal(state); +} + +void VoIPControllerWrapper::OnSignalBarsChanged(VoIPController* c, int count){ + reinterpret_cast(c->implData)->OnSignalBarsChangedInternal(count); +} + +void VoIPControllerWrapper::OnStateChangedInternal(int state){ + CallStateChanged(this, (CallState)state); +} + +void VoIPControllerWrapper::OnSignalBarsChangedInternal(int count){ + SignalBarsChanged(this, count); +} + +void VoIPControllerWrapper::SetConfig(VoIPConfig^ wrapper){ + VoIPController::Config config{0}; + config.initTimeout=wrapper->initTimeout; + config.recvTimeout=wrapper->recvTimeout; + config.dataSaving=(int)wrapper->dataSaving; + config.logFilePath; + config.statsDumpFilePath; + + config.enableAEC=wrapper->enableAEC; + config.enableNS=wrapper->enableNS; + config.enableAGC=wrapper->enableAGC; + + config.enableCallUpgrade=wrapper->enableCallUpgrade; + + config.logPacketStats=wrapper->logPacketStats; + config.enableVolumeControl=wrapper->enableVolumeControl; + + config.enableVideoSend=wrapper->enableVideoSend; + config.enableVideoReceive=wrapper->enableVideoReceive; + + if(wrapper->logFilePath!=nullptr&&!wrapper->logFilePath->IsEmpty()){ + config.logFilePath = wstring(wrapper->logFilePath->Data()); + } + if (wrapper->statsDumpFilePath != nullptr&&!wrapper->statsDumpFilePath->IsEmpty()){ + config.statsDumpFilePath = wstring(wrapper->statsDumpFilePath->Data()); + } + + controller->SetConfig(config); +} + +void VoIPControllerWrapper::SetProxy(ProxyProtocol protocol, Platform::String^ address, uint16_t port, Platform::String^ username, Platform::String^ password){ + char _address[2000]; + char _username[256]; + char _password[256]; + + WideCharToMultiByte(CP_UTF8, 0, address->Data(), -1, _address, sizeof(_address), NULL, NULL); + WideCharToMultiByte(CP_UTF8, 0, username->Data(), -1, _username, sizeof(_username), NULL, NULL); + WideCharToMultiByte(CP_UTF8, 0, password->Data(), -1, _password, sizeof(_password), NULL, NULL); + + controller->SetProxy((int)protocol, _address, port, _username, _password); +} + +void VoIPControllerWrapper::SetAudioOutputGainControlEnabled(bool enabled){ + controller->SetAudioOutputGainControlEnabled(enabled); +} + +void VoIPControllerWrapper::SetInputVolume(float level){ + controller->SetInputVolume(level); +} + +void VoIPControllerWrapper::SetOutputVolume(float level){ + controller->SetOutputVolume(level); +} + +void VoIPControllerWrapper::UpdateServerConfig(Platform::String^ json){ + std::string config=ToUtf8(json->Data(), json->Length()); + ServerConfig::GetSharedInstance()->Update(config); +} + +void VoIPControllerWrapper::SwitchSpeaker(bool external){ + auto routingManager = AudioRoutingManager::GetDefault(); + if (external){ + routingManager->SetAudioEndpoint(AudioRoutingEndpoint::Speakerphone); + } + else{ + if ((routingManager->AvailableAudioEndpoints & AvailableAudioRoutingEndpoints::Bluetooth) == AvailableAudioRoutingEndpoints::Bluetooth){ + routingManager->SetAudioEndpoint(AudioRoutingEndpoint::Bluetooth); + } + else if ((routingManager->AvailableAudioEndpoints & AvailableAudioRoutingEndpoints::Earpiece) == AvailableAudioRoutingEndpoints::Earpiece){ + routingManager->SetAudioEndpoint(AudioRoutingEndpoint::Earpiece); + } + } +} + +void MicrosoftCryptoImpl::AesIgeEncrypt(uint8_t* in, uint8_t* out, size_t len, uint8_t* key, uint8_t* iv){ + IBuffer^ keybuf=IBufferFromPtr(key, 32); + CryptographicKey^ _key=aesKeyProvider->CreateSymmetricKey(keybuf); + uint8_t tmpOut[16]; + uint8_t* xPrev=iv+16; + uint8_t* yPrev=iv; + uint8_t x[16]; + uint8_t y[16]; + for(size_t offset=0;offsetCreateSymmetricKey(keybuf); + uint8_t tmpOut[16]; + uint8_t* xPrev=iv; + uint8_t* yPrev=iv+16; + uint8_t x[16]; + uint8_t y[16]; + for(size_t offset=0;offset> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } + +typedef uint8_t u8; + +#define L_ENDIAN + +/* increment counter (128-bit int) by 2^64 */ +static void AES_ctr128_inc(unsigned char *counter) { + unsigned long c; + + /* Grab 3rd dword of counter and increment */ +#ifdef L_ENDIAN + c = GETU32(counter + 8); + c++; + PUTU32(counter + 8, c); +#else + c = GETU32(counter + 4); + c++; + PUTU32(counter + 4, c); +#endif + + /* if no overflow, we're done */ + if (c) + return; + + /* Grab top dword of counter and increment */ +#ifdef L_ENDIAN + c = GETU32(counter + 12); + c++; + PUTU32(counter + 12, c); +#else + c = GETU32(counter + 0); + c++; + PUTU32(counter + 0, c); +#endif + +} + +void MicrosoftCryptoImpl::AesCtrEncrypt(uint8_t* inout, size_t len, uint8_t* key, uint8_t* counter, uint8_t* ecount_buf, uint32_t* num){ + unsigned int n; + unsigned long l = len; + + //assert(in && out && key && counter && num); + //assert(*num < AES_BLOCK_SIZE); + + IBuffer^ keybuf = IBufferFromPtr(key, 32); + CryptographicKey^ _key = aesKeyProvider->CreateSymmetricKey(keybuf); + + n = *num; + + while (l--) { + if (n == 0) { + IBuffer^ inbuf = IBufferFromPtr(counter, 16); + IBuffer^ outbuf = CryptographicEngine::Encrypt(_key, inbuf, nullptr); + IBufferToPtr(outbuf, 16, ecount_buf); + //AES_encrypt(counter, ecount_buf, key); + AES_ctr128_inc(counter); + } + *inout = *(inout++) ^ ecount_buf[n]; + n = (n + 1) % 16; + } + + *num = n; +} + +void MicrosoftCryptoImpl::SHA1(uint8_t* msg, size_t len, uint8_t* out){ + //EnterCriticalSection(&hashMutex); + + IBuffer^ arr=IBufferFromPtr(msg, len); + CryptographicHash^ hash=sha1Provider->CreateHash(); + hash->Append(arr); + IBuffer^ res=hash->GetValueAndReset(); + IBufferToPtr(res, 20, out); + + //LeaveCriticalSection(&hashMutex); +} + +void MicrosoftCryptoImpl::SHA256(uint8_t* msg, size_t len, uint8_t* out){ + //EnterCriticalSection(&hashMutex); + + IBuffer^ arr=IBufferFromPtr(msg, len); + CryptographicHash^ hash=sha256Provider->CreateHash(); + hash->Append(arr); + IBuffer^ res=hash->GetValueAndReset(); + IBufferToPtr(res, 32, out); + //LeaveCriticalSection(&hashMutex); +} + +void MicrosoftCryptoImpl::RandBytes(uint8_t* buffer, size_t len){ + IBuffer^ res=CryptographicBuffer::GenerateRandom(len); + IBufferToPtr(res, len, buffer); +} + +void MicrosoftCryptoImpl::Init(){ + /*sha1Hash=HashAlgorithmProvider::OpenAlgorithm(HashAlgorithmNames::Sha1)->CreateHash(); + sha256Hash=HashAlgorithmProvider::OpenAlgorithm(HashAlgorithmNames::Sha256)->CreateHash();*/ + sha1Provider=HashAlgorithmProvider::OpenAlgorithm(HashAlgorithmNames::Sha1); + sha256Provider=HashAlgorithmProvider::OpenAlgorithm(HashAlgorithmNames::Sha256); + aesKeyProvider=SymmetricKeyAlgorithmProvider::OpenAlgorithm(SymmetricAlgorithmNames::AesEcb); +} + +void MicrosoftCryptoImpl::XorInt128(uint8_t* a, uint8_t* b, uint8_t* out){ + uint64_t* _a=reinterpret_cast(a); + uint64_t* _b=reinterpret_cast(b); + uint64_t* _out=reinterpret_cast(out); + _out[0]=_a[0]^_b[0]; + _out[1]=_a[1]^_b[1]; +} + +void MicrosoftCryptoImpl::IBufferToPtr(IBuffer^ buffer, size_t len, uint8_t* out) +{ + ComPtr bufferByteAccess; + reinterpret_cast(buffer)->QueryInterface(IID_PPV_ARGS(&bufferByteAccess)); + + byte* hashBuffer; + bufferByteAccess->Buffer(&hashBuffer); + CopyMemory(out, hashBuffer, len); +} + +IBuffer^ MicrosoftCryptoImpl::IBufferFromPtr(uint8_t* msg, size_t len) +{ + ComPtr nativeBuffer=Make((byte *)msg, len); + return reinterpret_cast(nativeBuffer.Get()); +} + +/*Platform::String^ VoIPControllerWrapper::TestAesIge(){ + MicrosoftCryptoImpl::Init(); + Platform::String^ res=""; + Platform::Array^ data=ref new Platform::Array(32); + Platform::Array^ out=ref new Platform::Array(32); + Platform::Array^ key=ref new Platform::Array(16); + Platform::Array^ iv=ref new Platform::Array(32); + + + CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("0000000000000000000000000000000000000000000000000000000000000000"), &data); + CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"), &iv); + CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("000102030405060708090a0b0c0d0e0f"), &key); + MicrosoftCryptoImpl::AesIgeEncrypt(data->Data, out->Data, 32, key->Data, iv->Data); + res+=CryptographicBuffer::EncodeToHexString(CryptographicBuffer::CreateFromByteArray(out)); + res+="\n"; + + CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("1A8519A6557BE652E9DA8E43DA4EF4453CF456B4CA488AA383C79C98B34797CB"), &data); + CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"), &iv); + CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("000102030405060708090a0b0c0d0e0f"), &key); + MicrosoftCryptoImpl::AesIgeDecrypt(data->Data, out->Data, 32, key->Data, iv->Data); + res+=CryptographicBuffer::EncodeToHexString(CryptographicBuffer::CreateFromByteArray(out)); + res+="\n"; + + CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("99706487A1CDE613BC6DE0B6F24B1C7AA448C8B9C3403E3467A8CAD89340F53B"), &data); + CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("6D656E746174696F6E206F6620494745206D6F646520666F72204F70656E5353"), &iv); + CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("5468697320697320616E20696D706C65"), &key); + MicrosoftCryptoImpl::AesIgeEncrypt(data->Data, out->Data, 32, key->Data, iv->Data); + res+=CryptographicBuffer::EncodeToHexString(CryptographicBuffer::CreateFromByteArray(out)); + res+="\n"; + + CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("4C2E204C6574277320686F70652042656E20676F74206974207269676874210A"), &data); + CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("6D656E746174696F6E206F6620494745206D6F646520666F72204F70656E5353"), &iv); + CryptographicBuffer::CopyToByteArray(CryptographicBuffer::DecodeFromHexString("5468697320697320616E20696D706C65"), &key); + MicrosoftCryptoImpl::AesIgeDecrypt(data->Data, out->Data, 32, key->Data, iv->Data); + res+=CryptographicBuffer::EncodeToHexString(CryptographicBuffer::CreateFromByteArray(out)); + return res; }*/ \ No newline at end of file diff --git a/TMessagesProj/jni/libtgvoip/os/windows/CXWrapper.h b/TMessagesProj/jni/libtgvoip/os/windows/CXWrapper.h index edcf7c0f2..cc22fd15b 100644 --- a/TMessagesProj/jni/libtgvoip/os/windows/CXWrapper.h +++ b/TMessagesProj/jni/libtgvoip/os/windows/CXWrapper.h @@ -1,273 +1,273 @@ -#pragma once - -#include -#include -#include -#include -#include -#include "../../VoIPController.h" -#include "../../VoIPServerConfig.h" - -using namespace Platform; - -#define STACK_ARRAY(TYPE, LEN) \ - static_cast(::alloca((LEN) * sizeof(TYPE))) - -inline std::wstring ToUtf16(const char* utf8, size_t len) { - int len16 = ::MultiByteToWideChar(CP_UTF8, 0, utf8, static_cast(len), - nullptr, 0); - wchar_t* ws = STACK_ARRAY(wchar_t, len16); - ::MultiByteToWideChar(CP_UTF8, 0, utf8, static_cast(len), ws, len16); - return std::wstring(ws, len16); -} - -inline std::wstring ToUtf16(const std::string& str) { - return ToUtf16(str.data(), str.length()); -} - -inline std::string ToUtf8(const wchar_t* wide, size_t len) { - int len8 = ::WideCharToMultiByte(CP_UTF8, 0, wide, static_cast(len), - nullptr, 0, nullptr, nullptr); - char* ns = STACK_ARRAY(char, len8); - ::WideCharToMultiByte(CP_UTF8, 0, wide, static_cast(len), ns, len8, - nullptr, nullptr); - return std::string(ns, len8); -} - -inline std::string ToUtf8(const wchar_t* wide) { - return ToUtf8(wide, wcslen(wide)); -} - -inline std::string ToUtf8(const std::wstring& wstr) { - return ToUtf8(wstr.data(), wstr.length()); -} - -namespace libtgvoip{ - public ref class Endpoint sealed{ - public: - property int64 id; - property uint16 port; - property Platform::String^ ipv4; - property Platform::String^ ipv6; - property Platform::Array^ peerTag; - }; - - public ref class TrafficStats sealed{ - public: - property uint64_t bytesSentWifi; - property uint64_t bytesRecvdWifi; - property uint64_t bytesSentMobile; - property uint64_t bytesRecvdMobile; - }; - - public enum class CallState : int{ - WaitInit=1, - WaitInitAck, - Established, - Failed - }; - - public enum class Error : int{ - Unknown=0, - Incompatible, - Timeout, - AudioIO - }; - - public enum class NetworkType : int{ - Unknown=0, - GPRS, - EDGE, - UMTS, - HSPA, - LTE, - WiFi, - Ethernet, - OtherHighSpeed, - OtherLowSpeed, - Dialup, - OtherMobile - }; - - public enum class DataSavingMode{ - Never=0, - MobileOnly, - Always - }; - - public enum class ProxyProtocol{ - None=0, - SOCKS5 - }; - - public ref class VoIPConfig sealed { - public: - VoIPConfig() { - logPacketStats = false; - enableVolumeControl = false; - enableVideoSend = false; - enableVideoReceive = false; - } - - property double initTimeout; - property double recvTimeout; - property DataSavingMode dataSaving; - property String^ logFilePath; - property String^ statsDumpFilePath; - - property bool enableAEC; - property bool enableNS; - property bool enableAGC; - - property bool enableCallUpgrade; - - property bool logPacketStats; - property bool enableVolumeControl; - - property bool enableVideoSend; - property bool enableVideoReceive; - }; - - ref class VoIPControllerWrapper; - public delegate void CallStateChangedEventHandler(VoIPControllerWrapper^ sender, CallState newState); - - ref class VoIPControllerWrapper; - public delegate void SignalBarsChangedEventHandler(VoIPControllerWrapper^ sender, int newCount); - - public ref class VoIPControllerWrapper sealed{ - public: - VoIPControllerWrapper(); - virtual ~VoIPControllerWrapper(); - void Start(); - void Connect(); - void SetPublicEndpoints(const Platform::Array^ endpoints, bool allowP2P, int32_t connectionMaxLayer); - void SetNetworkType(NetworkType type); - void SetMicMute(bool mute); - void SetEncryptionKey(const Platform::Array^ key, bool isOutgoing); - void SetConfig(VoIPConfig^ config); - void SetProxy(ProxyProtocol protocol, Platform::String^ address, uint16_t port, Platform::String^ username, Platform::String^ password); - int GetSignalBarsCount(); - CallState GetConnectionState(); - TrafficStats^ GetStats(); - Platform::String^ GetDebugString(); - Platform::String^ GetDebugLog(); - Error GetLastError(); - static Platform::String^ GetVersion(); - int64 GetPreferredRelayID(); - void SetAudioOutputGainControlEnabled(bool enabled); - - void SetInputVolume(float level); - void SetOutputVolume(float level); - - property String^ CurrentAudioInput - { - String^ get() - { - return ref new String(ToUtf16(controller->GetCurrentAudioInputID()).data()); - } - void set(String^ value) - { - controller->SetCurrentAudioInput(ToUtf8(value->Data())); - } - } - - property String^ CurrentAudioOutput - { - String^ get() - { - return ref new String(ToUtf16(controller->GetCurrentAudioOutputID()).data()); - } - void set(String^ value) - { - controller->SetCurrentAudioOutput(ToUtf8(value->Data())); - } - } - - static int32_t GetConnectionMaxLayer(); - static void UpdateServerConfig(Platform::String^ json); - static void SwitchSpeaker(bool external); - //static Platform::String^ TestAesIge(); - - event CallStateChangedEventHandler^ CallStateChanged; - event SignalBarsChangedEventHandler^ SignalBarsChanged; - - private: - static void OnStateChanged(tgvoip::VoIPController* c, int state); - static void OnSignalBarsChanged(tgvoip::VoIPController* c, int count); - void OnStateChangedInternal(int state); - void OnSignalBarsChangedInternal(int count); - tgvoip::VoIPController* controller; - }; - - ref class MicrosoftCryptoImpl{ - public: - static void AesIgeEncrypt(uint8_t* in, uint8_t* out, size_t len, uint8_t* key, uint8_t* iv); - static void AesIgeDecrypt(uint8_t* in, uint8_t* out, size_t len, uint8_t* key, uint8_t* iv); - static void AesCtrEncrypt(uint8_t* inout, size_t len, uint8_t* key, uint8_t* iv, uint8_t* ecount, uint32_t* num); - static void SHA1(uint8_t* msg, size_t len, uint8_t* out); - static void SHA256(uint8_t* msg, size_t len, uint8_t* out); - static void RandBytes(uint8_t* buffer, size_t len); - static void Init(); - private: - static inline void XorInt128(uint8_t* a, uint8_t* b, uint8_t* out); - static void IBufferToPtr(Windows::Storage::Streams::IBuffer^ buffer, size_t len, uint8_t* out); - static Windows::Storage::Streams::IBuffer^ IBufferFromPtr(uint8_t* msg, size_t len); - /*static Windows::Security::Cryptography::Core::CryptographicHash^ sha1Hash; - static Windows::Security::Cryptography::Core::CryptographicHash^ sha256Hash;*/ - static Windows::Security::Cryptography::Core::HashAlgorithmProvider^ sha1Provider; - static Windows::Security::Cryptography::Core::HashAlgorithmProvider^ sha256Provider; - static Windows::Security::Cryptography::Core::SymmetricKeyAlgorithmProvider^ aesKeyProvider; - }; - - class NativeBuffer : - public Microsoft::WRL::RuntimeClass, - ABI::Windows::Storage::Streams::IBuffer, - Windows::Storage::Streams::IBufferByteAccess> - { - public: - NativeBuffer(byte *buffer, UINT totalSize) - { - m_length=totalSize; - m_buffer=buffer; - } - - virtual ~NativeBuffer() - { - } - - STDMETHODIMP RuntimeClassInitialize(byte *buffer, UINT totalSize) - { - m_length=totalSize; - m_buffer=buffer; - return S_OK; - } - - STDMETHODIMP Buffer(byte **value) - { - *value=m_buffer; - return S_OK; - } - - STDMETHODIMP get_Capacity(UINT32 *value) - { - *value=m_length; - return S_OK; - } - - STDMETHODIMP get_Length(UINT32 *value) - { - *value=m_length; - return S_OK; - } - - STDMETHODIMP put_Length(UINT32 value) - { - m_length=value; - return S_OK; - } - - private: - UINT32 m_length; - byte *m_buffer; - }; +#pragma once + +#include +#include +#include +#include +#include +#include "../../VoIPController.h" +#include "../../VoIPServerConfig.h" + +using namespace Platform; + +#define STACK_ARRAY(TYPE, LEN) \ + static_cast(::alloca((LEN) * sizeof(TYPE))) + +inline std::wstring ToUtf16(const char* utf8, size_t len) { + int len16 = ::MultiByteToWideChar(CP_UTF8, 0, utf8, static_cast(len), + nullptr, 0); + wchar_t* ws = STACK_ARRAY(wchar_t, len16); + ::MultiByteToWideChar(CP_UTF8, 0, utf8, static_cast(len), ws, len16); + return std::wstring(ws, len16); +} + +inline std::wstring ToUtf16(const std::string& str) { + return ToUtf16(str.data(), str.length()); +} + +inline std::string ToUtf8(const wchar_t* wide, size_t len) { + int len8 = ::WideCharToMultiByte(CP_UTF8, 0, wide, static_cast(len), + nullptr, 0, nullptr, nullptr); + char* ns = STACK_ARRAY(char, len8); + ::WideCharToMultiByte(CP_UTF8, 0, wide, static_cast(len), ns, len8, + nullptr, nullptr); + return std::string(ns, len8); +} + +inline std::string ToUtf8(const wchar_t* wide) { + return ToUtf8(wide, wcslen(wide)); +} + +inline std::string ToUtf8(const std::wstring& wstr) { + return ToUtf8(wstr.data(), wstr.length()); +} + +namespace libtgvoip{ + public ref class Endpoint sealed{ + public: + property int64 id; + property uint16 port; + property Platform::String^ ipv4; + property Platform::String^ ipv6; + property Platform::Array^ peerTag; + }; + + public ref class TrafficStats sealed{ + public: + property uint64_t bytesSentWifi; + property uint64_t bytesRecvdWifi; + property uint64_t bytesSentMobile; + property uint64_t bytesRecvdMobile; + }; + + public enum class CallState : int{ + WaitInit=1, + WaitInitAck, + Established, + Failed + }; + + public enum class Error : int{ + Unknown=0, + Incompatible, + Timeout, + AudioIO + }; + + public enum class NetworkType : int{ + Unknown=0, + GPRS, + EDGE, + UMTS, + HSPA, + LTE, + WiFi, + Ethernet, + OtherHighSpeed, + OtherLowSpeed, + Dialup, + OtherMobile + }; + + public enum class DataSavingMode{ + Never=0, + MobileOnly, + Always + }; + + public enum class ProxyProtocol{ + None=0, + SOCKS5 + }; + + public ref class VoIPConfig sealed { + public: + VoIPConfig() { + logPacketStats = false; + enableVolumeControl = false; + enableVideoSend = false; + enableVideoReceive = false; + } + + property double initTimeout; + property double recvTimeout; + property DataSavingMode dataSaving; + property String^ logFilePath; + property String^ statsDumpFilePath; + + property bool enableAEC; + property bool enableNS; + property bool enableAGC; + + property bool enableCallUpgrade; + + property bool logPacketStats; + property bool enableVolumeControl; + + property bool enableVideoSend; + property bool enableVideoReceive; + }; + + ref class VoIPControllerWrapper; + public delegate void CallStateChangedEventHandler(VoIPControllerWrapper^ sender, CallState newState); + + ref class VoIPControllerWrapper; + public delegate void SignalBarsChangedEventHandler(VoIPControllerWrapper^ sender, int newCount); + + public ref class VoIPControllerWrapper sealed{ + public: + VoIPControllerWrapper(); + virtual ~VoIPControllerWrapper(); + void Start(); + void Connect(); + void SetPublicEndpoints(const Platform::Array^ endpoints, bool allowP2P, int32_t connectionMaxLayer); + void SetNetworkType(NetworkType type); + void SetMicMute(bool mute); + void SetEncryptionKey(const Platform::Array^ key, bool isOutgoing); + void SetConfig(VoIPConfig^ config); + void SetProxy(ProxyProtocol protocol, Platform::String^ address, uint16_t port, Platform::String^ username, Platform::String^ password); + int GetSignalBarsCount(); + CallState GetConnectionState(); + TrafficStats^ GetStats(); + Platform::String^ GetDebugString(); + Platform::String^ GetDebugLog(); + Error GetLastError(); + static Platform::String^ GetVersion(); + int64 GetPreferredRelayID(); + void SetAudioOutputGainControlEnabled(bool enabled); + + void SetInputVolume(float level); + void SetOutputVolume(float level); + + property String^ CurrentAudioInput + { + String^ get() + { + return ref new String(ToUtf16(controller->GetCurrentAudioInputID()).data()); + } + void set(String^ value) + { + controller->SetCurrentAudioInput(ToUtf8(value->Data())); + } + } + + property String^ CurrentAudioOutput + { + String^ get() + { + return ref new String(ToUtf16(controller->GetCurrentAudioOutputID()).data()); + } + void set(String^ value) + { + controller->SetCurrentAudioOutput(ToUtf8(value->Data())); + } + } + + static int32_t GetConnectionMaxLayer(); + static void UpdateServerConfig(Platform::String^ json); + static void SwitchSpeaker(bool external); + //static Platform::String^ TestAesIge(); + + event CallStateChangedEventHandler^ CallStateChanged; + event SignalBarsChangedEventHandler^ SignalBarsChanged; + + private: + static void OnStateChanged(tgvoip::VoIPController* c, int state); + static void OnSignalBarsChanged(tgvoip::VoIPController* c, int count); + void OnStateChangedInternal(int state); + void OnSignalBarsChangedInternal(int count); + tgvoip::VoIPController* controller; + }; + + ref class MicrosoftCryptoImpl{ + public: + static void AesIgeEncrypt(uint8_t* in, uint8_t* out, size_t len, uint8_t* key, uint8_t* iv); + static void AesIgeDecrypt(uint8_t* in, uint8_t* out, size_t len, uint8_t* key, uint8_t* iv); + static void AesCtrEncrypt(uint8_t* inout, size_t len, uint8_t* key, uint8_t* iv, uint8_t* ecount, uint32_t* num); + static void SHA1(uint8_t* msg, size_t len, uint8_t* out); + static void SHA256(uint8_t* msg, size_t len, uint8_t* out); + static void RandBytes(uint8_t* buffer, size_t len); + static void Init(); + private: + static inline void XorInt128(uint8_t* a, uint8_t* b, uint8_t* out); + static void IBufferToPtr(Windows::Storage::Streams::IBuffer^ buffer, size_t len, uint8_t* out); + static Windows::Storage::Streams::IBuffer^ IBufferFromPtr(uint8_t* msg, size_t len); + /*static Windows::Security::Cryptography::Core::CryptographicHash^ sha1Hash; + static Windows::Security::Cryptography::Core::CryptographicHash^ sha256Hash;*/ + static Windows::Security::Cryptography::Core::HashAlgorithmProvider^ sha1Provider; + static Windows::Security::Cryptography::Core::HashAlgorithmProvider^ sha256Provider; + static Windows::Security::Cryptography::Core::SymmetricKeyAlgorithmProvider^ aesKeyProvider; + }; + + class NativeBuffer : + public Microsoft::WRL::RuntimeClass, + ABI::Windows::Storage::Streams::IBuffer, + Windows::Storage::Streams::IBufferByteAccess> + { + public: + NativeBuffer(byte *buffer, UINT totalSize) + { + m_length=totalSize; + m_buffer=buffer; + } + + virtual ~NativeBuffer() + { + } + + STDMETHODIMP RuntimeClassInitialize(byte *buffer, UINT totalSize) + { + m_length=totalSize; + m_buffer=buffer; + return S_OK; + } + + STDMETHODIMP Buffer(byte **value) + { + *value=m_buffer; + return S_OK; + } + + STDMETHODIMP get_Capacity(UINT32 *value) + { + *value=m_length; + return S_OK; + } + + STDMETHODIMP get_Length(UINT32 *value) + { + *value=m_length; + return S_OK; + } + + STDMETHODIMP put_Length(UINT32 value) + { + m_length=value; + return S_OK; + } + + private: + UINT32 m_length; + byte *m_buffer; + }; } \ No newline at end of file diff --git a/TMessagesProj/jni/libtgvoip/os/windows/WindowsSandboxUtils.cpp b/TMessagesProj/jni/libtgvoip/os/windows/WindowsSandboxUtils.cpp index 8fbba664e..9c7e1c549 100644 --- a/TMessagesProj/jni/libtgvoip/os/windows/WindowsSandboxUtils.cpp +++ b/TMessagesProj/jni/libtgvoip/os/windows/WindowsSandboxUtils.cpp @@ -1,68 +1,68 @@ - -// -// libtgvoip is free and unencumbered public domain software. -// For more information, see http://unlicense.org or the UNLICENSE file -// you should have received with this source code distribution. -// - -#include "WindowsSandboxUtils.h" -#include -#include -#ifdef TGVOIP_WP_SILVERLIGHT -#include -#endif - -using namespace tgvoip; -using namespace Microsoft::WRL; - - -IAudioClient2* WindowsSandboxUtils::ActivateAudioDevice(const wchar_t* devID, HRESULT* callRes, HRESULT* actRes) { -#ifndef TGVOIP_WP_SILVERLIGHT - // Did I say that I hate pointlessly asynchronous things? - HANDLE event = CreateEventEx(NULL, NULL, 0, EVENT_ALL_ACCESS); - ActivationHandler activationHandler(event); - IActivateAudioInterfaceAsyncOperation* actHandler; - HRESULT cr = ActivateAudioInterfaceAsync(devID, __uuidof(IAudioClient2), NULL, (IActivateAudioInterfaceCompletionHandler*)&activationHandler, &actHandler); - if (callRes) - *callRes = cr; - DWORD resulttt = WaitForSingleObjectEx(event, INFINITE, false); - DWORD last = GetLastError(); - CloseHandle(event); - if (actRes) - *actRes = activationHandler.actResult; - return activationHandler.client; -#else - IAudioClient2* client; - HRESULT res=ActivateAudioInterface(devID, __uuidof(IAudioClient2), (void**)&client); - if(callRes) - *callRes=S_OK; - if(actRes) - *actRes=res; - return client; -#endif -} - -#ifndef TGVOIP_WP_SILVERLIGHT -ActivationHandler::ActivationHandler(HANDLE _event) : event(_event) -{ - -} - -STDMETHODIMP ActivationHandler::ActivateCompleted(IActivateAudioInterfaceAsyncOperation * operation) -{ - HRESULT hr = S_OK; - HRESULT hrActivateResult = S_OK; - IUnknown *punkAudioInterface = nullptr; - - hr = operation->GetActivateResult(&hrActivateResult, &punkAudioInterface); - if (SUCCEEDED(hr) && SUCCEEDED(hrActivateResult)) - { - punkAudioInterface->QueryInterface(IID_PPV_ARGS(&client)); - } - - SetEvent(event); - - return hr; -} - + +// +// libtgvoip is free and unencumbered public domain software. +// For more information, see http://unlicense.org or the UNLICENSE file +// you should have received with this source code distribution. +// + +#include "WindowsSandboxUtils.h" +#include +#include +#ifdef TGVOIP_WP_SILVERLIGHT +#include +#endif + +using namespace tgvoip; +using namespace Microsoft::WRL; + + +IAudioClient2* WindowsSandboxUtils::ActivateAudioDevice(const wchar_t* devID, HRESULT* callRes, HRESULT* actRes) { +#ifndef TGVOIP_WP_SILVERLIGHT + // Did I say that I hate pointlessly asynchronous things? + HANDLE event = CreateEventEx(NULL, NULL, 0, EVENT_ALL_ACCESS); + ActivationHandler activationHandler(event); + IActivateAudioInterfaceAsyncOperation* actHandler; + HRESULT cr = ActivateAudioInterfaceAsync(devID, __uuidof(IAudioClient2), NULL, (IActivateAudioInterfaceCompletionHandler*)&activationHandler, &actHandler); + if (callRes) + *callRes = cr; + DWORD resulttt = WaitForSingleObjectEx(event, INFINITE, false); + DWORD last = GetLastError(); + CloseHandle(event); + if (actRes) + *actRes = activationHandler.actResult; + return activationHandler.client; +#else + IAudioClient2* client; + HRESULT res=ActivateAudioInterface(devID, __uuidof(IAudioClient2), (void**)&client); + if(callRes) + *callRes=S_OK; + if(actRes) + *actRes=res; + return client; +#endif +} + +#ifndef TGVOIP_WP_SILVERLIGHT +ActivationHandler::ActivationHandler(HANDLE _event) : event(_event) +{ + +} + +STDMETHODIMP ActivationHandler::ActivateCompleted(IActivateAudioInterfaceAsyncOperation * operation) +{ + HRESULT hr = S_OK; + HRESULT hrActivateResult = S_OK; + IUnknown *punkAudioInterface = nullptr; + + hr = operation->GetActivateResult(&hrActivateResult, &punkAudioInterface); + if (SUCCEEDED(hr) && SUCCEEDED(hrActivateResult)) + { + punkAudioInterface->QueryInterface(IID_PPV_ARGS(&client)); + } + + SetEvent(event); + + return hr; +} + #endif \ No newline at end of file diff --git a/TMessagesProj/jni/libtgvoip/os/windows/WindowsSandboxUtils.h b/TMessagesProj/jni/libtgvoip/os/windows/WindowsSandboxUtils.h index aeddca076..a43c707cb 100644 --- a/TMessagesProj/jni/libtgvoip/os/windows/WindowsSandboxUtils.h +++ b/TMessagesProj/jni/libtgvoip/os/windows/WindowsSandboxUtils.h @@ -1,42 +1,42 @@ -// -// libtgvoip is free and unencumbered public domain software. -// For more information, see http://unlicense.org or the UNLICENSE file -// you should have received with this source code distribution. -// - -#ifndef LIBTGVOIP_WINDOWS_SANDBOX_UTILS -#define LIBTGVOIP_WINDOWS_SANDBOX_UTILS - -#include -#include -#ifndef TGVOIP_WP_SILVERLIGHT -#include -#endif -#include -#include - -using namespace Microsoft::WRL; - -namespace tgvoip { - -#ifndef TGVOIP_WP_SILVERLIGHT - class ActivationHandler : - public RuntimeClass< RuntimeClassFlags< ClassicCom >, FtmBase, IActivateAudioInterfaceCompletionHandler > - { - public: - STDMETHOD(ActivateCompleted)(IActivateAudioInterfaceAsyncOperation *operation); - - ActivationHandler(HANDLE _event); - HANDLE event; - IAudioClient2* client; - HRESULT actResult; - }; -#endif - - class WindowsSandboxUtils { - public: - static IAudioClient2* ActivateAudioDevice(const wchar_t* devID, HRESULT* callResult, HRESULT* actResult); - }; -} - -#endif // LIBTGVOIP_WINDOWS_SANDBOX_UTILS +// +// libtgvoip is free and unencumbered public domain software. +// For more information, see http://unlicense.org or the UNLICENSE file +// you should have received with this source code distribution. +// + +#ifndef LIBTGVOIP_WINDOWS_SANDBOX_UTILS +#define LIBTGVOIP_WINDOWS_SANDBOX_UTILS + +#include +#include +#ifndef TGVOIP_WP_SILVERLIGHT +#include +#endif +#include +#include + +using namespace Microsoft::WRL; + +namespace tgvoip { + +#ifndef TGVOIP_WP_SILVERLIGHT + class ActivationHandler : + public RuntimeClass< RuntimeClassFlags< ClassicCom >, FtmBase, IActivateAudioInterfaceCompletionHandler > + { + public: + STDMETHOD(ActivateCompleted)(IActivateAudioInterfaceAsyncOperation *operation); + + ActivationHandler(HANDLE _event); + HANDLE event; + IAudioClient2* client; + HRESULT actResult; + }; +#endif + + class WindowsSandboxUtils { + public: + static IAudioClient2* ActivateAudioDevice(const wchar_t* devID, HRESULT* callResult, HRESULT* actResult); + }; +} + +#endif // LIBTGVOIP_WINDOWS_SANDBOX_UTILS diff --git a/TMessagesProj/jni/libtgvoip/os/windows/WindowsSpecific.cpp b/TMessagesProj/jni/libtgvoip/os/windows/WindowsSpecific.cpp index 3a1a23f9f..fa5b19dec 100755 --- a/TMessagesProj/jni/libtgvoip/os/windows/WindowsSpecific.cpp +++ b/TMessagesProj/jni/libtgvoip/os/windows/WindowsSpecific.cpp @@ -1,9 +1,9 @@ -#include "WindowsSpecific.h" - -using namespace tgvoip; - -std::string WindowsSpecific::GetErrorMessage(DWORD code){ - char buf[1024]={0}; - FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), buf, sizeof(buf), NULL); - return std::string(buf); +#include "WindowsSpecific.h" + +using namespace tgvoip; + +std::string WindowsSpecific::GetErrorMessage(DWORD code){ + char buf[1024]={0}; + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, code, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), buf, sizeof(buf), NULL); + return std::string(buf); } \ No newline at end of file diff --git a/TMessagesProj/libs/libv2ray.aar b/TMessagesProj/libs/libv2ray.aar new file mode 100644 index 000000000..7c0e0f1e9 Binary files /dev/null and b/TMessagesProj/libs/libv2ray.aar differ diff --git a/TMessagesProj/libs/ss-libev-release.aar b/TMessagesProj/libs/ss-libev-release.aar new file mode 100644 index 000000000..53638cb71 Binary files /dev/null and b/TMessagesProj/libs/ss-libev-release.aar differ diff --git a/TMessagesProj/libs/ssr-libev-release.aar b/TMessagesProj/libs/ssr-libev-release.aar new file mode 100644 index 000000000..1bc2b45a8 Binary files /dev/null and b/TMessagesProj/libs/ssr-libev-release.aar differ diff --git a/TMessagesProj/proguard-rules.pro b/TMessagesProj/proguard-rules.pro index 3e972f585..c13f00124 100644 --- a/TMessagesProj/proguard-rules.pro +++ b/TMessagesProj/proguard-rules.pro @@ -4,9 +4,14 @@ @com.google.android.gms.common.annotation.KeepName *; } -keep class org.telegram.** { *; } +-keep class com.google.android.exoplayer2.decoder.** { *; } -keep class com.google.android.exoplayer2.ext.** { *; } -keep class com.google.android.exoplayer2.util.** { *; } -keep class com.google.android.exoplayer2.metadata.** { *; } +-keep class com.v2ray.ang.dto.** { *; } + +-keep class io.github.trojan_gfw.** { *; } + -dontwarn com.coremedia.** -dontwarn org.telegram.** -dontwarn com.google.android.exoplayer2.ext.** @@ -16,8 +21,9 @@ -dontwarn com.google.common.cache.** -dontwarn com.google.common.primitives.** -dontwarn com.googlecode.mp4parser.** + # Use -keep to explicitly keep any other classes shrinking would remove --dontoptimize +# -dontoptimize -dontobfuscate # https://github.com/osmdroid/osmdroid/issues/633 diff --git a/TMessagesProj/release.keystore b/TMessagesProj/release.keystore new file mode 100644 index 000000000..ab1a9fb2c Binary files /dev/null and b/TMessagesProj/release.keystore differ diff --git a/TMessagesProj/src/gservcies/AndroidManifest.xml b/TMessagesProj/src/gservcies/AndroidManifest.xml new file mode 100644 index 000000000..12fd89ee5 --- /dev/null +++ b/TMessagesProj/src/gservcies/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TMessagesProj/src/gservcies/java/org/telegram/messenger/GcmPushListenerService.java b/TMessagesProj/src/gservcies/java/org/telegram/messenger/GcmPushListenerService.java new file mode 100644 index 000000000..fb2ddcb9b --- /dev/null +++ b/TMessagesProj/src/gservcies/java/org/telegram/messenger/GcmPushListenerService.java @@ -0,0 +1,1008 @@ +/* + * This is the source code of Telegram for Android v. 5.x.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2018. + */ + +package org.telegram.messenger; + +import android.os.SystemClock; +import android.text.TextUtils; +import android.util.Base64; +import android.util.SparseArray; + +import com.google.firebase.messaging.FirebaseMessagingService; +import com.google.firebase.messaging.RemoteMessage; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.BuildVars; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.LocationController; +import org.telegram.messenger.MessageKeyData; +import org.telegram.messenger.MessageObject; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.MessagesStorage; +import org.telegram.messenger.NotificationsController; +import org.telegram.messenger.R; +import org.telegram.messenger.SharedConfig; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.Utilities; +import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.NativeByteBuffer; +import org.telegram.tgnet.TLRPC; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +public class GcmPushListenerService extends FirebaseMessagingService { + + public static final int NOTIFICATION_ID = 1; + private CountDownLatch countDownLatch = new CountDownLatch(1); + + @Override + public void onMessageReceived(RemoteMessage message) { + String from = message.getFrom(); + final Map data = message.getData(); + final long time = message.getSentTime(); + final long receiveTime = SystemClock.elapsedRealtime(); + if (BuildVars.LOGS_ENABLED) { + FileLog.d("GCM received data: " + data + " from: " + from); + } + AndroidUtilities.runOnUIThread(() -> { + ApplicationLoader.postInitApplication(); + Utilities.stageQueue.postRunnable(() -> { + if (BuildVars.LOGS_ENABLED) { + FileLog.d("GCM START PROCESSING"); + } + int currentAccount = -1; + String loc_key = null; + String jsonString = null; + try { + Object value = data.get("p"); + if (!(value instanceof String)) { + if (BuildVars.LOGS_ENABLED) { + FileLog.d("GCM DECRYPT ERROR 1"); + } + onDecryptError(); + return; + } + byte[] bytes = Base64.decode((String) value, Base64.URL_SAFE); + NativeByteBuffer buffer = new NativeByteBuffer(bytes.length); + buffer.writeBytes(bytes); + buffer.position(0); + + if (SharedConfig.pushAuthKeyId == null) { + SharedConfig.pushAuthKeyId = new byte[8]; + byte[] authKeyHash = Utilities.computeSHA1(SharedConfig.pushAuthKey); + System.arraycopy(authKeyHash, authKeyHash.length - 8, SharedConfig.pushAuthKeyId, 0, 8); + } + byte[] inAuthKeyId = new byte[8]; + buffer.readBytes(inAuthKeyId, true); + if (!Arrays.equals(SharedConfig.pushAuthKeyId, inAuthKeyId)) { + onDecryptError(); + if (BuildVars.LOGS_ENABLED) { + FileLog.d(String.format(Locale.US, "GCM DECRYPT ERROR 2 k1=%s k2=%s, key=%s", Utilities.bytesToHex(SharedConfig.pushAuthKeyId), Utilities.bytesToHex(inAuthKeyId), Utilities.bytesToHex(SharedConfig.pushAuthKey))); + } + return; + } + + byte[] messageKey = new byte[16]; + buffer.readBytes(messageKey, true); + + MessageKeyData messageKeyData = MessageKeyData.generateMessageKeyData(SharedConfig.pushAuthKey, messageKey, true, 2); + Utilities.aesIgeEncryption(buffer.buffer, messageKeyData.aesKey, messageKeyData.aesIv, false, false, 24, bytes.length - 24); + + byte[] messageKeyFull = Utilities.computeSHA256(SharedConfig.pushAuthKey, 88 + 8, 32, buffer.buffer, 24, buffer.buffer.limit()); + if (!Utilities.arraysEquals(messageKey, 0, messageKeyFull, 8)) { + onDecryptError(); + if (BuildVars.LOGS_ENABLED) { + FileLog.d(String.format("GCM DECRYPT ERROR 3, key = %s", Utilities.bytesToHex(SharedConfig.pushAuthKey))); + } + return; + } + + int len = buffer.readInt32(true); + byte[] strBytes = new byte[len]; + buffer.readBytes(strBytes, true); + jsonString = new String(strBytes); + JSONObject json = new JSONObject(jsonString); + + if (json.has("loc_key")) { + loc_key = json.getString("loc_key"); + } else { + loc_key = ""; + } + + JSONObject custom; + Object object = json.get("custom"); + if (object instanceof JSONObject) { + custom = json.getJSONObject("custom"); + } else { + custom = new JSONObject(); + } + + Object userIdObject; + if (json.has("user_id")) { + userIdObject = json.get("user_id"); + } else { + userIdObject = null; + } + int accountUserId; + if (userIdObject == null) { + accountUserId = UserConfig.getInstance(UserConfig.selectedAccount).getClientUserId(); + } else { + if (userIdObject instanceof Integer) { + accountUserId = (Integer) userIdObject; + } else if (userIdObject instanceof String) { + accountUserId = Utilities.parseInt((String) userIdObject); + } else { + accountUserId = UserConfig.getInstance(UserConfig.selectedAccount).getClientUserId(); + } + } + int account = UserConfig.selectedAccount; + for (int a = 0; a < UserConfig.MAX_ACCOUNT_COUNT; a++) { + if (UserConfig.getInstance(a).getClientUserId() == accountUserId) { + account = a; + break; + } + } + final int accountFinal = currentAccount = account; + if (!UserConfig.getInstance(currentAccount).isClientActivated()) { + if (BuildVars.LOGS_ENABLED) { + FileLog.d("GCM ACCOUNT NOT ACTIVATED"); + } + countDownLatch.countDown(); + return; + } + Object obj = data.get("google.sent_time"); + switch (loc_key) { + case "DC_UPDATE": { + int dc = custom.getInt("dc"); + String addr = custom.getString("addr"); + String[] parts = addr.split(":"); + if (parts.length != 2) { + countDownLatch.countDown(); + return; + } + String ip = parts[0]; + int port = Integer.parseInt(parts[1]); + ConnectionsManager.getInstance(currentAccount).applyDatacenterAddress(dc, ip, port); + ConnectionsManager.getInstance(currentAccount).resumeNetworkMaybe(); + countDownLatch.countDown(); + return; + } + case "MESSAGE_ANNOUNCEMENT": { + TLRPC.TL_updateServiceNotification update = new TLRPC.TL_updateServiceNotification(); + update.popup = false; + update.flags = 2; + update.inbox_date = (int) (time / 1000); + update.message = json.getString("message"); + update.type = "announcement"; + update.media = new TLRPC.TL_messageMediaEmpty(); + final TLRPC.TL_updates updates = new TLRPC.TL_updates(); + updates.updates.add(update); + Utilities.stageQueue.postRunnable(() -> MessagesController.getInstance(accountFinal).processUpdates(updates, false)); + ConnectionsManager.getInstance(currentAccount).resumeNetworkMaybe(); + countDownLatch.countDown(); + return; + } + case "SESSION_REVOKE": { + AndroidUtilities.runOnUIThread(() -> { + if (UserConfig.getInstance(accountFinal).getClientUserId() != 0) { + UserConfig.getInstance(accountFinal).clearConfig(); + MessagesController.getInstance(accountFinal).performLogout(0); + } + }); + countDownLatch.countDown(); + return; + } + case "GEO_LIVE_PENDING": { + //Utilities.stageQueue.postRunnable(() -> LocationController.getInstance(accountFinal).setNewLocationEndWatchTime()); + countDownLatch.countDown(); + return; + } + } + + int channel_id; + int chat_id; + int user_id; + long dialog_id = 0; + boolean scheduled; + if (custom.has("channel_id")) { + channel_id = custom.getInt("channel_id"); + dialog_id = -channel_id; + } else { + channel_id = 0; + } + if (custom.has("from_id")) { + user_id = custom.getInt("from_id"); + dialog_id = user_id; + } else { + user_id = 0; + } + if (custom.has("chat_id")) { + chat_id = custom.getInt("chat_id"); + dialog_id = -chat_id; + } else { + chat_id = 0; + } + if (custom.has("encryption_id")) { + dialog_id = ((long) custom.getInt("encryption_id")) << 32; + } + if (custom.has("schedule")) { + scheduled = custom.getInt("schedule") == 1; + } else { + scheduled = false; + } + if (dialog_id == 0 && "ENCRYPTED_MESSAGE".equals(loc_key)) { + dialog_id = -(1L << 32); + } + boolean canRelease = true; + if (dialog_id != 0) { + if ("READ_HISTORY".equals(loc_key)) { + int max_id = custom.getInt("max_id"); + final ArrayList updates = new ArrayList<>(); + if (BuildVars.LOGS_ENABLED) { + FileLog.d("GCM received read notification max_id = " + max_id + " for dialogId = " + dialog_id); + } + if (channel_id != 0) { + TLRPC.TL_updateReadChannelInbox update = new TLRPC.TL_updateReadChannelInbox(); + update.channel_id = channel_id; + update.max_id = max_id; + updates.add(update); + } else { + TLRPC.TL_updateReadHistoryInbox update = new TLRPC.TL_updateReadHistoryInbox(); + if (user_id != 0) { + update.peer = new TLRPC.TL_peerUser(); + update.peer.user_id = user_id; + } else { + update.peer = new TLRPC.TL_peerChat(); + update.peer.chat_id = chat_id; + } + update.max_id = max_id; + updates.add(update); + } + MessagesController.getInstance(accountFinal).processUpdateArray(updates, null, null, false, 0); + } else if ("MESSAGE_DELETED".equals(loc_key)) { + String messages = custom.getString("messages"); + String[] messagesArgs = messages.split(","); + SparseArray> deletedMessages = new SparseArray<>(); + ArrayList ids = new ArrayList<>(); + for (int a = 0; a < messagesArgs.length; a++) { + ids.add(Utilities.parseInt(messagesArgs[a])); + } + deletedMessages.put(channel_id, ids); + NotificationsController.getInstance(currentAccount).removeDeletedMessagesFromNotifications(deletedMessages); + + MessagesController.getInstance(currentAccount).deleteMessagesByPush(dialog_id, ids, channel_id); + if (BuildVars.LOGS_ENABLED) { + FileLog.d("GCM received " + loc_key + " for dialogId = " + dialog_id + " mids = " + TextUtils.join(",", ids)); + } + } else if (!TextUtils.isEmpty(loc_key)) { + int msg_id; + if (custom.has("msg_id")) { + msg_id = custom.getInt("msg_id"); + } else { + msg_id = 0; + } + + long random_id; + if (custom.has("random_id")) { + random_id = Utilities.parseLong(custom.getString("random_id")); + } else { + random_id = 0; + } + + boolean processNotification = false; + if (msg_id != 0) { + Integer currentReadValue = MessagesController.getInstance(currentAccount).dialogs_read_inbox_max.get(dialog_id); + if (currentReadValue == null) { + currentReadValue = MessagesStorage.getInstance(currentAccount).getDialogReadMax(false, dialog_id); + MessagesController.getInstance(accountFinal).dialogs_read_inbox_max.put(dialog_id, currentReadValue); + } + if (msg_id > currentReadValue) { + processNotification = true; + } + } else if (random_id != 0) { + if (!MessagesStorage.getInstance(account).checkMessageByRandomId(random_id)) { + processNotification = true; + } + } + if (processNotification) { + int chat_from_id; + + if (custom.has("chat_from_id")) { + chat_from_id = custom.getInt("chat_from_id"); + } else { + chat_from_id = 0; + } + boolean mention = custom.has("mention") && custom.getInt("mention") != 0; + boolean silent = custom.has("silent") && custom.getInt("silent") != 0; + + String[] args; + if (json.has("loc_args")) { + JSONArray loc_args = json.getJSONArray("loc_args"); + args = new String[loc_args.length()]; + for (int a = 0; a < args.length; a++) { + args[a] = loc_args.getString(a); + } + } else { + args = null; + } + String messageText = null; + String message1 = null; + String name = args[0]; + String userName = null; + boolean localMessage = false; + boolean supergroup = false; + boolean pinned = false; + boolean channel = false; + boolean edited = custom.has("edit_date"); + if (loc_key.startsWith("CHAT_")) { + supergroup = channel_id != 0; + userName = name; + name = args[1]; + } else if (loc_key.startsWith("PINNED_")) { + supergroup = chat_from_id != 0; + pinned = true; + } else if (loc_key.startsWith("CHANNEL_")) { + channel = true; + } + + if (BuildVars.LOGS_ENABLED) { + FileLog.d("GCM received message notification " + loc_key + " for dialogId = " + dialog_id + " mid = " + msg_id); + } + switch (loc_key) { + case "MESSAGE_TEXT": + case "CHANNEL_MESSAGE_TEXT": { + messageText = LocaleController.formatString("NotificationMessageText", R.string.NotificationMessageText, args[0], args[1]); + message1 = args[1]; + break; + } + case "MESSAGE_NOTEXT": { + messageText = LocaleController.formatString("NotificationMessageNoText", R.string.NotificationMessageNoText, args[0]); + message1 = LocaleController.getString("Message", R.string.Message); + break; + } + case "MESSAGE_PHOTO": { + messageText = LocaleController.formatString("NotificationMessagePhoto", R.string.NotificationMessagePhoto, args[0]); + message1 = LocaleController.getString("AttachPhoto", R.string.AttachPhoto); + break; + } + case "MESSAGE_PHOTO_SECRET": { + messageText = LocaleController.formatString("NotificationMessageSDPhoto", R.string.NotificationMessageSDPhoto, args[0]); + message1 = LocaleController.getString("AttachDestructingPhoto", R.string.AttachDestructingPhoto); + break; + } + case "MESSAGE_VIDEO": { + messageText = LocaleController.formatString("NotificationMessageVideo", R.string.NotificationMessageVideo, args[0]); + message1 = LocaleController.getString("AttachVideo", R.string.AttachVideo); + break; + } + case "MESSAGE_VIDEO_SECRET": { + messageText = LocaleController.formatString("NotificationMessageSDVideo", R.string.NotificationMessageSDVideo, args[0]); + message1 = LocaleController.getString("AttachDestructingVideo", R.string.AttachDestructingVideo); + break; + } + case "MESSAGE_SCREENSHOT": { + messageText = LocaleController.getString("ActionTakeScreenshoot", R.string.ActionTakeScreenshoot).replace("un1", args[0]); + break; + } + case "MESSAGE_ROUND": { + messageText = LocaleController.formatString("NotificationMessageRound", R.string.NotificationMessageRound, args[0]); + message1 = LocaleController.getString("AttachRound", R.string.AttachRound); + break; + } + case "MESSAGE_DOC": { + messageText = LocaleController.formatString("NotificationMessageDocument", R.string.NotificationMessageDocument, args[0]); + message1 = LocaleController.getString("AttachDocument", R.string.AttachDocument); + break; + } + case "MESSAGE_STICKER": { + if (args.length > 1 && !TextUtils.isEmpty(args[1])) { + messageText = LocaleController.formatString("NotificationMessageStickerEmoji", R.string.NotificationMessageStickerEmoji, args[0], args[1]); + message1 = args[1] + " " + LocaleController.getString("AttachSticker", R.string.AttachSticker); + } else { + messageText = LocaleController.formatString("NotificationMessageSticker", R.string.NotificationMessageSticker, args[0]); + message1 = LocaleController.getString("AttachSticker", R.string.AttachSticker); + } + break; + } + case "MESSAGE_AUDIO": { + messageText = LocaleController.formatString("NotificationMessageAudio", R.string.NotificationMessageAudio, args[0]); + message1 = LocaleController.getString("AttachAudio", R.string.AttachAudio); + break; + } + case "MESSAGE_CONTACT": { + messageText = LocaleController.formatString("NotificationMessageContact2", R.string.NotificationMessageContact2, args[0], args[1]); + message1 = LocaleController.getString("AttachContact", R.string.AttachContact); + break; + } + case "MESSAGE_QUIZ": { + messageText = LocaleController.formatString("NotificationMessageQuiz2", R.string.NotificationMessageQuiz2, args[0], args[1]); + message1 = LocaleController.getString("QuizPoll", R.string.QuizPoll); + break; + } + case "MESSAGE_POLL": { + messageText = LocaleController.formatString("NotificationMessagePoll2", R.string.NotificationMessagePoll2, args[0], args[1]); + message1 = LocaleController.getString("Poll", R.string.Poll); + break; + } + case "MESSAGE_GEO": { + messageText = LocaleController.formatString("NotificationMessageMap", R.string.NotificationMessageMap, args[0]); + message1 = LocaleController.getString("AttachLocation", R.string.AttachLocation); + break; + } + case "MESSAGE_GEOLIVE": { + messageText = LocaleController.formatString("NotificationMessageLiveLocation", R.string.NotificationMessageLiveLocation, args[0]); + message1 = LocaleController.getString("AttachLiveLocation", R.string.AttachLiveLocation); + break; + } + case "MESSAGE_GIF": { + messageText = LocaleController.formatString("NotificationMessageGif", R.string.NotificationMessageGif, args[0]); + message1 = LocaleController.getString("AttachGif", R.string.AttachGif); + break; + } + case "MESSAGE_GAME": { + messageText = LocaleController.formatString("NotificationMessageGame", R.string.NotificationMessageGame, args[0], args[1]); + message1 = LocaleController.getString("AttachGame", R.string.AttachGame); + break; + } + case "MESSAGE_GAME_SCORE": + case "CHANNEL_MESSAGE_GAME_SCORE":{ + messageText = LocaleController.formatString("NotificationMessageGameScored", R.string.NotificationMessageGameScored, args[0], args[1], args[2]); + break; + } + case "MESSAGE_INVOICE": { + messageText = LocaleController.formatString("NotificationMessageInvoice", R.string.NotificationMessageInvoice, args[0], args[1]); + message1 = LocaleController.getString("PaymentInvoice", R.string.PaymentInvoice); + break; + } + case "MESSAGE_FWDS": { + messageText = LocaleController.formatString("NotificationMessageForwardFew", R.string.NotificationMessageForwardFew, args[0], LocaleController.formatPluralString("messages", Utilities.parseInt(args[1]))); + localMessage = true; + break; + } + case "MESSAGE_PHOTOS": { + messageText = LocaleController.formatString("NotificationMessageFew", R.string.NotificationMessageFew, args[0], LocaleController.formatPluralString("Photos", Utilities.parseInt(args[1]))); + localMessage = true; + break; + } + case "MESSAGE_VIDEOS": { + messageText = LocaleController.formatString("NotificationMessageFew", R.string.NotificationMessageFew, args[0], LocaleController.formatPluralString("Videos", Utilities.parseInt(args[1]))); + localMessage = true; + break; + } + case "MESSAGES": { + messageText = LocaleController.formatString("NotificationMessageAlbum", R.string.NotificationMessageAlbum, args[0]); + localMessage = true; + break; + } + case "CHANNEL_MESSAGE_NOTEXT": { + messageText = LocaleController.formatString("ChannelMessageNoText", R.string.ChannelMessageNoText, args[0]); + message1 = LocaleController.getString("Message", R.string.Message); + break; + } + case "CHANNEL_MESSAGE_PHOTO": { + messageText = LocaleController.formatString("ChannelMessagePhoto", R.string.ChannelMessagePhoto, args[0]); + message1 = LocaleController.getString("AttachPhoto", R.string.AttachPhoto); + break; + } + case "CHANNEL_MESSAGE_VIDEO": { + messageText = LocaleController.formatString("ChannelMessageVideo", R.string.ChannelMessageVideo, args[0]); + message1 = LocaleController.getString("AttachVideo", R.string.AttachVideo); + break; + } + case "CHANNEL_MESSAGE_ROUND": { + messageText = LocaleController.formatString("ChannelMessageRound", R.string.ChannelMessageRound, args[0]); + message1 = LocaleController.getString("AttachRound", R.string.AttachRound); + break; + } + case "CHANNEL_MESSAGE_DOC": { + messageText = LocaleController.formatString("ChannelMessageDocument", R.string.ChannelMessageDocument, args[0]); + message1 = LocaleController.getString("AttachDocument", R.string.AttachDocument); + break; + } + case "CHANNEL_MESSAGE_STICKER": { + if (args.length > 1 && !TextUtils.isEmpty(args[1])) { + messageText = LocaleController.formatString("ChannelMessageStickerEmoji", R.string.ChannelMessageStickerEmoji, args[0], args[1]); + message1 = args[1] + " " + LocaleController.getString("AttachSticker", R.string.AttachSticker); + } else { + messageText = LocaleController.formatString("ChannelMessageSticker", R.string.ChannelMessageSticker, args[0]); + message1 = LocaleController.getString("AttachSticker", R.string.AttachSticker); + } + break; + } + case "CHANNEL_MESSAGE_AUDIO": { + messageText = LocaleController.formatString("ChannelMessageAudio", R.string.ChannelMessageAudio, args[0]); + message1 = LocaleController.getString("AttachAudio", R.string.AttachAudio); + break; + } + case "CHANNEL_MESSAGE_CONTACT": { + messageText = LocaleController.formatString("ChannelMessageContact2", R.string.ChannelMessageContact2, args[0], args[1]); + message1 = LocaleController.getString("AttachContact", R.string.AttachContact); + break; + } + case "CHANNEL_MESSAGE_QUIZ": { + messageText = LocaleController.formatString("ChannelMessageQuiz2", R.string.ChannelMessageQuiz2, args[0], args[1]); + message1 = LocaleController.getString("QuizPoll", R.string.QuizPoll); + break; + } + case "CHANNEL_MESSAGE_POLL": { + messageText = LocaleController.formatString("ChannelMessagePoll2", R.string.ChannelMessagePoll2, args[0], args[1]); + message1 = LocaleController.getString("Poll", R.string.Poll); + break; + } + case "CHANNEL_MESSAGE_GEO": { + messageText = LocaleController.formatString("ChannelMessageMap", R.string.ChannelMessageMap, args[0]); + message1 = LocaleController.getString("AttachLocation", R.string.AttachLocation); + break; + } + case "CHANNEL_MESSAGE_GEOLIVE": { + messageText = LocaleController.formatString("ChannelMessageLiveLocation", R.string.ChannelMessageLiveLocation, args[0]); + message1 = LocaleController.getString("AttachLiveLocation", R.string.AttachLiveLocation); + break; + } + case "CHANNEL_MESSAGE_GIF": { + messageText = LocaleController.formatString("ChannelMessageGIF", R.string.ChannelMessageGIF, args[0]); + message1 = LocaleController.getString("AttachGif", R.string.AttachGif); + break; + } + case "CHANNEL_MESSAGE_GAME": { + messageText = LocaleController.formatString("NotificationMessageGame", R.string.NotificationMessageGame, args[0]); + message1 = LocaleController.getString("AttachGame", R.string.AttachGame); + break; + } + case "CHANNEL_MESSAGE_FWDS": { + messageText = LocaleController.formatString("ChannelMessageFew", R.string.ChannelMessageFew, args[0], LocaleController.formatPluralString("ForwardedMessageCount", Utilities.parseInt(args[1])).toLowerCase()); + localMessage = true; + break; + } + case "CHANNEL_MESSAGE_PHOTOS": { + messageText = LocaleController.formatString("ChannelMessageFew", R.string.ChannelMessageFew, args[0], LocaleController.formatPluralString("Photos", Utilities.parseInt(args[1]))); + localMessage = true; + break; + } + case "CHANNEL_MESSAGE_VIDEOS": { + messageText = LocaleController.formatString("ChannelMessageFew", R.string.ChannelMessageFew, args[0], LocaleController.formatPluralString("Videos", Utilities.parseInt(args[1]))); + localMessage = true; + break; + } + case "CHANNEL_MESSAGES": { + messageText = LocaleController.formatString("ChannelMessageAlbum", R.string.ChannelMessageAlbum, args[0]); + localMessage = true; + break; + } + case "CHAT_MESSAGE_TEXT": { + messageText = LocaleController.formatString("NotificationMessageGroupText", R.string.NotificationMessageGroupText, args[0], args[1], args[2]); + message1 = args[2]; + break; + } + case "CHAT_MESSAGE_NOTEXT": { + messageText = LocaleController.formatString("NotificationMessageGroupNoText", R.string.NotificationMessageGroupNoText, args[0], args[1]); + message1 = LocaleController.getString("Message", R.string.Message); + break; + } + case "CHAT_MESSAGE_PHOTO": { + messageText = LocaleController.formatString("NotificationMessageGroupPhoto", R.string.NotificationMessageGroupPhoto, args[0], args[1]); + message1 = LocaleController.getString("AttachPhoto", R.string.AttachPhoto); + break; + } + case "CHAT_MESSAGE_VIDEO": { + messageText = LocaleController.formatString("NotificationMessageGroupVideo", R.string.NotificationMessageGroupVideo, args[0], args[1]); + message1 = LocaleController.getString("AttachVideo", R.string.AttachVideo); + break; + } + case "CHAT_MESSAGE_ROUND": { + messageText = LocaleController.formatString("NotificationMessageGroupRound", R.string.NotificationMessageGroupRound, args[0], args[1]); + message1 = LocaleController.getString("AttachRound", R.string.AttachRound); + break; + } + case "CHAT_MESSAGE_DOC": { + messageText = LocaleController.formatString("NotificationMessageGroupDocument", R.string.NotificationMessageGroupDocument, args[0], args[1]); + message1 = LocaleController.getString("AttachDocument", R.string.AttachDocument); + break; + } + case "CHAT_MESSAGE_STICKER": { + if (args.length > 2 && !TextUtils.isEmpty(args[2])) { + messageText = LocaleController.formatString("NotificationMessageGroupStickerEmoji", R.string.NotificationMessageGroupStickerEmoji, args[0], args[1], args[2]); + message1 = args[2] + " " + LocaleController.getString("AttachSticker", R.string.AttachSticker); + } else { + messageText = LocaleController.formatString("NotificationMessageGroupSticker", R.string.NotificationMessageGroupSticker, args[0], args[1]); + message1 = args[1] + " " + LocaleController.getString("AttachSticker", R.string.AttachSticker); + } + break; + } + case "CHAT_MESSAGE_AUDIO": { + messageText = LocaleController.formatString("NotificationMessageGroupAudio", R.string.NotificationMessageGroupAudio, args[0], args[1]); + message1 = LocaleController.getString("AttachAudio", R.string.AttachAudio); + break; + } + case "CHAT_MESSAGE_CONTACT": { + messageText = LocaleController.formatString("NotificationMessageGroupContact2", R.string.NotificationMessageGroupContact2, args[0], args[1], args[2]); + message1 = LocaleController.getString("AttachContact", R.string.AttachContact); + break; + } + case "CHAT_MESSAGE_QUIZ": { + messageText = LocaleController.formatString("NotificationMessageGroupQuiz2", R.string.NotificationMessageGroupQuiz2, args[0], args[1], args[2]); + message1 = LocaleController.getString("PollQuiz", R.string.PollQuiz); + break; + } + case "CHAT_MESSAGE_POLL": { + messageText = LocaleController.formatString("NotificationMessageGroupPoll2", R.string.NotificationMessageGroupPoll2, args[0], args[1], args[2]); + message1 = LocaleController.getString("Poll", R.string.Poll); + break; + } + case "CHAT_MESSAGE_GEO": { + messageText = LocaleController.formatString("NotificationMessageGroupMap", R.string.NotificationMessageGroupMap, args[0], args[1]); + message1 = LocaleController.getString("AttachLocation", R.string.AttachLocation); + break; + } + case "CHAT_MESSAGE_GEOLIVE": { + messageText = LocaleController.formatString("NotificationMessageGroupLiveLocation", R.string.NotificationMessageGroupLiveLocation, args[0], args[1]); + message1 = LocaleController.getString("AttachLiveLocation", R.string.AttachLiveLocation); + break; + } + case "CHAT_MESSAGE_GIF": { + messageText = LocaleController.formatString("NotificationMessageGroupGif", R.string.NotificationMessageGroupGif, args[0], args[1]); + message1 = LocaleController.getString("AttachGif", R.string.AttachGif); + break; + } + case "CHAT_MESSAGE_GAME": { + messageText = LocaleController.formatString("NotificationMessageGroupGame", R.string.NotificationMessageGroupGame, args[0], args[1], args[2]); + message1 = LocaleController.getString("AttachGame", R.string.AttachGame); + break; + } + case "CHAT_MESSAGE_GAME_SCORE": { + messageText = LocaleController.formatString("NotificationMessageGroupGameScored", R.string.NotificationMessageGroupGameScored, args[0], args[1], args[2], args[3]); + break; + } + case "CHAT_MESSAGE_INVOICE": { + messageText = LocaleController.formatString("NotificationMessageGroupInvoice", R.string.NotificationMessageGroupInvoice, args[0], args[1], args[2]); + message1 = LocaleController.getString("PaymentInvoice", R.string.PaymentInvoice); + break; + } + case "CHAT_CREATED": + case "CHAT_ADD_YOU": { + messageText = LocaleController.formatString("NotificationInvitedToGroup", R.string.NotificationInvitedToGroup, args[0], args[1]); + break; + } + case "CHAT_TITLE_EDITED": { + messageText = LocaleController.formatString("NotificationEditedGroupName", R.string.NotificationEditedGroupName, args[0], args[1]); + break; + } + case "CHAT_PHOTO_EDITED": { + messageText = LocaleController.formatString("NotificationEditedGroupPhoto", R.string.NotificationEditedGroupPhoto, args[0], args[1]); + break; + } + case "CHAT_ADD_MEMBER": { + messageText = LocaleController.formatString("NotificationGroupAddMember", R.string.NotificationGroupAddMember, args[0], args[1], args[2]); + break; + } + case "CHAT_DELETE_MEMBER": { + messageText = LocaleController.formatString("NotificationGroupKickMember", R.string.NotificationGroupKickMember, args[0], args[1]); + break; + } + case "CHAT_DELETE_YOU": { + messageText = LocaleController.formatString("NotificationGroupKickYou", R.string.NotificationGroupKickYou, args[0], args[1]); + break; + } + case "CHAT_LEFT": { + messageText = LocaleController.formatString("NotificationGroupLeftMember", R.string.NotificationGroupLeftMember, args[0], args[1]); + break; + } + case "CHAT_RETURNED": { + messageText = LocaleController.formatString("NotificationGroupAddSelf", R.string.NotificationGroupAddSelf, args[0], args[1]); + break; + } + case "CHAT_JOINED": { + messageText = LocaleController.formatString("NotificationGroupAddSelfMega", R.string.NotificationGroupAddSelfMega, args[0], args[1]); + break; + } + case "CHAT_MESSAGE_FWDS": { + messageText = LocaleController.formatString("NotificationGroupForwardedFew", R.string.NotificationGroupForwardedFew, args[0], args[1], LocaleController.formatPluralString("messages", Utilities.parseInt(args[2]))); + localMessage = true; + break; + } + case "CHAT_MESSAGE_PHOTOS": { + messageText = LocaleController.formatString("NotificationGroupFew", R.string.NotificationGroupFew, args[0], args[1], LocaleController.formatPluralString("Photos", Utilities.parseInt(args[2]))); + localMessage = true; + break; + } + case "CHAT_MESSAGE_VIDEOS": { + messageText = LocaleController.formatString("NotificationGroupFew", R.string.NotificationGroupFew, args[0], args[1], LocaleController.formatPluralString("Videos", Utilities.parseInt(args[2]))); + localMessage = true; + break; + } + case "CHAT_MESSAGES": { + messageText = LocaleController.formatString("NotificationGroupAlbum", R.string.NotificationGroupAlbum, args[0], args[1]); + localMessage = true; + break; + } + case "PINNED_TEXT": { + if (chat_from_id != 0) { + messageText = LocaleController.formatString("NotificationActionPinnedText", R.string.NotificationActionPinnedText, args[0], args[1], args[2]); + } else { + messageText = LocaleController.formatString("NotificationActionPinnedTextChannel", R.string.NotificationActionPinnedTextChannel, args[0], args[1]); + } + break; + } + case "PINNED_NOTEXT": { + if (chat_from_id != 0) { + messageText = LocaleController.formatString("NotificationActionPinnedNoText", R.string.NotificationActionPinnedNoText, args[0], args[1]); + } else { + messageText = LocaleController.formatString("NotificationActionPinnedNoTextChannel", R.string.NotificationActionPinnedNoTextChannel, args[0]); + } + break; + } + case "PINNED_PHOTO": { + if (chat_from_id != 0) { + messageText = LocaleController.formatString("NotificationActionPinnedPhoto", R.string.NotificationActionPinnedPhoto, args[0], args[1]); + } else { + messageText = LocaleController.formatString("NotificationActionPinnedPhotoChannel", R.string.NotificationActionPinnedPhotoChannel, args[0]); + } + break; + } + case "PINNED_VIDEO": { + if (chat_from_id != 0) { + messageText = LocaleController.formatString("NotificationActionPinnedVideo", R.string.NotificationActionPinnedVideo, args[0], args[1]); + } else { + messageText = LocaleController.formatString("NotificationActionPinnedVideoChannel", R.string.NotificationActionPinnedVideoChannel, args[0]); + } + break; + } + case "PINNED_ROUND": { + if (chat_from_id != 0) { + messageText = LocaleController.formatString("NotificationActionPinnedRound", R.string.NotificationActionPinnedRound, args[0], args[1]); + } else { + messageText = LocaleController.formatString("NotificationActionPinnedRoundChannel", R.string.NotificationActionPinnedRoundChannel, args[0]); + } + break; + } + case "PINNED_DOC": { + if (chat_from_id != 0) { + messageText = LocaleController.formatString("NotificationActionPinnedFile", R.string.NotificationActionPinnedFile, args[0], args[1]); + } else { + messageText = LocaleController.formatString("NotificationActionPinnedFileChannel", R.string.NotificationActionPinnedFileChannel, args[0]); + } + break; + } + case "PINNED_STICKER": { + if (chat_from_id != 0) { + if (args.length > 2 && !TextUtils.isEmpty(args[2])) { + messageText = LocaleController.formatString("NotificationActionPinnedStickerEmoji", R.string.NotificationActionPinnedStickerEmoji, args[0], args[2], args[1]); + } else { + messageText = LocaleController.formatString("NotificationActionPinnedSticker", R.string.NotificationActionPinnedSticker, args[0], args[1]); + } + } else { + if (args.length > 1 && !TextUtils.isEmpty(args[1])) { + messageText = LocaleController.formatString("NotificationActionPinnedStickerEmojiChannel", R.string.NotificationActionPinnedStickerEmojiChannel, args[0], args[1]); + } else { + messageText = LocaleController.formatString("NotificationActionPinnedStickerChannel", R.string.NotificationActionPinnedStickerChannel, args[0]); + } + } + break; + } + case "PINNED_AUDIO": { + if (chat_from_id != 0) { + messageText = LocaleController.formatString("NotificationActionPinnedVoice", R.string.NotificationActionPinnedVoice, args[0], args[1]); + } else { + messageText = LocaleController.formatString("NotificationActionPinnedVoiceChannel", R.string.NotificationActionPinnedVoiceChannel, args[0]); + } + break; + } + case "PINNED_CONTACT": { + if (chat_from_id != 0) { + messageText = LocaleController.formatString("NotificationActionPinnedContact2", R.string.NotificationActionPinnedContact2, args[0], args[2], args[1]); + } else { + messageText = LocaleController.formatString("NotificationActionPinnedContactChannel2", R.string.NotificationActionPinnedContactChannel2, args[0], args[1]); + } + break; + } + case "PINNED_QUIZ": { + if (chat_from_id != 0) { + messageText = LocaleController.formatString("NotificationActionPinnedQuiz2", R.string.NotificationActionPinnedQuiz2, args[0], args[2], args[1]); + } else { + messageText = LocaleController.formatString("NotificationActionPinnedQuizChannel2", R.string.NotificationActionPinnedQuizChannel2, args[0], args[1]); + } + break; + } + case "PINNED_POLL": { + if (chat_from_id != 0) { + messageText = LocaleController.formatString("NotificationActionPinnedPoll2", R.string.NotificationActionPinnedPoll2, args[0], args[2], args[1]); + } else { + messageText = LocaleController.formatString("NotificationActionPinnedPollChannel2", R.string.NotificationActionPinnedPollChannel2, args[0], args[1]); + } + break; + } + case "PINNED_GEO": { + if (chat_from_id != 0) { + messageText = LocaleController.formatString("NotificationActionPinnedGeo", R.string.NotificationActionPinnedGeo, args[0], args[1]); + } else { + messageText = LocaleController.formatString("NotificationActionPinnedGeoChannel", R.string.NotificationActionPinnedGeoChannel, args[0]); + } + break; + } + case "PINNED_GEOLIVE": { + if (chat_from_id != 0) { + messageText = LocaleController.formatString("NotificationActionPinnedGeoLive", R.string.NotificationActionPinnedGeoLive, args[0], args[1]); + } else { + messageText = LocaleController.formatString("NotificationActionPinnedGeoLiveChannel", R.string.NotificationActionPinnedGeoLiveChannel, args[0]); + } + break; + } + case "PINNED_GAME": { + if (chat_from_id != 0) { + messageText = LocaleController.formatString("NotificationActionPinnedGame", R.string.NotificationActionPinnedGame, args[0], args[1]); + } else { + messageText = LocaleController.formatString("NotificationActionPinnedGameChannel", R.string.NotificationActionPinnedGameChannel, args[0]); + } + break; + } + case "PINNED_GAME_SCORE": { + if (chat_from_id != 0) { + messageText = LocaleController.formatString("NotificationActionPinnedGameScore", R.string.NotificationActionPinnedGameScore, args[0], args[1]); + } else { + messageText = LocaleController.formatString("NotificationActionPinnedGameScoreChannel", R.string.NotificationActionPinnedGameScoreChannel, args[0]); + } + break; + } + case "PINNED_INVOICE": { + if (chat_from_id != 0) { + messageText = LocaleController.formatString("NotificationActionPinnedInvoice", R.string.NotificationActionPinnedInvoice, args[0], args[1]); + } else { + messageText = LocaleController.formatString("NotificationActionPinnedInvoiceChannel", R.string.NotificationActionPinnedInvoiceChannel, args[0]); + } + break; + } + case "PINNED_GIF": { + if (chat_from_id != 0) { + messageText = LocaleController.formatString("NotificationActionPinnedGif", R.string.NotificationActionPinnedGif, args[0], args[1]); + } else { + messageText = LocaleController.formatString("NotificationActionPinnedGifChannel", R.string.NotificationActionPinnedGifChannel, args[0]); + } + break; + } + case "ENCRYPTED_MESSAGE": { + messageText = LocaleController.getString("YouHaveNewMessage", R.string.YouHaveNewMessage); + name = LocaleController.getString("SecretChatName", R.string.SecretChatName); + localMessage = true; + break; + } + case "CONTACT_JOINED": + case "AUTH_UNKNOWN": + case "AUTH_REGION": + case "LOCKED_MESSAGE": + case "ENCRYPTION_REQUEST": + case "ENCRYPTION_ACCEPT": + case "PHONE_CALL_REQUEST": + case "MESSAGE_MUTED": + case "PHONE_CALL_MISSED": { + //ignored + break; + } + default: { + if (BuildVars.LOGS_ENABLED) { + FileLog.w("unhandled loc_key = " + loc_key); + } + break; + } + } + if (messageText != null) { + TLRPC.TL_message messageOwner = new TLRPC.TL_message(); + messageOwner.id = msg_id; + messageOwner.random_id = random_id; + messageOwner.message = message1 != null ? message1 : messageText; + messageOwner.date = (int) (time / 1000); + if (pinned) { + messageOwner.action = new TLRPC.TL_messageActionPinMessage(); + } + if (supergroup) { + messageOwner.flags |= TLRPC.MESSAGE_FLAG_MEGAGROUP; + } + messageOwner.dialog_id = dialog_id; + if (channel_id != 0) { + messageOwner.to_id = new TLRPC.TL_peerChannel(); + messageOwner.to_id.channel_id = channel_id; + } else if (chat_id != 0) { + messageOwner.to_id = new TLRPC.TL_peerChat(); + messageOwner.to_id.chat_id = chat_id; + } else { + messageOwner.to_id = new TLRPC.TL_peerUser(); + messageOwner.to_id.user_id = user_id; + } + messageOwner.flags |= 256; + messageOwner.from_id = chat_from_id; + messageOwner.mentioned = mention || pinned; + messageOwner.silent = silent; + messageOwner.from_scheduled = scheduled; + + MessageObject messageObject = new MessageObject(currentAccount, messageOwner, messageText, name, userName, localMessage, channel, edited); + ArrayList arrayList = new ArrayList<>(); + arrayList.add(messageObject); + canRelease = false; + NotificationsController.getInstance(currentAccount).processNewMessages(arrayList, true, true, countDownLatch); + } + } + } + } + if (canRelease) { + countDownLatch.countDown(); + } + + ConnectionsManager.onInternalPushReceived(currentAccount); + ConnectionsManager.getInstance(currentAccount).resumeNetworkMaybe(); + } catch (Throwable e) { + if (currentAccount != -1) { + ConnectionsManager.onInternalPushReceived(currentAccount); + ConnectionsManager.getInstance(currentAccount).resumeNetworkMaybe(); + countDownLatch.countDown(); + } else { + onDecryptError(); + } + if (BuildVars.LOGS_ENABLED) { + FileLog.e("error in loc_key = " + loc_key + " json " + jsonString); + } + FileLog.e(e); + } + }); + }); + try { + countDownLatch.await(); + } catch (Throwable ignore) { + + } + if (BuildVars.DEBUG_VERSION) { + FileLog.d("finished GCM service, time = " + (SystemClock.elapsedRealtime() - receiveTime)); + } + } + + private void onDecryptError() { + for (int a = 0; a < UserConfig.MAX_ACCOUNT_COUNT; a++) { + if (UserConfig.getInstance(a).isClientActivated()) { + ConnectionsManager.onInternalPushReceived(a); + ConnectionsManager.getInstance(a).resumeNetworkMaybe(); + } + } + countDownLatch.countDown(); + } + + @Override + public void onNewToken(String token) { + AndroidUtilities.runOnUIThread(() -> { + if (BuildVars.LOGS_ENABLED) { + FileLog.d("Refreshed token: " + token); + } + ApplicationLoader.postInitApplication(); + sendRegistrationToServer(token); + }); + } + + public static void sendRegistrationToServer(final String token) { + Utilities.stageQueue.postRunnable(() -> { + ConnectionsManager.setRegId(token, SharedConfig.pushStringStatus); + if (token == null) { + return; + } + SharedConfig.pushString = token; + for (int a = 0; a < UserConfig.MAX_ACCOUNT_COUNT; a++) { + UserConfig userConfig = UserConfig.getInstance(a); + userConfig.registeredForPush = false; + userConfig.saveConfig(false); + if (userConfig.getClientUserId() != 0) { + final int currentAccount = a; + AndroidUtilities.runOnUIThread(() -> MessagesController.getInstance(currentAccount).registerForPush(token)); + } + } + }); + } +} diff --git a/TMessagesProj/src/gservcies/java/tw/nekomimi/nekogram/GcmImpl.java b/TMessagesProj/src/gservcies/java/tw/nekomimi/nekogram/GcmImpl.java new file mode 100644 index 000000000..f891e0248 --- /dev/null +++ b/TMessagesProj/src/gservcies/java/tw/nekomimi/nekogram/GcmImpl.java @@ -0,0 +1,77 @@ +package tw.nekomimi.nekogram; + +import android.text.TextUtils; + +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.GooglePlayServicesUtil; +import com.google.firebase.iid.FirebaseInstanceId; + +import org.telegram.messenger.GcmPushListenerService; +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.BuildVars; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.SharedConfig; +import org.telegram.messenger.Utilities; + +public class GcmImpl implements ExternalGcm.Interface { + + public static boolean hasPlayServices; + + @Override + public void initPlayServices() { + AndroidUtilities.runOnUIThread(() -> { + if (hasPlayServices = checkPlayServices()) { + final String currentPushString = SharedConfig.pushString; + if (!TextUtils.isEmpty(currentPushString)) { + if (BuildVars.DEBUG_PRIVATE_VERSION && BuildVars.LOGS_ENABLED) { + FileLog.d("GCM regId = " + currentPushString); + } + } else { + if (BuildVars.LOGS_ENABLED) { + FileLog.d("GCM Registration not found."); + } + } + Utilities.globalQueue.postRunnable(() -> { + try { + FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(instanceIdResult -> { + String token = instanceIdResult.getToken(); + if (!TextUtils.isEmpty(token)) { + GcmPushListenerService.sendRegistrationToServer(token); + } + }).addOnFailureListener(e -> { + if (BuildVars.LOGS_ENABLED) { + FileLog.d("Failed to get regid"); + } + SharedConfig.pushStringStatus = "__FIREBASE_FAILED__"; + GcmPushListenerService.sendRegistrationToServer(null); + }); + } catch (Throwable e) { + FileLog.e(e); + } + }); + } else { + if (BuildVars.LOGS_ENABLED) { + FileLog.d("No valid Google Play Services APK found."); + } + SharedConfig.pushStringStatus = "__NO_GOOGLE_PLAY_SERVICES__"; + GcmPushListenerService.sendRegistrationToServer(null); + } + }, 1000); + } + + private boolean checkPlayServices() { + try { + int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(ApplicationLoader.applicationContext); + return resultCode == ConnectionResult.SUCCESS; + } catch (Exception e) { + FileLog.e(e); + } + return true; + } + + @Override + public void sendRegistrationToServer() { + GcmPushListenerService.sendRegistrationToServer(SharedConfig.pushString); + } +} diff --git a/TMessagesProj/src/main/AndroidManifest.xml b/TMessagesProj/src/main/AndroidManifest.xml index dfea68646..d4a97455b 100644 --- a/TMessagesProj/src/main/AndroidManifest.xml +++ b/TMessagesProj/src/main/AndroidManifest.xml @@ -11,6 +11,16 @@ android:smallScreens="true" android:xlargeScreens="true"/> + + + + + + + + + + @@ -47,7 +57,6 @@ - @@ -65,21 +74,21 @@ - - + + - + android:supportsRtl="true"> + + + + @@ -196,6 +209,15 @@ android:resizeableActivity="false" android:windowSoftInputMode="adjustResize|stateHidden"> + + + + + + + + + + + + @@ -367,10 +397,6 @@ - - diff --git a/TMessagesProj/src/main/assets/countries.txt b/TMessagesProj/src/main/assets/countries.txt index 2f252dc53..9751ed5a1 100644 --- a/TMessagesProj/src/main/assets/countries.txt +++ b/TMessagesProj/src/main/assets/countries.txt @@ -1,237 +1,237 @@ -1876;JM;Jamaica;XXX XXXX -1869;KN;Saint Kitts & Nevis;XXX XXXX -1868;TT;Trinidad & Tobago;XXX XXXX -1784;VC;Saint Vincent & the Grenadines;XXX XXXX -1767;DM;Dominica;XXX XXXX -1758;LC;Saint Lucia;XXX XXXX -1721;SX;Sint Maarten;XXX XXXX -1684;AS;American Samoa;XXX XXXX -1671;GU;Guam;XXX XXXX -1670;MP;Northern Mariana Islands;XXX XXXX -1664;MS;Montserrat;XXX XXXX -1649;TC;Turks & Caicos Islands;XXX XXXX -1473;GD;Grenada;XXX XXXX -1441;BM;Bermuda;XXX XXXX -1345;KY;Cayman Islands;XXX XXXX -1340;VI;US Virgin Islands;XXX XXXX -1284;VG;British Virgin Islands;XXX XXXX -1268;AG;Antigua & Barbuda;XXX XXXX -1264;AI;Anguilla;XXX XXXX -1246;BB;Barbados;XXX XXXX -1242;BS;Bahamas;XXX XXXX -998;UZ;Uzbekistan;XX XXXXXXX -996;KG;Kyrgyzstan;XXX XXXXXX -995;GE;Georgia;XXX XXX XXX -994;AZ;Azerbaijan;XX XXX XXXX -993;TM;Turkmenistan;XX XXXXXX -992;TJ;Tajikistan;XX XXX XXXX -977;NP;Nepal;XX XXXX XXXX -976;MN;Mongolia;XX XX XXXX -975;BT;Bhutan;XX XXX XXX -974;QA;Qatar;XX XXX XXX -973;BH;Bahrain;XXXX XXXX -972;IL;Israel;XX XXX XXXX -971;AE;United Arab Emirates;XX XXX XXXX -970;PS;Palestine;XXX XX XXXX -968;OM;Oman;XXXX XXXX -967;YE;Yemen;XXX XXX XXX -966;SA;Saudi Arabia;XX XXX XXXX -965;KW;Kuwait;XXXX XXXX -964;IQ;Iraq;XXX XXX XXXX -963;SY;Syria;XXX XXX XXX -962;JO;Jordan;X XXXX XXXX -961;LB;Lebanon -960;MV;Maldives;XXX XXXX -886;TW;Taiwan;XXX XXX XXX -883;GO;International Networks -882;GO;International Networks -881;GO;Global Mobile Satellite -880;BD;Bangladesh -856;LA;Laos;XX XX XXX XXX -855;KH;Cambodia -853;MO;Macau;XXXX XXXX -852;HK;Hong Kong;X XXX XXXX -850;KP;North Korea -692;MH;Marshall Islands -691;FM;Micronesia -690;TK;Tokelau -689;PF;French Polynesia -688;TV;Tuvalu -687;NC;New Caledonia -686;KI;Kiribati -685;WS;Samoa -683;NU;Niue -682;CK;Cook Islands -681;WF;Wallis & Futuna -680;PW;Palau -679;FJ;Fiji -678;VU;Vanuatu -677;SB;Solomon Islands -676;TO;Tonga -675;PG;Papua New Guinea -674;NR;Nauru -673;BN;Brunei Darussalam;XXX XXXX -672;NF;Norfolk Island -670;TL;Timor-Leste -599;BQ;Bonaire, Sint Eustatius & Saba -599;CW;Curaçao -598;UY;Uruguay;X XXX XXXX -597;SR;Suriname;XXX XXXX -596;MQ;Martinique -595;PY;Paraguay;XXX XXX XXX -594;GF;French Guiana -593;EC;Ecuador;XX XXX XXXX -592;GY;Guyana -591;BO;Bolivia;X XXX XXXX -590;GP;Guadeloupe;XXX XX XX XX -509;HT;Haiti -508;PM;Saint Pierre & Miquelon -507;PA;Panama;XXXX XXXX -506;CR;Costa Rica;XXXX XXXX -505;NI;Nicaragua;XXXX XXXX -504;HN;Honduras;XXXX XXXX -503;SV;El Salvador;XXXX XXXX -502;GT;Guatemala;X XXX XXXX -501;BZ;Belize -500;FK;Falkland Islands -423;LI;Liechtenstein -421;SK;Slovakia;XXX XXX XXX -420;CZ;Czech Republic;XXX XXX XXX -389;MK;Macedonia;XX XXX XXX -387;BA;Bosnia & Herzegovina;XX XXX XXX -386;SI;Slovenia;XX XXX XXX -385;HR;Croatia -383;XK;Kosovo;XXXX XXXX -382;ME;Montenegro -381;RS;Serbia;XX XXX XXXX -380;UA;Ukraine;XX XXX XX XX -378;SM;San Marino;XXX XXX XXXX -377;MC;Monaco;XXXX XXXX -376;AD;Andorra;XX XX XX -375;BY;Belarus;XX XXX XXXX -374;AM;Armenia;XX XXX XXX -373;MD;Moldova;XX XXX XXX -372;EE;Estonia -371;LV;Latvia;XXX XXXXX -370;LT;Lithuania;XXX XXXXX -359;BG;Bulgaria -358;FI;Finland -357;CY;Cyprus;XXXX XXXX -356;MT;Malta;XX XX XX XX -355;AL;Albania;XX XXX XXXX -354;IS;Iceland;XXX XXXX -353;IE;Ireland;XX XXX XXXX -352;LU;Luxembourg -351;PT;Portugal;X XXXX XXXX -350;GI;Gibraltar;XXXX XXXX -299;GL;Greenland;XXX XXX -298;FO;Faroe Islands;XXX XXX -297;AW;Aruba;XXX XXXX -291;ER;Eritrea;X XXX XXX -290;SH;Saint Helena;XX XXX -269;KM;Comoros;XXX XXXX -268;SZ;Swaziland;XXXX XXXX -267;BW;Botswana;XX XXX XXX -266;LS;Lesotho;XX XXX XXX -265;MW;Malawi;77 XXX XXXX -264;NA;Namibia;XX XXX XXXX -263;ZW;Zimbabwe;XX XXX XXXX -262;RE;Réunion;XXX XXX XXX -261;MG;Madagascar;XX XX XXX XX -260;ZM;Zambia;XX XXX XXXX -258;MZ;Mozambique;XX XXX XXXX -257;BI;Burundi;XX XX XXXX -256;UG;Uganda;XX XXX XXXX -255;TZ;Tanzania;XX XXX XXXX -254;KE;Kenya;XXX XXX XXX -253;DJ;Djibouti;XX XX XX XX -252;SO;Somalia;XX XXX XXX -251;ET;Ethiopia;XX XXX XXXX -250;RW;Rwanda;XXX XXX XXX -249;SD;Sudan;XX XXX XXXX -248;SC;Seychelles;X XX XX XX -247;SH;Saint Helena;XXXX -246;IO;Diego Garcia;XXX XXXX -245;GW;Guinea-Bissau;XXX XXXX -244;AO;Angola;XXX XXX XXX -243;CD;Congo (Dem. Rep.);XX XXX XXXX -242;CG;Congo (Rep.);XX XXX XXXX -241;GA;Gabon;X XX XX XX -240;GQ;Equatorial Guinea;XXX XXX XXX -239;ST;São Tomé & Príncipe;XX XXXXX -238;CV;Cape Verde;XXX XXXX -237;CM;Cameroon;XXXX XXXX -236;CF;Central African Rep.;XX XX XX XX -235;TD;Chad;XX XX XX XX -234;NG;Nigeria -233;GH;Ghana -232;SL;Sierra Leone;XX XXX XXX -231;LR;Liberia -230;MU;Mauritius -229;BJ;Benin;XX XXX XXX -228;TG;Togo;XX XXX XXX -227;NE;Niger;XX XX XX XX -226;BF;Burkina Faso;XX XX XX XX -225;CI;Côte d`Ivoire;XX XXX XXX -224;GN;Guinea;XXX XXX XXX -223;ML;Mali;XXXX XXXX -222;MR;Mauritania;XXXX XXXX -221;SN;Senegal;XX XXX XXXX -220;GM;Gambia;XXX XXXX -218;LY;Libya;XX XXX XXXX -216;TN;Tunisia;XX XXX XXX -213;DZ;Algeria;XXX XX XX XX -212;MA;Morocco;XX XXX XXXX -211;SS;South Sudan;XX XXX XXXX -98;IR;Iran;XXX XXX XXXX -95;MM;Myanmar -94;LK;Sri Lanka;XX XXX XXXX -93;AF;Afghanistan;XXX XXX XXX -92;PK;Pakistan;XXX XXX XXXX -91;IN;India;XXXXX XXXXX -90;TR;Turkey;XXX XXX XXXX -86;CN;China;XXX XXXX XXXX -84;VN;Vietnam -82;KR;South Korea -81;JP;Japan;XX XXXX XXXX -66;TH;Thailand;X XXXX XXXX -65;SG;Singapore;XXXX XXXX -64;NZ;New Zealand -63;PH;Philippines;XXX XXX XXXX -62;ID;Indonesia -61;AU;Australia;XXX XXX XXX -60;MY;Malaysia -58;VE;Venezuela;XXX XXX XXXX -57;CO;Colombia;XXX XXX XXXX -56;CL;Chile;X XXXX XXXX -55;BR;Brazil;XX XXXXX XXXX -54;AR;Argentina -53;CU;Cuba;XXXX XXXX -52;MX;Mexico -51;PE;Peru;XXX XXX XXX -49;DE;Germany -48;PL;Poland;XXX XXX XXX -47;NO;Norway;XXXX XXXX -46;SE;Sweden;XX XXX XXXX -45;DK;Denmark;XXXX XXXX -44;GB;United Kingdom;XXXX XXXXXX -43;AT;Austria -42;YL;Y-land -41;CH;Switzerland;XX XXX XXXX -40;RO;Romania;XXX XXX XXX -39;IT;Italy -36;HU;Hungary;XXX XXX XXX -34;ES;Spain;XXX XXX XXX -33;FR;France;X XX XX XX XX -32;BE;Belgium;XXX XX XX XX -31;NL;Netherlands;X XX XX XX XX -30;GR;Greece;XXX XXX XXXX -27;ZA;South Africa;XX XXX XXXX -20;EG;Egypt;XX XXXX XXXX -7;KZ;Kazakhstan;XXX XXX XX XX -7;RU;Russian Federation;XXX XXX XXXX -1;PR;Puerto Rico;XXX XXX XXXX -1;DO;Dominican Rep.;XXX XXX XXXX -1;CA;Canada;XXX XXX XXXX -1;US;USA;XXX XXX XXXX +1876;JM;Jamaica;XXX XXXX +1869;KN;Saint Kitts & Nevis;XXX XXXX +1868;TT;Trinidad & Tobago;XXX XXXX +1784;VC;Saint Vincent & the Grenadines;XXX XXXX +1767;DM;Dominica;XXX XXXX +1758;LC;Saint Lucia;XXX XXXX +1721;SX;Sint Maarten;XXX XXXX +1684;AS;American Samoa;XXX XXXX +1671;GU;Guam;XXX XXXX +1670;MP;Northern Mariana Islands;XXX XXXX +1664;MS;Montserrat;XXX XXXX +1649;TC;Turks & Caicos Islands;XXX XXXX +1473;GD;Grenada;XXX XXXX +1441;BM;Bermuda;XXX XXXX +1345;KY;Cayman Islands;XXX XXXX +1340;VI;US Virgin Islands;XXX XXXX +1284;VG;British Virgin Islands;XXX XXXX +1268;AG;Antigua & Barbuda;XXX XXXX +1264;AI;Anguilla;XXX XXXX +1246;BB;Barbados;XXX XXXX +1242;BS;Bahamas;XXX XXXX +998;UZ;Uzbekistan;XX XXXXXXX +996;KG;Kyrgyzstan;XXX XXXXXX +995;GE;Georgia;XXX XXX XXX +994;AZ;Azerbaijan;XX XXX XXXX +993;TM;Turkmenistan;XX XXXXXX +992;TJ;Tajikistan;XX XXX XXXX +977;NP;Nepal;XX XXXX XXXX +976;MN;Mongolia;XX XX XXXX +975;BT;Bhutan;XX XXX XXX +974;QA;Qatar;XX XXX XXX +973;BH;Bahrain;XXXX XXXX +972;IL;Israel;XX XXX XXXX +971;AE;United Arab Emirates;XX XXX XXXX +970;PS;Palestine;XXX XX XXXX +968;OM;Oman;XXXX XXXX +967;YE;Yemen;XXX XXX XXX +966;SA;Saudi Arabia;XX XXX XXXX +965;KW;Kuwait;XXXX XXXX +964;IQ;Iraq;XXX XXX XXXX +963;SY;Syria;XXX XXX XXX +962;JO;Jordan;X XXXX XXXX +961;LB;Lebanon +960;MV;Maldives;XXX XXXX +886;TW;Taiwan;XXX XXX XXX +883;GO;International Networks +882;GO;International Networks +881;GO;Global Mobile Satellite +880;BD;Bangladesh +856;LA;Laos;XX XX XXX XXX +855;KH;Cambodia +853;MO;Macau;XXXX XXXX +852;HK;Hong Kong;X XXX XXXX +850;KP;North Korea +692;MH;Marshall Islands +691;FM;Micronesia +690;TK;Tokelau +689;PF;French Polynesia +688;TV;Tuvalu +687;NC;New Caledonia +686;KI;Kiribati +685;WS;Samoa +683;NU;Niue +682;CK;Cook Islands +681;WF;Wallis & Futuna +680;PW;Palau +679;FJ;Fiji +678;VU;Vanuatu +677;SB;Solomon Islands +676;TO;Tonga +675;PG;Papua New Guinea +674;NR;Nauru +673;BN;Brunei Darussalam;XXX XXXX +672;NF;Norfolk Island +670;TL;Timor-Leste +599;BQ;Bonaire, Sint Eustatius & Saba +599;CW;Curaçao +598;UY;Uruguay;X XXX XXXX +597;SR;Suriname;XXX XXXX +596;MQ;Martinique +595;PY;Paraguay;XXX XXX XXX +594;GF;French Guiana +593;EC;Ecuador;XX XXX XXXX +592;GY;Guyana +591;BO;Bolivia;X XXX XXXX +590;GP;Guadeloupe;XXX XX XX XX +509;HT;Haiti +508;PM;Saint Pierre & Miquelon +507;PA;Panama;XXXX XXXX +506;CR;Costa Rica;XXXX XXXX +505;NI;Nicaragua;XXXX XXXX +504;HN;Honduras;XXXX XXXX +503;SV;El Salvador;XXXX XXXX +502;GT;Guatemala;X XXX XXXX +501;BZ;Belize +500;FK;Falkland Islands +423;LI;Liechtenstein +421;SK;Slovakia;XXX XXX XXX +420;CZ;Czech Republic;XXX XXX XXX +389;MK;Macedonia;XX XXX XXX +387;BA;Bosnia & Herzegovina;XX XXX XXX +386;SI;Slovenia;XX XXX XXX +385;HR;Croatia +383;XK;Kosovo;XXXX XXXX +382;ME;Montenegro +381;RS;Serbia;XX XXX XXXX +380;UA;Ukraine;XX XXX XX XX +378;SM;San Marino;XXX XXX XXXX +377;MC;Monaco;XXXX XXXX +376;AD;Andorra;XX XX XX +375;BY;Belarus;XX XXX XXXX +374;AM;Armenia;XX XXX XXX +373;MD;Moldova;XX XXX XXX +372;EE;Estonia +371;LV;Latvia;XXX XXXXX +370;LT;Lithuania;XXX XXXXX +359;BG;Bulgaria +358;FI;Finland +357;CY;Cyprus;XXXX XXXX +356;MT;Malta;XX XX XX XX +355;AL;Albania;XX XXX XXXX +354;IS;Iceland;XXX XXXX +353;IE;Ireland;XX XXX XXXX +352;LU;Luxembourg +351;PT;Portugal;X XXXX XXXX +350;GI;Gibraltar;XXXX XXXX +299;GL;Greenland;XXX XXX +298;FO;Faroe Islands;XXX XXX +297;AW;Aruba;XXX XXXX +291;ER;Eritrea;X XXX XXX +290;SH;Saint Helena;XX XXX +269;KM;Comoros;XXX XXXX +268;SZ;Swaziland;XXXX XXXX +267;BW;Botswana;XX XXX XXX +266;LS;Lesotho;XX XXX XXX +265;MW;Malawi;77 XXX XXXX +264;NA;Namibia;XX XXX XXXX +263;ZW;Zimbabwe;XX XXX XXXX +262;RE;Réunion;XXX XXX XXX +261;MG;Madagascar;XX XX XXX XX +260;ZM;Zambia;XX XXX XXXX +258;MZ;Mozambique;XX XXX XXXX +257;BI;Burundi;XX XX XXXX +256;UG;Uganda;XX XXX XXXX +255;TZ;Tanzania;XX XXX XXXX +254;KE;Kenya;XXX XXX XXX +253;DJ;Djibouti;XX XX XX XX +252;SO;Somalia;XX XXX XXX +251;ET;Ethiopia;XX XXX XXXX +250;RW;Rwanda;XXX XXX XXX +249;SD;Sudan;XX XXX XXXX +248;SC;Seychelles;X XX XX XX +247;SH;Saint Helena;XXXX +246;IO;Diego Garcia;XXX XXXX +245;GW;Guinea-Bissau;XXX XXXX +244;AO;Angola;XXX XXX XXX +243;CD;Congo (Dem. Rep.);XX XXX XXXX +242;CG;Congo (Rep.);XX XXX XXXX +241;GA;Gabon;X XX XX XX +240;GQ;Equatorial Guinea;XXX XXX XXX +239;ST;São Tomé & Príncipe;XX XXXXX +238;CV;Cape Verde;XXX XXXX +237;CM;Cameroon;XXXX XXXX +236;CF;Central African Rep.;XX XX XX XX +235;TD;Chad;XX XX XX XX +234;NG;Nigeria +233;GH;Ghana +232;SL;Sierra Leone;XX XXX XXX +231;LR;Liberia +230;MU;Mauritius +229;BJ;Benin;XX XXX XXX +228;TG;Togo;XX XXX XXX +227;NE;Niger;XX XX XX XX +226;BF;Burkina Faso;XX XX XX XX +225;CI;Côte d`Ivoire;XX XXX XXX +224;GN;Guinea;XXX XXX XXX +223;ML;Mali;XXXX XXXX +222;MR;Mauritania;XXXX XXXX +221;SN;Senegal;XX XXX XXXX +220;GM;Gambia;XXX XXXX +218;LY;Libya;XX XXX XXXX +216;TN;Tunisia;XX XXX XXX +213;DZ;Algeria;XXX XX XX XX +212;MA;Morocco;XX XXX XXXX +211;SS;South Sudan;XX XXX XXXX +98;IR;Iran;XXX XXX XXXX +95;MM;Myanmar +94;LK;Sri Lanka;XX XXX XXXX +93;AF;Afghanistan;XXX XXX XXX +92;PK;Pakistan;XXX XXX XXXX +91;IN;India;XXXXX XXXXX +90;TR;Turkey;XXX XXX XXXX +86;CN;China;XXX XXXX XXXX +84;VN;Vietnam +82;KR;South Korea +81;JP;Japan;XX XXXX XXXX +66;TH;Thailand;X XXXX XXXX +65;SG;Singapore;XXXX XXXX +64;NZ;New Zealand +63;PH;Philippines;XXX XXX XXXX +62;ID;Indonesia +61;AU;Australia;XXX XXX XXX +60;MY;Malaysia +58;VE;Venezuela;XXX XXX XXXX +57;CO;Colombia;XXX XXX XXXX +56;CL;Chile;X XXXX XXXX +55;BR;Brazil;XX XXXXX XXXX +54;AR;Argentina +53;CU;Cuba;XXXX XXXX +52;MX;Mexico +51;PE;Peru;XXX XXX XXX +49;DE;Germany +48;PL;Poland;XXX XXX XXX +47;NO;Norway;XXXX XXXX +46;SE;Sweden;XX XXX XXXX +45;DK;Denmark;XXXX XXXX +44;GB;United Kingdom;XXXX XXXXXX +43;AT;Austria +42;YL;Y-land +41;CH;Switzerland;XX XXX XXXX +40;RO;Romania;XXX XXX XXX +39;IT;Italy +36;HU;Hungary;XXX XXX XXX +34;ES;Spain;XXX XXX XXX +33;FR;France;X XX XX XX XX +32;BE;Belgium;XXX XX XX XX +31;NL;Netherlands;X XX XX XX XX +30;GR;Greece;XXX XXX XXXX +27;ZA;South Africa;XX XXX XXXX +20;EG;Egypt;XX XXXX XXXX +7;KZ;Kazakhstan;XXX XXX XX XX +7;RU;Russian Federation;XXX XXX XXXX +1;PR;Puerto Rico;XXX XXX XXXX +1;DO;Dominican Rep.;XXX XXX XXXX +1;CA;Canada;XXX XXX XXXX +1;US;USA;XXX XXX XXXX 999;TG;Test Number;XX X XXXX \ No newline at end of file diff --git a/TMessagesProj/src/main/assets/indigo.attheme b/TMessagesProj/src/main/assets/indigo.attheme new file mode 100644 index 000000000..36b268af5 --- /dev/null +++ b/TMessagesProj/src/main/assets/indigo.attheme @@ -0,0 +1,441 @@ +windowBackgroundGray=#eeeeee +chats_tabletSelectedOverlay=#eeeeee +login_progressInner=#eeeeee +contextProgressInner1=#eeeeee +chat_emojiPanelShadowLine=#eeeeee +chat_emojiSearchBackground=#eeeeee +windowBackgroundWhite=-328966 +dialogBackground=-328966 +chats_menuBackground=-328966 +files_folderIcon=-328966 +graySection=-328966 +groupcreate_checkboxCheck=-328966 +chat_topPanelBackground=-328966 +chat_unreadMessagesStartBackground=-328966 +chat_emojiPanelBackground=-328966 +player_background=-328966 +windowBackgroundWhiteBlackText=#424242 +actionBarDefaultSubmenuItem=#424242 +inappPlayerPerformer=#424242 +inappPlayerTitle=#424242 +dialogTextBlack=#424242 +chats_name=#424242 +chats_nameIcon=#424242 +chats_menuItemText=#424242 +contextProgressInner3=#424242 +chat_messagePanelText=#424242 +chat_messageTextIn=#424242 +chat_replyPanelMessage=#424242 +chat_searchPanelText=#424242 +chat_emojiPanelTrendingTitle=#424242 +chat_inReplyMessageText=#424242 +windowBackgroundWhiteInputField=#9e9e9e +windowBackgroundWhiteGrayText=#9e9e9e +windowBackgroundWhiteGrayText2=#9e9e9e +windowBackgroundWhiteGrayText3=#9e9e9e +windowBackgroundWhiteGrayText4=#9e9e9e +windowBackgroundWhiteGrayText5=#9e9e9e +windowBackgroundWhiteGrayText6=#9e9e9e +windowBackgroundWhiteGrayText7=#9e9e9e +windowBackgroundWhiteGrayText8=#9e9e9e +windowBackgroundWhiteGrayIcon=#9e9e9e +windowBackgroundWhiteHintText=#9e9e9e +windowBackgroundWhiteGrayLine=#9e9e9e +player_button=#9e9e9e +sessions_devicesImage=#9e9e9e +checkboxSquareUnchecked=#9e9e9e +dialogIcon=#9e9e9e +dialogCheckboxSquareUnchecked=#9e9e9e +dialogInputField=#9e9e9e +dialogRadioBackground=#9e9e9e +dialogTextGray=#9e9e9e +dialogTextGray2=#9e9e9e +dialogTextGray3=#9e9e9e +dialogTextGray4=#9e9e9e +dialogTextHint=#9e9e9e +calls_ratingStar=#9e9e9e +chats_date=#9e9e9e +chats_menuItemIcon=#9e9e9e +chats_muteIcon=#9e9e9e +chats_pinnedIcon=#9e9e9e +emptyListPlaceholder=#9e9e9e +fastScrollInactive=#9e9e9e +groupcreate_hintText=#9e9e9e +groupcreate_offlineText=#9e9e9e +groupcreate_sectionText=#9e9e9e +stickers_menu=#9e9e9e +chat_messagePanelHint=#9e9e9e +chat_messagePanelIcons=#9e9e9e +chat_messagePanelVoiceDelete=#9e9e9e +chat_recordTime=#9e9e9e +chat_replyPanelClose=#9e9e9e +chat_secretChatStatusText=#9e9e9e +chat_topPanelClose=#9e9e9e +chat_emojiPanelEmptyText=#9e9e9e +chat_emojiPanelMasksIcon=#9e9e9e +chat_emojiPanelTrendingDescription=#9e9e9e +chat_topPanelMessage=#9e9e9e +key_chat_messagePanelVoiceLock=#9e9e9e +chat_emojiPanelStickerSetName=#9e9e9e +chat_emojiPanelStickerSetNameIcon=#9e9e9e +windowBackgroundWhiteRedText=-769226 +windowBackgroundWhiteRedText2=-769226 +windowBackgroundWhiteRedText3=-769226 +windowBackgroundWhiteRedText4=-769226 +windowBackgroundWhiteRedText5=-769226 +windowBackgroundWhiteRedText6=-769226 +dialogTextRed=-769226 +calls_callReceivedRedIcon=-769226 +chats_sentError=-769226 +featuredStickers_delButton=-769226 +chat_sentError=-769226 +chat_reportSpam=-769226 +location_sendLiveLocationBackground=-769226 +divider=83886080 +actionBarActionModeDefaultIcon=-1 +groupcreate_spanText=-1 +profile_verifiedCheck=-1 +returnToCallBackground=-1 +chat_messageTextOut=-1 +chat_secretTimeText=-1 +chat_botKeyboardButtonText=-1 +chat_goDownButtonCounterBackground=-1 +chat_goDownButtonIcon=-1 +chat_mediaLoaderPhotoIconSelected=-1 +chat_outAudioSeekbarFill=-1 +chat_outAudioTitleText=-1 +chat_outContactBackground=-1 +chat_outContactNameText=-1 +chat_outFileBackground=-1 +chat_outFileBackgroundSelected=-1 +chat_outFileNameText=-1 +chat_outInstant=-1 +chat_outLoader=-1 +chat_outLoaderPhotoIcon=-1 +chat_outLoaderPhotoIconSelected=-1 +chat_outLoaderSelected=-1 +chat_outLocationBackground=-1 +chat_outPreviewInstantText=-1 +chat_outPreviewLine=-1 +chat_outReplyLine=-1 +chat_outReplyNameText=-1 +chat_outSiteNameText=-1 +chat_outVenueNameText=-1 +chat_outVoiceSeekbarFill=-1 +chat_inAudioSelectedProgress=-1 +chat_inFileIcon=-1 +chat_inFileProgress=-1 +chat_inFileProgressSelected=-1 +chat_inFileSelectedIcon=-1 +chat_inLoaderPhoto=-1 +chat_inLoaderPhotoSelected=-1 +chat_inLocationIcon=-1 +actionBarActionModeDefaultTop=0 +chats_verifiedBackground=0 +profile_verifiedBackground=0 +actionBarActionModeDefaultSelector=#000000 +actionBarDefaultSearchPlaceholder=#80ffffff +actionBarDefaultSelector=#80ffffff +actionBarDefaultSubtitle=#80ffffff +avatar_actionBarSelectorBlue=#80ffffff +avatar_actionBarSelectorCyan=#80ffffff +avatar_actionBarSelectorGreen=#80ffffff +avatar_actionBarSelectorOrange=#80ffffff +avatar_actionBarSelectorPink=#80ffffff +avatar_actionBarSelectorRed=#80ffffff +avatar_actionBarSelectorViolet=#80ffffff +avatar_subtitleInProfileBlue=#80ffffff +avatar_subtitleInProfileCyan=#80ffffff +avatar_subtitleInProfileGreen=#80ffffff +avatar_subtitleInProfileOrange=#80ffffff +avatar_subtitleInProfilePink=#80ffffff +avatar_subtitleInProfileRed=#80ffffff +avatar_subtitleInProfileViolet=#80ffffff +chats_menuPhone=#80ffffff +chats_menuPhoneCats=#80ffffff +contextProgressInner2=#80ffffff +chat_recordedVoicePlayPausePressed=#80ffffff +chat_recordedVoiceProgress=#80ffffff +chat_muteIcon=#80ffffff +chat_outAudioDurationSelectedText=#80ffffff +chat_outAudioDurationText=#80ffffff +chat_outForwardedNameText=#80ffffff +chat_outInstantSelected=#80ffffff +chat_outMenu=#80ffffff +chat_outMenuSelected=#80ffffff +chat_outPreviewInstantSelectedText=#80ffffff +chat_outReplyMessageText=#80ffffff +chat_outSentCheck=#80ffffff +chat_outSentCheckSelected=#80ffffff +chat_outSentClock=#80ffffff +chat_outSentClockSelected=#80ffffff +chat_outTimeSelectedText=#80ffffff +chat_outTimeText=#80ffffff +chat_outViaBotNameText=#80ffffff +chat_outViews=#80ffffff +chat_outViewsSelected=#80ffffff +actionBarWhiteSelector=#40000000 +chats_actionPressedBackground=#40000000 +profile_actionPressedBackground=#40000000 +player_actionBarTop=855638016 +player_placeholder=#809e9e9e +inappPlayerClose=#809e9e9e +dialogLineProgressBackground=#809e9e9e +picker_disabledButton=#809e9e9e +radioBackground=#809e9e9e +switchTrack=#809e9e9e +chat_messagePanelCancelInlineBot=#809e9e9e +player_progressBackground=#409e9e9e +chat_wallpaper=-657931 +chats_pinnedOverlay=-657931 +switchThumb=-657931 +dialogBackgroundGray=-657931 +player_placeholderBackground=-657931 +listSelectorSDK21=#80000000 +dialogButtonSelector=#80000000 +chats_menuCloudBackgroundCats=#80000000 +chats_menuTopShadow=#80000000 +location_markerX=#80000000 +stickers_menuSelector=#80000000 +chat_serviceBackgroundSelected=#80000000 +chat_mediaLoaderPhoto=#80000000 +chat_mediaLoaderPhotoSelected=#80000000 +chat_mediaTimeBackground=#80000000 +dialogGrayLine=167772160 +chat_replyPanelLine=167772160 +chats_message=#757575 +featuredStickers_delButtonPressed=#80f44336 +chat_inBubbleSelected=#c0ffffff +chat_outAudioPerfomerText=#c0ffffff +chat_outContactPhoneText=#c0ffffff +chat_outFileInfoSelectedText=#c0ffffff +chat_outFileInfoText=#c0ffffff +chat_outReplyMediaMessageSelectedText=#c0ffffff +chat_outReplyMediaMessageText=#c0ffffff +chat_outVenueInfoSelectedText=#c0ffffff +chat_outVenueInfoText=#c0ffffff +chat_messageLinkOut=#c0ffffff +chat_inBubbleShadow=#000000 +chat_serviceBackground=#60000000 +chat_emojiPanelBackspace=#609e9e9e +chat_emojiPanelIcon=#51000000 +chat_outAudioSeekbar=#60ffffff +chat_outAudioSeekbarSelected=#60ffffff +chat_outVoiceSeekbar=#60ffffff +chat_outVoiceSeekbarSelected=#60ffffff +windowBackgroundWhiteBlueHeader=#3f51b5 +windowBackgroundWhiteBlueText=#3f51b5 +windowBackgroundWhiteBlueText2=#3f51b5 +windowBackgroundWhiteBlueText3=#3f51b5 +windowBackgroundWhiteBlueText4=#3f51b5 +windowBackgroundWhiteBlueText5=#3f51b5 +windowBackgroundWhiteBlueText6=#3f51b5 +windowBackgroundWhiteBlueText7=#3f51b5 +windowBackgroundWhiteGreenText=#3f51b5 +windowBackgroundWhiteGreenText2=#3f51b5 +windowBackgroundWhiteValueText=#3f51b5 +windowBackgroundWhiteInputFieldActivated=#3f51b5 +windowBackgroundWhiteLinkText=#3f51b5 +actionBarActionModeDefault=#3f51b5 +actionBarDefault=#3f51b5 +player_actionBarItems=#3f51b5 +player_actionBarTitle=#3f51b5 +avatar_backgroundActionBarCyan=#3f51b5 +avatar_backgroundActionBarBlue=#3f51b5 +avatar_backgroundActionBarGreen=#3f51b5 +avatar_backgroundActionBarOrange=#3f51b5 +avatar_backgroundActionBarPink=#3f51b5 +avatar_backgroundActionBarViolet=#3f51b5 +avatar_backgroundActionBarRed=#3f51b5 +player_progress=#3f51b5 +player_buttonActive=#3f51b5 +player_time=#3f51b5 +avatar_backgroundBlue=#3f51b5 +avatar_backgroundCyan=#3f51b5 +avatar_backgroundGreen=#3f51b5 +avatar_backgroundOrange=#3f51b5 +avatar_backgroundInProfileBlue=#3f51b5 +avatar_backgroundInProfileCyan=#3f51b5 +avatar_backgroundInProfileGreen=#3f51b5 +avatar_backgroundInProfileOrange=#3f51b5 +avatar_backgroundInProfilePink=#3f51b5 +avatar_backgroundInProfileRed=#3f51b5 +avatar_backgroundInProfileViolet=#3f51b5 +avatar_backgroundPink=#3f51b5 +avatar_backgroundRed=#3f51b5 +avatar_backgroundViolet=#3f51b5 +avatar_nameInMessageBlue=#3f51b5 +avatar_nameInMessageCyan=#3f51b5 +avatar_nameInMessageGreen=#3f51b5 +avatar_nameInMessageOrange=#3f51b5 +avatar_nameInMessagePink=#3f51b5 +avatar_nameInMessageRed=#3f51b5 +avatar_nameInMessageViolet=#3f51b5 +inappPlayerPlayPause=#3f51b5 +checkboxSquareBackground=#3f51b5 +changephoneinfo_image=#3f51b5 +dialogBadgeBackground=#3f51b5 +dialogButton=#3f51b5 +dialogCheckboxSquareBackground=#3f51b5 +dialogInputFieldActivated=#3f51b5 +dialogLineProgress=#3f51b5 +dialogProgressCircle=#3f51b5 +dialogRadioBackgroundChecked=#3f51b5 +dialogRoundCheckBox=#3f51b5 +dialogScrollGlow=#3f51b5 +dialogTextBlue=#3f51b5 +dialogTextBlue2=#3f51b5 +dialogTextBlue3=#3f51b5 +dialogTextBlue4=#3f51b5 +dialogTextLink=#3f51b5 +calls_ratingStarSelected=#3f51b5 +calls_callReceivedGreenIcon=#3f51b5 +chats_actionBackground=#3f51b5 +chats_actionMessage=#3f51b5 +chats_attachMessage=#3f51b5 +chats_draft=#3f51b5 +chats_nameMessage=#3f51b5 +chats_secretIcon=#3f51b5 +chats_secretName=#3f51b5 +chats_sentCheck=#3f51b5 +chats_sentClock=#3f51b5 +chats_unreadCounter=#3f51b5 +chats_verifiedCheck=#3f51b5 +checkbox=#3f51b5 +fastScrollActive=#3f51b5 +featuredStickers_addButton=#3f51b5 +featuredStickers_addedIcon=#3f51b5 +featuredStickers_unread=#3f51b5 +files_folderIconBackground=#3f51b5 +groupcreate_checkbox=#3f51b5 +groupcreate_cursor=#3f51b5 +groupcreate_onlineText=#3f51b5 +avatar_backgroundGroupCreateSpanBlue=#3f51b5 +location_sendLocationBackground=#3f51b5 +login_progressOuter=#3f51b5 +contextProgressOuter1=#3f51b5 +musicPicker_buttonBackground=#3f51b5 +musicPicker_checkbox=#3f51b5 +picker_badge=#3f51b5 +picker_enabledButton=#3f51b5 +profile_actionIcon=#3f51b5 +profile_creatorIcon=#3f51b5 +progressCircle=#3f51b5 +radioBackgroundChecked=#3f51b5 +returnToCallText=#3f51b5 +sharedMedia_linkPlaceholder=#3f51b5 +sharedMedia_startStopLoadIcon=#3f51b5 +switchThumbChecked=#3f51b5 +chat_messagePanelSend=#3f51b5 +chat_messagePanelVoiceBackground=#3f51b5 +chat_messagePanelVoiceShadow=#3f51b5 +chat_outBubble=#3f51b5 +chat_recordVoiceCancel=#3f51b5 +chat_recordedVoiceBackground=#3f51b5 +chat_recordedVoiceDot=#3f51b5 +chat_replyPanelIcons=#3f51b5 +chat_replyPanelName=#3f51b5 +chat_searchPanelIcons=#3f51b5 +chat_secretTimerBackground=#3f51b5 +chat_topPanelLine=#3f51b5 +chat_topPanelTitle=#3f51b5 +chat_unreadMessagesStartArrowIcon=#3f51b5 +chat_unreadMessagesStartText=#3f51b5 +chat_addContact=#3f51b5 +chat_botKeyboardButtonBackground=#3f51b5 +chat_botSwitchToInlineText=#3f51b5 +chat_editDoneIcon=#3f51b5 +chat_emojiPanelIconSelected=#3f51b5 +chat_emojiPanelIconSelector=#3f51b5 +chat_emojiPanelMasksIconSelected=#3f51b5 +chat_emojiPanelNewTrending=#3f51b5 +chat_emojiPanelStickerPackSelector=#3f51b5 +chat_fieldOverlayText=#3f51b5 +chat_gifSaveHintBackground=#3f51b5 +chat_goDownButton=#3f51b5 +chat_goDownButtonCounter=#3f51b5 +chat_inlineResultIcon=#3f51b5 +chat_outAudioProgress=#3f51b5 +chat_outAudioSelectedProgress=#3f51b5 +chat_outContactIcon=#3f51b5 +chat_outFileIcon=#3f51b5 +chat_outFileProgress=#3f51b5 +chat_outFileProgressSelected=#3f51b5 +chat_outFileSelectedIcon=#3f51b5 +chat_outLoaderPhoto=#3f51b5 +chat_outLoaderPhotoSelected=#3f51b5 +chat_outLocationIcon=#3f51b5 +chat_messageLinkIn=#3f51b5 +chat_inAudioSeekbarFill=#3f51b5 +chat_inAudioTitleText=#3f51b5 +chat_inContactBackground=#3f51b5 +chat_inContactNameText=#3f51b5 +chat_inFileBackground=#3f51b5 +chat_inFileBackgroundSelected=#3f51b5 +chat_inFileNameText=#3f51b5 +chat_inInstant=#3f51b5 +chat_inLoader=#3f51b5 +chat_inLoaderPhotoIcon=#3f51b5 +chat_inLoaderPhotoIconSelected=#3f51b5 +chat_inLoaderSelected=#3f51b5 +chat_inLocationBackground=#3f51b5 +chat_inPreviewInstantText=#3f51b5 +chat_inPreviewLine=#3f51b5 +chat_inReplyLine=#3f51b5 +chat_inReplyNameText=#3f51b5 +chat_inSiteNameText=#3f51b5 +chat_inVenueNameText=#3f51b5 +chat_inVoiceSeekbarFill=#3f51b5 +avatar_backgroundSaved=#3f51b5 +contacts_inviteBackground=#3f51b5 +dialogTopBackground=#3f51b5 +location_liveLocationProgress=#3f51b5 +location_placeLocationBackground=#3f51b5 +chats_menuItemCheck=#3f51b5 +windowBackgroundWhiteLinkSelection=#403f51b5 +chat_selectedBackground=#403f51b5 +chat_inAudioSeekbar=#403f51b5 +chat_inAudioSeekbarSelected=#403f51b5 +chat_inVoiceSeekbar=#403f51b5 +chat_inVoiceSeekbarSelected=#403f51b5 +player_actionBarSubtitle=#803f51b5 +player_actionBarSelector=#803f51b5 +checkboxSquareDisabled=#803f51b5 +dialogCheckboxSquareDisabled=#803f51b5 +dialogLinkSelection=#803f51b5 +featuredStickers_addButtonPressed=#803f51b5 +profile_adminIcon=#803f51b5 +switchTrackChecked=#803f51b5 +chat_botKeyboardButtonBackgroundPressed=#803f51b5 +chat_linkSelectBackground=#803f51b5 +chat_inAudioDurationSelectedText=#803f51b5 +chat_inAudioDurationText=#803f51b5 +chat_inForwardedNameText=#803f51b5 +chat_inInstantSelected=#803f51b5 +chat_inMenu=#803f51b5 +chat_inMenuSelected=#803f51b5 +chat_inPreviewInstantSelectedText=#803f51b5 +chat_inSentClock=#803f51b5 +chat_inSentClockSelected=#803f51b5 +chat_inTimeSelectedText=#803f51b5 +chat_inTimeText=#803f51b5 +chat_inVenueInfoSelectedText=#803f51b5 +chat_inVenueInfoText=#803f51b5 +chat_inViaBotNameText=#803f51b5 +chat_inViews=#803f51b5 +chat_inViewsSelected=#803f51b5 +chat_adminText=#803f51b5 +chat_adminSelectedText=#803f51b5 +key_player_progressCachedBackground=#803f51b5 +chat_inAudioCacheSeekbar=#803f51b5 +chat_outAudioCacheSeekbar=#803f51b5 +chat_outBubbleSelected=#c03f51b5 +chat_inAudioPerfomerText=#c03f51b5 +chat_inContactPhoneText=#c03f51b5 +chat_inFileInfoSelectedText=#c03f51b5 +chat_inFileInfoText=#c03f51b5 +chat_inReplyMediaMessageSelectedText=#c03f51b5 +chat_inReplyMediaMessageText=#c03f51b5 +groupcreate_spanBackground=#7986cb +chat_textSelectBackground=#7986cb +chat_outBubbleShadow=#1a237e diff --git a/TMessagesProj/src/main/assets/languages.zip b/TMessagesProj/src/main/assets/languages.zip new file mode 100644 index 000000000..3c206671a Binary files /dev/null and b/TMessagesProj/src/main/assets/languages.zip differ diff --git a/TMessagesProj/src/main/assets/v2ray_config.json b/TMessagesProj/src/main/assets/v2ray_config.json new file mode 100644 index 000000000..2ff9d56b4 --- /dev/null +++ b/TMessagesProj/src/main/assets/v2ray_config.json @@ -0,0 +1,62 @@ +{ + "inbounds": [{ + "tag": "socks-in", + "listen": "127.0.0.1", + "port": 114514, + "protocol": "socks", + "settings": { + "auth": "noauth", + "udp": true, + "userLevel": 8 + }, + "sniffing": { + "destOverride": [ + "http", + "tls" + ], + "enabled": true + } + } + ], + "outbounds": [{ + "tag": "proxy", + "protocol": "vmess", + "settings": { + "vnext": [{ + "address": "v2ray.cool", + "port": 443, + "users": [{ + "alterId": 64, + "id": "73670f86-1145-4ffd-b468-6cd73cea1f29", + "level": 8, + "security": "none" + }] + }] + }, + "streamSettings": { + "network": "ws", + "security": "tls", + "tlssettings": { + "allowInsecure": true, + "serverName": "baidu.com" + }, + "wssettings": { + "connectionReuse": true, + "headers": { + "Host": "baidu.com" + }, + "path": "/search" + } + } + }], + "routing": { + "domainStrategy": "IPIfNonMatch", + "rules": [{ + "inboundTag": [ + "socks-in" + ], + "outboundTag": "proxy", + "type": "field" + }] + } +} \ No newline at end of file diff --git a/TMessagesProj/src/main/ic_launcher-playstore.png b/TMessagesProj/src/main/ic_launcher-playstore.png new file mode 100644 index 000000000..2bfeec964 Binary files /dev/null and b/TMessagesProj/src/main/ic_launcher-playstore.png differ diff --git a/TMessagesProj/src/main/ic_launcher-web.png b/TMessagesProj/src/main/ic_launcher-web.png deleted file mode 100644 index 0d3f659b2..000000000 Binary files a/TMessagesProj/src/main/ic_launcher-web.png and /dev/null differ diff --git a/TMessagesProj/src/main/ic_launcher_web.png b/TMessagesProj/src/main/ic_launcher_web.png new file mode 100644 index 000000000..19258d1b0 Binary files /dev/null and b/TMessagesProj/src/main/ic_launcher_web.png differ diff --git a/TMessagesProj/src/main/java/com/google/zxing/BarcodeFormat.java b/TMessagesProj/src/main/java/com/google/zxing/BarcodeFormat.java deleted file mode 100755 index a7ca3ec4a..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/BarcodeFormat.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing; - -/** - * Enumerates barcode formats known to this package. Please keep alphabetized. - * - * @author Sean Owen - */ -public enum BarcodeFormat { - /** QR Code 2D barcode format. */ - QR_CODE -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/Binarizer.java b/TMessagesProj/src/main/java/com/google/zxing/Binarizer.java deleted file mode 100755 index 02af0832f..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/Binarizer.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2009 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing; - -import com.google.zxing.common.BitArray; -import com.google.zxing.common.BitMatrix; - -/** - * This class hierarchy provides a set of methods to convert luminance data to 1 bit data. - * It allows the algorithm to vary polymorphically, for example allowing a very expensive - * thresholding technique for servers and a fast one for mobile. It also permits the implementation - * to vary, e.g. a JNI version for Android and a Java fallback version for other platforms. - * - * @author dswitkin@google.com (Daniel Switkin) - */ -public abstract class Binarizer { - - private final LuminanceSource source; - - protected Binarizer(LuminanceSource source) { - this.source = source; - } - - public final LuminanceSource getLuminanceSource() { - return source; - } - - /** - * Converts one row of luminance data to 1 bit data. May actually do the conversion, or return - * cached data. Callers should assume this method is expensive and call it as seldom as possible. - * This method is intended for decoding 1D barcodes and may choose to apply sharpening. - * For callers which only examine one row of pixels at a time, the same BitArray should be reused - * and passed in with each call for performance. However it is legal to keep more than one row - * at a time if needed. - * - * @param y The row to fetch, which must be in [0, bitmap height) - * @param row An optional preallocated array. If null or too small, it will be ignored. - * If used, the Binarizer will call BitArray.clear(). Always use the returned object. - * @return The array of bits for this row (true means black). - * @throws NotFoundException if row can't be binarized - */ - public abstract BitArray getBlackRow(int y, BitArray row) throws NotFoundException; - - /** - * Converts a 2D array of luminance data to 1 bit data. As above, assume this method is expensive - * and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or - * may not apply sharpening. Therefore, a row from this matrix may not be identical to one - * fetched using getBlackRow(), so don't mix and match between them. - * - * @return The 2D array of bits for the image (true means black). - * @throws NotFoundException if image can't be binarized to make a matrix - */ - public abstract BitMatrix getBlackMatrix() throws NotFoundException; - - /** - * Creates a new object with the same type as this Binarizer implementation, but with pristine - * state. This is needed because Binarizer implementations may be stateful, e.g. keeping a cache - * of 1 bit data. See Effective Java for why we can't use Java's clone() method. - * - * @param source The LuminanceSource this Binarizer will operate on. - * @return A new concrete Binarizer implementation object. - */ - public abstract Binarizer createBinarizer(LuminanceSource source); - - public final int getWidth() { - return source.getWidth(); - } - - public final int getHeight() { - return source.getHeight(); - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/BinaryBitmap.java b/TMessagesProj/src/main/java/com/google/zxing/BinaryBitmap.java deleted file mode 100755 index c1ef8a13e..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/BinaryBitmap.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2009 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing; - -import com.google.zxing.common.BitArray; -import com.google.zxing.common.BitMatrix; - -/** - * This class is the core bitmap class used by ZXing to represent 1 bit data. Reader objects - * accept a BinaryBitmap and attempt to decode it. - * - * @author dswitkin@google.com (Daniel Switkin) - */ -public final class BinaryBitmap { - - private final Binarizer binarizer; - private BitMatrix matrix; - - public BinaryBitmap(Binarizer binarizer) { - if (binarizer == null) { - throw new IllegalArgumentException("Binarizer must be non-null."); - } - this.binarizer = binarizer; - } - - /** - * @return The width of the bitmap. - */ - public int getWidth() { - return binarizer.getWidth(); - } - - /** - * @return The height of the bitmap. - */ - public int getHeight() { - return binarizer.getHeight(); - } - - /** - * Converts one row of luminance data to 1 bit data. May actually do the conversion, or return - * cached data. Callers should assume this method is expensive and call it as seldom as possible. - * This method is intended for decoding 1D barcodes and may choose to apply sharpening. - * - * @param y The row to fetch, which must be in [0, bitmap height) - * @param row An optional preallocated array. If null or too small, it will be ignored. - * If used, the Binarizer will call BitArray.clear(). Always use the returned object. - * @return The array of bits for this row (true means black). - * @throws NotFoundException if row can't be binarized - */ - public BitArray getBlackRow(int y, BitArray row) throws NotFoundException { - return binarizer.getBlackRow(y, row); - } - - /** - * Converts a 2D array of luminance data to 1 bit. As above, assume this method is expensive - * and do not call it repeatedly. This method is intended for decoding 2D barcodes and may or - * may not apply sharpening. Therefore, a row from this matrix may not be identical to one - * fetched using getBlackRow(), so don't mix and match between them. - * - * @return The 2D array of bits for the image (true means black). - * @throws NotFoundException if image can't be binarized to make a matrix - */ - public BitMatrix getBlackMatrix() throws NotFoundException { - // The matrix is created on demand the first time it is requested, then cached. There are two - // reasons for this: - // 1. This work will never be done if the caller only installs 1D Reader objects, or if a - // 1D Reader finds a barcode before the 2D Readers run. - // 2. This work will only be done once even if the caller installs multiple 2D Readers. - if (matrix == null) { - matrix = binarizer.getBlackMatrix(); - } - return matrix; - } - - /** - * @return Whether this bitmap can be cropped. - */ - public boolean isCropSupported() { - return binarizer.getLuminanceSource().isCropSupported(); - } - - /** - * Returns a new object with cropped image data. Implementations may keep a reference to the - * original data rather than a copy. Only callable if isCropSupported() is true. - * - * @param left The left coordinate, which must be in [0,getWidth()) - * @param top The top coordinate, which must be in [0,getHeight()) - * @param width The width of the rectangle to crop. - * @param height The height of the rectangle to crop. - * @return A cropped version of this object. - */ - public BinaryBitmap crop(int left, int top, int width, int height) { - LuminanceSource newSource = binarizer.getLuminanceSource().crop(left, top, width, height); - return new BinaryBitmap(binarizer.createBinarizer(newSource)); - } - - /** - * @return Whether this bitmap supports counter-clockwise rotation. - */ - public boolean isRotateSupported() { - return binarizer.getLuminanceSource().isRotateSupported(); - } - - /** - * Returns a new object with rotated image data by 90 degrees counterclockwise. - * Only callable if {@link #isRotateSupported()} is true. - * - * @return A rotated version of this object. - */ - public BinaryBitmap rotateCounterClockwise() { - LuminanceSource newSource = binarizer.getLuminanceSource().rotateCounterClockwise(); - return new BinaryBitmap(binarizer.createBinarizer(newSource)); - } - - /** - * Returns a new object with rotated image data by 45 degrees counterclockwise. - * Only callable if {@link #isRotateSupported()} is true. - * - * @return A rotated version of this object. - */ - public BinaryBitmap rotateCounterClockwise45() { - LuminanceSource newSource = binarizer.getLuminanceSource().rotateCounterClockwise45(); - return new BinaryBitmap(binarizer.createBinarizer(newSource)); - } - - @Override - public String toString() { - try { - return getBlackMatrix().toString(); - } catch (NotFoundException e) { - return ""; - } - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/ChecksumException.java b/TMessagesProj/src/main/java/com/google/zxing/ChecksumException.java deleted file mode 100755 index c5acbe3ee..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/ChecksumException.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing; - -/** - * Thrown when a barcode was successfully detected and decoded, but - * was not returned because its checksum feature failed. - * - * @author Sean Owen - */ -public final class ChecksumException extends ReaderException { - - private static final ChecksumException INSTANCE = new ChecksumException(); - static { - INSTANCE.setStackTrace(NO_TRACE); // since it's meaningless - } - - private ChecksumException() { - // do nothing - } - - private ChecksumException(Throwable cause) { - super(cause); - } - - public static ChecksumException getChecksumInstance() { - return isStackTrace ? new ChecksumException() : INSTANCE; - } - - public static ChecksumException getChecksumInstance(Throwable cause) { - return isStackTrace ? new ChecksumException(cause) : INSTANCE; - } -} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/com/google/zxing/DecodeHintType.java b/TMessagesProj/src/main/java/com/google/zxing/DecodeHintType.java deleted file mode 100755 index 11f579e62..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/DecodeHintType.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing; - -import java.util.List; - -/** - * Encapsulates a type of hint that a caller may pass to a barcode reader to help it - * more quickly or accurately decode it. It is up to implementations to decide what, - * if anything, to do with the information that is supplied. - * - * @author Sean Owen - * @author dswitkin@google.com (Daniel Switkin) - * @see Reader#decode(BinaryBitmap,java.util.Map) - */ -public enum DecodeHintType { - - /** - * Unspecified, application-specific hint. Maps to an unspecified {@link Object}. - */ - OTHER(Object.class), - - /** - * Image is a pure monochrome image of a barcode. Doesn't matter what it maps to; - * use {@link Boolean#TRUE}. - */ - PURE_BARCODE(Void.class), - - /** - * Image is known to be of one of a few possible formats. - * Maps to a {@link List} of {@link BarcodeFormat}s. - */ - POSSIBLE_FORMATS(List.class), - - /** - * Spend more time to try to find a barcode; optimize for accuracy, not speed. - * Doesn't matter what it maps to; use {@link Boolean#TRUE}. - */ - TRY_HARDER(Void.class), - - /** - * Specifies what character encoding to use when decoding, where applicable (type String) - */ - CHARACTER_SET(String.class), - - /** - * Allowed lengths of encoded data -- reject anything else. Maps to an {@code int[]}. - */ - ALLOWED_LENGTHS(int[].class), - - /** - * Assume Code 39 codes employ a check digit. Doesn't matter what it maps to; - * use {@link Boolean#TRUE}. - */ - ASSUME_CODE_39_CHECK_DIGIT(Void.class), - - /** - * Assume the barcode is being processed as a GS1 barcode, and modify behavior as needed. - * For example this affects FNC1 handling for Code 128 (aka GS1-128). Doesn't matter what it maps to; - * use {@link Boolean#TRUE}. - */ - ASSUME_GS1(Void.class), - - /** - * If true, return the start and end digits in a Codabar barcode instead of stripping them. They - * are alpha, whereas the rest are numeric. By default, they are stripped, but this causes them - * to not be. Doesn't matter what it maps to; use {@link Boolean#TRUE}. - */ - RETURN_CODABAR_START_END(Void.class), - - /** - * The caller needs to be notified via callback when a possible {@link ResultPoint} - * is found. Maps to a {@link ResultPointCallback}. - */ - NEED_RESULT_POINT_CALLBACK(ResultPointCallback.class), - - - /** - * Allowed extension lengths for EAN or UPC barcodes. Other formats will ignore this. - * Maps to an {@code int[]} of the allowed extension lengths, for example [2], [5], or [2, 5]. - * If it is optional to have an extension, do not set this hint. If this is set, - * and a UPC or EAN barcode is found but an extension is not, then no result will be returned - * at all. - */ - ALLOWED_EAN_EXTENSIONS(int[].class), - - // End of enumeration values. - ; - - /** - * Data type the hint is expecting. - * Among the possible values the {@link Void} stands out as being used for - * hints that do not expect a value to be supplied (flag hints). Such hints - * will possibly have their value ignored, or replaced by a - * {@link Boolean#TRUE}. Hint suppliers should probably use - * {@link Boolean#TRUE} as directed by the actual hint documentation. - */ - private final Class valueType; - - DecodeHintType(Class valueType) { - this.valueType = valueType; - } - - public Class getValueType() { - return valueType; - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/Dimension.java b/TMessagesProj/src/main/java/com/google/zxing/Dimension.java deleted file mode 100755 index dd40d06d0..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/Dimension.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2012 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing; - -/** - * Simply encapsulates a width and height. - */ -public final class Dimension { - - private final int width; - private final int height; - - public Dimension(int width, int height) { - if (width < 0 || height < 0) { - throw new IllegalArgumentException(); - } - this.width = width; - this.height = height; - } - - public int getWidth() { - return width; - } - - public int getHeight() { - return height; - } - - @Override - public boolean equals(Object other) { - if (other instanceof Dimension) { - Dimension d = (Dimension) other; - return width == d.width && height == d.height; - } - return false; - } - - @Override - public int hashCode() { - return width * 32713 + height; - } - - @Override - public String toString() { - return width + "x" + height; - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/EncodeHintType.java b/TMessagesProj/src/main/java/com/google/zxing/EncodeHintType.java deleted file mode 100755 index 9a2fd57e6..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/EncodeHintType.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2008 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing; - -/** - * These are a set of hints that you may pass to Writers to specify their behavior. - * - * @author dswitkin@google.com (Daniel Switkin) - */ -public enum EncodeHintType { - - /** - * Specifies what degree of error correction to use, for example in QR Codes. - * Type depends on the encoder. For example for QR codes it's type - * {@link com.google.zxing.qrcode.decoder.ErrorCorrectionLevel ErrorCorrectionLevel}. - * For Aztec it is of type {@link Integer}, representing the minimal percentage of error correction words. - * For PDF417 it is of type {@link Integer}, valid values being 0 to 8. - * In all cases, it can also be a {@link String} representation of the desired value as well. - * Note: an Aztec symbol should have a minimum of 25% EC words. - */ - ERROR_CORRECTION, - - /** - * Specifies what character encoding to use where applicable (type {@link String}) - */ - CHARACTER_SET, - - /** - * Specifies the matrix shape for Data Matrix (type {@link com.google.zxing.datamatrix.encoder.SymbolShapeHint}) - */ - DATA_MATRIX_SHAPE, - - /** - * Specifies a minimum barcode size (type {@link Dimension}). Only applicable to Data Matrix now. - * - * @deprecated use width/height params in - * {@link com.google.zxing.datamatrix.DataMatrixWriter#encode(String, BarcodeFormat, int, int)} - */ - @Deprecated - MIN_SIZE, - - /** - * Specifies a maximum barcode size (type {@link Dimension}). Only applicable to Data Matrix now. - * - * @deprecated without replacement - */ - @Deprecated - MAX_SIZE, - - /** - * Specifies margin, in pixels, to use when generating the barcode. The meaning can vary - * by format; for example it controls margin before and after the barcode horizontally for - * most 1D formats. (Type {@link Integer}, or {@link String} representation of the integer value). - */ - MARGIN, - - /** - * Specifies whether to use compact mode for PDF417 (type {@link Boolean}, or "true" or "false" - * {@link String} value). - */ - PDF417_COMPACT, - - /** - * Specifies what compaction mode to use for PDF417 (type - * {@link com.google.zxing.pdf417.encoder.Compaction Compaction} or {@link String} value of one of its - * enum values). - */ - PDF417_COMPACTION, - - /** - * Specifies the minimum and maximum number of rows and columns for PDF417 (type - * {@link com.google.zxing.pdf417.encoder.Dimensions Dimensions}). - */ - PDF417_DIMENSIONS, - - /** - * Specifies the required number of layers for an Aztec code. - * A negative number (-1, -2, -3, -4) specifies a compact Aztec code. - * 0 indicates to use the minimum number of layers (the default). - * A positive number (1, 2, .. 32) specifies a normal (non-compact) Aztec code. - * (Type {@link Integer}, or {@link String} representation of the integer value). - */ - AZTEC_LAYERS, - - /** - * Specifies the exact version of QR code to be encoded. - * (Type {@link Integer}, or {@link String} representation of the integer value). - */ - QR_VERSION, - - /** - * Specifies the QR code mask pattern to be used. Allowed values are - * 0..QRCode.NUM_MASK_PATTERNS-1. By default the code will automatically select - * the optimal mask pattern. - * * (Type {@link Integer}, or {@link String} representation of the integer value). - */ - QR_MASK_PATTERN, - - /** - * Specifies whether the data should be encoded to the GS1 standard (type {@link Boolean}, or "true" or "false" - * {@link String } value). - */ - GS1_FORMAT, -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/FormatException.java b/TMessagesProj/src/main/java/com/google/zxing/FormatException.java deleted file mode 100755 index ebd800862..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/FormatException.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing; - -/** - * Thrown when a barcode was successfully detected, but some aspect of - * the content did not conform to the barcode's format rules. This could have - * been due to a mis-detection. - * - * @author Sean Owen - */ -public final class FormatException extends ReaderException { - - private static final FormatException INSTANCE = new FormatException(); - static { - INSTANCE.setStackTrace(NO_TRACE); // since it's meaningless - } - - private FormatException() { - } - - private FormatException(Throwable cause) { - super(cause); - } - - public static FormatException getFormatInstance() { - return isStackTrace ? new FormatException() : INSTANCE; - } - - public static FormatException getFormatInstance(Throwable cause) { - return isStackTrace ? new FormatException(cause) : INSTANCE; - } -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/InvertedLuminanceSource.java b/TMessagesProj/src/main/java/com/google/zxing/InvertedLuminanceSource.java deleted file mode 100755 index a64e15d60..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/InvertedLuminanceSource.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2013 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing; - -/** - * A wrapper implementation of {@link LuminanceSource} which inverts the luminances it returns -- black becomes - * white and vice versa, and each value becomes (255-value). - * - * @author Sean Owen - */ -public final class InvertedLuminanceSource extends LuminanceSource { - - private final LuminanceSource delegate; - - public InvertedLuminanceSource(LuminanceSource delegate) { - super(delegate.getWidth(), delegate.getHeight()); - this.delegate = delegate; - } - - @Override - public byte[] getRow(int y, byte[] row) { - row = delegate.getRow(y, row); - int width = getWidth(); - for (int i = 0; i < width; i++) { - row[i] = (byte) (255 - (row[i] & 0xFF)); - } - return row; - } - - @Override - public byte[] getMatrix() { - byte[] matrix = delegate.getMatrix(); - int length = getWidth() * getHeight(); - byte[] invertedMatrix = new byte[length]; - for (int i = 0; i < length; i++) { - invertedMatrix[i] = (byte) (255 - (matrix[i] & 0xFF)); - } - return invertedMatrix; - } - - @Override - public boolean isCropSupported() { - return delegate.isCropSupported(); - } - - @Override - public LuminanceSource crop(int left, int top, int width, int height) { - return new InvertedLuminanceSource(delegate.crop(left, top, width, height)); - } - - @Override - public boolean isRotateSupported() { - return delegate.isRotateSupported(); - } - - /** - * @return original delegate {@link LuminanceSource} since invert undoes itself - */ - @Override - public LuminanceSource invert() { - return delegate; - } - - @Override - public LuminanceSource rotateCounterClockwise() { - return new InvertedLuminanceSource(delegate.rotateCounterClockwise()); - } - - @Override - public LuminanceSource rotateCounterClockwise45() { - return new InvertedLuminanceSource(delegate.rotateCounterClockwise45()); - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/LuminanceSource.java b/TMessagesProj/src/main/java/com/google/zxing/LuminanceSource.java deleted file mode 100755 index 1946d023c..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/LuminanceSource.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2009 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing; - -/** - * The purpose of this class hierarchy is to abstract different bitmap implementations across - * platforms into a standard interface for requesting greyscale luminance values. The interface - * only provides immutable methods; therefore crop and rotation create copies. This is to ensure - * that one Reader does not modify the original luminance source and leave it in an unknown state - * for other Readers in the chain. - * - * @author dswitkin@google.com (Daniel Switkin) - */ -public abstract class LuminanceSource { - - private final int width; - private final int height; - - protected LuminanceSource(int width, int height) { - this.width = width; - this.height = height; - } - - /** - * Fetches one row of luminance data from the underlying platform's bitmap. Values range from - * 0 (black) to 255 (white). Because Java does not have an unsigned byte type, callers will have - * to bitwise and with 0xff for each value. It is preferable for implementations of this method - * to only fetch this row rather than the whole image, since no 2D Readers may be installed and - * getMatrix() may never be called. - * - * @param y The row to fetch, which must be in [0,getHeight()) - * @param row An optional preallocated array. If null or too small, it will be ignored. - * Always use the returned object, and ignore the .length of the array. - * @return An array containing the luminance data. - */ - public abstract byte[] getRow(int y, byte[] row); - - /** - * Fetches luminance data for the underlying bitmap. Values should be fetched using: - * {@code int luminance = array[y * width + x] & 0xff} - * - * @return A row-major 2D array of luminance values. Do not use result.length as it may be - * larger than width * height bytes on some platforms. Do not modify the contents - * of the result. - */ - public abstract byte[] getMatrix(); - - /** - * @return The width of the bitmap. - */ - public final int getWidth() { - return width; - } - - /** - * @return The height of the bitmap. - */ - public final int getHeight() { - return height; - } - - /** - * @return Whether this subclass supports cropping. - */ - public boolean isCropSupported() { - return false; - } - - /** - * Returns a new object with cropped image data. Implementations may keep a reference to the - * original data rather than a copy. Only callable if isCropSupported() is true. - * - * @param left The left coordinate, which must be in [0,getWidth()) - * @param top The top coordinate, which must be in [0,getHeight()) - * @param width The width of the rectangle to crop. - * @param height The height of the rectangle to crop. - * @return A cropped version of this object. - */ - public LuminanceSource crop(int left, int top, int width, int height) { - throw new UnsupportedOperationException("This luminance source does not support cropping."); - } - - /** - * @return Whether this subclass supports counter-clockwise rotation. - */ - public boolean isRotateSupported() { - return false; - } - - /** - * @return a wrapper of this {@code LuminanceSource} which inverts the luminances it returns -- black becomes - * white and vice versa, and each value becomes (255-value). - */ - public LuminanceSource invert() { - return new InvertedLuminanceSource(this); - } - - /** - * Returns a new object with rotated image data by 90 degrees counterclockwise. - * Only callable if {@link #isRotateSupported()} is true. - * - * @return A rotated version of this object. - */ - public LuminanceSource rotateCounterClockwise() { - throw new UnsupportedOperationException("This luminance source does not support rotation by 90 degrees."); - } - - /** - * Returns a new object with rotated image data by 45 degrees counterclockwise. - * Only callable if {@link #isRotateSupported()} is true. - * - * @return A rotated version of this object. - */ - public LuminanceSource rotateCounterClockwise45() { - throw new UnsupportedOperationException("This luminance source does not support rotation by 45 degrees."); - } - - @Override - public final String toString() { - byte[] row = new byte[width]; - StringBuilder result = new StringBuilder(height * (width + 1)); - for (int y = 0; y < height; y++) { - row = getRow(y, row); - for (int x = 0; x < width; x++) { - int luminance = row[x] & 0xFF; - char c; - if (luminance < 0x40) { - c = '#'; - } else if (luminance < 0x80) { - c = '+'; - } else if (luminance < 0xC0) { - c = '.'; - } else { - c = ' '; - } - result.append(c); - } - result.append('\n'); - } - return result.toString(); - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/NotFoundException.java b/TMessagesProj/src/main/java/com/google/zxing/NotFoundException.java deleted file mode 100755 index 863526af4..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/NotFoundException.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing; - -/** - * Thrown when a barcode was not found in the image. It might have been - * partially detected but could not be confirmed. - * - * @author Sean Owen - */ -public final class NotFoundException extends ReaderException { - - private static final NotFoundException INSTANCE = new NotFoundException(); - static { - INSTANCE.setStackTrace(NO_TRACE); // since it's meaningless - } - - private NotFoundException() { - // do nothing - } - - public static NotFoundException getNotFoundInstance() { - return INSTANCE; - } - -} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/com/google/zxing/PlanarYUVLuminanceSource.java b/TMessagesProj/src/main/java/com/google/zxing/PlanarYUVLuminanceSource.java deleted file mode 100755 index cecff3efa..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/PlanarYUVLuminanceSource.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2009 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing; - -/** - * This object extends LuminanceSource around an array of YUV data returned from the camera driver, - * with the option to crop to a rectangle within the full data. This can be used to exclude - * superfluous pixels around the perimeter and speed up decoding. - * - * It works for any pixel format where the Y channel is planar and appears first, including - * YCbCr_420_SP and YCbCr_422_SP. - * - * @author dswitkin@google.com (Daniel Switkin) - */ -public final class PlanarYUVLuminanceSource extends LuminanceSource { - - private static final int THUMBNAIL_SCALE_FACTOR = 2; - - private final byte[] yuvData; - private final int dataWidth; - private final int dataHeight; - private final int left; - private final int top; - - public PlanarYUVLuminanceSource(byte[] yuvData, - int dataWidth, - int dataHeight, - int left, - int top, - int width, - int height, - boolean reverseHorizontal) { - super(width, height); - - if (left + width > dataWidth || top + height > dataHeight) { - throw new IllegalArgumentException("Crop rectangle does not fit within image data."); - } - - this.yuvData = yuvData; - this.dataWidth = dataWidth; - this.dataHeight = dataHeight; - this.left = left; - this.top = top; - if (reverseHorizontal) { - reverseHorizontal(width, height); - } - } - - @Override - public byte[] getRow(int y, byte[] row) { - if (y < 0 || y >= getHeight()) { - throw new IllegalArgumentException("Requested row is outside the image: " + y); - } - int width = getWidth(); - if (row == null || row.length < width) { - row = new byte[width]; - } - int offset = (y + top) * dataWidth + left; - System.arraycopy(yuvData, offset, row, 0, width); - return row; - } - - @Override - public byte[] getMatrix() { - int width = getWidth(); - int height = getHeight(); - - // If the caller asks for the entire underlying image, save the copy and give them the - // original data. The docs specifically warn that result.length must be ignored. - if (width == dataWidth && height == dataHeight) { - return yuvData; - } - - int area = width * height; - byte[] matrix = new byte[area]; - int inputOffset = top * dataWidth + left; - - // If the width matches the full width of the underlying data, perform a single copy. - if (width == dataWidth) { - System.arraycopy(yuvData, inputOffset, matrix, 0, area); - return matrix; - } - - // Otherwise copy one cropped row at a time. - for (int y = 0; y < height; y++) { - int outputOffset = y * width; - System.arraycopy(yuvData, inputOffset, matrix, outputOffset, width); - inputOffset += dataWidth; - } - return matrix; - } - - @Override - public boolean isCropSupported() { - return true; - } - - @Override - public LuminanceSource crop(int left, int top, int width, int height) { - return new PlanarYUVLuminanceSource(yuvData, - dataWidth, - dataHeight, - this.left + left, - this.top + top, - width, - height, - false); - } - - public int[] renderThumbnail() { - int width = getWidth() / THUMBNAIL_SCALE_FACTOR; - int height = getHeight() / THUMBNAIL_SCALE_FACTOR; - int[] pixels = new int[width * height]; - byte[] yuv = yuvData; - int inputOffset = top * dataWidth + left; - - for (int y = 0; y < height; y++) { - int outputOffset = y * width; - for (int x = 0; x < width; x++) { - int grey = yuv[inputOffset + x * THUMBNAIL_SCALE_FACTOR] & 0xff; - pixels[outputOffset + x] = 0xFF000000 | (grey * 0x00010101); - } - inputOffset += dataWidth * THUMBNAIL_SCALE_FACTOR; - } - return pixels; - } - - /** - * @return width of image from {@link #renderThumbnail()} - */ - public int getThumbnailWidth() { - return getWidth() / THUMBNAIL_SCALE_FACTOR; - } - - /** - * @return height of image from {@link #renderThumbnail()} - */ - public int getThumbnailHeight() { - return getHeight() / THUMBNAIL_SCALE_FACTOR; - } - - private void reverseHorizontal(int width, int height) { - byte[] yuvData = this.yuvData; - for (int y = 0, rowStart = top * dataWidth + left; y < height; y++, rowStart += dataWidth) { - int middle = rowStart + width / 2; - for (int x1 = rowStart, x2 = rowStart + width - 1; x1 < middle; x1++, x2--) { - byte temp = yuvData[x1]; - yuvData[x1] = yuvData[x2]; - yuvData[x2] = temp; - } - } - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/RGBLuminanceSource.java b/TMessagesProj/src/main/java/com/google/zxing/RGBLuminanceSource.java deleted file mode 100755 index 3950ce8b8..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/RGBLuminanceSource.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2009 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing; - -/** - * This class is used to help decode images from files which arrive as RGB data from - * an ARGB pixel array. It does not support rotation. - * - * @author dswitkin@google.com (Daniel Switkin) - * @author Betaminos - */ -public final class RGBLuminanceSource extends LuminanceSource { - - private final byte[] luminances; - private final int dataWidth; - private final int dataHeight; - private final int left; - private final int top; - - public RGBLuminanceSource(int width, int height, int[] pixels) { - super(width, height); - - dataWidth = width; - dataHeight = height; - left = 0; - top = 0; - - // In order to measure pure decoding speed, we convert the entire image to a greyscale array - // up front, which is the same as the Y channel of the YUVLuminanceSource in the real app. - // - // Total number of pixels suffices, can ignore shape - int size = width * height; - luminances = new byte[size]; - for (int offset = 0; offset < size; offset++) { - int pixel = pixels[offset]; - int r = (pixel >> 16) & 0xff; // red - int g2 = (pixel >> 7) & 0x1fe; // 2 * green - int b = pixel & 0xff; // blue - // Calculate green-favouring average cheaply - luminances[offset] = (byte) ((r + g2 + b) / 4); - } - } - - private RGBLuminanceSource(byte[] pixels, - int dataWidth, - int dataHeight, - int left, - int top, - int width, - int height) { - super(width, height); - if (left + width > dataWidth || top + height > dataHeight) { - throw new IllegalArgumentException("Crop rectangle does not fit within image data."); - } - this.luminances = pixels; - this.dataWidth = dataWidth; - this.dataHeight = dataHeight; - this.left = left; - this.top = top; - } - - @Override - public byte[] getRow(int y, byte[] row) { - if (y < 0 || y >= getHeight()) { - throw new IllegalArgumentException("Requested row is outside the image: " + y); - } - int width = getWidth(); - if (row == null || row.length < width) { - row = new byte[width]; - } - int offset = (y + top) * dataWidth + left; - System.arraycopy(luminances, offset, row, 0, width); - return row; - } - - @Override - public byte[] getMatrix() { - int width = getWidth(); - int height = getHeight(); - - // If the caller asks for the entire underlying image, save the copy and give them the - // original data. The docs specifically warn that result.length must be ignored. - if (width == dataWidth && height == dataHeight) { - return luminances; - } - - int area = width * height; - byte[] matrix = new byte[area]; - int inputOffset = top * dataWidth + left; - - // If the width matches the full width of the underlying data, perform a single copy. - if (width == dataWidth) { - System.arraycopy(luminances, inputOffset, matrix, 0, area); - return matrix; - } - - // Otherwise copy one cropped row at a time. - for (int y = 0; y < height; y++) { - int outputOffset = y * width; - System.arraycopy(luminances, inputOffset, matrix, outputOffset, width); - inputOffset += dataWidth; - } - return matrix; - } - - @Override - public boolean isCropSupported() { - return true; - } - - @Override - public LuminanceSource crop(int left, int top, int width, int height) { - return new RGBLuminanceSource(luminances, - dataWidth, - dataHeight, - this.left + left, - this.top + top, - width, - height); - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/Reader.java b/TMessagesProj/src/main/java/com/google/zxing/Reader.java deleted file mode 100755 index 0e6af109e..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/Reader.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing; - -import java.util.Map; - -/** - * Implementations of this interface can decode an image of a barcode in some format into - * the String it encodes. For example, {@link com.google.zxing.qrcode.QRCodeReader} can - * decode a QR code. The decoder may optionally receive hints from the caller which may help - * it decode more quickly or accurately. - * - * See {@link MultiFormatReader}, which attempts to determine what barcode - * format is present within the image as well, and then decodes it accordingly. - * - * @author Sean Owen - * @author dswitkin@google.com (Daniel Switkin) - */ -public interface Reader { - - /** - * Locates and decodes a barcode in some format within an image. - * - * @param image image of barcode to decode - * @return String which the barcode encodes - * @throws NotFoundException if no potential barcode is found - * @throws ChecksumException if a potential barcode is found but does not pass its checksum - * @throws FormatException if a potential barcode is found but format is invalid - */ - Result decode(BinaryBitmap image) throws NotFoundException, ChecksumException, FormatException; - - /** - * Locates and decodes a barcode in some format within an image. This method also accepts - * hints, each possibly associated to some data, which may help the implementation decode. - * - * @param image image of barcode to decode - * @param hints passed as a {@link Map} from {@link DecodeHintType} - * to arbitrary data. The - * meaning of the data depends upon the hint type. The implementation may or may not do - * anything with these hints. - * @return String which the barcode encodes - * @throws NotFoundException if no potential barcode is found - * @throws ChecksumException if a potential barcode is found but does not pass its checksum - * @throws FormatException if a potential barcode is found but format is invalid - */ - Result decode(BinaryBitmap image, Map hints) - throws NotFoundException, ChecksumException, FormatException; - - /** - * Resets any internal state the implementation has after a decode, to prepare it - * for reuse. - */ - void reset(); - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/ReaderException.java b/TMessagesProj/src/main/java/com/google/zxing/ReaderException.java deleted file mode 100755 index 32bbcda9a..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/ReaderException.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing; - -/** - * The general exception class throw when something goes wrong during decoding of a barcode. - * This includes, but is not limited to, failing checksums / error correction algorithms, being - * unable to locate finder timing patterns, and so on. - * - * @author Sean Owen - */ -public abstract class ReaderException extends Exception { - - // disable stack traces when not running inside test units - protected static final boolean isStackTrace = - System.getProperty("surefire.test.class.path") != null; - protected static final StackTraceElement[] NO_TRACE = new StackTraceElement[0]; - - ReaderException() { - // do nothing - } - - ReaderException(Throwable cause) { - super(cause); - } - - // Prevent stack traces from being taken - @Override - public final synchronized Throwable fillInStackTrace() { - return null; - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/Result.java b/TMessagesProj/src/main/java/com/google/zxing/Result.java deleted file mode 100755 index 3df435f2a..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/Result.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing; - -import java.util.EnumMap; -import java.util.Map; - -/** - *

Encapsulates the result of decoding a barcode within an image.

- * - * @author Sean Owen - */ -public final class Result { - - private final String text; - private final byte[] rawBytes; - private final int numBits; - private ResultPoint[] resultPoints; - private final BarcodeFormat format; - private Map resultMetadata; - private final long timestamp; - - public Result(String text, - byte[] rawBytes, - ResultPoint[] resultPoints, - BarcodeFormat format) { - this(text, rawBytes, resultPoints, format, System.currentTimeMillis()); - } - - public Result(String text, - byte[] rawBytes, - ResultPoint[] resultPoints, - BarcodeFormat format, - long timestamp) { - this(text, rawBytes, rawBytes == null ? 0 : 8 * rawBytes.length, - resultPoints, format, timestamp); - } - - public Result(String text, - byte[] rawBytes, - int numBits, - ResultPoint[] resultPoints, - BarcodeFormat format, - long timestamp) { - this.text = text; - this.rawBytes = rawBytes; - this.numBits = numBits; - this.resultPoints = resultPoints; - this.format = format; - this.resultMetadata = null; - this.timestamp = timestamp; - } - - /** - * @return raw text encoded by the barcode - */ - public String getText() { - return text; - } - - /** - * @return raw bytes encoded by the barcode, if applicable, otherwise {@code null} - */ - public byte[] getRawBytes() { - return rawBytes; - } - - /** - * @return how many bits of {@link #getRawBytes()} are valid; typically 8 times its length - * @since 3.3.0 - */ - public int getNumBits() { - return numBits; - } - - /** - * @return points related to the barcode in the image. These are typically points - * identifying finder patterns or the corners of the barcode. The exact meaning is - * specific to the type of barcode that was decoded. - */ - public ResultPoint[] getResultPoints() { - return resultPoints; - } - - /** - * @return {@link BarcodeFormat} representing the format of the barcode that was decoded - */ - public BarcodeFormat getBarcodeFormat() { - return format; - } - - /** - * @return {@link Map} mapping {@link ResultMetadataType} keys to values. May be - * {@code null}. This contains optional metadata about what was detected about the barcode, - * like orientation. - */ - public Map getResultMetadata() { - return resultMetadata; - } - - public void putMetadata(ResultMetadataType type, Object value) { - if (resultMetadata == null) { - resultMetadata = new EnumMap<>(ResultMetadataType.class); - } - resultMetadata.put(type, value); - } - - public void putAllMetadata(Map metadata) { - if (metadata != null) { - if (resultMetadata == null) { - resultMetadata = metadata; - } else { - resultMetadata.putAll(metadata); - } - } - } - - public void addResultPoints(ResultPoint[] newPoints) { - ResultPoint[] oldPoints = resultPoints; - if (oldPoints == null) { - resultPoints = newPoints; - } else if (newPoints != null && newPoints.length > 0) { - ResultPoint[] allPoints = new ResultPoint[oldPoints.length + newPoints.length]; - System.arraycopy(oldPoints, 0, allPoints, 0, oldPoints.length); - System.arraycopy(newPoints, 0, allPoints, oldPoints.length, newPoints.length); - resultPoints = allPoints; - } - } - - public long getTimestamp() { - return timestamp; - } - - @Override - public String toString() { - return text; - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/ResultMetadataType.java b/TMessagesProj/src/main/java/com/google/zxing/ResultMetadataType.java deleted file mode 100755 index 67c5363a7..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/ResultMetadataType.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2008 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing; - -/** - * Represents some type of metadata about the result of the decoding that the decoder - * wishes to communicate back to the caller. - * - * @author Sean Owen - */ -public enum ResultMetadataType { - - /** - * Unspecified, application-specific metadata. Maps to an unspecified {@link Object}. - */ - OTHER, - - /** - * Denotes the likely approximate orientation of the barcode in the image. This value - * is given as degrees rotated clockwise from the normal, upright orientation. - * For example a 1D barcode which was found by reading top-to-bottom would be - * said to have orientation "90". This key maps to an {@link Integer} whose - * value is in the range [0,360). - */ - ORIENTATION, - - /** - *

2D barcode formats typically encode text, but allow for a sort of 'byte mode' - * which is sometimes used to encode binary data. While {@link Result} makes available - * the complete raw bytes in the barcode for these formats, it does not offer the bytes - * from the byte segments alone.

- * - *

This maps to a {@link java.util.List} of byte arrays corresponding to the - * raw bytes in the byte segments in the barcode, in order.

- */ - BYTE_SEGMENTS, - - /** - * Error correction level used, if applicable. The value type depends on the - * format, but is typically a String. - */ - ERROR_CORRECTION_LEVEL, - - /** - * For some periodicals, indicates the issue number as an {@link Integer}. - */ - ISSUE_NUMBER, - - /** - * For some products, indicates the suggested retail price in the barcode as a - * formatted {@link String}. - */ - SUGGESTED_PRICE, - - /** - * For some products, the possible country of manufacture as a {@link String} denoting the - * ISO country code. Some map to multiple possible countries, like "US/CA". - */ - POSSIBLE_COUNTRY, - - /** - * For some products, the extension text - */ - UPC_EAN_EXTENSION, - - /** - * PDF417-specific metadata - */ - PDF417_EXTRA_METADATA, - - /** - * If the code format supports structured append and the current scanned code is part of one then the - * sequence number is given with it. - */ - STRUCTURED_APPEND_SEQUENCE, - - /** - * If the code format supports structured append and the current scanned code is part of one then the - * parity is given with it. - */ - STRUCTURED_APPEND_PARITY, - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/ResultPoint.java b/TMessagesProj/src/main/java/com/google/zxing/ResultPoint.java deleted file mode 100755 index 9bd8cd2e6..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/ResultPoint.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing; - -import com.google.zxing.common.detector.MathUtils; - -/** - *

Encapsulates a point of interest in an image containing a barcode. Typically, this - * would be the location of a finder pattern or the corner of the barcode, for example.

- * - * @author Sean Owen - */ -public class ResultPoint { - - private final float x; - private final float y; - - public ResultPoint(float x, float y) { - this.x = x; - this.y = y; - } - - public final float getX() { - return x; - } - - public final float getY() { - return y; - } - - @Override - public final boolean equals(Object other) { - if (other instanceof ResultPoint) { - ResultPoint otherPoint = (ResultPoint) other; - return x == otherPoint.x && y == otherPoint.y; - } - return false; - } - - @Override - public final int hashCode() { - return 31 * Float.floatToIntBits(x) + Float.floatToIntBits(y); - } - - @Override - public final String toString() { - return "(" + x + ',' + y + ')'; - } - - /** - * Orders an array of three ResultPoints in an order [A,B,C] such that AB is less than AC - * and BC is less than AC, and the angle between BC and BA is less than 180 degrees. - * - * @param patterns array of three {@code ResultPoint} to order - */ - public static void orderBestPatterns(ResultPoint[] patterns) { - - // Find distances between pattern centers - float zeroOneDistance = distance(patterns[0], patterns[1]); - float oneTwoDistance = distance(patterns[1], patterns[2]); - float zeroTwoDistance = distance(patterns[0], patterns[2]); - - ResultPoint pointA; - ResultPoint pointB; - ResultPoint pointC; - // Assume one closest to other two is B; A and C will just be guesses at first - if (oneTwoDistance >= zeroOneDistance && oneTwoDistance >= zeroTwoDistance) { - pointB = patterns[0]; - pointA = patterns[1]; - pointC = patterns[2]; - } else if (zeroTwoDistance >= oneTwoDistance && zeroTwoDistance >= zeroOneDistance) { - pointB = patterns[1]; - pointA = patterns[0]; - pointC = patterns[2]; - } else { - pointB = patterns[2]; - pointA = patterns[0]; - pointC = patterns[1]; - } - - // Use cross product to figure out whether A and C are correct or flipped. - // This asks whether BC x BA has a positive z component, which is the arrangement - // we want for A, B, C. If it's negative, then we've got it flipped around and - // should swap A and C. - if (crossProductZ(pointA, pointB, pointC) < 0.0f) { - ResultPoint temp = pointA; - pointA = pointC; - pointC = temp; - } - - patterns[0] = pointA; - patterns[1] = pointB; - patterns[2] = pointC; - } - - /** - * @param pattern1 first pattern - * @param pattern2 second pattern - * @return distance between two points - */ - public static float distance(ResultPoint pattern1, ResultPoint pattern2) { - return MathUtils.distance(pattern1.x, pattern1.y, pattern2.x, pattern2.y); - } - - /** - * Returns the z component of the cross product between vectors BC and BA. - */ - private static float crossProductZ(ResultPoint pointA, - ResultPoint pointB, - ResultPoint pointC) { - float bX = pointB.x; - float bY = pointB.y; - return ((pointC.x - bX) * (pointA.y - bY)) - ((pointC.y - bY) * (pointA.x - bX)); - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/ResultPointCallback.java b/TMessagesProj/src/main/java/com/google/zxing/ResultPointCallback.java deleted file mode 100755 index 0c85410bc..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/ResultPointCallback.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2009 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing; - -/** - * Callback which is invoked when a possible result point (significant - * point in the barcode image such as a corner) is found. - * - * @see DecodeHintType#NEED_RESULT_POINT_CALLBACK - */ -public interface ResultPointCallback { - - void foundPossibleResultPoint(ResultPoint point); - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/WriterException.java b/TMessagesProj/src/main/java/com/google/zxing/WriterException.java deleted file mode 100755 index d2a37bc52..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/WriterException.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2008 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing; - -/** - * A base class which covers the range of exceptions which may occur when encoding a barcode using - * the Writer framework. - * - * @author dswitkin@google.com (Daniel Switkin) - */ -public final class WriterException extends Exception { - - public WriterException() { - } - - public WriterException(String message) { - super(message); - } - - public WriterException(Throwable cause) { - super(cause); - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/common/BitArray.java b/TMessagesProj/src/main/java/com/google/zxing/common/BitArray.java deleted file mode 100755 index 19f0c99fd..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/common/BitArray.java +++ /dev/null @@ -1,357 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.common; - -import java.util.Arrays; - -/** - *

A simple, fast array of bits, represented compactly by an array of ints internally.

- * - * @author Sean Owen - */ -public final class BitArray implements Cloneable { - - private int[] bits; - private int size; - - public BitArray() { - this.size = 0; - this.bits = new int[1]; - } - - public BitArray(int size) { - this.size = size; - this.bits = makeArray(size); - } - - // For testing only - BitArray(int[] bits, int size) { - this.bits = bits; - this.size = size; - } - - public int getSize() { - return size; - } - - public int getSizeInBytes() { - return (size + 7) / 8; - } - - private void ensureCapacity(int size) { - if (size > bits.length * 32) { - int[] newBits = makeArray(size); - System.arraycopy(bits, 0, newBits, 0, bits.length); - this.bits = newBits; - } - } - - /** - * @param i bit to get - * @return true iff bit i is set - */ - public boolean get(int i) { - return (bits[i / 32] & (1 << (i & 0x1F))) != 0; - } - - /** - * Sets bit i. - * - * @param i bit to set - */ - public void set(int i) { - bits[i / 32] |= 1 << (i & 0x1F); - } - - /** - * Flips bit i. - * - * @param i bit to set - */ - public void flip(int i) { - bits[i / 32] ^= 1 << (i & 0x1F); - } - - /** - * @param from first bit to check - * @return index of first bit that is set, starting from the given index, or size if none are set - * at or beyond this given index - * @see #getNextUnset(int) - */ - public int getNextSet(int from) { - if (from >= size) { - return size; - } - int bitsOffset = from / 32; - int currentBits = bits[bitsOffset]; - // mask off lesser bits first - currentBits &= -(1 << (from & 0x1F)); - while (currentBits == 0) { - if (++bitsOffset == bits.length) { - return size; - } - currentBits = bits[bitsOffset]; - } - int result = (bitsOffset * 32) + Integer.numberOfTrailingZeros(currentBits); - return Math.min(result, size); - } - - /** - * @param from index to start looking for unset bit - * @return index of next unset bit, or {@code size} if none are unset until the end - * @see #getNextSet(int) - */ - public int getNextUnset(int from) { - if (from >= size) { - return size; - } - int bitsOffset = from / 32; - int currentBits = ~bits[bitsOffset]; - // mask off lesser bits first - currentBits &= -(1 << (from & 0x1F)); - while (currentBits == 0) { - if (++bitsOffset == bits.length) { - return size; - } - currentBits = ~bits[bitsOffset]; - } - int result = (bitsOffset * 32) + Integer.numberOfTrailingZeros(currentBits); - return Math.min(result, size); - } - - /** - * Sets a block of 32 bits, starting at bit i. - * - * @param i first bit to set - * @param newBits the new value of the next 32 bits. Note again that the least-significant bit - * corresponds to bit i, the next-least-significant to i+1, and so on. - */ - public void setBulk(int i, int newBits) { - bits[i / 32] = newBits; - } - - /** - * Sets a range of bits. - * - * @param start start of range, inclusive. - * @param end end of range, exclusive - */ - public void setRange(int start, int end) { - if (end < start || start < 0 || end > size) { - throw new IllegalArgumentException(); - } - if (end == start) { - return; - } - end--; // will be easier to treat this as the last actually set bit -- inclusive - int firstInt = start / 32; - int lastInt = end / 32; - for (int i = firstInt; i <= lastInt; i++) { - int firstBit = i > firstInt ? 0 : start & 0x1F; - int lastBit = i < lastInt ? 31 : end & 0x1F; - // Ones from firstBit to lastBit, inclusive - int mask = (2 << lastBit) - (1 << firstBit); - bits[i] |= mask; - } - } - - /** - * Clears all bits (sets to false). - */ - public void clear() { - int max = bits.length; - for (int i = 0; i < max; i++) { - bits[i] = 0; - } - } - - /** - * Efficient method to check if a range of bits is set, or not set. - * - * @param start start of range, inclusive. - * @param end end of range, exclusive - * @param value if true, checks that bits in range are set, otherwise checks that they are not set - * @return true iff all bits are set or not set in range, according to value argument - * @throws IllegalArgumentException if end is less than start or the range is not contained in the array - */ - public boolean isRange(int start, int end, boolean value) { - if (end < start || start < 0 || end > size) { - throw new IllegalArgumentException(); - } - if (end == start) { - return true; // empty range matches - } - end--; // will be easier to treat this as the last actually set bit -- inclusive - int firstInt = start / 32; - int lastInt = end / 32; - for (int i = firstInt; i <= lastInt; i++) { - int firstBit = i > firstInt ? 0 : start & 0x1F; - int lastBit = i < lastInt ? 31 : end & 0x1F; - // Ones from firstBit to lastBit, inclusive - int mask = (2 << lastBit) - (1 << firstBit); - - // Return false if we're looking for 1s and the masked bits[i] isn't all 1s (that is, - // equals the mask, or we're looking for 0s and the masked portion is not all 0s - if ((bits[i] & mask) != (value ? mask : 0)) { - return false; - } - } - return true; - } - - public void appendBit(boolean bit) { - ensureCapacity(size + 1); - if (bit) { - bits[size / 32] |= 1 << (size & 0x1F); - } - size++; - } - - /** - * Appends the least-significant bits, from value, in order from most-significant to - * least-significant. For example, appending 6 bits from 0x000001E will append the bits - * 0, 1, 1, 1, 1, 0 in that order. - * - * @param value {@code int} containing bits to append - * @param numBits bits from value to append - */ - public void appendBits(int value, int numBits) { - if (numBits < 0 || numBits > 32) { - throw new IllegalArgumentException("Num bits must be between 0 and 32"); - } - ensureCapacity(size + numBits); - for (int numBitsLeft = numBits; numBitsLeft > 0; numBitsLeft--) { - appendBit(((value >> (numBitsLeft - 1)) & 0x01) == 1); - } - } - - public void appendBitArray(BitArray other) { - int otherSize = other.size; - ensureCapacity(size + otherSize); - for (int i = 0; i < otherSize; i++) { - appendBit(other.get(i)); - } - } - - public void xor(BitArray other) { - if (size != other.size) { - throw new IllegalArgumentException("Sizes don't match"); - } - for (int i = 0; i < bits.length; i++) { - // The last int could be incomplete (i.e. not have 32 bits in - // it) but there is no problem since 0 XOR 0 == 0. - bits[i] ^= other.bits[i]; - } - } - - /** - * - * @param bitOffset first bit to start writing - * @param array array to write into. Bytes are written most-significant byte first. This is the opposite - * of the internal representation, which is exposed by {@link #getBitArray()} - * @param offset position in array to start writing - * @param numBytes how many bytes to write - */ - public void toBytes(int bitOffset, byte[] array, int offset, int numBytes) { - for (int i = 0; i < numBytes; i++) { - int theByte = 0; - for (int j = 0; j < 8; j++) { - if (get(bitOffset)) { - theByte |= 1 << (7 - j); - } - bitOffset++; - } - array[offset + i] = (byte) theByte; - } - } - - /** - * @return underlying array of ints. The first element holds the first 32 bits, and the least - * significant bit is bit 0. - */ - public int[] getBitArray() { - return bits; - } - - /** - * Reverses all bits in the array. - */ - public void reverse() { - int[] newBits = new int[bits.length]; - // reverse all int's first - int len = (size - 1) / 32; - int oldBitsLen = len + 1; - for (int i = 0; i < oldBitsLen; i++) { - long x = bits[i]; - x = ((x >> 1) & 0x55555555L) | ((x & 0x55555555L) << 1); - x = ((x >> 2) & 0x33333333L) | ((x & 0x33333333L) << 2); - x = ((x >> 4) & 0x0f0f0f0fL) | ((x & 0x0f0f0f0fL) << 4); - x = ((x >> 8) & 0x00ff00ffL) | ((x & 0x00ff00ffL) << 8); - x = ((x >> 16) & 0x0000ffffL) | ((x & 0x0000ffffL) << 16); - newBits[len - i] = (int) x; - } - // now correct the int's if the bit size isn't a multiple of 32 - if (size != oldBitsLen * 32) { - int leftOffset = oldBitsLen * 32 - size; - int currentInt = newBits[0] >>> leftOffset; - for (int i = 1; i < oldBitsLen; i++) { - int nextInt = newBits[i]; - currentInt |= nextInt << (32 - leftOffset); - newBits[i - 1] = currentInt; - currentInt = nextInt >>> leftOffset; - } - newBits[oldBitsLen - 1] = currentInt; - } - bits = newBits; - } - - private static int[] makeArray(int size) { - return new int[(size + 31) / 32]; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof BitArray)) { - return false; - } - BitArray other = (BitArray) o; - return size == other.size && Arrays.equals(bits, other.bits); - } - - @Override - public int hashCode() { - return 31 * size + Arrays.hashCode(bits); - } - - @Override - public String toString() { - StringBuilder result = new StringBuilder(size + (size / 8) + 1); - for (int i = 0; i < size; i++) { - if ((i & 0x07) == 0) { - result.append(' '); - } - result.append(get(i) ? 'X' : '.'); - } - return result.toString(); - } - - @Override - public BitArray clone() { - return new BitArray(bits.clone(), size); - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/common/BitMatrix.java b/TMessagesProj/src/main/java/com/google/zxing/common/BitMatrix.java deleted file mode 100755 index fa38120e0..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/common/BitMatrix.java +++ /dev/null @@ -1,488 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.common; - -import java.util.Arrays; - -/** - *

Represents a 2D matrix of bits. In function arguments below, and throughout the common - * module, x is the column position, and y is the row position. The ordering is always x, y. - * The origin is at the top-left.

- * - *

Internally the bits are represented in a 1-D array of 32-bit ints. However, each row begins - * with a new int. This is done intentionally so that we can copy out a row into a BitArray very - * efficiently.

- * - *

The ordering of bits is row-major. Within each int, the least significant bits are used first, - * meaning they represent lower x values. This is compatible with BitArray's implementation.

- * - * @author Sean Owen - * @author dswitkin@google.com (Daniel Switkin) - */ -public final class BitMatrix implements Cloneable { - - private final int width; - private final int height; - private final int rowSize; - private final int[] bits; - private final int multiple; - - /** - * Creates an empty square {@code BitMatrix}. - * - * @param dimension height and width - */ - public BitMatrix(int dimension) { - this(dimension, dimension, 1); - } - - /** - * Creates an empty {@code BitMatrix}. - * - * @param width bit matrix width - * @param height bit matrix height - */ - public BitMatrix(int width, int height, int multiple) { - if (width < 1 || height < 1) { - throw new IllegalArgumentException("Both dimensions must be greater than 0"); - } - this.width = width; - this.height = height; - this.multiple = multiple; - this.rowSize = (width + 31) / 32; - bits = new int[rowSize * height]; - } - - private BitMatrix(int width, int height, int rowSize, int[] bits) { - this.width = width; - this.height = height; - this.rowSize = rowSize; - this.bits = bits; - this.multiple = 1; - } - - /** - * Interprets a 2D array of booleans as a {@code BitMatrix}, where "true" means an "on" bit. - * - * @param image bits of the image, as a row-major 2D array. Elements are arrays representing rows - * @return {@code BitMatrix} representation of image - */ - public static BitMatrix parse(boolean[][] image) { - int height = image.length; - int width = image[0].length; - BitMatrix bits = new BitMatrix(width, height, 1); - for (int i = 0; i < height; i++) { - boolean[] imageI = image[i]; - for (int j = 0; j < width; j++) { - if (imageI[j]) { - bits.set(j, i); - } - } - } - return bits; - } - - public static BitMatrix parse(String stringRepresentation, String setString, String unsetString) { - if (stringRepresentation == null) { - throw new IllegalArgumentException(); - } - - boolean[] bits = new boolean[stringRepresentation.length()]; - int bitsPos = 0; - int rowStartPos = 0; - int rowLength = -1; - int nRows = 0; - int pos = 0; - while (pos < stringRepresentation.length()) { - if (stringRepresentation.charAt(pos) == '\n' || - stringRepresentation.charAt(pos) == '\r') { - if (bitsPos > rowStartPos) { - if (rowLength == -1) { - rowLength = bitsPos - rowStartPos; - } else if (bitsPos - rowStartPos != rowLength) { - throw new IllegalArgumentException("row lengths do not match"); - } - rowStartPos = bitsPos; - nRows++; - } - pos++; - } else if (stringRepresentation.substring(pos, pos + setString.length()).equals(setString)) { - pos += setString.length(); - bits[bitsPos] = true; - bitsPos++; - } else if (stringRepresentation.substring(pos, pos + unsetString.length()).equals(unsetString)) { - pos += unsetString.length(); - bits[bitsPos] = false; - bitsPos++; - } else { - throw new IllegalArgumentException( - "illegal character encountered: " + stringRepresentation.substring(pos)); - } - } - - // no EOL at end? - if (bitsPos > rowStartPos) { - if (rowLength == -1) { - rowLength = bitsPos - rowStartPos; - } else if (bitsPos - rowStartPos != rowLength) { - throw new IllegalArgumentException("row lengths do not match"); - } - nRows++; - } - - BitMatrix matrix = new BitMatrix(rowLength, nRows, 1); - for (int i = 0; i < bitsPos; i++) { - if (bits[i]) { - matrix.set(i % rowLength, i / rowLength); - } - } - return matrix; - } - - /** - *

Gets the requested bit, where true means black.

- * - * @param x The horizontal component (i.e. which column) - * @param y The vertical component (i.e. which row) - * @return value of given bit in matrix - */ - public boolean get(int x, int y) { - int offset = y * rowSize + (x / 32); - return ((bits[offset] >>> (x & 0x1f)) & 1) != 0; - } - - /** - *

Sets the given bit to true.

- * - * @param x The horizontal component (i.e. which column) - * @param y The vertical component (i.e. which row) - */ - public void set(int x, int y) { - int offset = y * rowSize + (x / 32); - bits[offset] |= 1 << (x & 0x1f); - } - - public void unset(int x, int y) { - int offset = y * rowSize + (x / 32); - bits[offset] &= ~(1 << (x & 0x1f)); - } - - /** - *

Flips the given bit.

- * - * @param x The horizontal component (i.e. which column) - * @param y The vertical component (i.e. which row) - */ - public void flip(int x, int y) { - int offset = y * rowSize + (x / 32); - bits[offset] ^= 1 << (x & 0x1f); - } - - /** - * Exclusive-or (XOR): Flip the bit in this {@code BitMatrix} if the corresponding - * mask bit is set. - * - * @param mask XOR mask - */ - public void xor(BitMatrix mask) { - if (width != mask.getWidth() || height != mask.getHeight() - || rowSize != mask.getRowSize()) { - throw new IllegalArgumentException("input matrix dimensions do not match"); - } - BitArray rowArray = new BitArray(width); - for (int y = 0; y < height; y++) { - int offset = y * rowSize; - int[] row = mask.getRow(y, rowArray).getBitArray(); - for (int x = 0; x < rowSize; x++) { - bits[offset + x] ^= row[x]; - } - } - } - - /** - * Clears all bits (sets to false). - */ - public void clear() { - int max = bits.length; - for (int i = 0; i < max; i++) { - bits[i] = 0; - } - } - - /** - *

Sets a square region of the bit matrix to true.

- * - * @param left The horizontal position to begin at (inclusive) - * @param top The vertical position to begin at (inclusive) - * @param width The width of the region - * @param height The height of the region - */ - public void setRegion(int left, int top, int width, int height) { - if (top < 0 || left < 0) { - throw new IllegalArgumentException("Left and top must be nonnegative"); - } - if (height < 1 || width < 1) { - throw new IllegalArgumentException("Height and width must be at least 1"); - } - int right = left + width; - int bottom = top + height; - if (bottom > this.height || right > this.width) { - throw new IllegalArgumentException("The region must fit inside the matrix"); - } - for (int y = top; y < bottom; y++) { - int offset = y * rowSize; - for (int x = left; x < right; x++) { - bits[offset + (x / 32)] |= 1 << (x & 0x1f); - } - } - } - - /** - * A fast method to retrieve one row of data from the matrix as a BitArray. - * - * @param y The row to retrieve - * @param row An optional caller-allocated BitArray, will be allocated if null or too small - * @return The resulting BitArray - this reference should always be used even when passing - * your own row - */ - public BitArray getRow(int y, BitArray row) { - if (row == null || row.getSize() < width) { - row = new BitArray(width); - } else { - row.clear(); - } - int offset = y * rowSize; - for (int x = 0; x < rowSize; x++) { - row.setBulk(x * 32, bits[offset + x]); - } - return row; - } - - /** - * @param y row to set - * @param row {@link BitArray} to copy from - */ - public void setRow(int y, BitArray row) { - System.arraycopy(row.getBitArray(), 0, bits, y * rowSize, rowSize); - } - - /** - * Modifies this {@code BitMatrix} to represent the same but rotated 180 degrees - */ - public void rotate180() { - int width = getWidth(); - int height = getHeight(); - BitArray topRow = new BitArray(width); - BitArray bottomRow = new BitArray(width); - for (int i = 0; i < (height + 1) / 2; i++) { - topRow = getRow(i, topRow); - bottomRow = getRow(height - 1 - i, bottomRow); - topRow.reverse(); - bottomRow.reverse(); - setRow(i, bottomRow); - setRow(height - 1 - i, topRow); - } - } - - /** - * This is useful in detecting the enclosing rectangle of a 'pure' barcode. - * - * @return {@code left,top,width,height} enclosing rectangle of all 1 bits, or null if it is all white - */ - public int[] getEnclosingRectangle() { - int left = width; - int top = height; - int right = -1; - int bottom = -1; - - for (int y = 0; y < height; y++) { - for (int x32 = 0; x32 < rowSize; x32++) { - int theBits = bits[y * rowSize + x32]; - if (theBits != 0) { - if (y < top) { - top = y; - } - if (y > bottom) { - bottom = y; - } - if (x32 * 32 < left) { - int bit = 0; - while ((theBits << (31 - bit)) == 0) { - bit++; - } - if ((x32 * 32 + bit) < left) { - left = x32 * 32 + bit; - } - } - if (x32 * 32 + 31 > right) { - int bit = 31; - while ((theBits >>> bit) == 0) { - bit--; - } - if ((x32 * 32 + bit) > right) { - right = x32 * 32 + bit; - } - } - } - } - } - - if (right < left || bottom < top) { - return null; - } - - return new int[] {left, top, right - left + 1, bottom - top + 1}; - } - - /** - * This is useful in detecting a corner of a 'pure' barcode. - * - * @return {@code x,y} coordinate of top-left-most 1 bit, or null if it is all white - */ - public int[] getTopLeftOnBit() { - int bitsOffset = 0; - while (bitsOffset < bits.length && bits[bitsOffset] == 0) { - bitsOffset++; - } - if (bitsOffset == bits.length) { - return null; - } - int y = bitsOffset / rowSize; - int x = (bitsOffset % rowSize) * 32; - - int theBits = bits[bitsOffset]; - int bit = 0; - while ((theBits << (31 - bit)) == 0) { - bit++; - } - x += bit; - return new int[] {x, y}; - } - - public int[] getBottomRightOnBit() { - int bitsOffset = bits.length - 1; - while (bitsOffset >= 0 && bits[bitsOffset] == 0) { - bitsOffset--; - } - if (bitsOffset < 0) { - return null; - } - - int y = bitsOffset / rowSize; - int x = (bitsOffset % rowSize) * 32; - - int theBits = bits[bitsOffset]; - int bit = 31; - while ((theBits >>> bit) == 0) { - bit--; - } - x += bit; - - return new int[] {x, y}; - } - - /** - * @return The width of the matrix - */ - public int getWidth() { - return width; - } - - /** - * @return The height of the matrix - */ - public int getHeight() { - return height; - } - - public int getMultiple() { - return multiple; - } - - /** - * @return The row size of the matrix - */ - public int getRowSize() { - return rowSize; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof BitMatrix)) { - return false; - } - BitMatrix other = (BitMatrix) o; - return width == other.width && height == other.height && rowSize == other.rowSize && - Arrays.equals(bits, other.bits); - } - - @Override - public int hashCode() { - int hash = width; - hash = 31 * hash + width; - hash = 31 * hash + height; - hash = 31 * hash + rowSize; - hash = 31 * hash + Arrays.hashCode(bits); - return hash; - } - - /** - * @return string representation using "X" for set and " " for unset bits - */ - @Override - public String toString() { - return toString("X ", " "); - } - - /** - * @param setString representation of a set bit - * @param unsetString representation of an unset bit - * @return string representation of entire matrix utilizing given strings - */ - public String toString(String setString, String unsetString) { - return buildToString(setString, unsetString, "\n"); - } - - /** - * @param setString representation of a set bit - * @param unsetString representation of an unset bit - * @param lineSeparator newline character in string representation - * @return string representation of entire matrix utilizing given strings and line separator - * @deprecated call {@link #toString(String,String)} only, which uses \n line separator always - */ - @Deprecated - public String toString(String setString, String unsetString, String lineSeparator) { - return buildToString(setString, unsetString, lineSeparator); - } - - private String buildToString(String setString, String unsetString, String lineSeparator) { - StringBuilder result = new StringBuilder(height * (width + 1)); - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - result.append(get(x, y) ? setString : unsetString); - } - result.append(lineSeparator); - } - return result.toString(); - } - - @Override - public BitMatrix clone() { - return new BitMatrix(width, height, rowSize, bits.clone()); - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/common/BitSource.java b/TMessagesProj/src/main/java/com/google/zxing/common/BitSource.java deleted file mode 100755 index b4d0aafbf..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/common/BitSource.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.common; - -/** - *

This provides an easy abstraction to read bits at a time from a sequence of bytes, where the - * number of bits read is not often a multiple of 8.

- * - *

This class is thread-safe but not reentrant -- unless the caller modifies the bytes array - * it passed in, in which case all bets are off.

- * - * @author Sean Owen - */ -public final class BitSource { - - private final byte[] bytes; - private int byteOffset; - private int bitOffset; - - /** - * @param bytes bytes from which this will read bits. Bits will be read from the first byte first. - * Bits are read within a byte from most-significant to least-significant bit. - */ - public BitSource(byte[] bytes) { - this.bytes = bytes; - } - - /** - * @return index of next bit in current byte which would be read by the next call to {@link #readBits(int)}. - */ - public int getBitOffset() { - return bitOffset; - } - - /** - * @return index of next byte in input byte array which would be read by the next call to {@link #readBits(int)}. - */ - public int getByteOffset() { - return byteOffset; - } - - /** - * @param numBits number of bits to read - * @return int representing the bits read. The bits will appear as the least-significant - * bits of the int - * @throws IllegalArgumentException if numBits isn't in [1,32] or more than is available - */ - public int readBits(int numBits) { - if (numBits < 1 || numBits > 32 || numBits > available()) { - throw new IllegalArgumentException(String.valueOf(numBits)); - } - - int result = 0; - - // First, read remainder from current byte - if (bitOffset > 0) { - int bitsLeft = 8 - bitOffset; - int toRead = Math.min(numBits, bitsLeft); - int bitsToNotRead = bitsLeft - toRead; - int mask = (0xFF >> (8 - toRead)) << bitsToNotRead; - result = (bytes[byteOffset] & mask) >> bitsToNotRead; - numBits -= toRead; - bitOffset += toRead; - if (bitOffset == 8) { - bitOffset = 0; - byteOffset++; - } - } - - // Next read whole bytes - if (numBits > 0) { - while (numBits >= 8) { - result = (result << 8) | (bytes[byteOffset] & 0xFF); - byteOffset++; - numBits -= 8; - } - - // Finally read a partial byte - if (numBits > 0) { - int bitsToNotRead = 8 - numBits; - int mask = (0xFF >> bitsToNotRead) << bitsToNotRead; - result = (result << numBits) | ((bytes[byteOffset] & mask) >> bitsToNotRead); - bitOffset += numBits; - } - } - - return result; - } - - /** - * @return number of bits that can be read successfully - */ - public int available() { - return 8 * (bytes.length - byteOffset) - bitOffset; - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/common/CharacterSetECI.java b/TMessagesProj/src/main/java/com/google/zxing/common/CharacterSetECI.java deleted file mode 100755 index 4be90c234..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/common/CharacterSetECI.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2008 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.common; - -import com.google.zxing.FormatException; - -import java.util.HashMap; -import java.util.Map; - -/** - * Encapsulates a Character Set ECI, according to "Extended Channel Interpretations" 5.3.1.1 - * of ISO 18004. - * - * @author Sean Owen - */ -public enum CharacterSetECI { - - // Enum name is a Java encoding valid for java.lang and java.io - Cp437(new int[]{0,2}), - ISO8859_1(new int[]{1,3}, "ISO-8859-1"), - ISO8859_2(4, "ISO-8859-2"), - ISO8859_3(5, "ISO-8859-3"), - ISO8859_4(6, "ISO-8859-4"), - ISO8859_5(7, "ISO-8859-5"), - ISO8859_6(8, "ISO-8859-6"), - ISO8859_7(9, "ISO-8859-7"), - ISO8859_8(10, "ISO-8859-8"), - ISO8859_9(11, "ISO-8859-9"), - ISO8859_10(12, "ISO-8859-10"), - ISO8859_11(13, "ISO-8859-11"), - ISO8859_13(15, "ISO-8859-13"), - ISO8859_14(16, "ISO-8859-14"), - ISO8859_15(17, "ISO-8859-15"), - ISO8859_16(18, "ISO-8859-16"), - SJIS(20, "Shift_JIS"), - Cp1250(21, "windows-1250"), - Cp1251(22, "windows-1251"), - Cp1252(23, "windows-1252"), - Cp1256(24, "windows-1256"), - UnicodeBigUnmarked(25, "UTF-16BE", "UnicodeBig"), - UTF8(26, "UTF-8"), - ASCII(new int[] {27, 170}, "US-ASCII"), - Big5(28), - GB18030(29, "GB2312", "EUC_CN", "GBK"), - EUC_KR(30, "EUC-KR"); - - private static final Map VALUE_TO_ECI = new HashMap<>(); - private static final Map NAME_TO_ECI = new HashMap<>(); - static { - for (CharacterSetECI eci : values()) { - for (int value : eci.values) { - VALUE_TO_ECI.put(value, eci); - } - NAME_TO_ECI.put(eci.name(), eci); - for (String name : eci.otherEncodingNames) { - NAME_TO_ECI.put(name, eci); - } - } - } - - private final int[] values; - private final String[] otherEncodingNames; - - CharacterSetECI(int value) { - this(new int[] {value}); - } - - CharacterSetECI(int value, String... otherEncodingNames) { - this.values = new int[] {value}; - this.otherEncodingNames = otherEncodingNames; - } - - CharacterSetECI(int[] values, String... otherEncodingNames) { - this.values = values; - this.otherEncodingNames = otherEncodingNames; - } - - public int getValue() { - return values[0]; - } - - /** - * @param value character set ECI value - * @return {@code CharacterSetECI} representing ECI of given value, or null if it is legal but - * unsupported - * @throws FormatException if ECI value is invalid - */ - public static CharacterSetECI getCharacterSetECIByValue(int value) throws FormatException { - if (value < 0 || value >= 900) { - throw FormatException.getFormatInstance(); - } - return VALUE_TO_ECI.get(value); - } - - /** - * @param name character set ECI encoding name - * @return CharacterSetECI representing ECI for character encoding, or null if it is legal - * but unsupported - */ - public static CharacterSetECI getCharacterSetECIByName(String name) { - return NAME_TO_ECI.get(name); - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/common/DecoderResult.java b/TMessagesProj/src/main/java/com/google/zxing/common/DecoderResult.java deleted file mode 100755 index 9a0d1b1f3..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/common/DecoderResult.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.common; - -import java.util.List; - -/** - *

Encapsulates the result of decoding a matrix of bits. This typically - * applies to 2D barcode formats. For now it contains the raw bytes obtained, - * as well as a String interpretation of those bytes, if applicable.

- * - * @author Sean Owen - */ -public final class DecoderResult { - - private final byte[] rawBytes; - private int numBits; - private final String text; - private final List byteSegments; - private final String ecLevel; - private Integer errorsCorrected; - private Integer erasures; - private Object other; - private final int structuredAppendParity; - private final int structuredAppendSequenceNumber; - - public DecoderResult(byte[] rawBytes, - String text, - List byteSegments, - String ecLevel) { - this(rawBytes, text, byteSegments, ecLevel, -1, -1); - } - - public DecoderResult(byte[] rawBytes, - String text, - List byteSegments, - String ecLevel, - int saSequence, - int saParity) { - this.rawBytes = rawBytes; - this.numBits = rawBytes == null ? 0 : 8 * rawBytes.length; - this.text = text; - this.byteSegments = byteSegments; - this.ecLevel = ecLevel; - this.structuredAppendParity = saParity; - this.structuredAppendSequenceNumber = saSequence; - } - - /** - * @return raw bytes representing the result, or {@code null} if not applicable - */ - public byte[] getRawBytes() { - return rawBytes; - } - - /** - * @return how many bits of {@link #getRawBytes()} are valid; typically 8 times its length - * @since 3.3.0 - */ - public int getNumBits() { - return numBits; - } - - /** - * @param numBits overrides the number of bits that are valid in {@link #getRawBytes()} - * @since 3.3.0 - */ - public void setNumBits(int numBits) { - this.numBits = numBits; - } - - /** - * @return text representation of the result - */ - public String getText() { - return text; - } - - /** - * @return list of byte segments in the result, or {@code null} if not applicable - */ - public List getByteSegments() { - return byteSegments; - } - - /** - * @return name of error correction level used, or {@code null} if not applicable - */ - public String getECLevel() { - return ecLevel; - } - - /** - * @return number of errors corrected, or {@code null} if not applicable - */ - public Integer getErrorsCorrected() { - return errorsCorrected; - } - - public void setErrorsCorrected(Integer errorsCorrected) { - this.errorsCorrected = errorsCorrected; - } - - /** - * @return number of erasures corrected, or {@code null} if not applicable - */ - public Integer getErasures() { - return erasures; - } - - public void setErasures(Integer erasures) { - this.erasures = erasures; - } - - /** - * @return arbitrary additional metadata - */ - public Object getOther() { - return other; - } - - public void setOther(Object other) { - this.other = other; - } - - public boolean hasStructuredAppend() { - return structuredAppendParity >= 0 && structuredAppendSequenceNumber >= 0; - } - - public int getStructuredAppendParity() { - return structuredAppendParity; - } - - public int getStructuredAppendSequenceNumber() { - return structuredAppendSequenceNumber; - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/common/DefaultGridSampler.java b/TMessagesProj/src/main/java/com/google/zxing/common/DefaultGridSampler.java deleted file mode 100755 index ce7a0c9f7..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/common/DefaultGridSampler.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.common; - -import com.google.zxing.NotFoundException; - -/** - * @author Sean Owen - */ -public final class DefaultGridSampler extends GridSampler { - - @Override - public BitMatrix sampleGrid(BitMatrix image, - int dimensionX, - int dimensionY, - float p1ToX, float p1ToY, - float p2ToX, float p2ToY, - float p3ToX, float p3ToY, - float p4ToX, float p4ToY, - float p1FromX, float p1FromY, - float p2FromX, float p2FromY, - float p3FromX, float p3FromY, - float p4FromX, float p4FromY) throws NotFoundException { - - PerspectiveTransform transform = PerspectiveTransform.quadrilateralToQuadrilateral( - p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY, - p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY); - - return sampleGrid(image, dimensionX, dimensionY, transform); - } - - @Override - public BitMatrix sampleGrid(BitMatrix image, - int dimensionX, - int dimensionY, - PerspectiveTransform transform) throws NotFoundException { - if (dimensionX <= 0 || dimensionY <= 0) { - throw NotFoundException.getNotFoundInstance(); - } - BitMatrix bits = new BitMatrix(dimensionX, dimensionY, 1); - float[] points = new float[2 * dimensionX]; - for (int y = 0; y < dimensionY; y++) { - int max = points.length; - float iValue = y + 0.5f; - for (int x = 0; x < max; x += 2) { - points[x] = (float) (x / 2) + 0.5f; - points[x + 1] = iValue; - } - transform.transformPoints(points); - // Quick check to see if points transformed to something inside the image; - // sufficient to check the endpoints - checkAndNudgePoints(image, points); - try { - for (int x = 0; x < max; x += 2) { - if (image.get((int) points[x], (int) points[x + 1])) { - // Black(-ish) pixel - bits.set(x / 2, y); - } - } - } catch (ArrayIndexOutOfBoundsException aioobe) { - // This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting - // transform gets "twisted" such that it maps a straight line of points to a set of points - // whose endpoints are in bounds, but others are not. There is probably some mathematical - // way to detect this about the transformation that I don't know yet. - // This results in an ugly runtime exception despite our clever checks above -- can't have - // that. We could check each point's coordinates but that feels duplicative. We settle for - // catching and wrapping ArrayIndexOutOfBoundsException. - throw NotFoundException.getNotFoundInstance(); - } - } - return bits; - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/common/DetectorResult.java b/TMessagesProj/src/main/java/com/google/zxing/common/DetectorResult.java deleted file mode 100755 index 0f3cf15e7..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/common/DetectorResult.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.common; - -import com.google.zxing.ResultPoint; - -/** - *

Encapsulates the result of detecting a barcode in an image. This includes the raw - * matrix of black/white pixels corresponding to the barcode, and possibly points of interest - * in the image, like the location of finder patterns or corners of the barcode in the image.

- * - * @author Sean Owen - */ -public class DetectorResult { - - private final BitMatrix bits; - private final ResultPoint[] points; - - public DetectorResult(BitMatrix bits, ResultPoint[] points) { - this.bits = bits; - this.points = points; - } - - public final BitMatrix getBits() { - return bits; - } - - public final ResultPoint[] getPoints() { - return points; - } - -} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/com/google/zxing/common/GlobalHistogramBinarizer.java b/TMessagesProj/src/main/java/com/google/zxing/common/GlobalHistogramBinarizer.java deleted file mode 100755 index f7f201525..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/common/GlobalHistogramBinarizer.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright 2009 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.common; - -import com.google.zxing.Binarizer; -import com.google.zxing.LuminanceSource; -import com.google.zxing.NotFoundException; - -/** - * This Binarizer implementation uses the old ZXing global histogram approach. It is suitable - * for low-end mobile devices which don't have enough CPU or memory to use a local thresholding - * algorithm. However, because it picks a global black point, it cannot handle difficult shadows - * and gradients. - * - * Faster mobile devices and all desktop applications should probably use HybridBinarizer instead. - * - * @author dswitkin@google.com (Daniel Switkin) - * @author Sean Owen - */ -public class GlobalHistogramBinarizer extends Binarizer { - - private static final int LUMINANCE_BITS = 5; - private static final int LUMINANCE_SHIFT = 8 - LUMINANCE_BITS; - private static final int LUMINANCE_BUCKETS = 1 << LUMINANCE_BITS; - private static final byte[] EMPTY = new byte[0]; - - private byte[] luminances; - private final int[] buckets; - - public GlobalHistogramBinarizer(LuminanceSource source) { - super(source); - luminances = EMPTY; - buckets = new int[LUMINANCE_BUCKETS]; - } - - // Applies simple sharpening to the row data to improve performance of the 1D Readers. - @Override - public BitArray getBlackRow(int y, BitArray row) throws NotFoundException { - LuminanceSource source = getLuminanceSource(); - int width = source.getWidth(); - if (row == null || row.getSize() < width) { - row = new BitArray(width); - } else { - row.clear(); - } - - initArrays(width); - byte[] localLuminances = source.getRow(y, luminances); - int[] localBuckets = buckets; - for (int x = 0; x < width; x++) { - localBuckets[(localLuminances[x] & 0xff) >> LUMINANCE_SHIFT]++; - } - int blackPoint = estimateBlackPoint(localBuckets); - - if (width < 3) { - // Special case for very small images - for (int x = 0; x < width; x++) { - if ((localLuminances[x] & 0xff) < blackPoint) { - row.set(x); - } - } - } else { - int left = localLuminances[0] & 0xff; - int center = localLuminances[1] & 0xff; - for (int x = 1; x < width - 1; x++) { - int right = localLuminances[x + 1] & 0xff; - // A simple -1 4 -1 box filter with a weight of 2. - if (((center * 4) - left - right) / 2 < blackPoint) { - row.set(x); - } - left = center; - center = right; - } - } - return row; - } - - // Does not sharpen the data, as this call is intended to only be used by 2D Readers. - @Override - public BitMatrix getBlackMatrix() throws NotFoundException { - LuminanceSource source = getLuminanceSource(); - int width = source.getWidth(); - int height = source.getHeight(); - BitMatrix matrix = new BitMatrix(width, height, 1); - - // Quickly calculates the histogram by sampling four rows from the image. This proved to be - // more robust on the blackbox tests than sampling a diagonal as we used to do. - initArrays(width); - int[] localBuckets = buckets; - for (int y = 1; y < 5; y++) { - int row = height * y / 5; - byte[] localLuminances = source.getRow(row, luminances); - int right = (width * 4) / 5; - for (int x = width / 5; x < right; x++) { - int pixel = localLuminances[x] & 0xff; - localBuckets[pixel >> LUMINANCE_SHIFT]++; - } - } - int blackPoint = estimateBlackPoint(localBuckets); - - // We delay reading the entire image luminance until the black point estimation succeeds. - // Although we end up reading four rows twice, it is consistent with our motto of - // "fail quickly" which is necessary for continuous scanning. - byte[] localLuminances = source.getMatrix(); - for (int y = 0; y < height; y++) { - int offset = y * width; - for (int x = 0; x < width; x++) { - int pixel = localLuminances[offset + x] & 0xff; - if (pixel < blackPoint) { - matrix.set(x, y); - } - } - } - - return matrix; - } - - @Override - public Binarizer createBinarizer(LuminanceSource source) { - return new GlobalHistogramBinarizer(source); - } - - private void initArrays(int luminanceSize) { - if (luminances.length < luminanceSize) { - luminances = new byte[luminanceSize]; - } - for (int x = 0; x < LUMINANCE_BUCKETS; x++) { - buckets[x] = 0; - } - } - - private static int estimateBlackPoint(int[] buckets) throws NotFoundException { - // Find the tallest peak in the histogram. - int numBuckets = buckets.length; - int maxBucketCount = 0; - int firstPeak = 0; - int firstPeakSize = 0; - for (int x = 0; x < numBuckets; x++) { - if (buckets[x] > firstPeakSize) { - firstPeak = x; - firstPeakSize = buckets[x]; - } - if (buckets[x] > maxBucketCount) { - maxBucketCount = buckets[x]; - } - } - - // Find the second-tallest peak which is somewhat far from the tallest peak. - int secondPeak = 0; - int secondPeakScore = 0; - for (int x = 0; x < numBuckets; x++) { - int distanceToBiggest = x - firstPeak; - // Encourage more distant second peaks by multiplying by square of distance. - int score = buckets[x] * distanceToBiggest * distanceToBiggest; - if (score > secondPeakScore) { - secondPeak = x; - secondPeakScore = score; - } - } - - // Make sure firstPeak corresponds to the black peak. - if (firstPeak > secondPeak) { - int temp = firstPeak; - firstPeak = secondPeak; - secondPeak = temp; - } - - // If there is too little contrast in the image to pick a meaningful black point, throw rather - // than waste time trying to decode the image, and risk false positives. - if (secondPeak - firstPeak <= numBuckets / 16) { - throw NotFoundException.getNotFoundInstance(); - } - - // Find a valley between them that is low and closer to the white peak. - int bestValley = secondPeak - 1; - int bestValleyScore = -1; - for (int x = secondPeak - 1; x > firstPeak; x--) { - int fromFirst = x - firstPeak; - int score = fromFirst * fromFirst * (secondPeak - x) * (maxBucketCount - buckets[x]); - if (score > bestValleyScore) { - bestValley = x; - bestValleyScore = score; - } - } - - return bestValley << LUMINANCE_SHIFT; - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/common/GridSampler.java b/TMessagesProj/src/main/java/com/google/zxing/common/GridSampler.java deleted file mode 100755 index 43c3cf29b..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/common/GridSampler.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.common; - -import com.google.zxing.NotFoundException; - -/** - * Implementations of this class can, given locations of finder patterns for a QR code in an - * image, sample the right points in the image to reconstruct the QR code, accounting for - * perspective distortion. It is abstracted since it is relatively expensive and should be allowed - * to take advantage of platform-specific optimized implementations, like Sun's Java Advanced - * Imaging library, but which may not be available in other environments such as J2ME, and vice - * versa. - * - * The implementation used can be controlled by calling {@link #setGridSampler(GridSampler)} - * with an instance of a class which implements this interface. - * - * @author Sean Owen - */ -public abstract class GridSampler { - - private static GridSampler gridSampler = new DefaultGridSampler(); - - /** - * Sets the implementation of GridSampler used by the library. One global - * instance is stored, which may sound problematic. But, the implementation provided - * ought to be appropriate for the entire platform, and all uses of this library - * in the whole lifetime of the JVM. For instance, an Android activity can swap in - * an implementation that takes advantage of native platform libraries. - * - * @param newGridSampler The platform-specific object to install. - */ - public static void setGridSampler(GridSampler newGridSampler) { - gridSampler = newGridSampler; - } - - /** - * @return the current implementation of GridSampler - */ - public static GridSampler getInstance() { - return gridSampler; - } - - /** - * Samples an image for a rectangular matrix of bits of the given dimension. The sampling - * transformation is determined by the coordinates of 4 points, in the original and transformed - * image space. - * - * @param image image to sample - * @param dimensionX width of {@link BitMatrix} to sample from image - * @param dimensionY height of {@link BitMatrix} to sample from image - * @param p1ToX point 1 preimage X - * @param p1ToY point 1 preimage Y - * @param p2ToX point 2 preimage X - * @param p2ToY point 2 preimage Y - * @param p3ToX point 3 preimage X - * @param p3ToY point 3 preimage Y - * @param p4ToX point 4 preimage X - * @param p4ToY point 4 preimage Y - * @param p1FromX point 1 image X - * @param p1FromY point 1 image Y - * @param p2FromX point 2 image X - * @param p2FromY point 2 image Y - * @param p3FromX point 3 image X - * @param p3FromY point 3 image Y - * @param p4FromX point 4 image X - * @param p4FromY point 4 image Y - * @return {@link BitMatrix} representing a grid of points sampled from the image within a region - * defined by the "from" parameters - * @throws NotFoundException if image can't be sampled, for example, if the transformation defined - * by the given points is invalid or results in sampling outside the image boundaries - */ - public abstract BitMatrix sampleGrid(BitMatrix image, - int dimensionX, - int dimensionY, - float p1ToX, float p1ToY, - float p2ToX, float p2ToY, - float p3ToX, float p3ToY, - float p4ToX, float p4ToY, - float p1FromX, float p1FromY, - float p2FromX, float p2FromY, - float p3FromX, float p3FromY, - float p4FromX, float p4FromY) throws NotFoundException; - - public abstract BitMatrix sampleGrid(BitMatrix image, - int dimensionX, - int dimensionY, - PerspectiveTransform transform) throws NotFoundException; - - /** - *

Checks a set of points that have been transformed to sample points on an image against - * the image's dimensions to see if the point are even within the image.

- * - *

This method will actually "nudge" the endpoints back onto the image if they are found to be - * barely (less than 1 pixel) off the image. This accounts for imperfect detection of finder - * patterns in an image where the QR Code runs all the way to the image border.

- * - *

For efficiency, the method will check points from either end of the line until one is found - * to be within the image. Because the set of points are assumed to be linear, this is valid.

- * - * @param image image into which the points should map - * @param points actual points in x1,y1,...,xn,yn form - * @throws NotFoundException if an endpoint is lies outside the image boundaries - */ - protected static void checkAndNudgePoints(BitMatrix image, - float[] points) throws NotFoundException { - int width = image.getWidth(); - int height = image.getHeight(); - // Check and nudge points from start until we see some that are OK: - boolean nudged = true; - int maxOffset = points.length - 1; // points.length must be even - for (int offset = 0; offset < maxOffset && nudged; offset += 2) { - int x = (int) points[offset]; - int y = (int) points[offset + 1]; - if (x < -1 || x > width || y < -1 || y > height) { - throw NotFoundException.getNotFoundInstance(); - } - nudged = false; - if (x == -1) { - points[offset] = 0.0f; - nudged = true; - } else if (x == width) { - points[offset] = width - 1; - nudged = true; - } - if (y == -1) { - points[offset + 1] = 0.0f; - nudged = true; - } else if (y == height) { - points[offset + 1] = height - 1; - nudged = true; - } - } - // Check and nudge points from end: - nudged = true; - for (int offset = points.length - 2; offset >= 0 && nudged; offset -= 2) { - int x = (int) points[offset]; - int y = (int) points[offset + 1]; - if (x < -1 || x > width || y < -1 || y > height) { - throw NotFoundException.getNotFoundInstance(); - } - nudged = false; - if (x == -1) { - points[offset] = 0.0f; - nudged = true; - } else if (x == width) { - points[offset] = width - 1; - nudged = true; - } - if (y == -1) { - points[offset + 1] = 0.0f; - nudged = true; - } else if (y == height) { - points[offset + 1] = height - 1; - nudged = true; - } - } - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/common/HybridBinarizer.java b/TMessagesProj/src/main/java/com/google/zxing/common/HybridBinarizer.java deleted file mode 100755 index f97992ab9..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/common/HybridBinarizer.java +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright 2009 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.common; - -import com.google.zxing.Binarizer; -import com.google.zxing.LuminanceSource; -import com.google.zxing.NotFoundException; - -/** - * This class implements a local thresholding algorithm, which while slower than the - * GlobalHistogramBinarizer, is fairly efficient for what it does. It is designed for - * high frequency images of barcodes with black data on white backgrounds. For this application, - * it does a much better job than a global blackpoint with severe shadows and gradients. - * However it tends to produce artifacts on lower frequency images and is therefore not - * a good general purpose binarizer for uses outside ZXing. - * - * This class extends GlobalHistogramBinarizer, using the older histogram approach for 1D readers, - * and the newer local approach for 2D readers. 1D decoding using a per-row histogram is already - * inherently local, and only fails for horizontal gradients. We can revisit that problem later, - * but for now it was not a win to use local blocks for 1D. - * - * This Binarizer is the default for the unit tests and the recommended class for library users. - * - * @author dswitkin@google.com (Daniel Switkin) - */ -public final class HybridBinarizer extends GlobalHistogramBinarizer { - - // This class uses 5x5 blocks to compute local luminance, where each block is 8x8 pixels. - // So this is the smallest dimension in each axis we can accept. - private static final int BLOCK_SIZE_POWER = 3; - private static final int BLOCK_SIZE = 1 << BLOCK_SIZE_POWER; // ...0100...00 - private static final int BLOCK_SIZE_MASK = BLOCK_SIZE - 1; // ...0011...11 - private static final int MINIMUM_DIMENSION = BLOCK_SIZE * 5; - private static final int MIN_DYNAMIC_RANGE = 24; - - private BitMatrix matrix; - - public HybridBinarizer(LuminanceSource source) { - super(source); - } - - /** - * Calculates the final BitMatrix once for all requests. This could be called once from the - * constructor instead, but there are some advantages to doing it lazily, such as making - * profiling easier, and not doing heavy lifting when callers don't expect it. - */ - @Override - public BitMatrix getBlackMatrix() throws NotFoundException { - if (matrix != null) { - return matrix; - } - LuminanceSource source = getLuminanceSource(); - int width = source.getWidth(); - int height = source.getHeight(); - if (width >= MINIMUM_DIMENSION && height >= MINIMUM_DIMENSION) { - byte[] luminances = source.getMatrix(); - int subWidth = width >> BLOCK_SIZE_POWER; - if ((width & BLOCK_SIZE_MASK) != 0) { - subWidth++; - } - int subHeight = height >> BLOCK_SIZE_POWER; - if ((height & BLOCK_SIZE_MASK) != 0) { - subHeight++; - } - int[][] blackPoints = calculateBlackPoints(luminances, subWidth, subHeight, width, height); - - BitMatrix newMatrix = new BitMatrix(width, height, 1); - calculateThresholdForBlock(luminances, subWidth, subHeight, width, height, blackPoints, newMatrix); - matrix = newMatrix; - } else { - // If the image is too small, fall back to the global histogram approach. - matrix = super.getBlackMatrix(); - } - return matrix; - } - - @Override - public Binarizer createBinarizer(LuminanceSource source) { - return new HybridBinarizer(source); - } - - /** - * For each block in the image, calculate the average black point using a 5x5 grid - * of the blocks around it. Also handles the corner cases (fractional blocks are computed based - * on the last pixels in the row/column which are also used in the previous block). - */ - private static void calculateThresholdForBlock(byte[] luminances, - int subWidth, - int subHeight, - int width, - int height, - int[][] blackPoints, - BitMatrix matrix) { - int maxYOffset = height - BLOCK_SIZE; - int maxXOffset = width - BLOCK_SIZE; - for (int y = 0; y < subHeight; y++) { - int yoffset = y << BLOCK_SIZE_POWER; - if (yoffset > maxYOffset) { - yoffset = maxYOffset; - } - int top = cap(y, subHeight - 3); - for (int x = 0; x < subWidth; x++) { - int xoffset = x << BLOCK_SIZE_POWER; - if (xoffset > maxXOffset) { - xoffset = maxXOffset; - } - int left = cap(x, subWidth - 3); - int sum = 0; - for (int z = -2; z <= 2; z++) { - int[] blackRow = blackPoints[top + z]; - sum += blackRow[left - 2] + blackRow[left - 1] + blackRow[left] + blackRow[left + 1] + blackRow[left + 2]; - } - int average = sum / 25; - thresholdBlock(luminances, xoffset, yoffset, average, width, matrix); - } - } - } - - private static int cap(int value, int max) { - return value < 2 ? 2 : Math.min(value, max); - } - - /** - * Applies a single threshold to a block of pixels. - */ - private static void thresholdBlock(byte[] luminances, - int xoffset, - int yoffset, - int threshold, - int stride, - BitMatrix matrix) { - for (int y = 0, offset = yoffset * stride + xoffset; y < BLOCK_SIZE; y++, offset += stride) { - for (int x = 0; x < BLOCK_SIZE; x++) { - // Comparison needs to be <= so that black == 0 pixels are black even if the threshold is 0. - if ((luminances[offset + x] & 0xFF) <= threshold) { - matrix.set(xoffset + x, yoffset + y); - } - } - } - } - - /** - * Calculates a single black point for each block of pixels and saves it away. - * See the following thread for a discussion of this algorithm: - * http://groups.google.com/group/zxing/browse_thread/thread/d06efa2c35a7ddc0 - */ - private static int[][] calculateBlackPoints(byte[] luminances, - int subWidth, - int subHeight, - int width, - int height) { - int maxYOffset = height - BLOCK_SIZE; - int maxXOffset = width - BLOCK_SIZE; - int[][] blackPoints = new int[subHeight][subWidth]; - for (int y = 0; y < subHeight; y++) { - int yoffset = y << BLOCK_SIZE_POWER; - if (yoffset > maxYOffset) { - yoffset = maxYOffset; - } - for (int x = 0; x < subWidth; x++) { - int xoffset = x << BLOCK_SIZE_POWER; - if (xoffset > maxXOffset) { - xoffset = maxXOffset; - } - int sum = 0; - int min = 0xFF; - int max = 0; - for (int yy = 0, offset = yoffset * width + xoffset; yy < BLOCK_SIZE; yy++, offset += width) { - for (int xx = 0; xx < BLOCK_SIZE; xx++) { - int pixel = luminances[offset + xx] & 0xFF; - sum += pixel; - // still looking for good contrast - if (pixel < min) { - min = pixel; - } - if (pixel > max) { - max = pixel; - } - } - // short-circuit min/max tests once dynamic range is met - if (max - min > MIN_DYNAMIC_RANGE) { - // finish the rest of the rows quickly - for (yy++, offset += width; yy < BLOCK_SIZE; yy++, offset += width) { - for (int xx = 0; xx < BLOCK_SIZE; xx++) { - sum += luminances[offset + xx] & 0xFF; - } - } - } - } - - // The default estimate is the average of the values in the block. - int average = sum >> (BLOCK_SIZE_POWER * 2); - if (max - min <= MIN_DYNAMIC_RANGE) { - // If variation within the block is low, assume this is a block with only light or only - // dark pixels. In that case we do not want to use the average, as it would divide this - // low contrast area into black and white pixels, essentially creating data out of noise. - // - // The default assumption is that the block is light/background. Since no estimate for - // the level of dark pixels exists locally, use half the min for the block. - average = min / 2; - - if (y > 0 && x > 0) { - // Correct the "white background" assumption for blocks that have neighbors by comparing - // the pixels in this block to the previously calculated black points. This is based on - // the fact that dark barcode symbology is always surrounded by some amount of light - // background for which reasonable black point estimates were made. The bp estimated at - // the boundaries is used for the interior. - - // The (min < bp) is arbitrary but works better than other heuristics that were tried. - int averageNeighborBlackPoint = - (blackPoints[y - 1][x] + (2 * blackPoints[y][x - 1]) + blackPoints[y - 1][x - 1]) / 4; - if (min < averageNeighborBlackPoint) { - average = averageNeighborBlackPoint; - } - } - } - blackPoints[y][x] = average; - } - } - return blackPoints; - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/common/PerspectiveTransform.java b/TMessagesProj/src/main/java/com/google/zxing/common/PerspectiveTransform.java deleted file mode 100755 index 06dba94b9..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/common/PerspectiveTransform.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.common; - -/** - *

This class implements a perspective transform in two dimensions. Given four source and four - * destination points, it will compute the transformation implied between them. The code is based - * directly upon section 3.4.2 of George Wolberg's "Digital Image Warping"; see pages 54-56.

- * - * @author Sean Owen - */ -public final class PerspectiveTransform { - - private final float a11; - private final float a12; - private final float a13; - private final float a21; - private final float a22; - private final float a23; - private final float a31; - private final float a32; - private final float a33; - - private PerspectiveTransform(float a11, float a21, float a31, - float a12, float a22, float a32, - float a13, float a23, float a33) { - this.a11 = a11; - this.a12 = a12; - this.a13 = a13; - this.a21 = a21; - this.a22 = a22; - this.a23 = a23; - this.a31 = a31; - this.a32 = a32; - this.a33 = a33; - } - - public static PerspectiveTransform quadrilateralToQuadrilateral(float x0, float y0, - float x1, float y1, - float x2, float y2, - float x3, float y3, - float x0p, float y0p, - float x1p, float y1p, - float x2p, float y2p, - float x3p, float y3p) { - - PerspectiveTransform qToS = quadrilateralToSquare(x0, y0, x1, y1, x2, y2, x3, y3); - PerspectiveTransform sToQ = squareToQuadrilateral(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p); - return sToQ.times(qToS); - } - - public void transformPoints(float[] points) { - float a11 = this.a11; - float a12 = this.a12; - float a13 = this.a13; - float a21 = this.a21; - float a22 = this.a22; - float a23 = this.a23; - float a31 = this.a31; - float a32 = this.a32; - float a33 = this.a33; - int maxI = points.length - 1; // points.length must be even - for (int i = 0; i < maxI; i += 2) { - float x = points[i]; - float y = points[i + 1]; - float denominator = a13 * x + a23 * y + a33; - points[i] = (a11 * x + a21 * y + a31) / denominator; - points[i + 1] = (a12 * x + a22 * y + a32) / denominator; - } - } - - public void transformPoints(float[] xValues, float[] yValues) { - int n = xValues.length; - for (int i = 0; i < n; i++) { - float x = xValues[i]; - float y = yValues[i]; - float denominator = a13 * x + a23 * y + a33; - xValues[i] = (a11 * x + a21 * y + a31) / denominator; - yValues[i] = (a12 * x + a22 * y + a32) / denominator; - } - } - - public static PerspectiveTransform squareToQuadrilateral(float x0, float y0, - float x1, float y1, - float x2, float y2, - float x3, float y3) { - float dx3 = x0 - x1 + x2 - x3; - float dy3 = y0 - y1 + y2 - y3; - if (dx3 == 0.0f && dy3 == 0.0f) { - // Affine - return new PerspectiveTransform(x1 - x0, x2 - x1, x0, - y1 - y0, y2 - y1, y0, - 0.0f, 0.0f, 1.0f); - } else { - float dx1 = x1 - x2; - float dx2 = x3 - x2; - float dy1 = y1 - y2; - float dy2 = y3 - y2; - float denominator = dx1 * dy2 - dx2 * dy1; - float a13 = (dx3 * dy2 - dx2 * dy3) / denominator; - float a23 = (dx1 * dy3 - dx3 * dy1) / denominator; - return new PerspectiveTransform(x1 - x0 + a13 * x1, x3 - x0 + a23 * x3, x0, - y1 - y0 + a13 * y1, y3 - y0 + a23 * y3, y0, - a13, a23, 1.0f); - } - } - - public static PerspectiveTransform quadrilateralToSquare(float x0, float y0, - float x1, float y1, - float x2, float y2, - float x3, float y3) { - // Here, the adjoint serves as the inverse: - return squareToQuadrilateral(x0, y0, x1, y1, x2, y2, x3, y3).buildAdjoint(); - } - - PerspectiveTransform buildAdjoint() { - // Adjoint is the transpose of the cofactor matrix: - return new PerspectiveTransform(a22 * a33 - a23 * a32, - a23 * a31 - a21 * a33, - a21 * a32 - a22 * a31, - a13 * a32 - a12 * a33, - a11 * a33 - a13 * a31, - a12 * a31 - a11 * a32, - a12 * a23 - a13 * a22, - a13 * a21 - a11 * a23, - a11 * a22 - a12 * a21); - } - - PerspectiveTransform times(PerspectiveTransform other) { - return new PerspectiveTransform(a11 * other.a11 + a21 * other.a12 + a31 * other.a13, - a11 * other.a21 + a21 * other.a22 + a31 * other.a23, - a11 * other.a31 + a21 * other.a32 + a31 * other.a33, - a12 * other.a11 + a22 * other.a12 + a32 * other.a13, - a12 * other.a21 + a22 * other.a22 + a32 * other.a23, - a12 * other.a31 + a22 * other.a32 + a32 * other.a33, - a13 * other.a11 + a23 * other.a12 + a33 * other.a13, - a13 * other.a21 + a23 * other.a22 + a33 * other.a23, - a13 * other.a31 + a23 * other.a32 + a33 * other.a33); - - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/common/StringUtils.java b/TMessagesProj/src/main/java/com/google/zxing/common/StringUtils.java deleted file mode 100755 index 2eb3a3145..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/common/StringUtils.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright (C) 2010 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.common; - -import java.nio.charset.Charset; -import java.util.Map; - -import com.google.zxing.DecodeHintType; - -/** - * Common string-related functions. - * - * @author Sean Owen - * @author Alex Dupre - */ -public final class StringUtils { - - private static final String PLATFORM_DEFAULT_ENCODING = Charset.defaultCharset().name(); - public static final String SHIFT_JIS = "SJIS"; - public static final String GB2312 = "GB2312"; - private static final String EUC_JP = "EUC_JP"; - private static final String UTF8 = "UTF8"; - private static final String ISO88591 = "ISO8859_1"; - private static final boolean ASSUME_SHIFT_JIS = - SHIFT_JIS.equalsIgnoreCase(PLATFORM_DEFAULT_ENCODING) || - EUC_JP.equalsIgnoreCase(PLATFORM_DEFAULT_ENCODING); - - private StringUtils() { } - - /** - * @param bytes bytes encoding a string, whose encoding should be guessed - * @param hints decode hints if applicable - * @return name of guessed encoding; at the moment will only guess one of: - * {@link #SHIFT_JIS}, {@link #UTF8}, {@link #ISO88591}, or the platform - * default encoding if none of these can possibly be correct - */ - public static String guessEncoding(byte[] bytes, Map hints) { - if (hints != null && hints.containsKey(DecodeHintType.CHARACTER_SET)) { - return hints.get(DecodeHintType.CHARACTER_SET).toString(); - } - // For now, merely tries to distinguish ISO-8859-1, UTF-8 and Shift_JIS, - // which should be by far the most common encodings. - int length = bytes.length; - boolean canBeISO88591 = true; - boolean canBeShiftJIS = true; - boolean canBeUTF8 = true; - int utf8BytesLeft = 0; - int utf2BytesChars = 0; - int utf3BytesChars = 0; - int utf4BytesChars = 0; - int sjisBytesLeft = 0; - int sjisKatakanaChars = 0; - int sjisCurKatakanaWordLength = 0; - int sjisCurDoubleBytesWordLength = 0; - int sjisMaxKatakanaWordLength = 0; - int sjisMaxDoubleBytesWordLength = 0; - int isoHighOther = 0; - - boolean utf8bom = bytes.length > 3 && - bytes[0] == (byte) 0xEF && - bytes[1] == (byte) 0xBB && - bytes[2] == (byte) 0xBF; - - for (int i = 0; - i < length && (canBeISO88591 || canBeShiftJIS || canBeUTF8); - i++) { - - int value = bytes[i] & 0xFF; - - // UTF-8 stuff - if (canBeUTF8) { - if (utf8BytesLeft > 0) { - if ((value & 0x80) == 0) { - canBeUTF8 = false; - } else { - utf8BytesLeft--; - } - } else if ((value & 0x80) != 0) { - if ((value & 0x40) == 0) { - canBeUTF8 = false; - } else { - utf8BytesLeft++; - if ((value & 0x20) == 0) { - utf2BytesChars++; - } else { - utf8BytesLeft++; - if ((value & 0x10) == 0) { - utf3BytesChars++; - } else { - utf8BytesLeft++; - if ((value & 0x08) == 0) { - utf4BytesChars++; - } else { - canBeUTF8 = false; - } - } - } - } - } - } - - // ISO-8859-1 stuff - if (canBeISO88591) { - if (value > 0x7F && value < 0xA0) { - canBeISO88591 = false; - } else if (value > 0x9F && (value < 0xC0 || value == 0xD7 || value == 0xF7)) { - isoHighOther++; - } - } - - // Shift_JIS stuff - if (canBeShiftJIS) { - if (sjisBytesLeft > 0) { - if (value < 0x40 || value == 0x7F || value > 0xFC) { - canBeShiftJIS = false; - } else { - sjisBytesLeft--; - } - } else if (value == 0x80 || value == 0xA0 || value > 0xEF) { - canBeShiftJIS = false; - } else if (value > 0xA0 && value < 0xE0) { - sjisKatakanaChars++; - sjisCurDoubleBytesWordLength = 0; - sjisCurKatakanaWordLength++; - if (sjisCurKatakanaWordLength > sjisMaxKatakanaWordLength) { - sjisMaxKatakanaWordLength = sjisCurKatakanaWordLength; - } - } else if (value > 0x7F) { - sjisBytesLeft++; - //sjisDoubleBytesChars++; - sjisCurKatakanaWordLength = 0; - sjisCurDoubleBytesWordLength++; - if (sjisCurDoubleBytesWordLength > sjisMaxDoubleBytesWordLength) { - sjisMaxDoubleBytesWordLength = sjisCurDoubleBytesWordLength; - } - } else { - //sjisLowChars++; - sjisCurKatakanaWordLength = 0; - sjisCurDoubleBytesWordLength = 0; - } - } - } - - if (canBeUTF8 && utf8BytesLeft > 0) { - canBeUTF8 = false; - } - if (canBeShiftJIS && sjisBytesLeft > 0) { - canBeShiftJIS = false; - } - - // Easy -- if there is BOM or at least 1 valid not-single byte character (and no evidence it can't be UTF-8), done - if (canBeUTF8 && (utf8bom || utf2BytesChars + utf3BytesChars + utf4BytesChars > 0)) { - return UTF8; - } - // Easy -- if assuming Shift_JIS or >= 3 valid consecutive not-ascii characters (and no evidence it can't be), done - if (canBeShiftJIS && (ASSUME_SHIFT_JIS || sjisMaxKatakanaWordLength >= 3 || sjisMaxDoubleBytesWordLength >= 3)) { - return SHIFT_JIS; - } - // Distinguishing Shift_JIS and ISO-8859-1 can be a little tough for short words. The crude heuristic is: - // - If we saw - // - only two consecutive katakana chars in the whole text, or - // - at least 10% of bytes that could be "upper" not-alphanumeric Latin1, - // - then we conclude Shift_JIS, else ISO-8859-1 - if (canBeISO88591 && canBeShiftJIS) { - return (sjisMaxKatakanaWordLength == 2 && sjisKatakanaChars == 2) || isoHighOther * 10 >= length - ? SHIFT_JIS : ISO88591; - } - - // Otherwise, try in order ISO-8859-1, Shift JIS, UTF-8 and fall back to default platform encoding - if (canBeISO88591) { - return ISO88591; - } - if (canBeShiftJIS) { - return SHIFT_JIS; - } - if (canBeUTF8) { - return UTF8; - } - // Otherwise, we take a wild guess with platform encoding - return PLATFORM_DEFAULT_ENCODING; - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/common/detector/MathUtils.java b/TMessagesProj/src/main/java/com/google/zxing/common/detector/MathUtils.java deleted file mode 100755 index bd5931b31..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/common/detector/MathUtils.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2012 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.common.detector; - -/** - * General math-related and numeric utility functions. - */ -public final class MathUtils { - - private MathUtils() { - } - - /** - * Ends up being a bit faster than {@link Math#round(float)}. This merely rounds its - * argument to the nearest int, where x.5 rounds up to x+1. Semantics of this shortcut - * differ slightly from {@link Math#round(float)} in that half rounds down for negative - * values. -2.5 rounds to -3, not -2. For purposes here it makes no difference. - * - * @param d real value to round - * @return nearest {@code int} - */ - public static int round(float d) { - return (int) (d + (d < 0.0f ? -0.5f : 0.5f)); - } - - /** - * @param aX point A x coordinate - * @param aY point A y coordinate - * @param bX point B x coordinate - * @param bY point B y coordinate - * @return Euclidean distance between points A and B - */ - public static float distance(float aX, float aY, float bX, float bY) { - double xDiff = aX - bX; - double yDiff = aY - bY; - return (float) Math.sqrt(xDiff * xDiff + yDiff * yDiff); - } - - /** - * @param aX point A x coordinate - * @param aY point A y coordinate - * @param bX point B x coordinate - * @param bY point B y coordinate - * @return Euclidean distance between points A and B - */ - public static float distance(int aX, int aY, int bX, int bY) { - double xDiff = aX - bX; - double yDiff = aY - bY; - return (float) Math.sqrt(xDiff * xDiff + yDiff * yDiff); - } - - /** - * @param array values to sum - * @return sum of values in array - */ - public static int sum(int[] array) { - int count = 0; - for (int a : array) { - count += a; - } - return count; - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/common/detector/MonochromeRectangleDetector.java b/TMessagesProj/src/main/java/com/google/zxing/common/detector/MonochromeRectangleDetector.java deleted file mode 100755 index e87fa5531..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/common/detector/MonochromeRectangleDetector.java +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright 2009 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.common.detector; - -import com.google.zxing.NotFoundException; -import com.google.zxing.ResultPoint; -import com.google.zxing.common.BitMatrix; - -/** - *

A somewhat generic detector that looks for a barcode-like rectangular region within an image. - * It looks within a mostly white region of an image for a region of black and white, but mostly - * black. It returns the four corners of the region, as best it can determine.

- * - * @author Sean Owen - * @deprecated without replacement since 3.3.0 - */ -@Deprecated -public final class MonochromeRectangleDetector { - - private static final int MAX_MODULES = 32; - - private final BitMatrix image; - - public MonochromeRectangleDetector(BitMatrix image) { - this.image = image; - } - - /** - *

Detects a rectangular region of black and white -- mostly black -- with a region of mostly - * white, in an image.

- * - * @return {@link ResultPoint}[] describing the corners of the rectangular region. The first and - * last points are opposed on the diagonal, as are the second and third. The first point will be - * the topmost point and the last, the bottommost. The second point will be leftmost and the - * third, the rightmost - * @throws NotFoundException if no Data Matrix Code can be found - */ - public ResultPoint[] detect() throws NotFoundException { - int height = image.getHeight(); - int width = image.getWidth(); - int halfHeight = height / 2; - int halfWidth = width / 2; - int deltaY = Math.max(1, height / (MAX_MODULES * 8)); - int deltaX = Math.max(1, width / (MAX_MODULES * 8)); - - int top = 0; - int bottom = height; - int left = 0; - int right = width; - ResultPoint pointA = findCornerFromCenter(halfWidth, 0, left, right, - halfHeight, -deltaY, top, bottom, halfWidth / 2); - top = (int) pointA.getY() - 1; - ResultPoint pointB = findCornerFromCenter(halfWidth, -deltaX, left, right, - halfHeight, 0, top, bottom, halfHeight / 2); - left = (int) pointB.getX() - 1; - ResultPoint pointC = findCornerFromCenter(halfWidth, deltaX, left, right, - halfHeight, 0, top, bottom, halfHeight / 2); - right = (int) pointC.getX() + 1; - ResultPoint pointD = findCornerFromCenter(halfWidth, 0, left, right, - halfHeight, deltaY, top, bottom, halfWidth / 2); - bottom = (int) pointD.getY() + 1; - - // Go try to find point A again with better information -- might have been off at first. - pointA = findCornerFromCenter(halfWidth, 0, left, right, - halfHeight, -deltaY, top, bottom, halfWidth / 4); - - return new ResultPoint[] { pointA, pointB, pointC, pointD }; - } - - /** - * Attempts to locate a corner of the barcode by scanning up, down, left or right from a center - * point which should be within the barcode. - * - * @param centerX center's x component (horizontal) - * @param deltaX same as deltaY but change in x per step instead - * @param left minimum value of x - * @param right maximum value of x - * @param centerY center's y component (vertical) - * @param deltaY change in y per step. If scanning up this is negative; down, positive; - * left or right, 0 - * @param top minimum value of y to search through (meaningless when di == 0) - * @param bottom maximum value of y - * @param maxWhiteRun maximum run of white pixels that can still be considered to be within - * the barcode - * @return a {@link ResultPoint} encapsulating the corner that was found - * @throws NotFoundException if such a point cannot be found - */ - private ResultPoint findCornerFromCenter(int centerX, - int deltaX, - int left, - int right, - int centerY, - int deltaY, - int top, - int bottom, - int maxWhiteRun) throws NotFoundException { - int[] lastRange = null; - for (int y = centerY, x = centerX; - y < bottom && y >= top && x < right && x >= left; - y += deltaY, x += deltaX) { - int[] range; - if (deltaX == 0) { - // horizontal slices, up and down - range = blackWhiteRange(y, maxWhiteRun, left, right, true); - } else { - // vertical slices, left and right - range = blackWhiteRange(x, maxWhiteRun, top, bottom, false); - } - if (range == null) { - if (lastRange == null) { - throw NotFoundException.getNotFoundInstance(); - } - // lastRange was found - if (deltaX == 0) { - int lastY = y - deltaY; - if (lastRange[0] < centerX) { - if (lastRange[1] > centerX) { - // straddle, choose one or the other based on direction - return new ResultPoint(lastRange[deltaY > 0 ? 0 : 1], lastY); - } - return new ResultPoint(lastRange[0], lastY); - } else { - return new ResultPoint(lastRange[1], lastY); - } - } else { - int lastX = x - deltaX; - if (lastRange[0] < centerY) { - if (lastRange[1] > centerY) { - return new ResultPoint(lastX, lastRange[deltaX < 0 ? 0 : 1]); - } - return new ResultPoint(lastX, lastRange[0]); - } else { - return new ResultPoint(lastX, lastRange[1]); - } - } - } - lastRange = range; - } - throw NotFoundException.getNotFoundInstance(); - } - - /** - * Computes the start and end of a region of pixels, either horizontally or vertically, that could - * be part of a Data Matrix barcode. - * - * @param fixedDimension if scanning horizontally, this is the row (the fixed vertical location) - * where we are scanning. If scanning vertically it's the column, the fixed horizontal location - * @param maxWhiteRun largest run of white pixels that can still be considered part of the - * barcode region - * @param minDim minimum pixel location, horizontally or vertically, to consider - * @param maxDim maximum pixel location, horizontally or vertically, to consider - * @param horizontal if true, we're scanning left-right, instead of up-down - * @return int[] with start and end of found range, or null if no such range is found - * (e.g. only white was found) - */ - private int[] blackWhiteRange(int fixedDimension, int maxWhiteRun, int minDim, int maxDim, boolean horizontal) { - - int center = (minDim + maxDim) / 2; - - // Scan left/up first - int start = center; - while (start >= minDim) { - if (horizontal ? image.get(start, fixedDimension) : image.get(fixedDimension, start)) { - start--; - } else { - int whiteRunStart = start; - do { - start--; - } while (start >= minDim && !(horizontal ? image.get(start, fixedDimension) : - image.get(fixedDimension, start))); - int whiteRunSize = whiteRunStart - start; - if (start < minDim || whiteRunSize > maxWhiteRun) { - start = whiteRunStart; - break; - } - } - } - start++; - - // Then try right/down - int end = center; - while (end < maxDim) { - if (horizontal ? image.get(end, fixedDimension) : image.get(fixedDimension, end)) { - end++; - } else { - int whiteRunStart = end; - do { - end++; - } while (end < maxDim && !(horizontal ? image.get(end, fixedDimension) : - image.get(fixedDimension, end))); - int whiteRunSize = end - whiteRunStart; - if (end >= maxDim || whiteRunSize > maxWhiteRun) { - end = whiteRunStart; - break; - } - } - } - end--; - - return end > start ? new int[]{start, end} : null; - } - -} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/com/google/zxing/common/detector/WhiteRectangleDetector.java b/TMessagesProj/src/main/java/com/google/zxing/common/detector/WhiteRectangleDetector.java deleted file mode 100755 index 8d77b0de5..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/common/detector/WhiteRectangleDetector.java +++ /dev/null @@ -1,325 +0,0 @@ -/* - * Copyright 2010 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.common.detector; - -import com.google.zxing.NotFoundException; -import com.google.zxing.ResultPoint; -import com.google.zxing.common.BitMatrix; - -/** - *

- * Detects a candidate barcode-like rectangular region within an image. It - * starts around the center of the image, increases the size of the candidate - * region until it finds a white rectangular region. By keeping track of the - * last black points it encountered, it determines the corners of the barcode. - *

- * - * @author David Olivier - */ -public final class WhiteRectangleDetector { - - private static final int INIT_SIZE = 10; - private static final int CORR = 1; - - private final BitMatrix image; - private final int height; - private final int width; - private final int leftInit; - private final int rightInit; - private final int downInit; - private final int upInit; - - public WhiteRectangleDetector(BitMatrix image) throws NotFoundException { - this(image, INIT_SIZE, image.getWidth() / 2, image.getHeight() / 2); - } - - /** - * @param image barcode image to find a rectangle in - * @param initSize initial size of search area around center - * @param x x position of search center - * @param y y position of search center - * @throws NotFoundException if image is too small to accommodate {@code initSize} - */ - public WhiteRectangleDetector(BitMatrix image, int initSize, int x, int y) throws NotFoundException { - this.image = image; - height = image.getHeight(); - width = image.getWidth(); - int halfsize = initSize / 2; - leftInit = x - halfsize; - rightInit = x + halfsize; - upInit = y - halfsize; - downInit = y + halfsize; - if (upInit < 0 || leftInit < 0 || downInit >= height || rightInit >= width) { - throw NotFoundException.getNotFoundInstance(); - } - } - - /** - *

- * Detects a candidate barcode-like rectangular region within an image. It - * starts around the center of the image, increases the size of the candidate - * region until it finds a white rectangular region. - *

- * - * @return {@link ResultPoint}[] describing the corners of the rectangular - * region. The first and last points are opposed on the diagonal, as - * are the second and third. The first point will be the topmost - * point and the last, the bottommost. The second point will be - * leftmost and the third, the rightmost - * @throws NotFoundException if no Data Matrix Code can be found - */ - public ResultPoint[] detect() throws NotFoundException { - - int left = leftInit; - int right = rightInit; - int up = upInit; - int down = downInit; - boolean sizeExceeded = false; - boolean aBlackPointFoundOnBorder = true; - - boolean atLeastOneBlackPointFoundOnRight = false; - boolean atLeastOneBlackPointFoundOnBottom = false; - boolean atLeastOneBlackPointFoundOnLeft = false; - boolean atLeastOneBlackPointFoundOnTop = false; - - while (aBlackPointFoundOnBorder) { - - aBlackPointFoundOnBorder = false; - - // ..... - // . | - // ..... - boolean rightBorderNotWhite = true; - while ((rightBorderNotWhite || !atLeastOneBlackPointFoundOnRight) && right < width) { - rightBorderNotWhite = containsBlackPoint(up, down, right, false); - if (rightBorderNotWhite) { - right++; - aBlackPointFoundOnBorder = true; - atLeastOneBlackPointFoundOnRight = true; - } else if (!atLeastOneBlackPointFoundOnRight) { - right++; - } - } - - if (right >= width) { - sizeExceeded = true; - break; - } - - // ..... - // . . - // .___. - boolean bottomBorderNotWhite = true; - while ((bottomBorderNotWhite || !atLeastOneBlackPointFoundOnBottom) && down < height) { - bottomBorderNotWhite = containsBlackPoint(left, right, down, true); - if (bottomBorderNotWhite) { - down++; - aBlackPointFoundOnBorder = true; - atLeastOneBlackPointFoundOnBottom = true; - } else if (!atLeastOneBlackPointFoundOnBottom) { - down++; - } - } - - if (down >= height) { - sizeExceeded = true; - break; - } - - // ..... - // | . - // ..... - boolean leftBorderNotWhite = true; - while ((leftBorderNotWhite || !atLeastOneBlackPointFoundOnLeft) && left >= 0) { - leftBorderNotWhite = containsBlackPoint(up, down, left, false); - if (leftBorderNotWhite) { - left--; - aBlackPointFoundOnBorder = true; - atLeastOneBlackPointFoundOnLeft = true; - } else if (!atLeastOneBlackPointFoundOnLeft) { - left--; - } - } - - if (left < 0) { - sizeExceeded = true; - break; - } - - // .___. - // . . - // ..... - boolean topBorderNotWhite = true; - while ((topBorderNotWhite || !atLeastOneBlackPointFoundOnTop) && up >= 0) { - topBorderNotWhite = containsBlackPoint(left, right, up, true); - if (topBorderNotWhite) { - up--; - aBlackPointFoundOnBorder = true; - atLeastOneBlackPointFoundOnTop = true; - } else if (!atLeastOneBlackPointFoundOnTop) { - up--; - } - } - - if (up < 0) { - sizeExceeded = true; - break; - } - - } - - if (!sizeExceeded) { - - int maxSize = right - left; - - ResultPoint z = null; - for (int i = 1; z == null && i < maxSize; i++) { - z = getBlackPointOnSegment(left, down - i, left + i, down); - } - - if (z == null) { - throw NotFoundException.getNotFoundInstance(); - } - - ResultPoint t = null; - //go down right - for (int i = 1; t == null && i < maxSize; i++) { - t = getBlackPointOnSegment(left, up + i, left + i, up); - } - - if (t == null) { - throw NotFoundException.getNotFoundInstance(); - } - - ResultPoint x = null; - //go down left - for (int i = 1; x == null && i < maxSize; i++) { - x = getBlackPointOnSegment(right, up + i, right - i, up); - } - - if (x == null) { - throw NotFoundException.getNotFoundInstance(); - } - - ResultPoint y = null; - //go up left - for (int i = 1; y == null && i < maxSize; i++) { - y = getBlackPointOnSegment(right, down - i, right - i, down); - } - - if (y == null) { - throw NotFoundException.getNotFoundInstance(); - } - - return centerEdges(y, z, x, t); - - } else { - throw NotFoundException.getNotFoundInstance(); - } - } - - private ResultPoint getBlackPointOnSegment(float aX, float aY, float bX, float bY) { - int dist = MathUtils.round(MathUtils.distance(aX, aY, bX, bY)); - float xStep = (bX - aX) / dist; - float yStep = (bY - aY) / dist; - - for (int i = 0; i < dist; i++) { - int x = MathUtils.round(aX + i * xStep); - int y = MathUtils.round(aY + i * yStep); - if (image.get(x, y)) { - return new ResultPoint(x, y); - } - } - return null; - } - - /** - * recenters the points of a constant distance towards the center - * - * @param y bottom most point - * @param z left most point - * @param x right most point - * @param t top most point - * @return {@link ResultPoint}[] describing the corners of the rectangular - * region. The first and last points are opposed on the diagonal, as - * are the second and third. The first point will be the topmost - * point and the last, the bottommost. The second point will be - * leftmost and the third, the rightmost - */ - private ResultPoint[] centerEdges(ResultPoint y, ResultPoint z, - ResultPoint x, ResultPoint t) { - - // - // t t - // z x - // x OR z - // y y - // - - float yi = y.getX(); - float yj = y.getY(); - float zi = z.getX(); - float zj = z.getY(); - float xi = x.getX(); - float xj = x.getY(); - float ti = t.getX(); - float tj = t.getY(); - - if (yi < width / 2.0f) { - return new ResultPoint[]{ - new ResultPoint(ti - CORR, tj + CORR), - new ResultPoint(zi + CORR, zj + CORR), - new ResultPoint(xi - CORR, xj - CORR), - new ResultPoint(yi + CORR, yj - CORR)}; - } else { - return new ResultPoint[]{ - new ResultPoint(ti + CORR, tj + CORR), - new ResultPoint(zi + CORR, zj - CORR), - new ResultPoint(xi - CORR, xj + CORR), - new ResultPoint(yi - CORR, yj - CORR)}; - } - } - - /** - * Determines whether a segment contains a black point - * - * @param a min value of the scanned coordinate - * @param b max value of the scanned coordinate - * @param fixed value of fixed coordinate - * @param horizontal set to true if scan must be horizontal, false if vertical - * @return true if a black point has been found, else false. - */ - private boolean containsBlackPoint(int a, int b, int fixed, boolean horizontal) { - - if (horizontal) { - for (int x = a; x <= b; x++) { - if (image.get(x, fixed)) { - return true; - } - } - } else { - for (int y = a; y <= b; y++) { - if (image.get(fixed, y)) { - return true; - } - } - } - - return false; - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/common/reedsolomon/GenericGF.java b/TMessagesProj/src/main/java/com/google/zxing/common/reedsolomon/GenericGF.java deleted file mode 100755 index 2b32566c0..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/common/reedsolomon/GenericGF.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.common.reedsolomon; - -/** - *

This class contains utility methods for performing mathematical operations over - * the Galois Fields. Operations use a given primitive polynomial in calculations.

- * - *

Throughout this package, elements of the GF are represented as an {@code int} - * for convenience and speed (but at the cost of memory). - *

- * - * @author Sean Owen - * @author David Olivier - */ -public final class GenericGF { - - public static final GenericGF AZTEC_DATA_12 = new GenericGF(0x1069, 4096, 1); // x^12 + x^6 + x^5 + x^3 + 1 - public static final GenericGF AZTEC_DATA_10 = new GenericGF(0x409, 1024, 1); // x^10 + x^3 + 1 - public static final GenericGF AZTEC_DATA_6 = new GenericGF(0x43, 64, 1); // x^6 + x + 1 - public static final GenericGF AZTEC_PARAM = new GenericGF(0x13, 16, 1); // x^4 + x + 1 - public static final GenericGF QR_CODE_FIELD_256 = new GenericGF(0x011D, 256, 0); // x^8 + x^4 + x^3 + x^2 + 1 - public static final GenericGF DATA_MATRIX_FIELD_256 = new GenericGF(0x012D, 256, 1); // x^8 + x^5 + x^3 + x^2 + 1 - public static final GenericGF AZTEC_DATA_8 = DATA_MATRIX_FIELD_256; - public static final GenericGF MAXICODE_FIELD_64 = AZTEC_DATA_6; - - private final int[] expTable; - private final int[] logTable; - private final GenericGFPoly zero; - private final GenericGFPoly one; - private final int size; - private final int primitive; - private final int generatorBase; - - /** - * Create a representation of GF(size) using the given primitive polynomial. - * - * @param primitive irreducible polynomial whose coefficients are represented by - * the bits of an int, where the least-significant bit represents the constant - * coefficient - * @param size the size of the field - * @param b the factor b in the generator polynomial can be 0- or 1-based - * (g(x) = (x+a^b)(x+a^(b+1))...(x+a^(b+2t-1))). - * In most cases it should be 1, but for QR code it is 0. - */ - public GenericGF(int primitive, int size, int b) { - this.primitive = primitive; - this.size = size; - this.generatorBase = b; - - expTable = new int[size]; - logTable = new int[size]; - int x = 1; - for (int i = 0; i < size; i++) { - expTable[i] = x; - x *= 2; // we're assuming the generator alpha is 2 - if (x >= size) { - x ^= primitive; - x &= size - 1; - } - } - for (int i = 0; i < size - 1; i++) { - logTable[expTable[i]] = i; - } - // logTable[0] == 0 but this should never be used - zero = new GenericGFPoly(this, new int[]{0}); - one = new GenericGFPoly(this, new int[]{1}); - } - - GenericGFPoly getZero() { - return zero; - } - - GenericGFPoly getOne() { - return one; - } - - /** - * @return the monomial representing coefficient * x^degree - */ - GenericGFPoly buildMonomial(int degree, int coefficient) { - if (degree < 0) { - throw new IllegalArgumentException(); - } - if (coefficient == 0) { - return zero; - } - int[] coefficients = new int[degree + 1]; - coefficients[0] = coefficient; - return new GenericGFPoly(this, coefficients); - } - - /** - * Implements both addition and subtraction -- they are the same in GF(size). - * - * @return sum/difference of a and b - */ - static int addOrSubtract(int a, int b) { - return a ^ b; - } - - /** - * @return 2 to the power of a in GF(size) - */ - int exp(int a) { - return expTable[a]; - } - - /** - * @return base 2 log of a in GF(size) - */ - int log(int a) { - if (a == 0) { - throw new IllegalArgumentException(); - } - return logTable[a]; - } - - /** - * @return multiplicative inverse of a - */ - int inverse(int a) { - if (a == 0) { - throw new ArithmeticException(); - } - return expTable[size - logTable[a] - 1]; - } - - /** - * @return product of a and b in GF(size) - */ - int multiply(int a, int b) { - if (a == 0 || b == 0) { - return 0; - } - return expTable[(logTable[a] + logTable[b]) % (size - 1)]; - } - - public int getSize() { - return size; - } - - public int getGeneratorBase() { - return generatorBase; - } - - @Override - public String toString() { - return "GF(0x" + Integer.toHexString(primitive) + ',' + size + ')'; - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/common/reedsolomon/GenericGFPoly.java b/TMessagesProj/src/main/java/com/google/zxing/common/reedsolomon/GenericGFPoly.java deleted file mode 100755 index 03f67430d..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/common/reedsolomon/GenericGFPoly.java +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.common.reedsolomon; - -/** - *

Represents a polynomial whose coefficients are elements of a GF. - * Instances of this class are immutable.

- * - *

Much credit is due to William Rucklidge since portions of this code are an indirect - * port of his C++ Reed-Solomon implementation.

- * - * @author Sean Owen - */ -final class GenericGFPoly { - - private final GenericGF field; - private final int[] coefficients; - - /** - * @param field the {@link GenericGF} instance representing the field to use - * to perform computations - * @param coefficients coefficients as ints representing elements of GF(size), arranged - * from most significant (highest-power term) coefficient to least significant - * @throws IllegalArgumentException if argument is null or empty, - * or if leading coefficient is 0 and this is not a - * constant polynomial (that is, it is not the monomial "0") - */ - GenericGFPoly(GenericGF field, int[] coefficients) { - if (coefficients.length == 0) { - throw new IllegalArgumentException(); - } - this.field = field; - int coefficientsLength = coefficients.length; - if (coefficientsLength > 1 && coefficients[0] == 0) { - // Leading term must be non-zero for anything except the constant polynomial "0" - int firstNonZero = 1; - while (firstNonZero < coefficientsLength && coefficients[firstNonZero] == 0) { - firstNonZero++; - } - if (firstNonZero == coefficientsLength) { - this.coefficients = new int[]{0}; - } else { - this.coefficients = new int[coefficientsLength - firstNonZero]; - System.arraycopy(coefficients, - firstNonZero, - this.coefficients, - 0, - this.coefficients.length); - } - } else { - this.coefficients = coefficients; - } - } - - int[] getCoefficients() { - return coefficients; - } - - /** - * @return degree of this polynomial - */ - int getDegree() { - return coefficients.length - 1; - } - - /** - * @return true iff this polynomial is the monomial "0" - */ - boolean isZero() { - return coefficients[0] == 0; - } - - /** - * @return coefficient of x^degree term in this polynomial - */ - int getCoefficient(int degree) { - return coefficients[coefficients.length - 1 - degree]; - } - - /** - * @return evaluation of this polynomial at a given point - */ - int evaluateAt(int a) { - if (a == 0) { - // Just return the x^0 coefficient - return getCoefficient(0); - } - if (a == 1) { - // Just the sum of the coefficients - int result = 0; - for (int coefficient : coefficients) { - result = GenericGF.addOrSubtract(result, coefficient); - } - return result; - } - int result = coefficients[0]; - int size = coefficients.length; - for (int i = 1; i < size; i++) { - result = GenericGF.addOrSubtract(field.multiply(a, result), coefficients[i]); - } - return result; - } - - GenericGFPoly addOrSubtract(GenericGFPoly other) { - if (!field.equals(other.field)) { - throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field"); - } - if (isZero()) { - return other; - } - if (other.isZero()) { - return this; - } - - int[] smallerCoefficients = this.coefficients; - int[] largerCoefficients = other.coefficients; - if (smallerCoefficients.length > largerCoefficients.length) { - int[] temp = smallerCoefficients; - smallerCoefficients = largerCoefficients; - largerCoefficients = temp; - } - int[] sumDiff = new int[largerCoefficients.length]; - int lengthDiff = largerCoefficients.length - smallerCoefficients.length; - // Copy high-order terms only found in higher-degree polynomial's coefficients - System.arraycopy(largerCoefficients, 0, sumDiff, 0, lengthDiff); - - for (int i = lengthDiff; i < largerCoefficients.length; i++) { - sumDiff[i] = GenericGF.addOrSubtract(smallerCoefficients[i - lengthDiff], largerCoefficients[i]); - } - - return new GenericGFPoly(field, sumDiff); - } - - GenericGFPoly multiply(GenericGFPoly other) { - if (!field.equals(other.field)) { - throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field"); - } - if (isZero() || other.isZero()) { - return field.getZero(); - } - int[] aCoefficients = this.coefficients; - int aLength = aCoefficients.length; - int[] bCoefficients = other.coefficients; - int bLength = bCoefficients.length; - int[] product = new int[aLength + bLength - 1]; - for (int i = 0; i < aLength; i++) { - int aCoeff = aCoefficients[i]; - for (int j = 0; j < bLength; j++) { - product[i + j] = GenericGF.addOrSubtract(product[i + j], - field.multiply(aCoeff, bCoefficients[j])); - } - } - return new GenericGFPoly(field, product); - } - - GenericGFPoly multiply(int scalar) { - if (scalar == 0) { - return field.getZero(); - } - if (scalar == 1) { - return this; - } - int size = coefficients.length; - int[] product = new int[size]; - for (int i = 0; i < size; i++) { - product[i] = field.multiply(coefficients[i], scalar); - } - return new GenericGFPoly(field, product); - } - - GenericGFPoly multiplyByMonomial(int degree, int coefficient) { - if (degree < 0) { - throw new IllegalArgumentException(); - } - if (coefficient == 0) { - return field.getZero(); - } - int size = coefficients.length; - int[] product = new int[size + degree]; - for (int i = 0; i < size; i++) { - product[i] = field.multiply(coefficients[i], coefficient); - } - return new GenericGFPoly(field, product); - } - - GenericGFPoly[] divide(GenericGFPoly other) { - if (!field.equals(other.field)) { - throw new IllegalArgumentException("GenericGFPolys do not have same GenericGF field"); - } - if (other.isZero()) { - throw new IllegalArgumentException("Divide by 0"); - } - - GenericGFPoly quotient = field.getZero(); - GenericGFPoly remainder = this; - - int denominatorLeadingTerm = other.getCoefficient(other.getDegree()); - int inverseDenominatorLeadingTerm = field.inverse(denominatorLeadingTerm); - - while (remainder.getDegree() >= other.getDegree() && !remainder.isZero()) { - int degreeDifference = remainder.getDegree() - other.getDegree(); - int scale = field.multiply(remainder.getCoefficient(remainder.getDegree()), inverseDenominatorLeadingTerm); - GenericGFPoly term = other.multiplyByMonomial(degreeDifference, scale); - GenericGFPoly iterationQuotient = field.buildMonomial(degreeDifference, scale); - quotient = quotient.addOrSubtract(iterationQuotient); - remainder = remainder.addOrSubtract(term); - } - - return new GenericGFPoly[] { quotient, remainder }; - } - - @Override - public String toString() { - if (isZero()) { - return "0"; - } - StringBuilder result = new StringBuilder(8 * getDegree()); - for (int degree = getDegree(); degree >= 0; degree--) { - int coefficient = getCoefficient(degree); - if (coefficient != 0) { - if (coefficient < 0) { - if (degree == getDegree()) { - result.append("-"); - } else { - result.append(" - "); - } - coefficient = -coefficient; - } else { - if (result.length() > 0) { - result.append(" + "); - } - } - if (degree == 0 || coefficient != 1) { - int alphaPower = field.log(coefficient); - if (alphaPower == 0) { - result.append('1'); - } else if (alphaPower == 1) { - result.append('a'); - } else { - result.append("a^"); - result.append(alphaPower); - } - } - if (degree != 0) { - if (degree == 1) { - result.append('x'); - } else { - result.append("x^"); - result.append(degree); - } - } - } - } - return result.toString(); - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/common/reedsolomon/ReedSolomonDecoder.java b/TMessagesProj/src/main/java/com/google/zxing/common/reedsolomon/ReedSolomonDecoder.java deleted file mode 100755 index e406e2f00..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/common/reedsolomon/ReedSolomonDecoder.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.common.reedsolomon; - -/** - *

Implements Reed-Solomon decoding, as the name implies.

- * - *

The algorithm will not be explained here, but the following references were helpful - * in creating this implementation:

- * - * - * - *

Much credit is due to William Rucklidge since portions of this code are an indirect - * port of his C++ Reed-Solomon implementation.

- * - * @author Sean Owen - * @author William Rucklidge - * @author sanfordsquires - */ -public final class ReedSolomonDecoder { - - private final GenericGF field; - - public ReedSolomonDecoder(GenericGF field) { - this.field = field; - } - - /** - *

Decodes given set of received codewords, which include both data and error-correction - * codewords. Really, this means it uses Reed-Solomon to detect and correct errors, in-place, - * in the input.

- * - * @param received data and error-correction codewords - * @param twoS number of error-correction codewords available - * @throws ReedSolomonException if decoding fails for any reason - */ - public void decode(int[] received, int twoS) throws ReedSolomonException { - GenericGFPoly poly = new GenericGFPoly(field, received); - int[] syndromeCoefficients = new int[twoS]; - boolean noError = true; - for (int i = 0; i < twoS; i++) { - int eval = poly.evaluateAt(field.exp(i + field.getGeneratorBase())); - syndromeCoefficients[syndromeCoefficients.length - 1 - i] = eval; - if (eval != 0) { - noError = false; - } - } - if (noError) { - return; - } - GenericGFPoly syndrome = new GenericGFPoly(field, syndromeCoefficients); - GenericGFPoly[] sigmaOmega = - runEuclideanAlgorithm(field.buildMonomial(twoS, 1), syndrome, twoS); - GenericGFPoly sigma = sigmaOmega[0]; - GenericGFPoly omega = sigmaOmega[1]; - int[] errorLocations = findErrorLocations(sigma); - int[] errorMagnitudes = findErrorMagnitudes(omega, errorLocations); - for (int i = 0; i < errorLocations.length; i++) { - int position = received.length - 1 - field.log(errorLocations[i]); - if (position < 0) { - throw new ReedSolomonException("Bad error location"); - } - received[position] = GenericGF.addOrSubtract(received[position], errorMagnitudes[i]); - } - } - - private GenericGFPoly[] runEuclideanAlgorithm(GenericGFPoly a, GenericGFPoly b, int R) - throws ReedSolomonException { - // Assume a's degree is >= b's - if (a.getDegree() < b.getDegree()) { - GenericGFPoly temp = a; - a = b; - b = temp; - } - - GenericGFPoly rLast = a; - GenericGFPoly r = b; - GenericGFPoly tLast = field.getZero(); - GenericGFPoly t = field.getOne(); - - // Run Euclidean algorithm until r's degree is less than R/2 - while (r.getDegree() >= R / 2) { - GenericGFPoly rLastLast = rLast; - GenericGFPoly tLastLast = tLast; - rLast = r; - tLast = t; - - // Divide rLastLast by rLast, with quotient in q and remainder in r - if (rLast.isZero()) { - // Oops, Euclidean algorithm already terminated? - throw new ReedSolomonException("r_{i-1} was zero"); - } - r = rLastLast; - GenericGFPoly q = field.getZero(); - int denominatorLeadingTerm = rLast.getCoefficient(rLast.getDegree()); - int dltInverse = field.inverse(denominatorLeadingTerm); - while (r.getDegree() >= rLast.getDegree() && !r.isZero()) { - int degreeDiff = r.getDegree() - rLast.getDegree(); - int scale = field.multiply(r.getCoefficient(r.getDegree()), dltInverse); - q = q.addOrSubtract(field.buildMonomial(degreeDiff, scale)); - r = r.addOrSubtract(rLast.multiplyByMonomial(degreeDiff, scale)); - } - - t = q.multiply(tLast).addOrSubtract(tLastLast); - - if (r.getDegree() >= rLast.getDegree()) { - throw new IllegalStateException("Division algorithm failed to reduce polynomial?"); - } - } - - int sigmaTildeAtZero = t.getCoefficient(0); - if (sigmaTildeAtZero == 0) { - throw new ReedSolomonException("sigmaTilde(0) was zero"); - } - - int inverse = field.inverse(sigmaTildeAtZero); - GenericGFPoly sigma = t.multiply(inverse); - GenericGFPoly omega = r.multiply(inverse); - return new GenericGFPoly[]{sigma, omega}; - } - - private int[] findErrorLocations(GenericGFPoly errorLocator) throws ReedSolomonException { - // This is a direct application of Chien's search - int numErrors = errorLocator.getDegree(); - if (numErrors == 1) { // shortcut - return new int[] { errorLocator.getCoefficient(1) }; - } - int[] result = new int[numErrors]; - int e = 0; - for (int i = 1; i < field.getSize() && e < numErrors; i++) { - if (errorLocator.evaluateAt(i) == 0) { - result[e] = field.inverse(i); - e++; - } - } - if (e != numErrors) { - throw new ReedSolomonException("Error locator degree does not match number of roots"); - } - return result; - } - - private int[] findErrorMagnitudes(GenericGFPoly errorEvaluator, int[] errorLocations) { - // This is directly applying Forney's Formula - int s = errorLocations.length; - int[] result = new int[s]; - for (int i = 0; i < s; i++) { - int xiInverse = field.inverse(errorLocations[i]); - int denominator = 1; - for (int j = 0; j < s; j++) { - if (i != j) { - //denominator = field.multiply(denominator, - // GenericGF.addOrSubtract(1, field.multiply(errorLocations[j], xiInverse))); - // Above should work but fails on some Apple and Linux JDKs due to a Hotspot bug. - // Below is a funny-looking workaround from Steven Parkes - int term = field.multiply(errorLocations[j], xiInverse); - int termPlus1 = (term & 0x1) == 0 ? term | 1 : term & ~1; - denominator = field.multiply(denominator, termPlus1); - } - } - result[i] = field.multiply(errorEvaluator.evaluateAt(xiInverse), - field.inverse(denominator)); - if (field.getGeneratorBase() != 0) { - result[i] = field.multiply(result[i], xiInverse); - } - } - return result; - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java b/TMessagesProj/src/main/java/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java deleted file mode 100755 index 2e2b2f0a6..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2008 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.common.reedsolomon; - -import java.util.ArrayList; -import java.util.List; - -/** - *

Implements Reed-Solomon encoding, as the name implies.

- * - * @author Sean Owen - * @author William Rucklidge - */ -public final class ReedSolomonEncoder { - - private final GenericGF field; - private final List cachedGenerators; - - public ReedSolomonEncoder(GenericGF field) { - this.field = field; - this.cachedGenerators = new ArrayList<>(); - cachedGenerators.add(new GenericGFPoly(field, new int[]{1})); - } - - private GenericGFPoly buildGenerator(int degree) { - if (degree >= cachedGenerators.size()) { - GenericGFPoly lastGenerator = cachedGenerators.get(cachedGenerators.size() - 1); - for (int d = cachedGenerators.size(); d <= degree; d++) { - GenericGFPoly nextGenerator = lastGenerator.multiply( - new GenericGFPoly(field, new int[] { 1, field.exp(d - 1 + field.getGeneratorBase()) })); - cachedGenerators.add(nextGenerator); - lastGenerator = nextGenerator; - } - } - return cachedGenerators.get(degree); - } - - public void encode(int[] toEncode, int ecBytes) { - if (ecBytes == 0) { - throw new IllegalArgumentException("No error correction bytes"); - } - int dataBytes = toEncode.length - ecBytes; - if (dataBytes <= 0) { - throw new IllegalArgumentException("No data bytes provided"); - } - GenericGFPoly generator = buildGenerator(ecBytes); - int[] infoCoefficients = new int[dataBytes]; - System.arraycopy(toEncode, 0, infoCoefficients, 0, dataBytes); - GenericGFPoly info = new GenericGFPoly(field, infoCoefficients); - info = info.multiplyByMonomial(ecBytes, 1); - GenericGFPoly remainder = info.divide(generator)[1]; - int[] coefficients = remainder.getCoefficients(); - int numZeroCoefficients = ecBytes - coefficients.length; - for (int i = 0; i < numZeroCoefficients; i++) { - toEncode[dataBytes + i] = 0; - } - System.arraycopy(coefficients, 0, toEncode, dataBytes + numZeroCoefficients, coefficients.length); - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/common/reedsolomon/ReedSolomonException.java b/TMessagesProj/src/main/java/com/google/zxing/common/reedsolomon/ReedSolomonException.java deleted file mode 100755 index d5b45a612..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/common/reedsolomon/ReedSolomonException.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.common.reedsolomon; - -/** - *

Thrown when an exception occurs during Reed-Solomon decoding, such as when - * there are too many errors to correct.

- * - * @author Sean Owen - */ -public final class ReedSolomonException extends Exception { - - public ReedSolomonException(String message) { - super(message); - } - -} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/com/google/zxing/multi/ByQuadrantReader.java b/TMessagesProj/src/main/java/com/google/zxing/multi/ByQuadrantReader.java deleted file mode 100755 index 6674c8f35..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/multi/ByQuadrantReader.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2009 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.multi; - -import com.google.zxing.BinaryBitmap; -import com.google.zxing.ChecksumException; -import com.google.zxing.DecodeHintType; -import com.google.zxing.FormatException; -import com.google.zxing.NotFoundException; -import com.google.zxing.Reader; -import com.google.zxing.Result; -import com.google.zxing.ResultPoint; - -import java.util.Map; - -/** - * This class attempts to decode a barcode from an image, not by scanning the whole image, - * but by scanning subsets of the image. This is important when there may be multiple barcodes in - * an image, and detecting a barcode may find parts of multiple barcode and fail to decode - * (e.g. QR Codes). Instead this scans the four quadrants of the image -- and also the center - * 'quadrant' to cover the case where a barcode is found in the center. - * - * @see GenericMultipleBarcodeReader - */ -public final class ByQuadrantReader implements Reader { - - private final Reader delegate; - - public ByQuadrantReader(Reader delegate) { - this.delegate = delegate; - } - - @Override - public Result decode(BinaryBitmap image) - throws NotFoundException, ChecksumException, FormatException { - return decode(image, null); - } - - @Override - public Result decode(BinaryBitmap image, Map hints) - throws NotFoundException, ChecksumException, FormatException { - - int width = image.getWidth(); - int height = image.getHeight(); - int halfWidth = width / 2; - int halfHeight = height / 2; - - try { - // No need to call makeAbsolute as results will be relative to original top left here - return delegate.decode(image.crop(0, 0, halfWidth, halfHeight), hints); - } catch (NotFoundException re) { - // continue - } - - try { - Result result = delegate.decode(image.crop(halfWidth, 0, halfWidth, halfHeight), hints); - makeAbsolute(result.getResultPoints(), halfWidth, 0); - return result; - } catch (NotFoundException re) { - // continue - } - - try { - Result result = delegate.decode(image.crop(0, halfHeight, halfWidth, halfHeight), hints); - makeAbsolute(result.getResultPoints(), 0, halfHeight); - return result; - } catch (NotFoundException re) { - // continue - } - - try { - Result result = delegate.decode(image.crop(halfWidth, halfHeight, halfWidth, halfHeight), hints); - makeAbsolute(result.getResultPoints(), halfWidth, halfHeight); - return result; - } catch (NotFoundException re) { - // continue - } - - int quarterWidth = halfWidth / 2; - int quarterHeight = halfHeight / 2; - BinaryBitmap center = image.crop(quarterWidth, quarterHeight, halfWidth, halfHeight); - Result result = delegate.decode(center, hints); - makeAbsolute(result.getResultPoints(), quarterWidth, quarterHeight); - return result; - } - - @Override - public void reset() { - delegate.reset(); - } - - private static void makeAbsolute(ResultPoint[] points, int leftOffset, int topOffset) { - if (points != null) { - for (int i = 0; i < points.length; i++) { - ResultPoint relative = points[i]; - if (relative != null) { - points[i] = new ResultPoint(relative.getX() + leftOffset, relative.getY() + topOffset); - } - } - } - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/multi/GenericMultipleBarcodeReader.java b/TMessagesProj/src/main/java/com/google/zxing/multi/GenericMultipleBarcodeReader.java deleted file mode 100755 index 5e53419b5..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/multi/GenericMultipleBarcodeReader.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright 2009 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.multi; - -import com.google.zxing.BinaryBitmap; -import com.google.zxing.DecodeHintType; -import com.google.zxing.NotFoundException; -import com.google.zxing.Reader; -import com.google.zxing.ReaderException; -import com.google.zxing.Result; -import com.google.zxing.ResultPoint; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -/** - *

Attempts to locate multiple barcodes in an image by repeatedly decoding portion of the image. - * After one barcode is found, the areas left, above, right and below the barcode's - * {@link ResultPoint}s are scanned, recursively.

- * - *

A caller may want to also employ {@link ByQuadrantReader} when attempting to find multiple - * 2D barcodes, like QR Codes, in an image, where the presence of multiple barcodes might prevent - * detecting any one of them.

- * - *

That is, instead of passing a {@link Reader} a caller might pass - * {@code new ByQuadrantReader(reader)}.

- * - * @author Sean Owen - */ -public final class GenericMultipleBarcodeReader implements MultipleBarcodeReader { - - private static final int MIN_DIMENSION_TO_RECUR = 100; - private static final int MAX_DEPTH = 4; - - static final Result[] EMPTY_RESULT_ARRAY = new Result[0]; - - private final Reader delegate; - - public GenericMultipleBarcodeReader(Reader delegate) { - this.delegate = delegate; - } - - @Override - public Result[] decodeMultiple(BinaryBitmap image) throws NotFoundException { - return decodeMultiple(image, null); - } - - @Override - public Result[] decodeMultiple(BinaryBitmap image, Map hints) - throws NotFoundException { - List results = new ArrayList<>(); - doDecodeMultiple(image, hints, results, 0, 0, 0); - if (results.isEmpty()) { - throw NotFoundException.getNotFoundInstance(); - } - return results.toArray(EMPTY_RESULT_ARRAY); - } - - private void doDecodeMultiple(BinaryBitmap image, - Map hints, - List results, - int xOffset, - int yOffset, - int currentDepth) { - if (currentDepth > MAX_DEPTH) { - return; - } - - Result result; - try { - result = delegate.decode(image, hints); - } catch (ReaderException ignored) { - return; - } - boolean alreadyFound = false; - for (Result existingResult : results) { - if (existingResult.getText().equals(result.getText())) { - alreadyFound = true; - break; - } - } - if (!alreadyFound) { - results.add(translateResultPoints(result, xOffset, yOffset)); - } - ResultPoint[] resultPoints = result.getResultPoints(); - if (resultPoints == null || resultPoints.length == 0) { - return; - } - int width = image.getWidth(); - int height = image.getHeight(); - float minX = width; - float minY = height; - float maxX = 0.0f; - float maxY = 0.0f; - for (ResultPoint point : resultPoints) { - if (point == null) { - continue; - } - float x = point.getX(); - float y = point.getY(); - if (x < minX) { - minX = x; - } - if (y < minY) { - minY = y; - } - if (x > maxX) { - maxX = x; - } - if (y > maxY) { - maxY = y; - } - } - - // Decode left of barcode - if (minX > MIN_DIMENSION_TO_RECUR) { - doDecodeMultiple(image.crop(0, 0, (int) minX, height), - hints, results, - xOffset, yOffset, - currentDepth + 1); - } - // Decode above barcode - if (minY > MIN_DIMENSION_TO_RECUR) { - doDecodeMultiple(image.crop(0, 0, width, (int) minY), - hints, results, - xOffset, yOffset, - currentDepth + 1); - } - // Decode right of barcode - if (maxX < width - MIN_DIMENSION_TO_RECUR) { - doDecodeMultiple(image.crop((int) maxX, 0, width - (int) maxX, height), - hints, results, - xOffset + (int) maxX, yOffset, - currentDepth + 1); - } - // Decode below barcode - if (maxY < height - MIN_DIMENSION_TO_RECUR) { - doDecodeMultiple(image.crop(0, (int) maxY, width, height - (int) maxY), - hints, results, - xOffset, yOffset + (int) maxY, - currentDepth + 1); - } - } - - private static Result translateResultPoints(Result result, int xOffset, int yOffset) { - ResultPoint[] oldResultPoints = result.getResultPoints(); - if (oldResultPoints == null) { - return result; - } - ResultPoint[] newResultPoints = new ResultPoint[oldResultPoints.length]; - for (int i = 0; i < oldResultPoints.length; i++) { - ResultPoint oldPoint = oldResultPoints[i]; - if (oldPoint != null) { - newResultPoints[i] = new ResultPoint(oldPoint.getX() + xOffset, oldPoint.getY() + yOffset); - } - } - Result newResult = new Result(result.getText(), - result.getRawBytes(), - result.getNumBits(), - newResultPoints, - result.getBarcodeFormat(), - result.getTimestamp()); - newResult.putAllMetadata(result.getResultMetadata()); - return newResult; - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/multi/MultipleBarcodeReader.java b/TMessagesProj/src/main/java/com/google/zxing/multi/MultipleBarcodeReader.java deleted file mode 100755 index a35872799..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/multi/MultipleBarcodeReader.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2009 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.multi; - -import com.google.zxing.BinaryBitmap; -import com.google.zxing.DecodeHintType; -import com.google.zxing.NotFoundException; -import com.google.zxing.Result; - -import java.util.Map; - -/** - * Implementation of this interface attempt to read several barcodes from one image. - * - * @see com.google.zxing.Reader - * @author Sean Owen - */ -public interface MultipleBarcodeReader { - - Result[] decodeMultiple(BinaryBitmap image) throws NotFoundException; - - Result[] decodeMultiple(BinaryBitmap image, - Map hints) throws NotFoundException; - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/multi/qrcode/QRCodeMultiReader.java b/TMessagesProj/src/main/java/com/google/zxing/multi/qrcode/QRCodeMultiReader.java deleted file mode 100755 index c3663498b..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/multi/qrcode/QRCodeMultiReader.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2009 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.multi.qrcode; - -import com.google.zxing.BarcodeFormat; -import com.google.zxing.BinaryBitmap; -import com.google.zxing.DecodeHintType; -import com.google.zxing.NotFoundException; -import com.google.zxing.ReaderException; -import com.google.zxing.Result; -import com.google.zxing.ResultMetadataType; -import com.google.zxing.ResultPoint; -import com.google.zxing.common.DecoderResult; -import com.google.zxing.common.DetectorResult; -import com.google.zxing.multi.MultipleBarcodeReader; -import com.google.zxing.multi.qrcode.detector.MultiDetector; -import com.google.zxing.qrcode.QRCodeReader; -import com.google.zxing.qrcode.decoder.QRCodeDecoderMetaData; - -import java.io.ByteArrayOutputStream; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Collections; -import java.util.Comparator; - -/** - * This implementation can detect and decode multiple QR Codes in an image. - * - * @author Sean Owen - * @author Hannes Erven - */ -public final class QRCodeMultiReader extends QRCodeReader implements MultipleBarcodeReader { - - private static final Result[] EMPTY_RESULT_ARRAY = new Result[0]; - private static final ResultPoint[] NO_POINTS = new ResultPoint[0]; - - @Override - public Result[] decodeMultiple(BinaryBitmap image) throws NotFoundException { - return decodeMultiple(image, null); - } - - @Override - public Result[] decodeMultiple(BinaryBitmap image, Map hints) throws NotFoundException { - List results = new ArrayList<>(); - DetectorResult[] detectorResults = new MultiDetector(image.getBlackMatrix()).detectMulti(hints); - for (DetectorResult detectorResult : detectorResults) { - try { - DecoderResult decoderResult = getDecoder().decode(detectorResult.getBits(), hints); - ResultPoint[] points = detectorResult.getPoints(); - // If the code was mirrored: swap the bottom-left and the top-right points. - if (decoderResult.getOther() instanceof QRCodeDecoderMetaData) { - ((QRCodeDecoderMetaData) decoderResult.getOther()).applyMirroredCorrection(points); - } - Result result = new Result(decoderResult.getText(), decoderResult.getRawBytes(), points, - BarcodeFormat.QR_CODE); - List byteSegments = decoderResult.getByteSegments(); - if (byteSegments != null) { - result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, byteSegments); - } - String ecLevel = decoderResult.getECLevel(); - if (ecLevel != null) { - result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel); - } - if (decoderResult.hasStructuredAppend()) { - result.putMetadata(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE, - decoderResult.getStructuredAppendSequenceNumber()); - result.putMetadata(ResultMetadataType.STRUCTURED_APPEND_PARITY, - decoderResult.getStructuredAppendParity()); - } - results.add(result); - } catch (ReaderException re) { - // ignore and continue - } - } - if (results.isEmpty()) { - return EMPTY_RESULT_ARRAY; - } else { - results = processStructuredAppend(results); - return results.toArray(EMPTY_RESULT_ARRAY); - } - } - - static List processStructuredAppend(List results) { - List newResults = new ArrayList<>(); - List saResults = new ArrayList<>(); - for (Result result : results) { - if (result.getResultMetadata().containsKey(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE)) { - saResults.add(result); - } else { - newResults.add(result); - } - } - if (saResults.isEmpty()) { - return results; - } - - // sort and concatenate the SA list items - Collections.sort(saResults, new SAComparator()); - StringBuilder newText = new StringBuilder(); - ByteArrayOutputStream newRawBytes = new ByteArrayOutputStream(); - ByteArrayOutputStream newByteSegment = new ByteArrayOutputStream(); - for (Result saResult : saResults) { - newText.append(saResult.getText()); - byte[] saBytes = saResult.getRawBytes(); - newRawBytes.write(saBytes, 0, saBytes.length); - @SuppressWarnings("unchecked") - Iterable byteSegments = - (Iterable) saResult.getResultMetadata().get(ResultMetadataType.BYTE_SEGMENTS); - if (byteSegments != null) { - for (byte[] segment : byteSegments) { - newByteSegment.write(segment, 0, segment.length); - } - } - } - - Result newResult = new Result(newText.toString(), newRawBytes.toByteArray(), NO_POINTS, BarcodeFormat.QR_CODE); - if (newByteSegment.size() > 0) { - newResult.putMetadata(ResultMetadataType.BYTE_SEGMENTS, Collections.singletonList(newByteSegment.toByteArray())); - } - newResults.add(newResult); - return newResults; - } - - private static final class SAComparator implements Comparator, Serializable { - @Override - public int compare(Result a, Result b) { - int aNumber = (int) a.getResultMetadata().get(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE); - int bNumber = (int) b.getResultMetadata().get(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE); - return Integer.compare(aNumber, bNumber); - } - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/multi/qrcode/detector/MultiDetector.java b/TMessagesProj/src/main/java/com/google/zxing/multi/qrcode/detector/MultiDetector.java deleted file mode 100755 index 512b452c8..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/multi/qrcode/detector/MultiDetector.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2009 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.multi.qrcode.detector; - -import com.google.zxing.DecodeHintType; -import com.google.zxing.NotFoundException; -import com.google.zxing.ReaderException; -import com.google.zxing.ResultPointCallback; -import com.google.zxing.common.BitMatrix; -import com.google.zxing.common.DetectorResult; -import com.google.zxing.qrcode.detector.Detector; -import com.google.zxing.qrcode.detector.FinderPatternInfo; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -/** - *

Encapsulates logic that can detect one or more QR Codes in an image, even if the QR Code - * is rotated or skewed, or partially obscured.

- * - * @author Sean Owen - * @author Hannes Erven - */ -public final class MultiDetector extends Detector { - - private static final DetectorResult[] EMPTY_DETECTOR_RESULTS = new DetectorResult[0]; - - public MultiDetector(BitMatrix image) { - super(image); - } - - public DetectorResult[] detectMulti(Map hints) throws NotFoundException { - BitMatrix image = getImage(); - ResultPointCallback resultPointCallback = - hints == null ? null : (ResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK); - MultiFinderPatternFinder finder = new MultiFinderPatternFinder(image, resultPointCallback); - FinderPatternInfo[] infos = finder.findMulti(hints); - - if (infos.length == 0) { - throw NotFoundException.getNotFoundInstance(); - } - - List result = new ArrayList<>(); - for (FinderPatternInfo info : infos) { - try { - result.add(processFinderPatternInfo(info)); - } catch (ReaderException e) { - // ignore - } - } - if (result.isEmpty()) { - return EMPTY_DETECTOR_RESULTS; - } else { - return result.toArray(EMPTY_DETECTOR_RESULTS); - } - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/multi/qrcode/detector/MultiFinderPatternFinder.java b/TMessagesProj/src/main/java/com/google/zxing/multi/qrcode/detector/MultiFinderPatternFinder.java deleted file mode 100755 index c5ecd6adf..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/multi/qrcode/detector/MultiFinderPatternFinder.java +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Copyright 2009 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.multi.qrcode.detector; - -import com.google.zxing.DecodeHintType; -import com.google.zxing.NotFoundException; -import com.google.zxing.ResultPoint; -import com.google.zxing.ResultPointCallback; -import com.google.zxing.common.BitMatrix; -import com.google.zxing.qrcode.detector.FinderPattern; -import com.google.zxing.qrcode.detector.FinderPatternFinder; -import com.google.zxing.qrcode.detector.FinderPatternInfo; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Map; - -/** - *

This class attempts to find finder patterns in a QR Code. Finder patterns are the square - * markers at three corners of a QR Code.

- * - *

This class is thread-safe but not reentrant. Each thread must allocate its own object. - * - *

In contrast to {@link FinderPatternFinder}, this class will return an array of all possible - * QR code locations in the image.

- * - *

Use the TRY_HARDER hint to ask for a more thorough detection.

- * - * @author Sean Owen - * @author Hannes Erven - */ -final class MultiFinderPatternFinder extends FinderPatternFinder { - - private static final FinderPatternInfo[] EMPTY_RESULT_ARRAY = new FinderPatternInfo[0]; - private static final FinderPattern[] EMPTY_FP_ARRAY = new FinderPattern[0]; - private static final FinderPattern[][] EMPTY_FP_2D_ARRAY = new FinderPattern[0][]; - - // TODO MIN_MODULE_COUNT and MAX_MODULE_COUNT would be great hints to ask the user for - // since it limits the number of regions to decode - - // max. legal count of modules per QR code edge (177) - private static final float MAX_MODULE_COUNT_PER_EDGE = 180; - // min. legal count per modules per QR code edge (11) - private static final float MIN_MODULE_COUNT_PER_EDGE = 9; - - /** - * More or less arbitrary cutoff point for determining if two finder patterns might belong - * to the same code if they differ less than DIFF_MODSIZE_CUTOFF_PERCENT percent in their - * estimated modules sizes. - */ - private static final float DIFF_MODSIZE_CUTOFF_PERCENT = 0.05f; - - /** - * More or less arbitrary cutoff point for determining if two finder patterns might belong - * to the same code if they differ less than DIFF_MODSIZE_CUTOFF pixels/module in their - * estimated modules sizes. - */ - private static final float DIFF_MODSIZE_CUTOFF = 0.5f; - - - /** - * A comparator that orders FinderPatterns by their estimated module size. - */ - private static final class ModuleSizeComparator implements Comparator, Serializable { - @Override - public int compare(FinderPattern center1, FinderPattern center2) { - float value = center2.getEstimatedModuleSize() - center1.getEstimatedModuleSize(); - return value < 0.0 ? -1 : value > 0.0 ? 1 : 0; - } - } - - MultiFinderPatternFinder(BitMatrix image, ResultPointCallback resultPointCallback) { - super(image, resultPointCallback); - } - - /** - * @return the 3 best {@link FinderPattern}s from our list of candidates. The "best" are - * those that have been detected at least 2 times, and whose module - * size differs from the average among those patterns the least - * @throws NotFoundException if 3 such finder patterns do not exist - */ - private FinderPattern[][] selectMultipleBestPatterns() throws NotFoundException { - List possibleCenters = getPossibleCenters(); - int size = possibleCenters.size(); - - if (size < 3) { - // Couldn't find enough finder patterns - throw NotFoundException.getNotFoundInstance(); - } - - /* - * Begin HE modifications to safely detect multiple codes of equal size - */ - if (size == 3) { - return new FinderPattern[][] { possibleCenters.toArray(EMPTY_FP_ARRAY) }; - } - - // Sort by estimated module size to speed up the upcoming checks - Collections.sort(possibleCenters, new ModuleSizeComparator()); - - /* - * Now lets start: build a list of tuples of three finder locations that - * - feature similar module sizes - * - are placed in a distance so the estimated module count is within the QR specification - * - have similar distance between upper left/right and left top/bottom finder patterns - * - form a triangle with 90° angle (checked by comparing top right/bottom left distance - * with pythagoras) - * - * Note: we allow each point to be used for more than one code region: this might seem - * counterintuitive at first, but the performance penalty is not that big. At this point, - * we cannot make a good quality decision whether the three finders actually represent - * a QR code, or are just by chance laid out so it looks like there might be a QR code there. - * So, if the layout seems right, lets have the decoder try to decode. - */ - - List results = new ArrayList<>(); // holder for the results - - for (int i1 = 0; i1 < (size - 2); i1++) { - FinderPattern p1 = possibleCenters.get(i1); - if (p1 == null) { - continue; - } - - for (int i2 = i1 + 1; i2 < (size - 1); i2++) { - FinderPattern p2 = possibleCenters.get(i2); - if (p2 == null) { - continue; - } - - // Compare the expected module sizes; if they are really off, skip - float vModSize12 = (p1.getEstimatedModuleSize() - p2.getEstimatedModuleSize()) / - Math.min(p1.getEstimatedModuleSize(), p2.getEstimatedModuleSize()); - float vModSize12A = Math.abs(p1.getEstimatedModuleSize() - p2.getEstimatedModuleSize()); - if (vModSize12A > DIFF_MODSIZE_CUTOFF && vModSize12 >= DIFF_MODSIZE_CUTOFF_PERCENT) { - // break, since elements are ordered by the module size deviation there cannot be - // any more interesting elements for the given p1. - break; - } - - for (int i3 = i2 + 1; i3 < size; i3++) { - FinderPattern p3 = possibleCenters.get(i3); - if (p3 == null) { - continue; - } - - // Compare the expected module sizes; if they are really off, skip - float vModSize23 = (p2.getEstimatedModuleSize() - p3.getEstimatedModuleSize()) / - Math.min(p2.getEstimatedModuleSize(), p3.getEstimatedModuleSize()); - float vModSize23A = Math.abs(p2.getEstimatedModuleSize() - p3.getEstimatedModuleSize()); - if (vModSize23A > DIFF_MODSIZE_CUTOFF && vModSize23 >= DIFF_MODSIZE_CUTOFF_PERCENT) { - // break, since elements are ordered by the module size deviation there cannot be - // any more interesting elements for the given p1. - break; - } - - FinderPattern[] test = {p1, p2, p3}; - ResultPoint.orderBestPatterns(test); - - // Calculate the distances: a = topleft-bottomleft, b=topleft-topright, c = diagonal - FinderPatternInfo info = new FinderPatternInfo(test); - float dA = ResultPoint.distance(info.getTopLeft(), info.getBottomLeft()); - float dC = ResultPoint.distance(info.getTopRight(), info.getBottomLeft()); - float dB = ResultPoint.distance(info.getTopLeft(), info.getTopRight()); - - // Check the sizes - float estimatedModuleCount = (dA + dB) / (p1.getEstimatedModuleSize() * 2.0f); - if (estimatedModuleCount > MAX_MODULE_COUNT_PER_EDGE || - estimatedModuleCount < MIN_MODULE_COUNT_PER_EDGE) { - continue; - } - - // Calculate the difference of the edge lengths in percent - float vABBC = Math.abs((dA - dB) / Math.min(dA, dB)); - if (vABBC >= 0.1f) { - continue; - } - - // Calculate the diagonal length by assuming a 90° angle at topleft - float dCpy = (float) Math.sqrt((double) dA * dA + (double) dB * dB); - // Compare to the real distance in % - float vPyC = Math.abs((dC - dCpy) / Math.min(dC, dCpy)); - - if (vPyC >= 0.1f) { - continue; - } - - // All tests passed! - results.add(test); - } - } - } - - if (!results.isEmpty()) { - return results.toArray(EMPTY_FP_2D_ARRAY); - } - - // Nothing found! - throw NotFoundException.getNotFoundInstance(); - } - - public FinderPatternInfo[] findMulti(Map hints) throws NotFoundException { - boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER); - BitMatrix image = getImage(); - int maxI = image.getHeight(); - int maxJ = image.getWidth(); - // We are looking for black/white/black/white/black modules in - // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far - - // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the - // image, and then account for the center being 3 modules in size. This gives the smallest - // number of pixels the center could be, so skip this often. When trying harder, look for all - // QR versions regardless of how dense they are. - int iSkip = (3 * maxI) / (4 * MAX_MODULES); - if (iSkip < MIN_SKIP || tryHarder) { - iSkip = MIN_SKIP; - } - - int[] stateCount = new int[5]; - for (int i = iSkip - 1; i < maxI; i += iSkip) { - // Get a row of black/white values - clearCounts(stateCount); - int currentState = 0; - for (int j = 0; j < maxJ; j++) { - if (image.get(j, i)) { - // Black pixel - if ((currentState & 1) == 1) { // Counting white pixels - currentState++; - } - stateCount[currentState]++; - } else { // White pixel - if ((currentState & 1) == 0) { // Counting black pixels - if (currentState == 4) { // A winner? - if (foundPatternCross(stateCount) && handlePossibleCenter(stateCount, i, j)) { // Yes - // Clear state to start looking again - currentState = 0; - clearCounts(stateCount); - } else { // No, shift counts back by two - shiftCounts2(stateCount); - currentState = 3; - } - } else { - stateCount[++currentState]++; - } - } else { // Counting white pixels - stateCount[currentState]++; - } - } - } // for j=... - - if (foundPatternCross(stateCount)) { - handlePossibleCenter(stateCount, i, maxJ); - } - } // for i=iSkip-1 ... - FinderPattern[][] patternInfo = selectMultipleBestPatterns(); - List result = new ArrayList<>(); - for (FinderPattern[] pattern : patternInfo) { - ResultPoint.orderBestPatterns(pattern); - result.add(new FinderPatternInfo(pattern)); - } - - if (result.isEmpty()) { - return EMPTY_RESULT_ARRAY; - } else { - return result.toArray(EMPTY_RESULT_ARRAY); - } - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/qrcode/QRCodeReader.java b/TMessagesProj/src/main/java/com/google/zxing/qrcode/QRCodeReader.java deleted file mode 100755 index a47a8d00e..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/qrcode/QRCodeReader.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.qrcode; - -import com.google.zxing.BarcodeFormat; -import com.google.zxing.BinaryBitmap; -import com.google.zxing.ChecksumException; -import com.google.zxing.DecodeHintType; -import com.google.zxing.FormatException; -import com.google.zxing.NotFoundException; -import com.google.zxing.Reader; -import com.google.zxing.Result; -import com.google.zxing.ResultMetadataType; -import com.google.zxing.ResultPoint; -import com.google.zxing.common.BitMatrix; -import com.google.zxing.common.DecoderResult; -import com.google.zxing.common.DetectorResult; -import com.google.zxing.qrcode.decoder.Decoder; -import com.google.zxing.qrcode.decoder.QRCodeDecoderMetaData; -import com.google.zxing.qrcode.detector.Detector; - -import java.util.List; -import java.util.Map; - -/** - * This implementation can detect and decode QR Codes in an image. - * - * @author Sean Owen - */ -public class QRCodeReader implements Reader { - - private static final ResultPoint[] NO_POINTS = new ResultPoint[0]; - - private final Decoder decoder = new Decoder(); - - protected final Decoder getDecoder() { - return decoder; - } - - /** - * Locates and decodes a QR code in an image. - * - * @return a String representing the content encoded by the QR code - * @throws NotFoundException if a QR code cannot be found - * @throws FormatException if a QR code cannot be decoded - * @throws ChecksumException if error correction fails - */ - @Override - public Result decode(BinaryBitmap image) throws NotFoundException, ChecksumException, FormatException { - return decode(image, null); - } - - @Override - public final Result decode(BinaryBitmap image, Map hints) - throws NotFoundException, ChecksumException, FormatException { - DecoderResult decoderResult; - ResultPoint[] points; - if (hints != null && hints.containsKey(DecodeHintType.PURE_BARCODE)) { - BitMatrix bits = extractPureBits(image.getBlackMatrix()); - decoderResult = decoder.decode(bits, hints); - points = NO_POINTS; - } else { - DetectorResult detectorResult = new Detector(image.getBlackMatrix()).detect(hints); - decoderResult = decoder.decode(detectorResult.getBits(), hints); - points = detectorResult.getPoints(); - } - - // If the code was mirrored: swap the bottom-left and the top-right points. - if (decoderResult.getOther() instanceof QRCodeDecoderMetaData) { - ((QRCodeDecoderMetaData) decoderResult.getOther()).applyMirroredCorrection(points); - } - - Result result = new Result(decoderResult.getText(), decoderResult.getRawBytes(), points, BarcodeFormat.QR_CODE); - List byteSegments = decoderResult.getByteSegments(); - if (byteSegments != null) { - result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, byteSegments); - } - String ecLevel = decoderResult.getECLevel(); - if (ecLevel != null) { - result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel); - } - if (decoderResult.hasStructuredAppend()) { - result.putMetadata(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE, - decoderResult.getStructuredAppendSequenceNumber()); - result.putMetadata(ResultMetadataType.STRUCTURED_APPEND_PARITY, - decoderResult.getStructuredAppendParity()); - } - return result; - } - - @Override - public void reset() { - // do nothing - } - - /** - * This method detects a code in a "pure" image -- that is, pure monochrome image - * which contains only an unrotated, unskewed, image of a code, with some white border - * around it. This is a specialized method that works exceptionally fast in this special - * case. - */ - private static BitMatrix extractPureBits(BitMatrix image) throws NotFoundException { - - int[] leftTopBlack = image.getTopLeftOnBit(); - int[] rightBottomBlack = image.getBottomRightOnBit(); - if (leftTopBlack == null || rightBottomBlack == null) { - throw NotFoundException.getNotFoundInstance(); - } - - float moduleSize = moduleSize(leftTopBlack, image); - - int top = leftTopBlack[1]; - int bottom = rightBottomBlack[1]; - int left = leftTopBlack[0]; - int right = rightBottomBlack[0]; - - // Sanity check! - if (left >= right || top >= bottom) { - throw NotFoundException.getNotFoundInstance(); - } - - if (bottom - top != right - left) { - // Special case, where bottom-right module wasn't black so we found something else in the last row - // Assume it's a square, so use height as the width - right = left + (bottom - top); - if (right >= image.getWidth()) { - // Abort if that would not make sense -- off image - throw NotFoundException.getNotFoundInstance(); - } - } - - int matrixWidth = Math.round((right - left + 1) / moduleSize); - int matrixHeight = Math.round((bottom - top + 1) / moduleSize); - if (matrixWidth <= 0 || matrixHeight <= 0) { - throw NotFoundException.getNotFoundInstance(); - } - if (matrixHeight != matrixWidth) { - // Only possibly decode square regions - throw NotFoundException.getNotFoundInstance(); - } - - // Push in the "border" by half the module width so that we start - // sampling in the middle of the module. Just in case the image is a - // little off, this will help recover. - int nudge = (int) (moduleSize / 2.0f); - top += nudge; - left += nudge; - - // But careful that this does not sample off the edge - // "right" is the farthest-right valid pixel location -- right+1 is not necessarily - // This is positive by how much the inner x loop below would be too large - int nudgedTooFarRight = left + (int) ((matrixWidth - 1) * moduleSize) - right; - if (nudgedTooFarRight > 0) { - if (nudgedTooFarRight > nudge) { - // Neither way fits; abort - throw NotFoundException.getNotFoundInstance(); - } - left -= nudgedTooFarRight; - } - // See logic above - int nudgedTooFarDown = top + (int) ((matrixHeight - 1) * moduleSize) - bottom; - if (nudgedTooFarDown > 0) { - if (nudgedTooFarDown > nudge) { - // Neither way fits; abort - throw NotFoundException.getNotFoundInstance(); - } - top -= nudgedTooFarDown; - } - - // Now just read off the bits - BitMatrix bits = new BitMatrix(matrixWidth, matrixHeight, 1); - for (int y = 0; y < matrixHeight; y++) { - int iOffset = top + (int) (y * moduleSize); - for (int x = 0; x < matrixWidth; x++) { - if (image.get(left + (int) (x * moduleSize), iOffset)) { - bits.set(x, y); - } - } - } - return bits; - } - - private static float moduleSize(int[] leftTopBlack, BitMatrix image) throws NotFoundException { - int height = image.getHeight(); - int width = image.getWidth(); - int x = leftTopBlack[0]; - int y = leftTopBlack[1]; - boolean inBlack = true; - int transitions = 0; - while (x < width && y < height) { - if (inBlack != image.get(x, y)) { - if (++transitions == 5) { - break; - } - inBlack = !inBlack; - } - x++; - y++; - } - if (x == width || y == height) { - throw NotFoundException.getNotFoundInstance(); - } - return (x - leftTopBlack[0]) / 7.0f; - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/qrcode/QRCodeWriter.java b/TMessagesProj/src/main/java/com/google/zxing/qrcode/QRCodeWriter.java deleted file mode 100755 index 0a9983a3a..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/qrcode/QRCodeWriter.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright 2008 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.qrcode; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.GradientDrawable; - -import com.google.zxing.BarcodeFormat; -import com.google.zxing.EncodeHintType; -import com.google.zxing.WriterException; -import com.google.zxing.qrcode.encoder.ByteMatrix; -import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; -import com.google.zxing.qrcode.encoder.Encoder; -import com.google.zxing.qrcode.encoder.QRCode; - -import org.telegram.messenger.R; - -import java.util.Arrays; -import java.util.Map; - -/** - * This object renders a QR Code as a BitMatrix 2D array of greyscale values. - * - * @author dswitkin@google.com (Daniel Switkin) - */ -public final class QRCodeWriter { - - private static final int QUIET_ZONE_SIZE = 4; - private ByteMatrix input; - private float[] radii = new float[8]; - private int imageBloks; - private int imageBlockX; - private int sideQuadSize; - - public Bitmap encode(String contents, BarcodeFormat format, int width, int height, Map hints, Bitmap bitmap, Context context) throws WriterException { - - if (contents.isEmpty()) { - throw new IllegalArgumentException("Found empty contents"); - } - - if (width < 0 || height < 0) { - throw new IllegalArgumentException("Requested dimensions are too small: " + width + 'x' + height); - } - - ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.L; - int quietZone = QUIET_ZONE_SIZE; - if (hints != null) { - if (hints.containsKey(EncodeHintType.ERROR_CORRECTION)) { - errorCorrectionLevel = ErrorCorrectionLevel.valueOf(hints.get(EncodeHintType.ERROR_CORRECTION).toString()); - } - if (hints.containsKey(EncodeHintType.MARGIN)) { - quietZone = Integer.parseInt(hints.get(EncodeHintType.MARGIN).toString()); - } - } - - QRCode code = Encoder.encode(contents, errorCorrectionLevel, hints); - - input = code.getMatrix(); - if (input == null) { - throw new IllegalStateException(); - } - int inputWidth = input.getWidth(); - int inputHeight = input.getHeight(); - - for (int x = 0; x < inputWidth; x++) { - if (has(x, 0)) { - sideQuadSize++; - } else { - break; - } - } - - int qrWidth = inputWidth + (quietZone * 2); - int qrHeight = inputHeight + (quietZone * 2); - int outputWidth = Math.max(width, qrWidth); - int outputHeight = Math.max(height, qrHeight); - - int multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight); - - int padding = 16; - - int size = multiple * inputWidth + padding * 2; - if (bitmap == null || bitmap.getWidth() != size) { - bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); - } - Canvas canvas = new Canvas(bitmap); - canvas.drawColor(0xffffffff); - Paint blackPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - blackPaint.setColor(0xff000000); - - GradientDrawable rect = new GradientDrawable(); - rect.setShape(GradientDrawable.RECTANGLE); - rect.setCornerRadii(radii); - - imageBloks = Math.round((size - 32) / 4.65f / multiple); - if (imageBloks % 2 != inputWidth % 2) { - imageBloks++; - } - imageBlockX = (inputWidth - imageBloks) / 2; - int imageSize = imageBloks * multiple; - int imageX = (size - imageSize) / 2; - - for (int a = 0; a < 3; a++) { - int x, y; - if (a == 0) { - x = padding; - y = padding; - } else if (a == 1) { - x = size - sideQuadSize * multiple - padding; - y = padding; - } else { - x = padding; - y = size - sideQuadSize * multiple - padding; - } - - float r = (sideQuadSize * multiple) / 3.0f; - Arrays.fill(radii, r); - - rect.setColor(0xff000000); - rect.setBounds(x, y, x + sideQuadSize * multiple, y + sideQuadSize * multiple); - rect.draw(canvas); - - canvas.drawRect(x + multiple, y + multiple, x + (sideQuadSize - 1) * multiple, y + (sideQuadSize - 1) * multiple, blackPaint); - - r = (sideQuadSize * multiple) / 4.0f; - Arrays.fill(radii, r); - rect.setColor(0xffffffff); - rect.setBounds(x + multiple, y + multiple, x + (sideQuadSize - 1) * multiple, y + (sideQuadSize - 1) * multiple); - rect.draw(canvas); - - r = ((sideQuadSize - 2) * multiple) / 4.0f; - Arrays.fill(radii, r); - rect.setColor(0xff000000); - rect.setBounds(x + multiple * 2, y + multiple * 2, x + (sideQuadSize - 2) * multiple, y + (sideQuadSize - 2) * multiple); - rect.draw(canvas); - } - float r = multiple / 2.0f; - - for (int y = 0, outputY = padding; y < inputHeight; y++, outputY += multiple) { - for (int x = 0, outputX = padding; x < inputWidth; x++, outputX += multiple) { - if (has(x, y)) { - Arrays.fill(radii, r); - if (has(x, y - 1)) { - radii[0] = radii[1] = 0; - radii[2] = radii[3] = 0; - } - if (has(x, y + 1)) { - radii[6] = radii[7] = 0; - radii[4] = radii[5] = 0; - } - if (has(x - 1, y)) { - radii[0] = radii[1] = 0; - radii[6] = radii[7] = 0; - } - if (has(x + 1, y)) { - radii[2] = radii[3] = 0; - radii[4] = radii[5] = 0; - } - rect.setColor(0xff000000); - rect.setBounds(outputX, outputY, outputX + multiple, outputY + multiple); - rect.draw(canvas); - } else { - boolean has = false; - Arrays.fill(radii, 0); - if (has(x - 1, y - 1) && has(x - 1, y) && has(x, y - 1)) { - radii[0] = radii[1] = r; - has = true; - } - if (has(x + 1, y - 1) && has(x + 1, y) && has(x, y - 1)) { - radii[2] = radii[3] = r; - has = true; - } - if (has(x - 1, y + 1) && has(x - 1, y) && has(x, y + 1)) { - radii[6] = radii[7] = r; - has = true; - } - if (has(x + 1, y + 1) && has(x + 1, y) && has(x, y + 1)) { - radii[4] = radii[5] = r; - has = true; - } - if (has) { - canvas.drawRect(outputX, outputY, outputX + multiple, outputY + multiple, blackPaint); - rect.setColor(0xffffffff); - rect.setBounds(outputX, outputY, outputX + multiple, outputY + multiple); - rect.draw(canvas); - } - } - } - } - - Drawable drawable = context.getResources().getDrawable(R.drawable.gem_l).mutate(); - drawable.setBounds(imageX, imageX, imageX + imageSize, imageX + imageSize); - drawable.draw(canvas); - - canvas.setBitmap(null); - - return bitmap; - } - - private boolean has(int x, int y) { - if (x >= imageBlockX && x < imageBlockX + imageBloks && y >= imageBlockX && y < imageBlockX + imageBloks) { - return false; - } - if ((x < sideQuadSize || x >= input.getWidth() - sideQuadSize) && y < sideQuadSize) { - return false; - } - if (x < sideQuadSize && y >= input.getHeight() - sideQuadSize) { - return false; - } - return x >= 0 && y >= 0 && x < input.getWidth() && y < input.getHeight() && input.get(x, y) == 1; - } -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/BitMatrixParser.java b/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/BitMatrixParser.java deleted file mode 100755 index 229869578..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/BitMatrixParser.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.qrcode.decoder; - -import com.google.zxing.FormatException; -import com.google.zxing.common.BitMatrix; - -/** - * @author Sean Owen - */ -final class BitMatrixParser { - - private final BitMatrix bitMatrix; - private Version parsedVersion; - private FormatInformation parsedFormatInfo; - private boolean mirror; - - /** - * @param bitMatrix {@link BitMatrix} to parse - * @throws FormatException if dimension is not >= 21 and 1 mod 4 - */ - BitMatrixParser(BitMatrix bitMatrix) throws FormatException { - int dimension = bitMatrix.getHeight(); - if (dimension < 21 || (dimension & 0x03) != 1) { - throw FormatException.getFormatInstance(); - } - this.bitMatrix = bitMatrix; - } - - /** - *

Reads format information from one of its two locations within the QR Code.

- * - * @return {@link FormatInformation} encapsulating the QR Code's format info - * @throws FormatException if both format information locations cannot be parsed as - * the valid encoding of format information - */ - FormatInformation readFormatInformation() throws FormatException { - - if (parsedFormatInfo != null) { - return parsedFormatInfo; - } - - // Read top-left format info bits - int formatInfoBits1 = 0; - for (int i = 0; i < 6; i++) { - formatInfoBits1 = copyBit(i, 8, formatInfoBits1); - } - // .. and skip a bit in the timing pattern ... - formatInfoBits1 = copyBit(7, 8, formatInfoBits1); - formatInfoBits1 = copyBit(8, 8, formatInfoBits1); - formatInfoBits1 = copyBit(8, 7, formatInfoBits1); - // .. and skip a bit in the timing pattern ... - for (int j = 5; j >= 0; j--) { - formatInfoBits1 = copyBit(8, j, formatInfoBits1); - } - - // Read the top-right/bottom-left pattern too - int dimension = bitMatrix.getHeight(); - int formatInfoBits2 = 0; - int jMin = dimension - 7; - for (int j = dimension - 1; j >= jMin; j--) { - formatInfoBits2 = copyBit(8, j, formatInfoBits2); - } - for (int i = dimension - 8; i < dimension; i++) { - formatInfoBits2 = copyBit(i, 8, formatInfoBits2); - } - - parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits1, formatInfoBits2); - if (parsedFormatInfo != null) { - return parsedFormatInfo; - } - throw FormatException.getFormatInstance(); - } - - /** - *

Reads version information from one of its two locations within the QR Code.

- * - * @return {@link Version} encapsulating the QR Code's version - * @throws FormatException if both version information locations cannot be parsed as - * the valid encoding of version information - */ - Version readVersion() throws FormatException { - - if (parsedVersion != null) { - return parsedVersion; - } - - int dimension = bitMatrix.getHeight(); - - int provisionalVersion = (dimension - 17) / 4; - if (provisionalVersion <= 6) { - return Version.getVersionForNumber(provisionalVersion); - } - - // Read top-right version info: 3 wide by 6 tall - int versionBits = 0; - int ijMin = dimension - 11; - for (int j = 5; j >= 0; j--) { - for (int i = dimension - 9; i >= ijMin; i--) { - versionBits = copyBit(i, j, versionBits); - } - } - - Version theParsedVersion = Version.decodeVersionInformation(versionBits); - if (theParsedVersion != null && theParsedVersion.getDimensionForVersion() == dimension) { - parsedVersion = theParsedVersion; - return theParsedVersion; - } - - // Hmm, failed. Try bottom left: 6 wide by 3 tall - versionBits = 0; - for (int i = 5; i >= 0; i--) { - for (int j = dimension - 9; j >= ijMin; j--) { - versionBits = copyBit(i, j, versionBits); - } - } - - theParsedVersion = Version.decodeVersionInformation(versionBits); - if (theParsedVersion != null && theParsedVersion.getDimensionForVersion() == dimension) { - parsedVersion = theParsedVersion; - return theParsedVersion; - } - throw FormatException.getFormatInstance(); - } - - private int copyBit(int i, int j, int versionBits) { - boolean bit = mirror ? bitMatrix.get(j, i) : bitMatrix.get(i, j); - return bit ? (versionBits << 1) | 0x1 : versionBits << 1; - } - - /** - *

Reads the bits in the {@link BitMatrix} representing the finder pattern in the - * correct order in order to reconstruct the codewords bytes contained within the - * QR Code.

- * - * @return bytes encoded within the QR Code - * @throws FormatException if the exact number of bytes expected is not read - */ - byte[] readCodewords() throws FormatException { - - FormatInformation formatInfo = readFormatInformation(); - Version version = readVersion(); - - // Get the data mask for the format used in this QR Code. This will exclude - // some bits from reading as we wind through the bit matrix. - DataMask dataMask = DataMask.values()[formatInfo.getDataMask()]; - int dimension = bitMatrix.getHeight(); - dataMask.unmaskBitMatrix(bitMatrix, dimension); - - BitMatrix functionPattern = version.buildFunctionPattern(); - - boolean readingUp = true; - byte[] result = new byte[version.getTotalCodewords()]; - int resultOffset = 0; - int currentByte = 0; - int bitsRead = 0; - // Read columns in pairs, from right to left - for (int j = dimension - 1; j > 0; j -= 2) { - if (j == 6) { - // Skip whole column with vertical alignment pattern; - // saves time and makes the other code proceed more cleanly - j--; - } - // Read alternatingly from bottom to top then top to bottom - for (int count = 0; count < dimension; count++) { - int i = readingUp ? dimension - 1 - count : count; - for (int col = 0; col < 2; col++) { - // Ignore bits covered by the function pattern - if (!functionPattern.get(j - col, i)) { - // Read a bit - bitsRead++; - currentByte <<= 1; - if (bitMatrix.get(j - col, i)) { - currentByte |= 1; - } - // If we've made a whole byte, save it off - if (bitsRead == 8) { - result[resultOffset++] = (byte) currentByte; - bitsRead = 0; - currentByte = 0; - } - } - } - } - readingUp ^= true; // readingUp = !readingUp; // switch directions - } - if (resultOffset != version.getTotalCodewords()) { - throw FormatException.getFormatInstance(); - } - return result; - } - - /** - * Revert the mask removal done while reading the code words. The bit matrix should revert to its original state. - */ - void remask() { - if (parsedFormatInfo == null) { - return; // We have no format information, and have no data mask - } - DataMask dataMask = DataMask.values()[parsedFormatInfo.getDataMask()]; - int dimension = bitMatrix.getHeight(); - dataMask.unmaskBitMatrix(bitMatrix, dimension); - } - - /** - * Prepare the parser for a mirrored operation. - * This flag has effect only on the {@link #readFormatInformation()} and the - * {@link #readVersion()}. Before proceeding with {@link #readCodewords()} the - * {@link #mirror()} method should be called. - * - * @param mirror Whether to read version and format information mirrored. - */ - void setMirror(boolean mirror) { - parsedVersion = null; - parsedFormatInfo = null; - this.mirror = mirror; - } - - /** Mirror the bit matrix in order to attempt a second reading. */ - void mirror() { - for (int x = 0; x < bitMatrix.getWidth(); x++) { - for (int y = x + 1; y < bitMatrix.getHeight(); y++) { - if (bitMatrix.get(x, y) != bitMatrix.get(y, x)) { - bitMatrix.flip(y, x); - bitMatrix.flip(x, y); - } - } - } - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/DataBlock.java b/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/DataBlock.java deleted file mode 100755 index 8f5cdcba7..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/DataBlock.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.qrcode.decoder; - -/** - *

Encapsulates a block of data within a QR Code. QR Codes may split their data into - * multiple blocks, each of which is a unit of data and error-correction codewords. Each - * is represented by an instance of this class.

- * - * @author Sean Owen - */ -final class DataBlock { - - private final int numDataCodewords; - private final byte[] codewords; - - private DataBlock(int numDataCodewords, byte[] codewords) { - this.numDataCodewords = numDataCodewords; - this.codewords = codewords; - } - - /** - *

When QR Codes use multiple data blocks, they are actually interleaved. - * That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This - * method will separate the data into original blocks.

- * - * @param rawCodewords bytes as read directly from the QR Code - * @param version version of the QR Code - * @param ecLevel error-correction level of the QR Code - * @return DataBlocks containing original bytes, "de-interleaved" from representation in the - * QR Code - */ - static DataBlock[] getDataBlocks(byte[] rawCodewords, - Version version, - ErrorCorrectionLevel ecLevel) { - - if (rawCodewords.length != version.getTotalCodewords()) { - throw new IllegalArgumentException(); - } - - // Figure out the number and size of data blocks used by this version and - // error correction level - Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel); - - // First count the total number of data blocks - int totalBlocks = 0; - Version.ECB[] ecBlockArray = ecBlocks.getECBlocks(); - for (Version.ECB ecBlock : ecBlockArray) { - totalBlocks += ecBlock.getCount(); - } - - // Now establish DataBlocks of the appropriate size and number of data codewords - DataBlock[] result = new DataBlock[totalBlocks]; - int numResultBlocks = 0; - for (Version.ECB ecBlock : ecBlockArray) { - for (int i = 0; i < ecBlock.getCount(); i++) { - int numDataCodewords = ecBlock.getDataCodewords(); - int numBlockCodewords = ecBlocks.getECCodewordsPerBlock() + numDataCodewords; - result[numResultBlocks++] = new DataBlock(numDataCodewords, new byte[numBlockCodewords]); - } - } - - // All blocks have the same amount of data, except that the last n - // (where n may be 0) have 1 more byte. Figure out where these start. - int shorterBlocksTotalCodewords = result[0].codewords.length; - int longerBlocksStartAt = result.length - 1; - while (longerBlocksStartAt >= 0) { - int numCodewords = result[longerBlocksStartAt].codewords.length; - if (numCodewords == shorterBlocksTotalCodewords) { - break; - } - longerBlocksStartAt--; - } - longerBlocksStartAt++; - - int shorterBlocksNumDataCodewords = shorterBlocksTotalCodewords - ecBlocks.getECCodewordsPerBlock(); - // The last elements of result may be 1 element longer; - // first fill out as many elements as all of them have - int rawCodewordsOffset = 0; - for (int i = 0; i < shorterBlocksNumDataCodewords; i++) { - for (int j = 0; j < numResultBlocks; j++) { - result[j].codewords[i] = rawCodewords[rawCodewordsOffset++]; - } - } - // Fill out the last data block in the longer ones - for (int j = longerBlocksStartAt; j < numResultBlocks; j++) { - result[j].codewords[shorterBlocksNumDataCodewords] = rawCodewords[rawCodewordsOffset++]; - } - // Now add in error correction blocks - int max = result[0].codewords.length; - for (int i = shorterBlocksNumDataCodewords; i < max; i++) { - for (int j = 0; j < numResultBlocks; j++) { - int iOffset = j < longerBlocksStartAt ? i : i + 1; - result[j].codewords[iOffset] = rawCodewords[rawCodewordsOffset++]; - } - } - return result; - } - - int getNumDataCodewords() { - return numDataCodewords; - } - - byte[] getCodewords() { - return codewords; - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/DataMask.java b/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/DataMask.java deleted file mode 100755 index e60868d53..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/DataMask.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.qrcode.decoder; - -import com.google.zxing.common.BitMatrix; - -/** - *

Encapsulates data masks for the data bits in a QR code, per ISO 18004:2006 6.8. Implementations - * of this class can un-mask a raw BitMatrix. For simplicity, they will unmask the entire BitMatrix, - * including areas used for finder patterns, timing patterns, etc. These areas should be unused - * after the point they are unmasked anyway.

- * - *

Note that the diagram in section 6.8.1 is misleading since it indicates that i is column position - * and j is row position. In fact, as the text says, i is row position and j is column position.

- * - * @author Sean Owen - */ -enum DataMask { - - // See ISO 18004:2006 6.8.1 - - /** - * 000: mask bits for which (x + y) mod 2 == 0 - */ - DATA_MASK_000() { - @Override - boolean isMasked(int i, int j) { - return ((i + j) & 0x01) == 0; - } - }, - - /** - * 001: mask bits for which x mod 2 == 0 - */ - DATA_MASK_001() { - @Override - boolean isMasked(int i, int j) { - return (i & 0x01) == 0; - } - }, - - /** - * 010: mask bits for which y mod 3 == 0 - */ - DATA_MASK_010() { - @Override - boolean isMasked(int i, int j) { - return j % 3 == 0; - } - }, - - /** - * 011: mask bits for which (x + y) mod 3 == 0 - */ - DATA_MASK_011() { - @Override - boolean isMasked(int i, int j) { - return (i + j) % 3 == 0; - } - }, - - /** - * 100: mask bits for which (x/2 + y/3) mod 2 == 0 - */ - DATA_MASK_100() { - @Override - boolean isMasked(int i, int j) { - return (((i / 2) + (j / 3)) & 0x01) == 0; - } - }, - - /** - * 101: mask bits for which xy mod 2 + xy mod 3 == 0 - * equivalently, such that xy mod 6 == 0 - */ - DATA_MASK_101() { - @Override - boolean isMasked(int i, int j) { - return (i * j) % 6 == 0; - } - }, - - /** - * 110: mask bits for which (xy mod 2 + xy mod 3) mod 2 == 0 - * equivalently, such that xy mod 6 < 3 - */ - DATA_MASK_110() { - @Override - boolean isMasked(int i, int j) { - return ((i * j) % 6) < 3; - } - }, - - /** - * 111: mask bits for which ((x+y)mod 2 + xy mod 3) mod 2 == 0 - * equivalently, such that (x + y + xy mod 3) mod 2 == 0 - */ - DATA_MASK_111() { - @Override - boolean isMasked(int i, int j) { - return ((i + j + ((i * j) % 3)) & 0x01) == 0; - } - }; - - // End of enum constants. - - - /** - *

Implementations of this method reverse the data masking process applied to a QR Code and - * make its bits ready to read.

- * - * @param bits representation of QR Code bits - * @param dimension dimension of QR Code, represented by bits, being unmasked - */ - final void unmaskBitMatrix(BitMatrix bits, int dimension) { - for (int i = 0; i < dimension; i++) { - for (int j = 0; j < dimension; j++) { - if (isMasked(i, j)) { - bits.flip(j, i); - } - } - } - } - - abstract boolean isMasked(int i, int j); - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java b/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java deleted file mode 100755 index 0bfaf231c..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.qrcode.decoder; - -import com.google.zxing.DecodeHintType; -import com.google.zxing.FormatException; -import com.google.zxing.common.BitSource; -import com.google.zxing.common.CharacterSetECI; -import com.google.zxing.common.DecoderResult; -import com.google.zxing.common.StringUtils; - -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -/** - *

QR Codes can encode text as bits in one of several modes, and can use multiple modes - * in one QR Code. This class decodes the bits back into text.

- * - *

See ISO 18004:2006, 6.4.3 - 6.4.7

- * - * @author Sean Owen - */ -final class DecodedBitStreamParser { - - /** - * See ISO 18004:2006, 6.4.4 Table 5 - */ - private static final char[] ALPHANUMERIC_CHARS = - "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:".toCharArray(); - private static final int GB2312_SUBSET = 1; - - private DecodedBitStreamParser() { - } - - static DecoderResult decode(byte[] bytes, - Version version, - ErrorCorrectionLevel ecLevel, - Map hints) throws FormatException { - BitSource bits = new BitSource(bytes); - StringBuilder result = new StringBuilder(50); - List byteSegments = new ArrayList<>(1); - int symbolSequence = -1; - int parityData = -1; - - try { - CharacterSetECI currentCharacterSetECI = null; - boolean fc1InEffect = false; - Mode mode; - do { - // While still another segment to read... - if (bits.available() < 4) { - // OK, assume we're done. Really, a TERMINATOR mode should have been recorded here - mode = Mode.TERMINATOR; - } else { - mode = Mode.forBits(bits.readBits(4)); // mode is encoded by 4 bits - } - switch (mode) { - case TERMINATOR: - break; - case FNC1_FIRST_POSITION: - case FNC1_SECOND_POSITION: - // We do little with FNC1 except alter the parsed result a bit according to the spec - fc1InEffect = true; - break; - case STRUCTURED_APPEND: - if (bits.available() < 16) { - throw FormatException.getFormatInstance(); - } - // sequence number and parity is added later to the result metadata - // Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue - symbolSequence = bits.readBits(8); - parityData = bits.readBits(8); - break; - case ECI: - // Count doesn't apply to ECI - int value = parseECIValue(bits); - currentCharacterSetECI = CharacterSetECI.getCharacterSetECIByValue(value); - if (currentCharacterSetECI == null) { - throw FormatException.getFormatInstance(); - } - break; - case HANZI: - // First handle Hanzi mode which does not start with character count - // Chinese mode contains a sub set indicator right after mode indicator - int subset = bits.readBits(4); - int countHanzi = bits.readBits(mode.getCharacterCountBits(version)); - if (subset == GB2312_SUBSET) { - decodeHanziSegment(bits, result, countHanzi); - } - break; - default: - // "Normal" QR code modes: - // How many characters will follow, encoded in this mode? - int count = bits.readBits(mode.getCharacterCountBits(version)); - switch (mode) { - case NUMERIC: - decodeNumericSegment(bits, result, count); - break; - case ALPHANUMERIC: - decodeAlphanumericSegment(bits, result, count, fc1InEffect); - break; - case BYTE: - decodeByteSegment(bits, result, count, currentCharacterSetECI, byteSegments, hints); - break; - case KANJI: - decodeKanjiSegment(bits, result, count); - break; - default: - throw FormatException.getFormatInstance(); - } - break; - } - } while (mode != Mode.TERMINATOR); - } catch (IllegalArgumentException iae) { - // from readBits() calls - throw FormatException.getFormatInstance(); - } - - return new DecoderResult(bytes, - result.toString(), - byteSegments.isEmpty() ? null : byteSegments, - ecLevel == null ? null : ecLevel.toString(), - symbolSequence, - parityData); - } - - /** - * See specification GBT 18284-2000 - */ - private static void decodeHanziSegment(BitSource bits, - StringBuilder result, - int count) throws FormatException { - // Don't crash trying to read more bits than we have available. - if (count * 13 > bits.available()) { - throw FormatException.getFormatInstance(); - } - - // Each character will require 2 bytes. Read the characters as 2-byte pairs - // and decode as GB2312 afterwards - byte[] buffer = new byte[2 * count]; - int offset = 0; - while (count > 0) { - // Each 13 bits encodes a 2-byte character - int twoBytes = bits.readBits(13); - int assembledTwoBytes = ((twoBytes / 0x060) << 8) | (twoBytes % 0x060); - if (assembledTwoBytes < 0x00A00) { - // In the 0xA1A1 to 0xAAFE range - assembledTwoBytes += 0x0A1A1; - } else { - // In the 0xB0A1 to 0xFAFE range - assembledTwoBytes += 0x0A6A1; - } - buffer[offset] = (byte) ((assembledTwoBytes >> 8) & 0xFF); - buffer[offset + 1] = (byte) (assembledTwoBytes & 0xFF); - offset += 2; - count--; - } - - try { - result.append(new String(buffer, StringUtils.GB2312)); - } catch (UnsupportedEncodingException ignored) { - throw FormatException.getFormatInstance(); - } - } - - private static void decodeKanjiSegment(BitSource bits, - StringBuilder result, - int count) throws FormatException { - // Don't crash trying to read more bits than we have available. - if (count * 13 > bits.available()) { - throw FormatException.getFormatInstance(); - } - - // Each character will require 2 bytes. Read the characters as 2-byte pairs - // and decode as Shift_JIS afterwards - byte[] buffer = new byte[2 * count]; - int offset = 0; - while (count > 0) { - // Each 13 bits encodes a 2-byte character - int twoBytes = bits.readBits(13); - int assembledTwoBytes = ((twoBytes / 0x0C0) << 8) | (twoBytes % 0x0C0); - if (assembledTwoBytes < 0x01F00) { - // In the 0x8140 to 0x9FFC range - assembledTwoBytes += 0x08140; - } else { - // In the 0xE040 to 0xEBBF range - assembledTwoBytes += 0x0C140; - } - buffer[offset] = (byte) (assembledTwoBytes >> 8); - buffer[offset + 1] = (byte) assembledTwoBytes; - offset += 2; - count--; - } - // Shift_JIS may not be supported in some environments: - try { - result.append(new String(buffer, StringUtils.SHIFT_JIS)); - } catch (UnsupportedEncodingException ignored) { - throw FormatException.getFormatInstance(); - } - } - - private static void decodeByteSegment(BitSource bits, - StringBuilder result, - int count, - CharacterSetECI currentCharacterSetECI, - Collection byteSegments, - Map hints) throws FormatException { - // Don't crash trying to read more bits than we have available. - if (8 * count > bits.available()) { - throw FormatException.getFormatInstance(); - } - - byte[] readBytes = new byte[count]; - for (int i = 0; i < count; i++) { - readBytes[i] = (byte) bits.readBits(8); - } - String encoding; - if (currentCharacterSetECI == null) { - // The spec isn't clear on this mode; see - // section 6.4.5: t does not say which encoding to assuming - // upon decoding. I have seen ISO-8859-1 used as well as - // Shift_JIS -- without anything like an ECI designator to - // give a hint. - encoding = StringUtils.guessEncoding(readBytes, hints); - } else { - encoding = currentCharacterSetECI.name(); - } - try { - result.append(new String(readBytes, encoding)); - } catch (UnsupportedEncodingException ignored) { - throw FormatException.getFormatInstance(); - } - byteSegments.add(readBytes); - } - - private static char toAlphaNumericChar(int value) throws FormatException { - if (value >= ALPHANUMERIC_CHARS.length) { - throw FormatException.getFormatInstance(); - } - return ALPHANUMERIC_CHARS[value]; - } - - private static void decodeAlphanumericSegment(BitSource bits, - StringBuilder result, - int count, - boolean fc1InEffect) throws FormatException { - // Read two characters at a time - int start = result.length(); - while (count > 1) { - if (bits.available() < 11) { - throw FormatException.getFormatInstance(); - } - int nextTwoCharsBits = bits.readBits(11); - result.append(toAlphaNumericChar(nextTwoCharsBits / 45)); - result.append(toAlphaNumericChar(nextTwoCharsBits % 45)); - count -= 2; - } - if (count == 1) { - // special case: one character left - if (bits.available() < 6) { - throw FormatException.getFormatInstance(); - } - result.append(toAlphaNumericChar(bits.readBits(6))); - } - // See section 6.4.8.1, 6.4.8.2 - if (fc1InEffect) { - // We need to massage the result a bit if in an FNC1 mode: - for (int i = start; i < result.length(); i++) { - if (result.charAt(i) == '%') { - if (i < result.length() - 1 && result.charAt(i + 1) == '%') { - // %% is rendered as % - result.deleteCharAt(i + 1); - } else { - // In alpha mode, % should be converted to FNC1 separator 0x1D - result.setCharAt(i, (char) 0x1D); - } - } - } - } - } - - private static void decodeNumericSegment(BitSource bits, - StringBuilder result, - int count) throws FormatException { - // Read three digits at a time - while (count >= 3) { - // Each 10 bits encodes three digits - if (bits.available() < 10) { - throw FormatException.getFormatInstance(); - } - int threeDigitsBits = bits.readBits(10); - if (threeDigitsBits >= 1000) { - throw FormatException.getFormatInstance(); - } - result.append(toAlphaNumericChar(threeDigitsBits / 100)); - result.append(toAlphaNumericChar((threeDigitsBits / 10) % 10)); - result.append(toAlphaNumericChar(threeDigitsBits % 10)); - count -= 3; - } - if (count == 2) { - // Two digits left over to read, encoded in 7 bits - if (bits.available() < 7) { - throw FormatException.getFormatInstance(); - } - int twoDigitsBits = bits.readBits(7); - if (twoDigitsBits >= 100) { - throw FormatException.getFormatInstance(); - } - result.append(toAlphaNumericChar(twoDigitsBits / 10)); - result.append(toAlphaNumericChar(twoDigitsBits % 10)); - } else if (count == 1) { - // One digit left over to read - if (bits.available() < 4) { - throw FormatException.getFormatInstance(); - } - int digitBits = bits.readBits(4); - if (digitBits >= 10) { - throw FormatException.getFormatInstance(); - } - result.append(toAlphaNumericChar(digitBits)); - } - } - - private static int parseECIValue(BitSource bits) throws FormatException { - int firstByte = bits.readBits(8); - if ((firstByte & 0x80) == 0) { - // just one byte - return firstByte & 0x7F; - } - if ((firstByte & 0xC0) == 0x80) { - // two bytes - int secondByte = bits.readBits(8); - return ((firstByte & 0x3F) << 8) | secondByte; - } - if ((firstByte & 0xE0) == 0xC0) { - // three bytes - int secondThirdBytes = bits.readBits(16); - return ((firstByte & 0x1F) << 16) | secondThirdBytes; - } - throw FormatException.getFormatInstance(); - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/Decoder.java b/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/Decoder.java deleted file mode 100755 index 7fcb7d2b0..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/Decoder.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.qrcode.decoder; - -import com.google.zxing.ChecksumException; -import com.google.zxing.DecodeHintType; -import com.google.zxing.FormatException; -import com.google.zxing.common.BitMatrix; -import com.google.zxing.common.DecoderResult; -import com.google.zxing.common.reedsolomon.GenericGF; -import com.google.zxing.common.reedsolomon.ReedSolomonDecoder; -import com.google.zxing.common.reedsolomon.ReedSolomonException; - -import java.util.Map; - -/** - *

The main class which implements QR Code decoding -- as opposed to locating and extracting - * the QR Code from an image.

- * - * @author Sean Owen - */ -public final class Decoder { - - private final ReedSolomonDecoder rsDecoder; - - public Decoder() { - rsDecoder = new ReedSolomonDecoder(GenericGF.QR_CODE_FIELD_256); - } - - public DecoderResult decode(boolean[][] image) throws ChecksumException, FormatException { - return decode(image, null); - } - - /** - *

Convenience method that can decode a QR Code represented as a 2D array of booleans. - * "true" is taken to mean a black module.

- * - * @param image booleans representing white/black QR Code modules - * @param hints decoding hints that should be used to influence decoding - * @return text and bytes encoded within the QR Code - * @throws FormatException if the QR Code cannot be decoded - * @throws ChecksumException if error correction fails - */ - public DecoderResult decode(boolean[][] image, Map hints) - throws ChecksumException, FormatException { - return decode(BitMatrix.parse(image), hints); - } - - public DecoderResult decode(BitMatrix bits) throws ChecksumException, FormatException { - return decode(bits, null); - } - - /** - *

Decodes a QR Code represented as a {@link BitMatrix}. A 1 or "true" is taken to mean a black module.

- * - * @param bits booleans representing white/black QR Code modules - * @param hints decoding hints that should be used to influence decoding - * @return text and bytes encoded within the QR Code - * @throws FormatException if the QR Code cannot be decoded - * @throws ChecksumException if error correction fails - */ - public DecoderResult decode(BitMatrix bits, Map hints) - throws FormatException, ChecksumException { - - // Construct a parser and read version, error-correction level - BitMatrixParser parser = new BitMatrixParser(bits); - FormatException fe = null; - ChecksumException ce = null; - try { - return decode(parser, hints); - } catch (FormatException e) { - fe = e; - } catch (ChecksumException e) { - ce = e; - } - - try { - - // Revert the bit matrix - parser.remask(); - - // Will be attempting a mirrored reading of the version and format info. - parser.setMirror(true); - - // Preemptively read the version. - parser.readVersion(); - - // Preemptively read the format information. - parser.readFormatInformation(); - - /* - * Since we're here, this means we have successfully detected some kind - * of version and format information when mirrored. This is a good sign, - * that the QR code may be mirrored, and we should try once more with a - * mirrored content. - */ - // Prepare for a mirrored reading. - parser.mirror(); - - DecoderResult result = decode(parser, hints); - - // Success! Notify the caller that the code was mirrored. - result.setOther(new QRCodeDecoderMetaData(true)); - - return result; - - } catch (FormatException | ChecksumException e) { - // Throw the exception from the original reading - if (fe != null) { - throw fe; - } - throw ce; // If fe is null, this can't be - } - } - - private DecoderResult decode(BitMatrixParser parser, Map hints) - throws FormatException, ChecksumException { - Version version = parser.readVersion(); - ErrorCorrectionLevel ecLevel = parser.readFormatInformation().getErrorCorrectionLevel(); - - // Read codewords - byte[] codewords = parser.readCodewords(); - // Separate into data blocks - DataBlock[] dataBlocks = DataBlock.getDataBlocks(codewords, version, ecLevel); - - // Count total number of data bytes - int totalBytes = 0; - for (DataBlock dataBlock : dataBlocks) { - totalBytes += dataBlock.getNumDataCodewords(); - } - byte[] resultBytes = new byte[totalBytes]; - int resultOffset = 0; - - // Error-correct and copy data blocks together into a stream of bytes - for (DataBlock dataBlock : dataBlocks) { - byte[] codewordBytes = dataBlock.getCodewords(); - int numDataCodewords = dataBlock.getNumDataCodewords(); - correctErrors(codewordBytes, numDataCodewords); - for (int i = 0; i < numDataCodewords; i++) { - resultBytes[resultOffset++] = codewordBytes[i]; - } - } - - // Decode the contents of that stream of bytes - return DecodedBitStreamParser.decode(resultBytes, version, ecLevel, hints); - } - - /** - *

Given data and error-correction codewords received, possibly corrupted by errors, attempts to - * correct the errors in-place using Reed-Solomon error correction.

- * - * @param codewordBytes data and error correction codewords - * @param numDataCodewords number of codewords that are data bytes - * @throws ChecksumException if error correction fails - */ - private void correctErrors(byte[] codewordBytes, int numDataCodewords) throws ChecksumException { - int numCodewords = codewordBytes.length; - // First read into an array of ints - int[] codewordsInts = new int[numCodewords]; - for (int i = 0; i < numCodewords; i++) { - codewordsInts[i] = codewordBytes[i] & 0xFF; - } - try { - rsDecoder.decode(codewordsInts, codewordBytes.length - numDataCodewords); - } catch (ReedSolomonException ignored) { - throw ChecksumException.getChecksumInstance(); - } - // Copy back into array of bytes -- only need to worry about the bytes that were data - // We don't care about errors in the error-correction codewords - for (int i = 0; i < numDataCodewords; i++) { - codewordBytes[i] = (byte) codewordsInts[i]; - } - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java b/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java deleted file mode 100755 index 0f1e11387..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.qrcode.decoder; - -/** - *

See ISO 18004:2006, 6.5.1. This enum encapsulates the four error correction levels - * defined by the QR code standard.

- * - * @author Sean Owen - */ -public enum ErrorCorrectionLevel { - - /** L = ~7% correction */ - L(0x01), - /** M = ~15% correction */ - M(0x00), - /** Q = ~25% correction */ - Q(0x03), - /** H = ~30% correction */ - H(0x02); - - private static final ErrorCorrectionLevel[] FOR_BITS = {M, L, H, Q}; - - private final int bits; - - ErrorCorrectionLevel(int bits) { - this.bits = bits; - } - - public int getBits() { - return bits; - } - - /** - * @param bits int containing the two bits encoding a QR Code's error correction level - * @return ErrorCorrectionLevel representing the encoded error correction level - */ - public static ErrorCorrectionLevel forBits(int bits) { - if (bits < 0 || bits >= FOR_BITS.length) { - throw new IllegalArgumentException(); - } - return FOR_BITS[bits]; - } - - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/FormatInformation.java b/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/FormatInformation.java deleted file mode 100755 index 95ee70156..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/FormatInformation.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.qrcode.decoder; - -/** - *

Encapsulates a QR Code's format information, including the data mask used and - * error correction level.

- * - * @author Sean Owen - * @see DataMask - * @see ErrorCorrectionLevel - */ -final class FormatInformation { - - private static final int FORMAT_INFO_MASK_QR = 0x5412; - - /** - * See ISO 18004:2006, Annex C, Table C.1 - */ - private static final int[][] FORMAT_INFO_DECODE_LOOKUP = { - {0x5412, 0x00}, - {0x5125, 0x01}, - {0x5E7C, 0x02}, - {0x5B4B, 0x03}, - {0x45F9, 0x04}, - {0x40CE, 0x05}, - {0x4F97, 0x06}, - {0x4AA0, 0x07}, - {0x77C4, 0x08}, - {0x72F3, 0x09}, - {0x7DAA, 0x0A}, - {0x789D, 0x0B}, - {0x662F, 0x0C}, - {0x6318, 0x0D}, - {0x6C41, 0x0E}, - {0x6976, 0x0F}, - {0x1689, 0x10}, - {0x13BE, 0x11}, - {0x1CE7, 0x12}, - {0x19D0, 0x13}, - {0x0762, 0x14}, - {0x0255, 0x15}, - {0x0D0C, 0x16}, - {0x083B, 0x17}, - {0x355F, 0x18}, - {0x3068, 0x19}, - {0x3F31, 0x1A}, - {0x3A06, 0x1B}, - {0x24B4, 0x1C}, - {0x2183, 0x1D}, - {0x2EDA, 0x1E}, - {0x2BED, 0x1F}, - }; - - private final ErrorCorrectionLevel errorCorrectionLevel; - private final byte dataMask; - - private FormatInformation(int formatInfo) { - // Bits 3,4 - errorCorrectionLevel = ErrorCorrectionLevel.forBits((formatInfo >> 3) & 0x03); - // Bottom 3 bits - dataMask = (byte) (formatInfo & 0x07); - } - - static int numBitsDiffering(int a, int b) { - return Integer.bitCount(a ^ b); - } - - /** - * @param maskedFormatInfo1 format info indicator, with mask still applied - * @param maskedFormatInfo2 second copy of same info; both are checked at the same time - * to establish best match - * @return information about the format it specifies, or {@code null} - * if doesn't seem to match any known pattern - */ - static FormatInformation decodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2) { - FormatInformation formatInfo = doDecodeFormatInformation(maskedFormatInfo1, maskedFormatInfo2); - if (formatInfo != null) { - return formatInfo; - } - // Should return null, but, some QR codes apparently - // do not mask this info. Try again by actually masking the pattern - // first - return doDecodeFormatInformation(maskedFormatInfo1 ^ FORMAT_INFO_MASK_QR, - maskedFormatInfo2 ^ FORMAT_INFO_MASK_QR); - } - - private static FormatInformation doDecodeFormatInformation(int maskedFormatInfo1, int maskedFormatInfo2) { - // Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing - int bestDifference = Integer.MAX_VALUE; - int bestFormatInfo = 0; - for (int[] decodeInfo : FORMAT_INFO_DECODE_LOOKUP) { - int targetInfo = decodeInfo[0]; - if (targetInfo == maskedFormatInfo1 || targetInfo == maskedFormatInfo2) { - // Found an exact match - return new FormatInformation(decodeInfo[1]); - } - int bitsDifference = numBitsDiffering(maskedFormatInfo1, targetInfo); - if (bitsDifference < bestDifference) { - bestFormatInfo = decodeInfo[1]; - bestDifference = bitsDifference; - } - if (maskedFormatInfo1 != maskedFormatInfo2) { - // also try the other option - bitsDifference = numBitsDiffering(maskedFormatInfo2, targetInfo); - if (bitsDifference < bestDifference) { - bestFormatInfo = decodeInfo[1]; - bestDifference = bitsDifference; - } - } - } - // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits - // differing means we found a match - if (bestDifference <= 3) { - return new FormatInformation(bestFormatInfo); - } - return null; - } - - ErrorCorrectionLevel getErrorCorrectionLevel() { - return errorCorrectionLevel; - } - - byte getDataMask() { - return dataMask; - } - - @Override - public int hashCode() { - return (errorCorrectionLevel.ordinal() << 3) | dataMask; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof FormatInformation)) { - return false; - } - FormatInformation other = (FormatInformation) o; - return this.errorCorrectionLevel == other.errorCorrectionLevel && - this.dataMask == other.dataMask; - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/Mode.java b/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/Mode.java deleted file mode 100755 index b7e9ab3a9..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/Mode.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.qrcode.decoder; - -/** - *

See ISO 18004:2006, 6.4.1, Tables 2 and 3. This enum encapsulates the various modes in which - * data can be encoded to bits in the QR code standard.

- * - * @author Sean Owen - */ -public enum Mode { - - TERMINATOR(new int[]{0, 0, 0}, 0x00), // Not really a mode... - NUMERIC(new int[]{10, 12, 14}, 0x01), - ALPHANUMERIC(new int[]{9, 11, 13}, 0x02), - STRUCTURED_APPEND(new int[]{0, 0, 0}, 0x03), // Not supported - BYTE(new int[]{8, 16, 16}, 0x04), - ECI(new int[]{0, 0, 0}, 0x07), // character counts don't apply - KANJI(new int[]{8, 10, 12}, 0x08), - FNC1_FIRST_POSITION(new int[]{0, 0, 0}, 0x05), - FNC1_SECOND_POSITION(new int[]{0, 0, 0}, 0x09), - /** See GBT 18284-2000; "Hanzi" is a transliteration of this mode name. */ - HANZI(new int[]{8, 10, 12}, 0x0D); - - private final int[] characterCountBitsForVersions; - private final int bits; - - Mode(int[] characterCountBitsForVersions, int bits) { - this.characterCountBitsForVersions = characterCountBitsForVersions; - this.bits = bits; - } - - /** - * @param bits four bits encoding a QR Code data mode - * @return Mode encoded by these bits - * @throws IllegalArgumentException if bits do not correspond to a known mode - */ - public static Mode forBits(int bits) { - switch (bits) { - case 0x0: - return TERMINATOR; - case 0x1: - return NUMERIC; - case 0x2: - return ALPHANUMERIC; - case 0x3: - return STRUCTURED_APPEND; - case 0x4: - return BYTE; - case 0x5: - return FNC1_FIRST_POSITION; - case 0x7: - return ECI; - case 0x8: - return KANJI; - case 0x9: - return FNC1_SECOND_POSITION; - case 0xD: - // 0xD is defined in GBT 18284-2000, may not be supported in foreign country - return HANZI; - default: - throw new IllegalArgumentException(); - } - } - - /** - * @param version version in question - * @return number of bits used, in this QR Code symbol {@link Version}, to encode the - * count of characters that will follow encoded in this Mode - */ - public int getCharacterCountBits(Version version) { - int number = version.getVersionNumber(); - int offset; - if (number <= 9) { - offset = 0; - } else if (number <= 26) { - offset = 1; - } else { - offset = 2; - } - return characterCountBitsForVersions[offset]; - } - - public int getBits() { - return bits; - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/QRCodeDecoderMetaData.java b/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/QRCodeDecoderMetaData.java deleted file mode 100755 index 299bb6523..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/QRCodeDecoderMetaData.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2013 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.qrcode.decoder; - -import com.google.zxing.ResultPoint; - -/** - * Meta-data container for QR Code decoding. Instances of this class may be used to convey information back to the - * decoding caller. Callers are expected to process this. - * - * @see com.google.zxing.common.DecoderResult#getOther() - */ -public final class QRCodeDecoderMetaData { - - private final boolean mirrored; - - QRCodeDecoderMetaData(boolean mirrored) { - this.mirrored = mirrored; - } - - /** - * @return true if the QR Code was mirrored. - */ - public boolean isMirrored() { - return mirrored; - } - - /** - * Apply the result points' order correction due to mirroring. - * - * @param points Array of points to apply mirror correction to. - */ - public void applyMirroredCorrection(ResultPoint[] points) { - if (!mirrored || points == null || points.length < 3) { - return; - } - ResultPoint bottomLeft = points[0]; - points[0] = points[2]; - points[2] = bottomLeft; - // No need to 'fix' top-left and alignment pattern. - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/Version.java b/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/Version.java deleted file mode 100755 index 169e3fd36..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/qrcode/decoder/Version.java +++ /dev/null @@ -1,577 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.qrcode.decoder; - -import com.google.zxing.FormatException; -import com.google.zxing.common.BitMatrix; - -/** - * See ISO 18004:2006 Annex D - * - * @author Sean Owen - */ -public final class Version { - - /** - * See ISO 18004:2006 Annex D. - * Element i represents the raw version bits that specify version i + 7 - */ - private static final int[] VERSION_DECODE_INFO = { - 0x07C94, 0x085BC, 0x09A99, 0x0A4D3, 0x0BBF6, - 0x0C762, 0x0D847, 0x0E60D, 0x0F928, 0x10B78, - 0x1145D, 0x12A17, 0x13532, 0x149A6, 0x15683, - 0x168C9, 0x177EC, 0x18EC4, 0x191E1, 0x1AFAB, - 0x1B08E, 0x1CC1A, 0x1D33F, 0x1ED75, 0x1F250, - 0x209D5, 0x216F0, 0x228BA, 0x2379F, 0x24B0B, - 0x2542E, 0x26A64, 0x27541, 0x28C69 - }; - - private static final Version[] VERSIONS = buildVersions(); - - private final int versionNumber; - private final int[] alignmentPatternCenters; - private final ECBlocks[] ecBlocks; - private final int totalCodewords; - - private Version(int versionNumber, - int[] alignmentPatternCenters, - ECBlocks... ecBlocks) { - this.versionNumber = versionNumber; - this.alignmentPatternCenters = alignmentPatternCenters; - this.ecBlocks = ecBlocks; - int total = 0; - int ecCodewords = ecBlocks[0].getECCodewordsPerBlock(); - ECB[] ecbArray = ecBlocks[0].getECBlocks(); - for (ECB ecBlock : ecbArray) { - total += ecBlock.getCount() * (ecBlock.getDataCodewords() + ecCodewords); - } - this.totalCodewords = total; - } - - public int getVersionNumber() { - return versionNumber; - } - - public int[] getAlignmentPatternCenters() { - return alignmentPatternCenters; - } - - public int getTotalCodewords() { - return totalCodewords; - } - - public int getDimensionForVersion() { - return 17 + 4 * versionNumber; - } - - public ECBlocks getECBlocksForLevel(ErrorCorrectionLevel ecLevel) { - return ecBlocks[ecLevel.ordinal()]; - } - - /** - *

Deduces version information purely from QR Code dimensions.

- * - * @param dimension dimension in modules - * @return Version for a QR Code of that dimension - * @throws FormatException if dimension is not 1 mod 4 - */ - public static Version getProvisionalVersionForDimension(int dimension) throws FormatException { - if (dimension % 4 != 1) { - throw FormatException.getFormatInstance(); - } - try { - return getVersionForNumber((dimension - 17) / 4); - } catch (IllegalArgumentException ignored) { - throw FormatException.getFormatInstance(); - } - } - - public static Version getVersionForNumber(int versionNumber) { - if (versionNumber < 1 || versionNumber > 40) { - throw new IllegalArgumentException(); - } - return VERSIONS[versionNumber - 1]; - } - - static Version decodeVersionInformation(int versionBits) { - int bestDifference = Integer.MAX_VALUE; - int bestVersion = 0; - for (int i = 0; i < VERSION_DECODE_INFO.length; i++) { - int targetVersion = VERSION_DECODE_INFO[i]; - // Do the version info bits match exactly? done. - if (targetVersion == versionBits) { - return getVersionForNumber(i + 7); - } - // Otherwise see if this is the closest to a real version info bit string - // we have seen so far - int bitsDifference = FormatInformation.numBitsDiffering(versionBits, targetVersion); - if (bitsDifference < bestDifference) { - bestVersion = i + 7; - bestDifference = bitsDifference; - } - } - // We can tolerate up to 3 bits of error since no two version info codewords will - // differ in less than 8 bits. - if (bestDifference <= 3) { - return getVersionForNumber(bestVersion); - } - // If we didn't find a close enough match, fail - return null; - } - - /** - * See ISO 18004:2006 Annex E - */ - BitMatrix buildFunctionPattern() { - int dimension = getDimensionForVersion(); - BitMatrix bitMatrix = new BitMatrix(dimension); - - // Top left finder pattern + separator + format - bitMatrix.setRegion(0, 0, 9, 9); - // Top right finder pattern + separator + format - bitMatrix.setRegion(dimension - 8, 0, 8, 9); - // Bottom left finder pattern + separator + format - bitMatrix.setRegion(0, dimension - 8, 9, 8); - - // Alignment patterns - int max = alignmentPatternCenters.length; - for (int x = 0; x < max; x++) { - int i = alignmentPatternCenters[x] - 2; - for (int y = 0; y < max; y++) { - if ((x != 0 || (y != 0 && y != max - 1)) && (x != max - 1 || y != 0)) { - bitMatrix.setRegion(alignmentPatternCenters[y] - 2, i, 5, 5); - } - // else no o alignment patterns near the three finder patterns - } - } - - // Vertical timing pattern - bitMatrix.setRegion(6, 9, 1, dimension - 17); - // Horizontal timing pattern - bitMatrix.setRegion(9, 6, dimension - 17, 1); - - if (versionNumber > 6) { - // Version info, top right - bitMatrix.setRegion(dimension - 11, 0, 3, 6); - // Version info, bottom left - bitMatrix.setRegion(0, dimension - 11, 6, 3); - } - - return bitMatrix; - } - - /** - *

Encapsulates a set of error-correction blocks in one symbol version. Most versions will - * use blocks of differing sizes within one version, so, this encapsulates the parameters for - * each set of blocks. It also holds the number of error-correction codewords per block since it - * will be the same across all blocks within one version.

- */ - public static final class ECBlocks { - private final int ecCodewordsPerBlock; - private final ECB[] ecBlocks; - - ECBlocks(int ecCodewordsPerBlock, ECB... ecBlocks) { - this.ecCodewordsPerBlock = ecCodewordsPerBlock; - this.ecBlocks = ecBlocks; - } - - public int getECCodewordsPerBlock() { - return ecCodewordsPerBlock; - } - - public int getNumBlocks() { - int total = 0; - for (ECB ecBlock : ecBlocks) { - total += ecBlock.getCount(); - } - return total; - } - - public int getTotalECCodewords() { - return ecCodewordsPerBlock * getNumBlocks(); - } - - public ECB[] getECBlocks() { - return ecBlocks; - } - } - - /** - *

Encapsulates the parameters for one error-correction block in one symbol version. - * This includes the number of data codewords, and the number of times a block with these - * parameters is used consecutively in the QR code version's format.

- */ - public static final class ECB { - private final int count; - private final int dataCodewords; - - ECB(int count, int dataCodewords) { - this.count = count; - this.dataCodewords = dataCodewords; - } - - public int getCount() { - return count; - } - - public int getDataCodewords() { - return dataCodewords; - } - } - - @Override - public String toString() { - return String.valueOf(versionNumber); - } - - /** - * See ISO 18004:2006 6.5.1 Table 9 - */ - private static Version[] buildVersions() { - return new Version[]{ - new Version(1, new int[]{}, - new ECBlocks(7, new ECB(1, 19)), - new ECBlocks(10, new ECB(1, 16)), - new ECBlocks(13, new ECB(1, 13)), - new ECBlocks(17, new ECB(1, 9))), - new Version(2, new int[]{6, 18}, - new ECBlocks(10, new ECB(1, 34)), - new ECBlocks(16, new ECB(1, 28)), - new ECBlocks(22, new ECB(1, 22)), - new ECBlocks(28, new ECB(1, 16))), - new Version(3, new int[]{6, 22}, - new ECBlocks(15, new ECB(1, 55)), - new ECBlocks(26, new ECB(1, 44)), - new ECBlocks(18, new ECB(2, 17)), - new ECBlocks(22, new ECB(2, 13))), - new Version(4, new int[]{6, 26}, - new ECBlocks(20, new ECB(1, 80)), - new ECBlocks(18, new ECB(2, 32)), - new ECBlocks(26, new ECB(2, 24)), - new ECBlocks(16, new ECB(4, 9))), - new Version(5, new int[]{6, 30}, - new ECBlocks(26, new ECB(1, 108)), - new ECBlocks(24, new ECB(2, 43)), - new ECBlocks(18, new ECB(2, 15), - new ECB(2, 16)), - new ECBlocks(22, new ECB(2, 11), - new ECB(2, 12))), - new Version(6, new int[]{6, 34}, - new ECBlocks(18, new ECB(2, 68)), - new ECBlocks(16, new ECB(4, 27)), - new ECBlocks(24, new ECB(4, 19)), - new ECBlocks(28, new ECB(4, 15))), - new Version(7, new int[]{6, 22, 38}, - new ECBlocks(20, new ECB(2, 78)), - new ECBlocks(18, new ECB(4, 31)), - new ECBlocks(18, new ECB(2, 14), - new ECB(4, 15)), - new ECBlocks(26, new ECB(4, 13), - new ECB(1, 14))), - new Version(8, new int[]{6, 24, 42}, - new ECBlocks(24, new ECB(2, 97)), - new ECBlocks(22, new ECB(2, 38), - new ECB(2, 39)), - new ECBlocks(22, new ECB(4, 18), - new ECB(2, 19)), - new ECBlocks(26, new ECB(4, 14), - new ECB(2, 15))), - new Version(9, new int[]{6, 26, 46}, - new ECBlocks(30, new ECB(2, 116)), - new ECBlocks(22, new ECB(3, 36), - new ECB(2, 37)), - new ECBlocks(20, new ECB(4, 16), - new ECB(4, 17)), - new ECBlocks(24, new ECB(4, 12), - new ECB(4, 13))), - new Version(10, new int[]{6, 28, 50}, - new ECBlocks(18, new ECB(2, 68), - new ECB(2, 69)), - new ECBlocks(26, new ECB(4, 43), - new ECB(1, 44)), - new ECBlocks(24, new ECB(6, 19), - new ECB(2, 20)), - new ECBlocks(28, new ECB(6, 15), - new ECB(2, 16))), - new Version(11, new int[]{6, 30, 54}, - new ECBlocks(20, new ECB(4, 81)), - new ECBlocks(30, new ECB(1, 50), - new ECB(4, 51)), - new ECBlocks(28, new ECB(4, 22), - new ECB(4, 23)), - new ECBlocks(24, new ECB(3, 12), - new ECB(8, 13))), - new Version(12, new int[]{6, 32, 58}, - new ECBlocks(24, new ECB(2, 92), - new ECB(2, 93)), - new ECBlocks(22, new ECB(6, 36), - new ECB(2, 37)), - new ECBlocks(26, new ECB(4, 20), - new ECB(6, 21)), - new ECBlocks(28, new ECB(7, 14), - new ECB(4, 15))), - new Version(13, new int[]{6, 34, 62}, - new ECBlocks(26, new ECB(4, 107)), - new ECBlocks(22, new ECB(8, 37), - new ECB(1, 38)), - new ECBlocks(24, new ECB(8, 20), - new ECB(4, 21)), - new ECBlocks(22, new ECB(12, 11), - new ECB(4, 12))), - new Version(14, new int[]{6, 26, 46, 66}, - new ECBlocks(30, new ECB(3, 115), - new ECB(1, 116)), - new ECBlocks(24, new ECB(4, 40), - new ECB(5, 41)), - new ECBlocks(20, new ECB(11, 16), - new ECB(5, 17)), - new ECBlocks(24, new ECB(11, 12), - new ECB(5, 13))), - new Version(15, new int[]{6, 26, 48, 70}, - new ECBlocks(22, new ECB(5, 87), - new ECB(1, 88)), - new ECBlocks(24, new ECB(5, 41), - new ECB(5, 42)), - new ECBlocks(30, new ECB(5, 24), - new ECB(7, 25)), - new ECBlocks(24, new ECB(11, 12), - new ECB(7, 13))), - new Version(16, new int[]{6, 26, 50, 74}, - new ECBlocks(24, new ECB(5, 98), - new ECB(1, 99)), - new ECBlocks(28, new ECB(7, 45), - new ECB(3, 46)), - new ECBlocks(24, new ECB(15, 19), - new ECB(2, 20)), - new ECBlocks(30, new ECB(3, 15), - new ECB(13, 16))), - new Version(17, new int[]{6, 30, 54, 78}, - new ECBlocks(28, new ECB(1, 107), - new ECB(5, 108)), - new ECBlocks(28, new ECB(10, 46), - new ECB(1, 47)), - new ECBlocks(28, new ECB(1, 22), - new ECB(15, 23)), - new ECBlocks(28, new ECB(2, 14), - new ECB(17, 15))), - new Version(18, new int[]{6, 30, 56, 82}, - new ECBlocks(30, new ECB(5, 120), - new ECB(1, 121)), - new ECBlocks(26, new ECB(9, 43), - new ECB(4, 44)), - new ECBlocks(28, new ECB(17, 22), - new ECB(1, 23)), - new ECBlocks(28, new ECB(2, 14), - new ECB(19, 15))), - new Version(19, new int[]{6, 30, 58, 86}, - new ECBlocks(28, new ECB(3, 113), - new ECB(4, 114)), - new ECBlocks(26, new ECB(3, 44), - new ECB(11, 45)), - new ECBlocks(26, new ECB(17, 21), - new ECB(4, 22)), - new ECBlocks(26, new ECB(9, 13), - new ECB(16, 14))), - new Version(20, new int[]{6, 34, 62, 90}, - new ECBlocks(28, new ECB(3, 107), - new ECB(5, 108)), - new ECBlocks(26, new ECB(3, 41), - new ECB(13, 42)), - new ECBlocks(30, new ECB(15, 24), - new ECB(5, 25)), - new ECBlocks(28, new ECB(15, 15), - new ECB(10, 16))), - new Version(21, new int[]{6, 28, 50, 72, 94}, - new ECBlocks(28, new ECB(4, 116), - new ECB(4, 117)), - new ECBlocks(26, new ECB(17, 42)), - new ECBlocks(28, new ECB(17, 22), - new ECB(6, 23)), - new ECBlocks(30, new ECB(19, 16), - new ECB(6, 17))), - new Version(22, new int[]{6, 26, 50, 74, 98}, - new ECBlocks(28, new ECB(2, 111), - new ECB(7, 112)), - new ECBlocks(28, new ECB(17, 46)), - new ECBlocks(30, new ECB(7, 24), - new ECB(16, 25)), - new ECBlocks(24, new ECB(34, 13))), - new Version(23, new int[]{6, 30, 54, 78, 102}, - new ECBlocks(30, new ECB(4, 121), - new ECB(5, 122)), - new ECBlocks(28, new ECB(4, 47), - new ECB(14, 48)), - new ECBlocks(30, new ECB(11, 24), - new ECB(14, 25)), - new ECBlocks(30, new ECB(16, 15), - new ECB(14, 16))), - new Version(24, new int[]{6, 28, 54, 80, 106}, - new ECBlocks(30, new ECB(6, 117), - new ECB(4, 118)), - new ECBlocks(28, new ECB(6, 45), - new ECB(14, 46)), - new ECBlocks(30, new ECB(11, 24), - new ECB(16, 25)), - new ECBlocks(30, new ECB(30, 16), - new ECB(2, 17))), - new Version(25, new int[]{6, 32, 58, 84, 110}, - new ECBlocks(26, new ECB(8, 106), - new ECB(4, 107)), - new ECBlocks(28, new ECB(8, 47), - new ECB(13, 48)), - new ECBlocks(30, new ECB(7, 24), - new ECB(22, 25)), - new ECBlocks(30, new ECB(22, 15), - new ECB(13, 16))), - new Version(26, new int[]{6, 30, 58, 86, 114}, - new ECBlocks(28, new ECB(10, 114), - new ECB(2, 115)), - new ECBlocks(28, new ECB(19, 46), - new ECB(4, 47)), - new ECBlocks(28, new ECB(28, 22), - new ECB(6, 23)), - new ECBlocks(30, new ECB(33, 16), - new ECB(4, 17))), - new Version(27, new int[]{6, 34, 62, 90, 118}, - new ECBlocks(30, new ECB(8, 122), - new ECB(4, 123)), - new ECBlocks(28, new ECB(22, 45), - new ECB(3, 46)), - new ECBlocks(30, new ECB(8, 23), - new ECB(26, 24)), - new ECBlocks(30, new ECB(12, 15), - new ECB(28, 16))), - new Version(28, new int[]{6, 26, 50, 74, 98, 122}, - new ECBlocks(30, new ECB(3, 117), - new ECB(10, 118)), - new ECBlocks(28, new ECB(3, 45), - new ECB(23, 46)), - new ECBlocks(30, new ECB(4, 24), - new ECB(31, 25)), - new ECBlocks(30, new ECB(11, 15), - new ECB(31, 16))), - new Version(29, new int[]{6, 30, 54, 78, 102, 126}, - new ECBlocks(30, new ECB(7, 116), - new ECB(7, 117)), - new ECBlocks(28, new ECB(21, 45), - new ECB(7, 46)), - new ECBlocks(30, new ECB(1, 23), - new ECB(37, 24)), - new ECBlocks(30, new ECB(19, 15), - new ECB(26, 16))), - new Version(30, new int[]{6, 26, 52, 78, 104, 130}, - new ECBlocks(30, new ECB(5, 115), - new ECB(10, 116)), - new ECBlocks(28, new ECB(19, 47), - new ECB(10, 48)), - new ECBlocks(30, new ECB(15, 24), - new ECB(25, 25)), - new ECBlocks(30, new ECB(23, 15), - new ECB(25, 16))), - new Version(31, new int[]{6, 30, 56, 82, 108, 134}, - new ECBlocks(30, new ECB(13, 115), - new ECB(3, 116)), - new ECBlocks(28, new ECB(2, 46), - new ECB(29, 47)), - new ECBlocks(30, new ECB(42, 24), - new ECB(1, 25)), - new ECBlocks(30, new ECB(23, 15), - new ECB(28, 16))), - new Version(32, new int[]{6, 34, 60, 86, 112, 138}, - new ECBlocks(30, new ECB(17, 115)), - new ECBlocks(28, new ECB(10, 46), - new ECB(23, 47)), - new ECBlocks(30, new ECB(10, 24), - new ECB(35, 25)), - new ECBlocks(30, new ECB(19, 15), - new ECB(35, 16))), - new Version(33, new int[]{6, 30, 58, 86, 114, 142}, - new ECBlocks(30, new ECB(17, 115), - new ECB(1, 116)), - new ECBlocks(28, new ECB(14, 46), - new ECB(21, 47)), - new ECBlocks(30, new ECB(29, 24), - new ECB(19, 25)), - new ECBlocks(30, new ECB(11, 15), - new ECB(46, 16))), - new Version(34, new int[]{6, 34, 62, 90, 118, 146}, - new ECBlocks(30, new ECB(13, 115), - new ECB(6, 116)), - new ECBlocks(28, new ECB(14, 46), - new ECB(23, 47)), - new ECBlocks(30, new ECB(44, 24), - new ECB(7, 25)), - new ECBlocks(30, new ECB(59, 16), - new ECB(1, 17))), - new Version(35, new int[]{6, 30, 54, 78, 102, 126, 150}, - new ECBlocks(30, new ECB(12, 121), - new ECB(7, 122)), - new ECBlocks(28, new ECB(12, 47), - new ECB(26, 48)), - new ECBlocks(30, new ECB(39, 24), - new ECB(14, 25)), - new ECBlocks(30, new ECB(22, 15), - new ECB(41, 16))), - new Version(36, new int[]{6, 24, 50, 76, 102, 128, 154}, - new ECBlocks(30, new ECB(6, 121), - new ECB(14, 122)), - new ECBlocks(28, new ECB(6, 47), - new ECB(34, 48)), - new ECBlocks(30, new ECB(46, 24), - new ECB(10, 25)), - new ECBlocks(30, new ECB(2, 15), - new ECB(64, 16))), - new Version(37, new int[]{6, 28, 54, 80, 106, 132, 158}, - new ECBlocks(30, new ECB(17, 122), - new ECB(4, 123)), - new ECBlocks(28, new ECB(29, 46), - new ECB(14, 47)), - new ECBlocks(30, new ECB(49, 24), - new ECB(10, 25)), - new ECBlocks(30, new ECB(24, 15), - new ECB(46, 16))), - new Version(38, new int[]{6, 32, 58, 84, 110, 136, 162}, - new ECBlocks(30, new ECB(4, 122), - new ECB(18, 123)), - new ECBlocks(28, new ECB(13, 46), - new ECB(32, 47)), - new ECBlocks(30, new ECB(48, 24), - new ECB(14, 25)), - new ECBlocks(30, new ECB(42, 15), - new ECB(32, 16))), - new Version(39, new int[]{6, 26, 54, 82, 110, 138, 166}, - new ECBlocks(30, new ECB(20, 117), - new ECB(4, 118)), - new ECBlocks(28, new ECB(40, 47), - new ECB(7, 48)), - new ECBlocks(30, new ECB(43, 24), - new ECB(22, 25)), - new ECBlocks(30, new ECB(10, 15), - new ECB(67, 16))), - new Version(40, new int[]{6, 30, 58, 86, 114, 142, 170}, - new ECBlocks(30, new ECB(19, 118), - new ECB(6, 119)), - new ECBlocks(28, new ECB(18, 47), - new ECB(31, 48)), - new ECBlocks(30, new ECB(34, 24), - new ECB(34, 25)), - new ECBlocks(30, new ECB(20, 15), - new ECB(61, 16))) - }; - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/qrcode/detector/AlignmentPattern.java b/TMessagesProj/src/main/java/com/google/zxing/qrcode/detector/AlignmentPattern.java deleted file mode 100755 index 96d919422..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/qrcode/detector/AlignmentPattern.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.qrcode.detector; - -import com.google.zxing.ResultPoint; - -/** - *

Encapsulates an alignment pattern, which are the smaller square patterns found in - * all but the simplest QR Codes.

- * - * @author Sean Owen - */ -public final class AlignmentPattern extends ResultPoint { - - private final float estimatedModuleSize; - - AlignmentPattern(float posX, float posY, float estimatedModuleSize) { - super(posX, posY); - this.estimatedModuleSize = estimatedModuleSize; - } - - /** - *

Determines if this alignment pattern "about equals" an alignment pattern at the stated - * position and size -- meaning, it is at nearly the same center with nearly the same size.

- */ - boolean aboutEquals(float moduleSize, float i, float j) { - if (Math.abs(i - getY()) <= moduleSize && Math.abs(j - getX()) <= moduleSize) { - float moduleSizeDiff = Math.abs(moduleSize - estimatedModuleSize); - return moduleSizeDiff <= 1.0f || moduleSizeDiff <= estimatedModuleSize; - } - return false; - } - - /** - * Combines this object's current estimate of a finder pattern position and module size - * with a new estimate. It returns a new {@code FinderPattern} containing an average of the two. - */ - AlignmentPattern combineEstimate(float i, float j, float newModuleSize) { - float combinedX = (getX() + j) / 2.0f; - float combinedY = (getY() + i) / 2.0f; - float combinedModuleSize = (estimatedModuleSize + newModuleSize) / 2.0f; - return new AlignmentPattern(combinedX, combinedY, combinedModuleSize); - } - -} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/com/google/zxing/qrcode/detector/AlignmentPatternFinder.java b/TMessagesProj/src/main/java/com/google/zxing/qrcode/detector/AlignmentPatternFinder.java deleted file mode 100755 index c4f9aa0fd..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/qrcode/detector/AlignmentPatternFinder.java +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.qrcode.detector; - -import com.google.zxing.NotFoundException; -import com.google.zxing.ResultPointCallback; -import com.google.zxing.common.BitMatrix; - -import java.util.ArrayList; -import java.util.List; - -/** - *

This class attempts to find alignment patterns in a QR Code. Alignment patterns look like finder - * patterns but are smaller and appear at regular intervals throughout the image.

- * - *

At the moment this only looks for the bottom-right alignment pattern.

- * - *

This is mostly a simplified copy of {@link FinderPatternFinder}. It is copied, - * pasted and stripped down here for maximum performance but does unfortunately duplicate - * some code.

- * - *

This class is thread-safe but not reentrant. Each thread must allocate its own object.

- * - * @author Sean Owen - */ -final class AlignmentPatternFinder { - - private final BitMatrix image; - private final List possibleCenters; - private final int startX; - private final int startY; - private final int width; - private final int height; - private final float moduleSize; - private final int[] crossCheckStateCount; - private final ResultPointCallback resultPointCallback; - - /** - *

Creates a finder that will look in a portion of the whole image.

- * - * @param image image to search - * @param startX left column from which to start searching - * @param startY top row from which to start searching - * @param width width of region to search - * @param height height of region to search - * @param moduleSize estimated module size so far - */ - AlignmentPatternFinder(BitMatrix image, - int startX, - int startY, - int width, - int height, - float moduleSize, - ResultPointCallback resultPointCallback) { - this.image = image; - this.possibleCenters = new ArrayList<>(5); - this.startX = startX; - this.startY = startY; - this.width = width; - this.height = height; - this.moduleSize = moduleSize; - this.crossCheckStateCount = new int[3]; - this.resultPointCallback = resultPointCallback; - } - - /** - *

This method attempts to find the bottom-right alignment pattern in the image. It is a bit messy since - * it's pretty performance-critical and so is written to be fast foremost.

- * - * @return {@link AlignmentPattern} if found - * @throws NotFoundException if not found - */ - AlignmentPattern find() throws NotFoundException { - int startX = this.startX; - int height = this.height; - int maxJ = startX + width; - int middleI = startY + (height / 2); - // We are looking for black/white/black modules in 1:1:1 ratio; - // this tracks the number of black/white/black modules seen so far - int[] stateCount = new int[3]; - for (int iGen = 0; iGen < height; iGen++) { - // Search from middle outwards - int i = middleI + ((iGen & 0x01) == 0 ? (iGen + 1) / 2 : -((iGen + 1) / 2)); - stateCount[0] = 0; - stateCount[1] = 0; - stateCount[2] = 0; - int j = startX; - // Burn off leading white pixels before anything else; if we start in the middle of - // a white run, it doesn't make sense to count its length, since we don't know if the - // white run continued to the left of the start point - while (j < maxJ && !image.get(j, i)) { - j++; - } - int currentState = 0; - while (j < maxJ) { - if (image.get(j, i)) { - // Black pixel - if (currentState == 1) { // Counting black pixels - stateCount[1]++; - } else { // Counting white pixels - if (currentState == 2) { // A winner? - if (foundPatternCross(stateCount)) { // Yes - AlignmentPattern confirmed = handlePossibleCenter(stateCount, i, j); - if (confirmed != null) { - return confirmed; - } - } - stateCount[0] = stateCount[2]; - stateCount[1] = 1; - stateCount[2] = 0; - currentState = 1; - } else { - stateCount[++currentState]++; - } - } - } else { // White pixel - if (currentState == 1) { // Counting black pixels - currentState++; - } - stateCount[currentState]++; - } - j++; - } - if (foundPatternCross(stateCount)) { - AlignmentPattern confirmed = handlePossibleCenter(stateCount, i, maxJ); - if (confirmed != null) { - return confirmed; - } - } - - } - - // Hmm, nothing we saw was observed and confirmed twice. If we had - // any guess at all, return it. - if (!possibleCenters.isEmpty()) { - return possibleCenters.get(0); - } - - throw NotFoundException.getNotFoundInstance(); - } - - /** - * Given a count of black/white/black pixels just seen and an end position, - * figures the location of the center of this black/white/black run. - */ - private static float centerFromEnd(int[] stateCount, int end) { - return (end - stateCount[2]) - stateCount[1] / 2.0f; - } - - /** - * @param stateCount count of black/white/black pixels just read - * @return true iff the proportions of the counts is close enough to the 1/1/1 ratios - * used by alignment patterns to be considered a match - */ - private boolean foundPatternCross(int[] stateCount) { - float moduleSize = this.moduleSize; - float maxVariance = moduleSize / 2.0f; - for (int i = 0; i < 3; i++) { - if (Math.abs(moduleSize - stateCount[i]) >= maxVariance) { - return false; - } - } - return true; - } - - /** - *

After a horizontal scan finds a potential alignment pattern, this method - * "cross-checks" by scanning down vertically through the center of the possible - * alignment pattern to see if the same proportion is detected.

- * - * @param startI row where an alignment pattern was detected - * @param centerJ center of the section that appears to cross an alignment pattern - * @param maxCount maximum reasonable number of modules that should be - * observed in any reading state, based on the results of the horizontal scan - * @return vertical center of alignment pattern, or {@link Float#NaN} if not found - */ - private float crossCheckVertical(int startI, int centerJ, int maxCount, - int originalStateCountTotal) { - BitMatrix image = this.image; - - int maxI = image.getHeight(); - int[] stateCount = crossCheckStateCount; - stateCount[0] = 0; - stateCount[1] = 0; - stateCount[2] = 0; - - // Start counting up from center - int i = startI; - while (i >= 0 && image.get(centerJ, i) && stateCount[1] <= maxCount) { - stateCount[1]++; - i--; - } - // If already too many modules in this state or ran off the edge: - if (i < 0 || stateCount[1] > maxCount) { - return Float.NaN; - } - while (i >= 0 && !image.get(centerJ, i) && stateCount[0] <= maxCount) { - stateCount[0]++; - i--; - } - if (stateCount[0] > maxCount) { - return Float.NaN; - } - - // Now also count down from center - i = startI + 1; - while (i < maxI && image.get(centerJ, i) && stateCount[1] <= maxCount) { - stateCount[1]++; - i++; - } - if (i == maxI || stateCount[1] > maxCount) { - return Float.NaN; - } - while (i < maxI && !image.get(centerJ, i) && stateCount[2] <= maxCount) { - stateCount[2]++; - i++; - } - if (stateCount[2] > maxCount) { - return Float.NaN; - } - - int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2]; - if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) { - return Float.NaN; - } - - return foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : Float.NaN; - } - - /** - *

This is called when a horizontal scan finds a possible alignment pattern. It will - * cross check with a vertical scan, and if successful, will see if this pattern had been - * found on a previous horizontal scan. If so, we consider it confirmed and conclude we have - * found the alignment pattern.

- * - * @param stateCount reading state module counts from horizontal scan - * @param i row where alignment pattern may be found - * @param j end of possible alignment pattern in row - * @return {@link AlignmentPattern} if we have found the same pattern twice, or null if not - */ - private AlignmentPattern handlePossibleCenter(int[] stateCount, int i, int j) { - int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2]; - float centerJ = centerFromEnd(stateCount, j); - float centerI = crossCheckVertical(i, (int) centerJ, 2 * stateCount[1], stateCountTotal); - if (!Float.isNaN(centerI)) { - float estimatedModuleSize = (stateCount[0] + stateCount[1] + stateCount[2]) / 3.0f; - for (AlignmentPattern center : possibleCenters) { - // Look for about the same center and module size: - if (center.aboutEquals(estimatedModuleSize, centerI, centerJ)) { - return center.combineEstimate(centerI, centerJ, estimatedModuleSize); - } - } - // Hadn't found this before; save it - AlignmentPattern point = new AlignmentPattern(centerJ, centerI, estimatedModuleSize); - possibleCenters.add(point); - if (resultPointCallback != null) { - resultPointCallback.foundPossibleResultPoint(point); - } - } - return null; - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/qrcode/detector/Detector.java b/TMessagesProj/src/main/java/com/google/zxing/qrcode/detector/Detector.java deleted file mode 100755 index 5f8787dbd..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/qrcode/detector/Detector.java +++ /dev/null @@ -1,406 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.qrcode.detector; - -import com.google.zxing.DecodeHintType; -import com.google.zxing.FormatException; -import com.google.zxing.NotFoundException; -import com.google.zxing.ResultPoint; -import com.google.zxing.ResultPointCallback; -import com.google.zxing.common.BitMatrix; -import com.google.zxing.common.DetectorResult; -import com.google.zxing.common.GridSampler; -import com.google.zxing.common.PerspectiveTransform; -import com.google.zxing.common.detector.MathUtils; -import com.google.zxing.qrcode.decoder.Version; - -import java.util.Map; - -/** - *

Encapsulates logic that can detect a QR Code in an image, even if the QR Code - * is rotated or skewed, or partially obscured.

- * - * @author Sean Owen - */ -public class Detector { - - private final BitMatrix image; - private ResultPointCallback resultPointCallback; - - public Detector(BitMatrix image) { - this.image = image; - } - - protected final BitMatrix getImage() { - return image; - } - - protected final ResultPointCallback getResultPointCallback() { - return resultPointCallback; - } - - /** - *

Detects a QR Code in an image.

- * - * @return {@link DetectorResult} encapsulating results of detecting a QR Code - * @throws NotFoundException if QR Code cannot be found - * @throws FormatException if a QR Code cannot be decoded - */ - public DetectorResult detect() throws NotFoundException, FormatException { - return detect(null); - } - - /** - *

Detects a QR Code in an image.

- * - * @param hints optional hints to detector - * @return {@link DetectorResult} encapsulating results of detecting a QR Code - * @throws NotFoundException if QR Code cannot be found - * @throws FormatException if a QR Code cannot be decoded - */ - public final DetectorResult detect(Map hints) throws NotFoundException, FormatException { - - resultPointCallback = hints == null ? null : - (ResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK); - - FinderPatternFinder finder = new FinderPatternFinder(image, resultPointCallback); - FinderPatternInfo info = finder.find(hints); - - return processFinderPatternInfo(info); - } - - protected final DetectorResult processFinderPatternInfo(FinderPatternInfo info) - throws NotFoundException, FormatException { - - FinderPattern topLeft = info.getTopLeft(); - FinderPattern topRight = info.getTopRight(); - FinderPattern bottomLeft = info.getBottomLeft(); - - float moduleSize = calculateModuleSize(topLeft, topRight, bottomLeft); - if (moduleSize < 1.0f) { - throw NotFoundException.getNotFoundInstance(); - } - int dimension = computeDimension(topLeft, topRight, bottomLeft, moduleSize); - Version provisionalVersion = Version.getProvisionalVersionForDimension(dimension); - int modulesBetweenFPCenters = provisionalVersion.getDimensionForVersion() - 7; - - AlignmentPattern alignmentPattern = null; - // Anything above version 1 has an alignment pattern - if (provisionalVersion.getAlignmentPatternCenters().length > 0) { - - // Guess where a "bottom right" finder pattern would have been - float bottomRightX = topRight.getX() - topLeft.getX() + bottomLeft.getX(); - float bottomRightY = topRight.getY() - topLeft.getY() + bottomLeft.getY(); - - // Estimate that alignment pattern is closer by 3 modules - // from "bottom right" to known top left location - float correctionToTopLeft = 1.0f - 3.0f / modulesBetweenFPCenters; - int estAlignmentX = (int) (topLeft.getX() + correctionToTopLeft * (bottomRightX - topLeft.getX())); - int estAlignmentY = (int) (topLeft.getY() + correctionToTopLeft * (bottomRightY - topLeft.getY())); - - // Kind of arbitrary -- expand search radius before giving up - for (int i = 4; i <= 16; i <<= 1) { - try { - alignmentPattern = findAlignmentInRegion(moduleSize, - estAlignmentX, - estAlignmentY, - i); - break; - } catch (NotFoundException re) { - // try next round - } - } - // If we didn't find alignment pattern... well try anyway without it - } - - PerspectiveTransform transform = - createTransform(topLeft, topRight, bottomLeft, alignmentPattern, dimension); - - BitMatrix bits = sampleGrid(image, transform, dimension); - - ResultPoint[] points; - if (alignmentPattern == null) { - points = new ResultPoint[]{bottomLeft, topLeft, topRight}; - } else { - points = new ResultPoint[]{bottomLeft, topLeft, topRight, alignmentPattern}; - } - return new DetectorResult(bits, points); - } - - private static PerspectiveTransform createTransform(ResultPoint topLeft, - ResultPoint topRight, - ResultPoint bottomLeft, - ResultPoint alignmentPattern, - int dimension) { - float dimMinusThree = dimension - 3.5f; - float bottomRightX; - float bottomRightY; - float sourceBottomRightX; - float sourceBottomRightY; - if (alignmentPattern != null) { - bottomRightX = alignmentPattern.getX(); - bottomRightY = alignmentPattern.getY(); - sourceBottomRightX = dimMinusThree - 3.0f; - sourceBottomRightY = sourceBottomRightX; - } else { - // Don't have an alignment pattern, just make up the bottom-right point - bottomRightX = (topRight.getX() - topLeft.getX()) + bottomLeft.getX(); - bottomRightY = (topRight.getY() - topLeft.getY()) + bottomLeft.getY(); - sourceBottomRightX = dimMinusThree; - sourceBottomRightY = dimMinusThree; - } - - return PerspectiveTransform.quadrilateralToQuadrilateral( - 3.5f, - 3.5f, - dimMinusThree, - 3.5f, - sourceBottomRightX, - sourceBottomRightY, - 3.5f, - dimMinusThree, - topLeft.getX(), - topLeft.getY(), - topRight.getX(), - topRight.getY(), - bottomRightX, - bottomRightY, - bottomLeft.getX(), - bottomLeft.getY()); - } - - private static BitMatrix sampleGrid(BitMatrix image, - PerspectiveTransform transform, - int dimension) throws NotFoundException { - - GridSampler sampler = GridSampler.getInstance(); - return sampler.sampleGrid(image, dimension, dimension, transform); - } - - /** - *

Computes the dimension (number of modules on a size) of the QR Code based on the position - * of the finder patterns and estimated module size.

- */ - private static int computeDimension(ResultPoint topLeft, - ResultPoint topRight, - ResultPoint bottomLeft, - float moduleSize) throws NotFoundException { - int tltrCentersDimension = MathUtils.round(ResultPoint.distance(topLeft, topRight) / moduleSize); - int tlblCentersDimension = MathUtils.round(ResultPoint.distance(topLeft, bottomLeft) / moduleSize); - int dimension = ((tltrCentersDimension + tlblCentersDimension) / 2) + 7; - switch (dimension & 0x03) { // mod 4 - case 0: - dimension++; - break; - // 1? do nothing - case 2: - dimension--; - break; - case 3: - dimension += 2; - break; - } - return dimension; - } - - /** - *

Computes an average estimated module size based on estimated derived from the positions - * of the three finder patterns.

- * - * @param topLeft detected top-left finder pattern center - * @param topRight detected top-right finder pattern center - * @param bottomLeft detected bottom-left finder pattern center - * @return estimated module size - */ - protected final float calculateModuleSize(ResultPoint topLeft, - ResultPoint topRight, - ResultPoint bottomLeft) { - // Take the average - return (calculateModuleSizeOneWay(topLeft, topRight) + - calculateModuleSizeOneWay(topLeft, bottomLeft)) / 2.0f; - } - - /** - *

Estimates module size based on two finder patterns -- it uses - * {@link #sizeOfBlackWhiteBlackRunBothWays(int, int, int, int)} to figure the - * width of each, measuring along the axis between their centers.

- */ - private float calculateModuleSizeOneWay(ResultPoint pattern, ResultPoint otherPattern) { - float moduleSizeEst1 = sizeOfBlackWhiteBlackRunBothWays((int) pattern.getX(), - (int) pattern.getY(), - (int) otherPattern.getX(), - (int) otherPattern.getY()); - float moduleSizeEst2 = sizeOfBlackWhiteBlackRunBothWays((int) otherPattern.getX(), - (int) otherPattern.getY(), - (int) pattern.getX(), - (int) pattern.getY()); - if (Float.isNaN(moduleSizeEst1)) { - return moduleSizeEst2 / 7.0f; - } - if (Float.isNaN(moduleSizeEst2)) { - return moduleSizeEst1 / 7.0f; - } - // Average them, and divide by 7 since we've counted the width of 3 black modules, - // and 1 white and 1 black module on either side. Ergo, divide sum by 14. - return (moduleSizeEst1 + moduleSizeEst2) / 14.0f; - } - - /** - * See {@link #sizeOfBlackWhiteBlackRun(int, int, int, int)}; computes the total width of - * a finder pattern by looking for a black-white-black run from the center in the direction - * of another point (another finder pattern center), and in the opposite direction too. - */ - private float sizeOfBlackWhiteBlackRunBothWays(int fromX, int fromY, int toX, int toY) { - - float result = sizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY); - - // Now count other way -- don't run off image though of course - float scale = 1.0f; - int otherToX = fromX - (toX - fromX); - if (otherToX < 0) { - scale = fromX / (float) (fromX - otherToX); - otherToX = 0; - } else if (otherToX >= image.getWidth()) { - scale = (image.getWidth() - 1 - fromX) / (float) (otherToX - fromX); - otherToX = image.getWidth() - 1; - } - int otherToY = (int) (fromY - (toY - fromY) * scale); - - scale = 1.0f; - if (otherToY < 0) { - scale = fromY / (float) (fromY - otherToY); - otherToY = 0; - } else if (otherToY >= image.getHeight()) { - scale = (image.getHeight() - 1 - fromY) / (float) (otherToY - fromY); - otherToY = image.getHeight() - 1; - } - otherToX = (int) (fromX + (otherToX - fromX) * scale); - - result += sizeOfBlackWhiteBlackRun(fromX, fromY, otherToX, otherToY); - - // Middle pixel is double-counted this way; subtract 1 - return result - 1.0f; - } - - /** - *

This method traces a line from a point in the image, in the direction towards another point. - * It begins in a black region, and keeps going until it finds white, then black, then white again. - * It reports the distance from the start to this point.

- * - *

This is used when figuring out how wide a finder pattern is, when the finder pattern - * may be skewed or rotated.

- */ - private float sizeOfBlackWhiteBlackRun(int fromX, int fromY, int toX, int toY) { - // Mild variant of Bresenham's algorithm; - // see http://en.wikipedia.org/wiki/Bresenham's_line_algorithm - boolean steep = Math.abs(toY - fromY) > Math.abs(toX - fromX); - if (steep) { - int temp = fromX; - fromX = fromY; - fromY = temp; - temp = toX; - toX = toY; - toY = temp; - } - - int dx = Math.abs(toX - fromX); - int dy = Math.abs(toY - fromY); - int error = -dx / 2; - int xstep = fromX < toX ? 1 : -1; - int ystep = fromY < toY ? 1 : -1; - - // In black pixels, looking for white, first or second time. - int state = 0; - // Loop up until x == toX, but not beyond - int xLimit = toX + xstep; - for (int x = fromX, y = fromY; x != xLimit; x += xstep) { - int realX = steep ? y : x; - int realY = steep ? x : y; - - // Does current pixel mean we have moved white to black or vice versa? - // Scanning black in state 0,2 and white in state 1, so if we find the wrong - // color, advance to next state or end if we are in state 2 already - if ((state == 1) == image.get(realX, realY)) { - if (state == 2) { - return MathUtils.distance(x, y, fromX, fromY); - } - state++; - } - - error += dy; - if (error > 0) { - if (y == toY) { - break; - } - y += ystep; - error -= dx; - } - } - // Found black-white-black; give the benefit of the doubt that the next pixel outside the image - // is "white" so this last point at (toX+xStep,toY) is the right ending. This is really a - // small approximation; (toX+xStep,toY+yStep) might be really correct. Ignore this. - if (state == 2) { - return MathUtils.distance(toX + xstep, toY, fromX, fromY); - } - // else we didn't find even black-white-black; no estimate is really possible - return Float.NaN; - } - - /** - *

Attempts to locate an alignment pattern in a limited region of the image, which is - * guessed to contain it. This method uses {@link AlignmentPattern}.

- * - * @param overallEstModuleSize estimated module size so far - * @param estAlignmentX x coordinate of center of area probably containing alignment pattern - * @param estAlignmentY y coordinate of above - * @param allowanceFactor number of pixels in all directions to search from the center - * @return {@link AlignmentPattern} if found, or null otherwise - * @throws NotFoundException if an unexpected error occurs during detection - */ - protected final AlignmentPattern findAlignmentInRegion(float overallEstModuleSize, - int estAlignmentX, - int estAlignmentY, - float allowanceFactor) - throws NotFoundException { - // Look for an alignment pattern (3 modules in size) around where it - // should be - int allowance = (int) (allowanceFactor * overallEstModuleSize); - int alignmentAreaLeftX = Math.max(0, estAlignmentX - allowance); - int alignmentAreaRightX = Math.min(image.getWidth() - 1, estAlignmentX + allowance); - if (alignmentAreaRightX - alignmentAreaLeftX < overallEstModuleSize * 3) { - throw NotFoundException.getNotFoundInstance(); - } - - int alignmentAreaTopY = Math.max(0, estAlignmentY - allowance); - int alignmentAreaBottomY = Math.min(image.getHeight() - 1, estAlignmentY + allowance); - if (alignmentAreaBottomY - alignmentAreaTopY < overallEstModuleSize * 3) { - throw NotFoundException.getNotFoundInstance(); - } - - AlignmentPatternFinder alignmentFinder = - new AlignmentPatternFinder( - image, - alignmentAreaLeftX, - alignmentAreaTopY, - alignmentAreaRightX - alignmentAreaLeftX, - alignmentAreaBottomY - alignmentAreaTopY, - overallEstModuleSize, - resultPointCallback); - return alignmentFinder.find(); - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/qrcode/detector/FinderPattern.java b/TMessagesProj/src/main/java/com/google/zxing/qrcode/detector/FinderPattern.java deleted file mode 100755 index a64e7c2b3..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/qrcode/detector/FinderPattern.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.qrcode.detector; - -import com.google.zxing.ResultPoint; - -/** - *

Encapsulates a finder pattern, which are the three square patterns found in - * the corners of QR Codes. It also encapsulates a count of similar finder patterns, - * as a convenience to the finder's bookkeeping.

- * - * @author Sean Owen - */ -public final class FinderPattern extends ResultPoint { - - private final float estimatedModuleSize; - private final int count; - - FinderPattern(float posX, float posY, float estimatedModuleSize) { - this(posX, posY, estimatedModuleSize, 1); - } - - private FinderPattern(float posX, float posY, float estimatedModuleSize, int count) { - super(posX, posY); - this.estimatedModuleSize = estimatedModuleSize; - this.count = count; - } - - public float getEstimatedModuleSize() { - return estimatedModuleSize; - } - - int getCount() { - return count; - } - - /* - void incrementCount() { - this.count++; - } - */ - - /** - *

Determines if this finder pattern "about equals" a finder pattern at the stated - * position and size -- meaning, it is at nearly the same center with nearly the same size.

- */ - boolean aboutEquals(float moduleSize, float i, float j) { - if (Math.abs(i - getY()) <= moduleSize && Math.abs(j - getX()) <= moduleSize) { - float moduleSizeDiff = Math.abs(moduleSize - estimatedModuleSize); - return moduleSizeDiff <= 1.0f || moduleSizeDiff <= estimatedModuleSize; - } - return false; - } - - /** - * Combines this object's current estimate of a finder pattern position and module size - * with a new estimate. It returns a new {@code FinderPattern} containing a weighted average - * based on count. - */ - FinderPattern combineEstimate(float i, float j, float newModuleSize) { - int combinedCount = count + 1; - float combinedX = (count * getX() + j) / combinedCount; - float combinedY = (count * getY() + i) / combinedCount; - float combinedModuleSize = (count * estimatedModuleSize + newModuleSize) / combinedCount; - return new FinderPattern(combinedX, combinedY, combinedModuleSize, combinedCount); - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/qrcode/detector/FinderPatternFinder.java b/TMessagesProj/src/main/java/com/google/zxing/qrcode/detector/FinderPatternFinder.java deleted file mode 100755 index 40fa52f1b..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/qrcode/detector/FinderPatternFinder.java +++ /dev/null @@ -1,675 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.qrcode.detector; - -import com.google.zxing.DecodeHintType; -import com.google.zxing.NotFoundException; -import com.google.zxing.ResultPoint; -import com.google.zxing.ResultPointCallback; -import com.google.zxing.common.BitMatrix; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Map; - -/** - *

This class attempts to find finder patterns in a QR Code. Finder patterns are the square - * markers at three corners of a QR Code.

- * - *

This class is thread-safe but not reentrant. Each thread must allocate its own object. - * - * @author Sean Owen - */ -public class FinderPatternFinder { - - private static final int CENTER_QUORUM = 2; - private static final EstimatedModuleComparator moduleComparator = new EstimatedModuleComparator(); - protected static final int MIN_SKIP = 3; // 1 pixel/module times 3 modules/center - protected static final int MAX_MODULES = 97; // support up to version 20 for mobile clients - - private final BitMatrix image; - private final List possibleCenters; - private boolean hasSkipped; - private final int[] crossCheckStateCount; - private final ResultPointCallback resultPointCallback; - - /** - *

Creates a finder that will search the image for three finder patterns.

- * - * @param image image to search - */ - public FinderPatternFinder(BitMatrix image) { - this(image, null); - } - - public FinderPatternFinder(BitMatrix image, ResultPointCallback resultPointCallback) { - this.image = image; - this.possibleCenters = new ArrayList<>(); - this.crossCheckStateCount = new int[5]; - this.resultPointCallback = resultPointCallback; - } - - protected final BitMatrix getImage() { - return image; - } - - protected final List getPossibleCenters() { - return possibleCenters; - } - - final FinderPatternInfo find(Map hints) throws NotFoundException { - boolean tryHarder = hints != null && hints.containsKey(DecodeHintType.TRY_HARDER); - int maxI = image.getHeight(); - int maxJ = image.getWidth(); - // We are looking for black/white/black/white/black modules in - // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far - - // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the - // image, and then account for the center being 3 modules in size. This gives the smallest - // number of pixels the center could be, so skip this often. When trying harder, look for all - // QR versions regardless of how dense they are. - int iSkip = (3 * maxI) / (4 * MAX_MODULES); - if (iSkip < MIN_SKIP || tryHarder) { - iSkip = MIN_SKIP; - } - - boolean done = false; - int[] stateCount = new int[5]; - for (int i = iSkip - 1; i < maxI && !done; i += iSkip) { - // Get a row of black/white values - clearCounts(stateCount); - int currentState = 0; - for (int j = 0; j < maxJ; j++) { - if (image.get(j, i)) { - // Black pixel - if ((currentState & 1) == 1) { // Counting white pixels - currentState++; - } - stateCount[currentState]++; - } else { // White pixel - if ((currentState & 1) == 0) { // Counting black pixels - if (currentState == 4) { // A winner? - if (foundPatternCross(stateCount)) { // Yes - boolean confirmed = handlePossibleCenter(stateCount, i, j); - if (confirmed) { - // Start examining every other line. Checking each line turned out to be too - // expensive and didn't improve performance. - iSkip = 2; - if (hasSkipped) { - done = haveMultiplyConfirmedCenters(); - } else { - int rowSkip = findRowSkip(); - if (rowSkip > stateCount[2]) { - // Skip rows between row of lower confirmed center - // and top of presumed third confirmed center - // but back up a bit to get a full chance of detecting - // it, entire width of center of finder pattern - - // Skip by rowSkip, but back off by stateCount[2] (size of last center - // of pattern we saw) to be conservative, and also back off by iSkip which - // is about to be re-added - i += rowSkip - stateCount[2] - iSkip; - j = maxJ - 1; - } - } - } else { - shiftCounts2(stateCount); - currentState = 3; - continue; - } - // Clear state to start looking again - currentState = 0; - clearCounts(stateCount); - } else { // No, shift counts back by two - shiftCounts2(stateCount); - currentState = 3; - } - } else { - stateCount[++currentState]++; - } - } else { // Counting white pixels - stateCount[currentState]++; - } - } - } - if (foundPatternCross(stateCount)) { - boolean confirmed = handlePossibleCenter(stateCount, i, maxJ); - if (confirmed) { - iSkip = stateCount[0]; - if (hasSkipped) { - // Found a third one - done = haveMultiplyConfirmedCenters(); - } - } - } - } - - FinderPattern[] patternInfo = selectBestPatterns(); - ResultPoint.orderBestPatterns(patternInfo); - - return new FinderPatternInfo(patternInfo); - } - - /** - * Given a count of black/white/black/white/black pixels just seen and an end position, - * figures the location of the center of this run. - */ - private static float centerFromEnd(int[] stateCount, int end) { - return (end - stateCount[4] - stateCount[3]) - stateCount[2] / 2.0f; - } - - /** - * @param stateCount count of black/white/black/white/black pixels just read - * @return true iff the proportions of the counts is close enough to the 1/1/3/1/1 ratios - * used by finder patterns to be considered a match - */ - protected static boolean foundPatternCross(int[] stateCount) { - int totalModuleSize = 0; - for (int i = 0; i < 5; i++) { - int count = stateCount[i]; - if (count == 0) { - return false; - } - totalModuleSize += count; - } - if (totalModuleSize < 7) { - return false; - } - float moduleSize = totalModuleSize / 7.0f; - float maxVariance = moduleSize / 2.0f; - // Allow less than 50% variance from 1-1-3-1-1 proportions - return - Math.abs(moduleSize - stateCount[0]) < maxVariance && - Math.abs(moduleSize - stateCount[1]) < maxVariance && - Math.abs(3.0f * moduleSize - stateCount[2]) < 3 * maxVariance && - Math.abs(moduleSize - stateCount[3]) < maxVariance && - Math.abs(moduleSize - stateCount[4]) < maxVariance; - } - - /** - * @param stateCount count of black/white/black/white/black pixels just read - * @return true iff the proportions of the counts is close enough to the 1/1/3/1/1 ratios - * used by finder patterns to be considered a match - */ - protected static boolean foundPatternDiagonal(int[] stateCount) { - int totalModuleSize = 0; - for (int i = 0; i < 5; i++) { - int count = stateCount[i]; - if (count == 0) { - return false; - } - totalModuleSize += count; - } - if (totalModuleSize < 7) { - return false; - } - float moduleSize = totalModuleSize / 7.0f; - float maxVariance = moduleSize / 1.333f; - // Allow less than 75% variance from 1-1-3-1-1 proportions - return - Math.abs(moduleSize - stateCount[0]) < maxVariance && - Math.abs(moduleSize - stateCount[1]) < maxVariance && - Math.abs(3.0f * moduleSize - stateCount[2]) < 3 * maxVariance && - Math.abs(moduleSize - stateCount[3]) < maxVariance && - Math.abs(moduleSize - stateCount[4]) < maxVariance; - } - - private int[] getCrossCheckStateCount() { - clearCounts(crossCheckStateCount); - return crossCheckStateCount; - } - - protected final void clearCounts(int[] counts) { - Arrays.fill(counts, 0); - } - - protected final void shiftCounts2(int[] stateCount) { - stateCount[0] = stateCount[2]; - stateCount[1] = stateCount[3]; - stateCount[2] = stateCount[4]; - stateCount[3] = 1; - stateCount[4] = 0; - } - - /** - * After a vertical and horizontal scan finds a potential finder pattern, this method - * "cross-cross-cross-checks" by scanning down diagonally through the center of the possible - * finder pattern to see if the same proportion is detected. - * - * @param centerI row where a finder pattern was detected - * @param centerJ center of the section that appears to cross a finder pattern - * @return true if proportions are withing expected limits - */ - private boolean crossCheckDiagonal(int centerI, int centerJ) { - int[] stateCount = getCrossCheckStateCount(); - - // Start counting up, left from center finding black center mass - int i = 0; - while (centerI >= i && centerJ >= i && image.get(centerJ - i, centerI - i)) { - stateCount[2]++; - i++; - } - if (stateCount[2] == 0) { - return false; - } - - // Continue up, left finding white space - while (centerI >= i && centerJ >= i && !image.get(centerJ - i, centerI - i)) { - stateCount[1]++; - i++; - } - if (stateCount[1] == 0) { - return false; - } - - // Continue up, left finding black border - while (centerI >= i && centerJ >= i && image.get(centerJ - i, centerI - i)) { - stateCount[0]++; - i++; - } - if (stateCount[0] == 0) { - return false; - } - - int maxI = image.getHeight(); - int maxJ = image.getWidth(); - - // Now also count down, right from center - i = 1; - while (centerI + i < maxI && centerJ + i < maxJ && image.get(centerJ + i, centerI + i)) { - stateCount[2]++; - i++; - } - - while (centerI + i < maxI && centerJ + i < maxJ && !image.get(centerJ + i, centerI + i)) { - stateCount[3]++; - i++; - } - if (stateCount[3] == 0) { - return false; - } - - while (centerI + i < maxI && centerJ + i < maxJ && image.get(centerJ + i, centerI + i)) { - stateCount[4]++; - i++; - } - if (stateCount[4] == 0) { - return false; - } - - return foundPatternDiagonal(stateCount); - } - - /** - *

After a horizontal scan finds a potential finder pattern, this method - * "cross-checks" by scanning down vertically through the center of the possible - * finder pattern to see if the same proportion is detected.

- * - * @param startI row where a finder pattern was detected - * @param centerJ center of the section that appears to cross a finder pattern - * @param maxCount maximum reasonable number of modules that should be - * observed in any reading state, based on the results of the horizontal scan - * @return vertical center of finder pattern, or {@link Float#NaN} if not found - */ - private float crossCheckVertical(int startI, int centerJ, int maxCount, - int originalStateCountTotal) { - BitMatrix image = this.image; - - int maxI = image.getHeight(); - int[] stateCount = getCrossCheckStateCount(); - - // Start counting up from center - int i = startI; - while (i >= 0 && image.get(centerJ, i)) { - stateCount[2]++; - i--; - } - if (i < 0) { - return Float.NaN; - } - while (i >= 0 && !image.get(centerJ, i) && stateCount[1] <= maxCount) { - stateCount[1]++; - i--; - } - // If already too many modules in this state or ran off the edge: - if (i < 0 || stateCount[1] > maxCount) { - return Float.NaN; - } - while (i >= 0 && image.get(centerJ, i) && stateCount[0] <= maxCount) { - stateCount[0]++; - i--; - } - if (stateCount[0] > maxCount) { - return Float.NaN; - } - - // Now also count down from center - i = startI + 1; - while (i < maxI && image.get(centerJ, i)) { - stateCount[2]++; - i++; - } - if (i == maxI) { - return Float.NaN; - } - while (i < maxI && !image.get(centerJ, i) && stateCount[3] < maxCount) { - stateCount[3]++; - i++; - } - if (i == maxI || stateCount[3] >= maxCount) { - return Float.NaN; - } - while (i < maxI && image.get(centerJ, i) && stateCount[4] < maxCount) { - stateCount[4]++; - i++; - } - if (stateCount[4] >= maxCount) { - return Float.NaN; - } - - // If we found a finder-pattern-like section, but its size is more than 40% different than - // the original, assume it's a false positive - int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + - stateCount[4]; - if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) { - return Float.NaN; - } - - return foundPatternCross(stateCount) ? centerFromEnd(stateCount, i) : Float.NaN; - } - - /** - *

Like {@link #crossCheckVertical(int, int, int, int)}, and in fact is basically identical, - * except it reads horizontally instead of vertically. This is used to cross-cross - * check a vertical cross check and locate the real center of the alignment pattern.

- */ - private float crossCheckHorizontal(int startJ, int centerI, int maxCount, - int originalStateCountTotal) { - BitMatrix image = this.image; - - int maxJ = image.getWidth(); - int[] stateCount = getCrossCheckStateCount(); - - int j = startJ; - while (j >= 0 && image.get(j, centerI)) { - stateCount[2]++; - j--; - } - if (j < 0) { - return Float.NaN; - } - while (j >= 0 && !image.get(j, centerI) && stateCount[1] <= maxCount) { - stateCount[1]++; - j--; - } - if (j < 0 || stateCount[1] > maxCount) { - return Float.NaN; - } - while (j >= 0 && image.get(j, centerI) && stateCount[0] <= maxCount) { - stateCount[0]++; - j--; - } - if (stateCount[0] > maxCount) { - return Float.NaN; - } - - j = startJ + 1; - while (j < maxJ && image.get(j, centerI)) { - stateCount[2]++; - j++; - } - if (j == maxJ) { - return Float.NaN; - } - while (j < maxJ && !image.get(j, centerI) && stateCount[3] < maxCount) { - stateCount[3]++; - j++; - } - if (j == maxJ || stateCount[3] >= maxCount) { - return Float.NaN; - } - while (j < maxJ && image.get(j, centerI) && stateCount[4] < maxCount) { - stateCount[4]++; - j++; - } - if (stateCount[4] >= maxCount) { - return Float.NaN; - } - - // If we found a finder-pattern-like section, but its size is significantly different than - // the original, assume it's a false positive - int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + - stateCount[4]; - if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= originalStateCountTotal) { - return Float.NaN; - } - - return foundPatternCross(stateCount) ? centerFromEnd(stateCount, j) : Float.NaN; - } - - /** - * @param stateCount reading state module counts from horizontal scan - * @param i row where finder pattern may be found - * @param j end of possible finder pattern in row - * @param pureBarcode ignored - * @return true if a finder pattern candidate was found this time - * @deprecated only exists for backwards compatibility - * @see #handlePossibleCenter(int[], int, int) - */ - @Deprecated - protected final boolean handlePossibleCenter(int[] stateCount, int i, int j, boolean pureBarcode) { - return handlePossibleCenter(stateCount, i, j); - } - - /** - *

This is called when a horizontal scan finds a possible alignment pattern. It will - * cross check with a vertical scan, and if successful, will, ah, cross-cross-check - * with another horizontal scan. This is needed primarily to locate the real horizontal - * center of the pattern in cases of extreme skew. - * And then we cross-cross-cross check with another diagonal scan.

- * - *

If that succeeds the finder pattern location is added to a list that tracks - * the number of times each location has been nearly-matched as a finder pattern. - * Each additional find is more evidence that the location is in fact a finder - * pattern center - * - * @param stateCount reading state module counts from horizontal scan - * @param i row where finder pattern may be found - * @param j end of possible finder pattern in row - * @return true if a finder pattern candidate was found this time - */ - protected final boolean handlePossibleCenter(int[] stateCount, int i, int j) { - int stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2] + stateCount[3] + - stateCount[4]; - float centerJ = centerFromEnd(stateCount, j); - float centerI = crossCheckVertical(i, (int) centerJ, stateCount[2], stateCountTotal); - if (!Float.isNaN(centerI)) { - // Re-cross check - centerJ = crossCheckHorizontal((int) centerJ, (int) centerI, stateCount[2], stateCountTotal); - if (!Float.isNaN(centerJ) && crossCheckDiagonal((int) centerI, (int) centerJ)) { - float estimatedModuleSize = stateCountTotal / 7.0f; - boolean found = false; - for (int index = 0; index < possibleCenters.size(); index++) { - FinderPattern center = possibleCenters.get(index); - // Look for about the same center and module size: - if (center.aboutEquals(estimatedModuleSize, centerI, centerJ)) { - possibleCenters.set(index, center.combineEstimate(centerI, centerJ, estimatedModuleSize)); - found = true; - break; - } - } - if (!found) { - FinderPattern point = new FinderPattern(centerJ, centerI, estimatedModuleSize); - possibleCenters.add(point); - if (resultPointCallback != null) { - resultPointCallback.foundPossibleResultPoint(point); - } - } - return true; - } - } - return false; - } - - /** - * @return number of rows we could safely skip during scanning, based on the first - * two finder patterns that have been located. In some cases their position will - * allow us to infer that the third pattern must lie below a certain point farther - * down in the image. - */ - private int findRowSkip() { - int max = possibleCenters.size(); - if (max <= 1) { - return 0; - } - ResultPoint firstConfirmedCenter = null; - for (FinderPattern center : possibleCenters) { - if (center.getCount() >= CENTER_QUORUM) { - if (firstConfirmedCenter == null) { - firstConfirmedCenter = center; - } else { - // We have two confirmed centers - // How far down can we skip before resuming looking for the next - // pattern? In the worst case, only the difference between the - // difference in the x / y coordinates of the two centers. - // This is the case where you find top left last. - hasSkipped = true; - return (int) (Math.abs(firstConfirmedCenter.getX() - center.getX()) - - Math.abs(firstConfirmedCenter.getY() - center.getY())) / 2; - } - } - } - return 0; - } - - /** - * @return true iff we have found at least 3 finder patterns that have been detected - * at least {@link #CENTER_QUORUM} times each, and, the estimated module size of the - * candidates is "pretty similar" - */ - private boolean haveMultiplyConfirmedCenters() { - int confirmedCount = 0; - float totalModuleSize = 0.0f; - int max = possibleCenters.size(); - for (FinderPattern pattern : possibleCenters) { - if (pattern.getCount() >= CENTER_QUORUM) { - confirmedCount++; - totalModuleSize += pattern.getEstimatedModuleSize(); - } - } - if (confirmedCount < 3) { - return false; - } - // OK, we have at least 3 confirmed centers, but, it's possible that one is a "false positive" - // and that we need to keep looking. We detect this by asking if the estimated module sizes - // vary too much. We arbitrarily say that when the total deviation from average exceeds - // 5% of the total module size estimates, it's too much. - float average = totalModuleSize / max; - float totalDeviation = 0.0f; - for (FinderPattern pattern : possibleCenters) { - totalDeviation += Math.abs(pattern.getEstimatedModuleSize() - average); - } - return totalDeviation <= 0.05f * totalModuleSize; - } - - /** - * Get square of distance between a and b. - */ - private static double squaredDistance(FinderPattern a, FinderPattern b) { - double x = a.getX() - b.getX(); - double y = a.getY() - b.getY(); - return x * x + y * y; - } - - /** - * @return the 3 best {@link FinderPattern}s from our list of candidates. The "best" are - * those have similar module size and form a shape closer to a isosceles right triangle. - * @throws NotFoundException if 3 such finder patterns do not exist - */ - private FinderPattern[] selectBestPatterns() throws NotFoundException { - - int startSize = possibleCenters.size(); - if (startSize < 3) { - // Couldn't find enough finder patterns - throw NotFoundException.getNotFoundInstance(); - } - - Collections.sort(possibleCenters, moduleComparator); - - double distortion = Double.MAX_VALUE; - double[] squares = new double[3]; - FinderPattern[] bestPatterns = new FinderPattern[3]; - - for (int i = 0; i < possibleCenters.size() - 2; i++) { - FinderPattern fpi = possibleCenters.get(i); - float minModuleSize = fpi.getEstimatedModuleSize(); - - for (int j = i + 1; j < possibleCenters.size() - 1; j++) { - FinderPattern fpj = possibleCenters.get(j); - double squares0 = squaredDistance(fpi, fpj); - - for (int k = j + 1; k < possibleCenters.size(); k++) { - FinderPattern fpk = possibleCenters.get(k); - float maxModuleSize = fpk.getEstimatedModuleSize(); - if (maxModuleSize > minModuleSize * 1.4f) { - // module size is not similar - continue; - } - - squares[0] = squares0; - squares[1] = squaredDistance(fpj, fpk); - squares[2] = squaredDistance(fpi, fpk); - Arrays.sort(squares); - - // a^2 + b^2 = c^2 (Pythagorean theorem), and a = b (isosceles triangle). - // Since any right triangle satisfies the formula c^2 - b^2 - a^2 = 0, - // we need to check both two equal sides separately. - // The value of |c^2 - 2 * b^2| + |c^2 - 2 * a^2| increases as dissimilarity - // from isosceles right triangle. - double d = Math.abs(squares[2] - 2 * squares[1]) + Math.abs(squares[2] - 2 * squares[0]); - if (d < distortion) { - distortion = d; - bestPatterns[0] = fpi; - bestPatterns[1] = fpj; - bestPatterns[2] = fpk; - } - } - } - } - - if (distortion == Double.MAX_VALUE) { - throw NotFoundException.getNotFoundInstance(); - } - - return bestPatterns; - } - - /** - *

Orders by {@link FinderPattern#getEstimatedModuleSize()}

- */ - private static final class EstimatedModuleComparator implements Comparator, Serializable { - @Override - public int compare(FinderPattern center1, FinderPattern center2) { - return Float.compare(center1.getEstimatedModuleSize(), center2.getEstimatedModuleSize()); - } - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/qrcode/detector/FinderPatternInfo.java b/TMessagesProj/src/main/java/com/google/zxing/qrcode/detector/FinderPatternInfo.java deleted file mode 100755 index 3c3401085..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/qrcode/detector/FinderPatternInfo.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2007 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.qrcode.detector; - -/** - *

Encapsulates information about finder patterns in an image, including the location of - * the three finder patterns, and their estimated module size.

- * - * @author Sean Owen - */ -public final class FinderPatternInfo { - - private final FinderPattern bottomLeft; - private final FinderPattern topLeft; - private final FinderPattern topRight; - - public FinderPatternInfo(FinderPattern[] patternCenters) { - this.bottomLeft = patternCenters[0]; - this.topLeft = patternCenters[1]; - this.topRight = patternCenters[2]; - } - - public FinderPattern getBottomLeft() { - return bottomLeft; - } - - public FinderPattern getTopLeft() { - return topLeft; - } - - public FinderPattern getTopRight() { - return topRight; - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/qrcode/encoder/BlockPair.java b/TMessagesProj/src/main/java/com/google/zxing/qrcode/encoder/BlockPair.java deleted file mode 100755 index 5714d9c3a..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/qrcode/encoder/BlockPair.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2008 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.qrcode.encoder; - -final class BlockPair { - - private final byte[] dataBytes; - private final byte[] errorCorrectionBytes; - - BlockPair(byte[] data, byte[] errorCorrection) { - dataBytes = data; - errorCorrectionBytes = errorCorrection; - } - - public byte[] getDataBytes() { - return dataBytes; - } - - public byte[] getErrorCorrectionBytes() { - return errorCorrectionBytes; - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/qrcode/encoder/ByteMatrix.java b/TMessagesProj/src/main/java/com/google/zxing/qrcode/encoder/ByteMatrix.java deleted file mode 100755 index 35f344ca3..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/qrcode/encoder/ByteMatrix.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2008 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.qrcode.encoder; - -import java.util.Arrays; - -/** - * JAVAPORT: The original code was a 2D array of ints, but since it only ever gets assigned - * -1, 0, and 1, I'm going to use less memory and go with bytes. - * - * @author dswitkin@google.com (Daniel Switkin) - */ -public final class ByteMatrix { - - private final byte[][] bytes; - private final int width; - private final int height; - - public ByteMatrix(int width, int height) { - bytes = new byte[height][width]; - this.width = width; - this.height = height; - } - - public int getHeight() { - return height; - } - - public int getWidth() { - return width; - } - - public byte get(int x, int y) { - return bytes[y][x]; - } - - /** - * @return an internal representation as bytes, in row-major order. array[y][x] represents point (x,y) - */ - public byte[][] getArray() { - return bytes; - } - - public void set(int x, int y, byte value) { - bytes[y][x] = value; - } - - public void set(int x, int y, int value) { - bytes[y][x] = (byte) value; - } - - public void set(int x, int y, boolean value) { - bytes[y][x] = (byte) (value ? 1 : 0); - } - - public void clear(byte value) { - for (byte[] aByte : bytes) { - Arrays.fill(aByte, value); - } - } - - @Override - public String toString() { - StringBuilder result = new StringBuilder(2 * width * height + 2); - for (int y = 0; y < height; ++y) { - byte[] bytesY = bytes[y]; - for (int x = 0; x < width; ++x) { - switch (bytesY[x]) { - case 0: - result.append(" 0"); - break; - case 1: - result.append(" 1"); - break; - default: - result.append(" "); - break; - } - } - result.append('\n'); - } - return result.toString(); - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/qrcode/encoder/Encoder.java b/TMessagesProj/src/main/java/com/google/zxing/qrcode/encoder/Encoder.java deleted file mode 100755 index 3a80c7069..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/qrcode/encoder/Encoder.java +++ /dev/null @@ -1,630 +0,0 @@ -/* - * Copyright 2008 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.qrcode.encoder; - -import com.google.zxing.EncodeHintType; -import com.google.zxing.WriterException; -import com.google.zxing.common.BitArray; -import com.google.zxing.common.CharacterSetECI; -import com.google.zxing.common.reedsolomon.GenericGF; -import com.google.zxing.common.reedsolomon.ReedSolomonEncoder; -import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; -import com.google.zxing.qrcode.decoder.Mode; -import com.google.zxing.qrcode.decoder.Version; - -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Map; - -/** - * @author satorux@google.com (Satoru Takabayashi) - creator - * @author dswitkin@google.com (Daniel Switkin) - ported from C++ - */ -public final class Encoder { - - // The original table is defined in the table 5 of JISX0510:2004 (p.19). - private static final int[] ALPHANUMERIC_TABLE = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x00-0x0f - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x10-0x1f - 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, // 0x20-0x2f - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, // 0x30-0x3f - -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 0x40-0x4f - 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 0x50-0x5f - }; - - static final String DEFAULT_BYTE_MODE_ENCODING = "ISO-8859-1"; - - private Encoder() { - } - - // The mask penalty calculation is complicated. See Table 21 of JISX0510:2004 (p.45) for details. - // Basically it applies four rules and summate all penalties. - private static int calculateMaskPenalty(ByteMatrix matrix) { - return MaskUtil.applyMaskPenaltyRule1(matrix) - + MaskUtil.applyMaskPenaltyRule2(matrix) - + MaskUtil.applyMaskPenaltyRule3(matrix) - + MaskUtil.applyMaskPenaltyRule4(matrix); - } - - /** - * @param content text to encode - * @param ecLevel error correction level to use - * @return {@link QRCode} representing the encoded QR code - * @throws WriterException if encoding can't succeed, because of for example invalid content - * or configuration - */ - public static QRCode encode(String content, ErrorCorrectionLevel ecLevel) throws WriterException { - return encode(content, ecLevel, null); - } - - public static QRCode encode(String content, - ErrorCorrectionLevel ecLevel, - Map hints) throws WriterException { - - // Determine what character encoding has been specified by the caller, if any - String encoding = DEFAULT_BYTE_MODE_ENCODING; - boolean hasEncodingHint = hints != null && hints.containsKey(EncodeHintType.CHARACTER_SET); - if (hasEncodingHint) { - encoding = hints.get(EncodeHintType.CHARACTER_SET).toString(); - } - - // Pick an encoding mode appropriate for the content. Note that this will not attempt to use - // multiple modes / segments even if that were more efficient. Twould be nice. - Mode mode = chooseMode(content, encoding); - - // This will store the header information, like mode and - // length, as well as "header" segments like an ECI segment. - BitArray headerBits = new BitArray(); - - // Append ECI segment if applicable - if (mode == Mode.BYTE && hasEncodingHint) { - CharacterSetECI eci = CharacterSetECI.getCharacterSetECIByName(encoding); - if (eci != null) { - appendECI(eci, headerBits); - } - } - - // Append the FNC1 mode header for GS1 formatted data if applicable - boolean hasGS1FormatHint = hints != null && hints.containsKey(EncodeHintType.GS1_FORMAT); - if (hasGS1FormatHint && Boolean.parseBoolean(hints.get(EncodeHintType.GS1_FORMAT).toString())) { - // GS1 formatted codes are prefixed with a FNC1 in first position mode header - appendModeInfo(Mode.FNC1_FIRST_POSITION, headerBits); - } - - // (With ECI in place,) Write the mode marker - appendModeInfo(mode, headerBits); - - // Collect data within the main segment, separately, to count its size if needed. Don't add it to - // main payload yet. - BitArray dataBits = new BitArray(); - appendBytes(content, mode, dataBits, encoding); - - Version version; - if (hints != null && hints.containsKey(EncodeHintType.QR_VERSION)) { - int versionNumber = Integer.parseInt(hints.get(EncodeHintType.QR_VERSION).toString()); - version = Version.getVersionForNumber(versionNumber); - int bitsNeeded = calculateBitsNeeded(mode, headerBits, dataBits, version); - if (!willFit(bitsNeeded, version, ecLevel)) { - throw new WriterException("Data too big for requested version"); - } - } else { - version = recommendVersion(ecLevel, mode, headerBits, dataBits); - } - - BitArray headerAndDataBits = new BitArray(); - headerAndDataBits.appendBitArray(headerBits); - // Find "length" of main segment and write it - int numLetters = mode == Mode.BYTE ? dataBits.getSizeInBytes() : content.length(); - appendLengthInfo(numLetters, version, mode, headerAndDataBits); - // Put data together into the overall payload - headerAndDataBits.appendBitArray(dataBits); - - Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel); - int numDataBytes = version.getTotalCodewords() - ecBlocks.getTotalECCodewords(); - - // Terminate the bits properly. - terminateBits(numDataBytes, headerAndDataBits); - - // Interleave data bits with error correction code. - BitArray finalBits = interleaveWithECBytes(headerAndDataBits, - version.getTotalCodewords(), - numDataBytes, - ecBlocks.getNumBlocks()); - - QRCode qrCode = new QRCode(); - - qrCode.setECLevel(ecLevel); - qrCode.setMode(mode); - qrCode.setVersion(version); - - // Choose the mask pattern and set to "qrCode". - int dimension = version.getDimensionForVersion(); - ByteMatrix matrix = new ByteMatrix(dimension, dimension); - - // Enable manual selection of the pattern to be used via hint - int maskPattern = -1; - if (hints != null && hints.containsKey(EncodeHintType.QR_MASK_PATTERN)) { - int hintMaskPattern = Integer.parseInt(hints.get(EncodeHintType.QR_MASK_PATTERN).toString()); - maskPattern = QRCode.isValidMaskPattern(hintMaskPattern) ? hintMaskPattern : -1; - } - - if (maskPattern == -1) { - maskPattern = chooseMaskPattern(finalBits, ecLevel, version, matrix); - } - qrCode.setMaskPattern(maskPattern); - - // Build the matrix and set it to "qrCode". - MatrixUtil.buildMatrix(finalBits, ecLevel, version, maskPattern, matrix); - qrCode.setMatrix(matrix); - - return qrCode; - } - - /** - * Decides the smallest version of QR code that will contain all of the provided data. - * - * @throws WriterException if the data cannot fit in any version - */ - private static Version recommendVersion(ErrorCorrectionLevel ecLevel, - Mode mode, - BitArray headerBits, - BitArray dataBits) throws WriterException { - // Hard part: need to know version to know how many bits length takes. But need to know how many - // bits it takes to know version. First we take a guess at version by assuming version will be - // the minimum, 1: - int provisionalBitsNeeded = calculateBitsNeeded(mode, headerBits, dataBits, Version.getVersionForNumber(1)); - Version provisionalVersion = chooseVersion(provisionalBitsNeeded, ecLevel); - - // Use that guess to calculate the right version. I am still not sure this works in 100% of cases. - int bitsNeeded = calculateBitsNeeded(mode, headerBits, dataBits, provisionalVersion); - return chooseVersion(bitsNeeded, ecLevel); - } - - private static int calculateBitsNeeded(Mode mode, - BitArray headerBits, - BitArray dataBits, - Version version) { - return headerBits.getSize() + mode.getCharacterCountBits(version) + dataBits.getSize(); - } - - /** - * @return the code point of the table used in alphanumeric mode or - * -1 if there is no corresponding code in the table. - */ - static int getAlphanumericCode(int code) { - if (code < ALPHANUMERIC_TABLE.length) { - return ALPHANUMERIC_TABLE[code]; - } - return -1; - } - - public static Mode chooseMode(String content) { - return chooseMode(content, null); - } - - /** - * Choose the best mode by examining the content. Note that 'encoding' is used as a hint; - * if it is Shift_JIS, and the input is only double-byte Kanji, then we return {@link Mode#KANJI}. - */ - private static Mode chooseMode(String content, String encoding) { - if ("Shift_JIS".equals(encoding) && isOnlyDoubleByteKanji(content)) { - // Choose Kanji mode if all input are double-byte characters - return Mode.KANJI; - } - boolean hasNumeric = false; - boolean hasAlphanumeric = false; - for (int i = 0; i < content.length(); ++i) { - char c = content.charAt(i); - if (c >= '0' && c <= '9') { - hasNumeric = true; - } else if (getAlphanumericCode(c) != -1) { - hasAlphanumeric = true; - } else { - return Mode.BYTE; - } - } - if (hasAlphanumeric) { - return Mode.ALPHANUMERIC; - } - if (hasNumeric) { - return Mode.NUMERIC; - } - return Mode.BYTE; - } - - private static boolean isOnlyDoubleByteKanji(String content) { - byte[] bytes; - try { - bytes = content.getBytes("Shift_JIS"); - } catch (UnsupportedEncodingException ignored) { - return false; - } - int length = bytes.length; - if (length % 2 != 0) { - return false; - } - for (int i = 0; i < length; i += 2) { - int byte1 = bytes[i] & 0xFF; - if ((byte1 < 0x81 || byte1 > 0x9F) && (byte1 < 0xE0 || byte1 > 0xEB)) { - return false; - } - } - return true; - } - - private static int chooseMaskPattern(BitArray bits, - ErrorCorrectionLevel ecLevel, - Version version, - ByteMatrix matrix) throws WriterException { - - int minPenalty = Integer.MAX_VALUE; // Lower penalty is better. - int bestMaskPattern = -1; - // We try all mask patterns to choose the best one. - for (int maskPattern = 0; maskPattern < QRCode.NUM_MASK_PATTERNS; maskPattern++) { - MatrixUtil.buildMatrix(bits, ecLevel, version, maskPattern, matrix); - int penalty = calculateMaskPenalty(matrix); - if (penalty < minPenalty) { - minPenalty = penalty; - bestMaskPattern = maskPattern; - } - } - return bestMaskPattern; - } - - private static Version chooseVersion(int numInputBits, ErrorCorrectionLevel ecLevel) throws WriterException { - for (int versionNum = 1; versionNum <= 40; versionNum++) { - Version version = Version.getVersionForNumber(versionNum); - if (willFit(numInputBits, version, ecLevel)) { - return version; - } - } - throw new WriterException("Data too big"); - } - - /** - * @return true if the number of input bits will fit in a code with the specified version and - * error correction level. - */ - private static boolean willFit(int numInputBits, Version version, ErrorCorrectionLevel ecLevel) { - // In the following comments, we use numbers of Version 7-H. - // numBytes = 196 - int numBytes = version.getTotalCodewords(); - // getNumECBytes = 130 - Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel); - int numEcBytes = ecBlocks.getTotalECCodewords(); - // getNumDataBytes = 196 - 130 = 66 - int numDataBytes = numBytes - numEcBytes; - int totalInputBytes = (numInputBits + 7) / 8; - return numDataBytes >= totalInputBytes; - } - - /** - * Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24). - */ - static void terminateBits(int numDataBytes, BitArray bits) throws WriterException { - int capacity = numDataBytes * 8; - if (bits.getSize() > capacity) { - throw new WriterException("data bits cannot fit in the QR Code" + bits.getSize() + " > " + - capacity); - } - for (int i = 0; i < 4 && bits.getSize() < capacity; ++i) { - bits.appendBit(false); - } - // Append termination bits. See 8.4.8 of JISX0510:2004 (p.24) for details. - // If the last byte isn't 8-bit aligned, we'll add padding bits. - int numBitsInLastByte = bits.getSize() & 0x07; - if (numBitsInLastByte > 0) { - for (int i = numBitsInLastByte; i < 8; i++) { - bits.appendBit(false); - } - } - // If we have more space, we'll fill the space with padding patterns defined in 8.4.9 (p.24). - int numPaddingBytes = numDataBytes - bits.getSizeInBytes(); - for (int i = 0; i < numPaddingBytes; ++i) { - bits.appendBits((i & 0x01) == 0 ? 0xEC : 0x11, 8); - } - if (bits.getSize() != capacity) { - throw new WriterException("Bits size does not equal capacity"); - } - } - - /** - * Get number of data bytes and number of error correction bytes for block id "blockID". Store - * the result in "numDataBytesInBlock", and "numECBytesInBlock". See table 12 in 8.5.1 of - * JISX0510:2004 (p.30) - */ - static void getNumDataBytesAndNumECBytesForBlockID(int numTotalBytes, - int numDataBytes, - int numRSBlocks, - int blockID, - int[] numDataBytesInBlock, - int[] numECBytesInBlock) throws WriterException { - if (blockID >= numRSBlocks) { - throw new WriterException("Block ID too large"); - } - // numRsBlocksInGroup2 = 196 % 5 = 1 - int numRsBlocksInGroup2 = numTotalBytes % numRSBlocks; - // numRsBlocksInGroup1 = 5 - 1 = 4 - int numRsBlocksInGroup1 = numRSBlocks - numRsBlocksInGroup2; - // numTotalBytesInGroup1 = 196 / 5 = 39 - int numTotalBytesInGroup1 = numTotalBytes / numRSBlocks; - // numTotalBytesInGroup2 = 39 + 1 = 40 - int numTotalBytesInGroup2 = numTotalBytesInGroup1 + 1; - // numDataBytesInGroup1 = 66 / 5 = 13 - int numDataBytesInGroup1 = numDataBytes / numRSBlocks; - // numDataBytesInGroup2 = 13 + 1 = 14 - int numDataBytesInGroup2 = numDataBytesInGroup1 + 1; - // numEcBytesInGroup1 = 39 - 13 = 26 - int numEcBytesInGroup1 = numTotalBytesInGroup1 - numDataBytesInGroup1; - // numEcBytesInGroup2 = 40 - 14 = 26 - int numEcBytesInGroup2 = numTotalBytesInGroup2 - numDataBytesInGroup2; - // Sanity checks. - // 26 = 26 - if (numEcBytesInGroup1 != numEcBytesInGroup2) { - throw new WriterException("EC bytes mismatch"); - } - // 5 = 4 + 1. - if (numRSBlocks != numRsBlocksInGroup1 + numRsBlocksInGroup2) { - throw new WriterException("RS blocks mismatch"); - } - // 196 = (13 + 26) * 4 + (14 + 26) * 1 - if (numTotalBytes != - ((numDataBytesInGroup1 + numEcBytesInGroup1) * - numRsBlocksInGroup1) + - ((numDataBytesInGroup2 + numEcBytesInGroup2) * - numRsBlocksInGroup2)) { - throw new WriterException("Total bytes mismatch"); - } - - if (blockID < numRsBlocksInGroup1) { - numDataBytesInBlock[0] = numDataBytesInGroup1; - numECBytesInBlock[0] = numEcBytesInGroup1; - } else { - numDataBytesInBlock[0] = numDataBytesInGroup2; - numECBytesInBlock[0] = numEcBytesInGroup2; - } - } - - /** - * Interleave "bits" with corresponding error correction bytes. On success, store the result in - * "result". The interleave rule is complicated. See 8.6 of JISX0510:2004 (p.37) for details. - */ - static BitArray interleaveWithECBytes(BitArray bits, - int numTotalBytes, - int numDataBytes, - int numRSBlocks) throws WriterException { - - // "bits" must have "getNumDataBytes" bytes of data. - if (bits.getSizeInBytes() != numDataBytes) { - throw new WriterException("Number of bits and data bytes does not match"); - } - - // Step 1. Divide data bytes into blocks and generate error correction bytes for them. We'll - // store the divided data bytes blocks and error correction bytes blocks into "blocks". - int dataBytesOffset = 0; - int maxNumDataBytes = 0; - int maxNumEcBytes = 0; - - // Since, we know the number of reedsolmon blocks, we can initialize the vector with the number. - Collection blocks = new ArrayList<>(numRSBlocks); - - for (int i = 0; i < numRSBlocks; ++i) { - int[] numDataBytesInBlock = new int[1]; - int[] numEcBytesInBlock = new int[1]; - getNumDataBytesAndNumECBytesForBlockID( - numTotalBytes, numDataBytes, numRSBlocks, i, - numDataBytesInBlock, numEcBytesInBlock); - - int size = numDataBytesInBlock[0]; - byte[] dataBytes = new byte[size]; - bits.toBytes(8 * dataBytesOffset, dataBytes, 0, size); - byte[] ecBytes = generateECBytes(dataBytes, numEcBytesInBlock[0]); - blocks.add(new BlockPair(dataBytes, ecBytes)); - - maxNumDataBytes = Math.max(maxNumDataBytes, size); - maxNumEcBytes = Math.max(maxNumEcBytes, ecBytes.length); - dataBytesOffset += numDataBytesInBlock[0]; - } - if (numDataBytes != dataBytesOffset) { - throw new WriterException("Data bytes does not match offset"); - } - - BitArray result = new BitArray(); - - // First, place data blocks. - for (int i = 0; i < maxNumDataBytes; ++i) { - for (BlockPair block : blocks) { - byte[] dataBytes = block.getDataBytes(); - if (i < dataBytes.length) { - result.appendBits(dataBytes[i], 8); - } - } - } - // Then, place error correction blocks. - for (int i = 0; i < maxNumEcBytes; ++i) { - for (BlockPair block : blocks) { - byte[] ecBytes = block.getErrorCorrectionBytes(); - if (i < ecBytes.length) { - result.appendBits(ecBytes[i], 8); - } - } - } - if (numTotalBytes != result.getSizeInBytes()) { // Should be same. - throw new WriterException("Interleaving error: " + numTotalBytes + " and " + - result.getSizeInBytes() + " differ."); - } - - return result; - } - - static byte[] generateECBytes(byte[] dataBytes, int numEcBytesInBlock) { - int numDataBytes = dataBytes.length; - int[] toEncode = new int[numDataBytes + numEcBytesInBlock]; - for (int i = 0; i < numDataBytes; i++) { - toEncode[i] = dataBytes[i] & 0xFF; - } - new ReedSolomonEncoder(GenericGF.QR_CODE_FIELD_256).encode(toEncode, numEcBytesInBlock); - - byte[] ecBytes = new byte[numEcBytesInBlock]; - for (int i = 0; i < numEcBytesInBlock; i++) { - ecBytes[i] = (byte) toEncode[numDataBytes + i]; - } - return ecBytes; - } - - /** - * Append mode info. On success, store the result in "bits". - */ - static void appendModeInfo(Mode mode, BitArray bits) { - bits.appendBits(mode.getBits(), 4); - } - - - /** - * Append length info. On success, store the result in "bits". - */ - static void appendLengthInfo(int numLetters, Version version, Mode mode, BitArray bits) throws WriterException { - int numBits = mode.getCharacterCountBits(version); - if (numLetters >= (1 << numBits)) { - throw new WriterException(numLetters + " is bigger than " + ((1 << numBits) - 1)); - } - bits.appendBits(numLetters, numBits); - } - - /** - * Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits". - */ - static void appendBytes(String content, - Mode mode, - BitArray bits, - String encoding) throws WriterException { - switch (mode) { - case NUMERIC: - appendNumericBytes(content, bits); - break; - case ALPHANUMERIC: - appendAlphanumericBytes(content, bits); - break; - case BYTE: - append8BitBytes(content, bits, encoding); - break; - case KANJI: - appendKanjiBytes(content, bits); - break; - default: - throw new WriterException("Invalid mode: " + mode); - } - } - - static void appendNumericBytes(CharSequence content, BitArray bits) { - int length = content.length(); - int i = 0; - while (i < length) { - int num1 = content.charAt(i) - '0'; - if (i + 2 < length) { - // Encode three numeric letters in ten bits. - int num2 = content.charAt(i + 1) - '0'; - int num3 = content.charAt(i + 2) - '0'; - bits.appendBits(num1 * 100 + num2 * 10 + num3, 10); - i += 3; - } else if (i + 1 < length) { - // Encode two numeric letters in seven bits. - int num2 = content.charAt(i + 1) - '0'; - bits.appendBits(num1 * 10 + num2, 7); - i += 2; - } else { - // Encode one numeric letter in four bits. - bits.appendBits(num1, 4); - i++; - } - } - } - - static void appendAlphanumericBytes(CharSequence content, BitArray bits) throws WriterException { - int length = content.length(); - int i = 0; - while (i < length) { - int code1 = getAlphanumericCode(content.charAt(i)); - if (code1 == -1) { - throw new WriterException(); - } - if (i + 1 < length) { - int code2 = getAlphanumericCode(content.charAt(i + 1)); - if (code2 == -1) { - throw new WriterException(); - } - // Encode two alphanumeric letters in 11 bits. - bits.appendBits(code1 * 45 + code2, 11); - i += 2; - } else { - // Encode one alphanumeric letter in six bits. - bits.appendBits(code1, 6); - i++; - } - } - } - - static void append8BitBytes(String content, BitArray bits, String encoding) - throws WriterException { - byte[] bytes; - try { - bytes = content.getBytes(encoding); - } catch (UnsupportedEncodingException uee) { - throw new WriterException(uee); - } - for (byte b : bytes) { - bits.appendBits(b, 8); - } - } - - static void appendKanjiBytes(String content, BitArray bits) throws WriterException { - byte[] bytes; - try { - bytes = content.getBytes("Shift_JIS"); - } catch (UnsupportedEncodingException uee) { - throw new WriterException(uee); - } - if (bytes.length % 2 != 0) { - throw new WriterException("Kanji byte size not even"); - } - int maxI = bytes.length - 1; // bytes.length must be even - for (int i = 0; i < maxI; i += 2) { - int byte1 = bytes[i] & 0xFF; - int byte2 = bytes[i + 1] & 0xFF; - int code = (byte1 << 8) | byte2; - int subtracted = -1; - if (code >= 0x8140 && code <= 0x9ffc) { - subtracted = code - 0x8140; - } else if (code >= 0xe040 && code <= 0xebbf) { - subtracted = code - 0xc140; - } - if (subtracted == -1) { - throw new WriterException("Invalid byte sequence"); - } - int encoded = ((subtracted >> 8) * 0xc0) + (subtracted & 0xff); - bits.appendBits(encoded, 13); - } - } - - private static void appendECI(CharacterSetECI eci, BitArray bits) { - bits.appendBits(Mode.ECI.getBits(), 4); - // This is correct for values up to 127, which is all we need now. - bits.appendBits(eci.getValue(), 8); - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/qrcode/encoder/MaskUtil.java b/TMessagesProj/src/main/java/com/google/zxing/qrcode/encoder/MaskUtil.java deleted file mode 100755 index 11d780469..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/qrcode/encoder/MaskUtil.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright 2008 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.qrcode.encoder; - -/** - * @author Satoru Takabayashi - * @author Daniel Switkin - * @author Sean Owen - */ -final class MaskUtil { - - // Penalty weights from section 6.8.2.1 - private static final int N1 = 3; - private static final int N2 = 3; - private static final int N3 = 40; - private static final int N4 = 10; - - private MaskUtil() { - // do nothing - } - - /** - * Apply mask penalty rule 1 and return the penalty. Find repetitive cells with the same color and - * give penalty to them. Example: 00000 or 11111. - */ - static int applyMaskPenaltyRule1(ByteMatrix matrix) { - return applyMaskPenaltyRule1Internal(matrix, true) + applyMaskPenaltyRule1Internal(matrix, false); - } - - /** - * Apply mask penalty rule 2 and return the penalty. Find 2x2 blocks with the same color and give - * penalty to them. This is actually equivalent to the spec's rule, which is to find MxN blocks and give a - * penalty proportional to (M-1)x(N-1), because this is the number of 2x2 blocks inside such a block. - */ - static int applyMaskPenaltyRule2(ByteMatrix matrix) { - int penalty = 0; - byte[][] array = matrix.getArray(); - int width = matrix.getWidth(); - int height = matrix.getHeight(); - for (int y = 0; y < height - 1; y++) { - byte[] arrayY = array[y]; - for (int x = 0; x < width - 1; x++) { - int value = arrayY[x]; - if (value == arrayY[x + 1] && value == array[y + 1][x] && value == array[y + 1][x + 1]) { - penalty++; - } - } - } - return N2 * penalty; - } - - /** - * Apply mask penalty rule 3 and return the penalty. Find consecutive runs of 1:1:3:1:1:4 - * starting with black, or 4:1:1:3:1:1 starting with white, and give penalty to them. If we - * find patterns like 000010111010000, we give penalty once. - */ - static int applyMaskPenaltyRule3(ByteMatrix matrix) { - int numPenalties = 0; - byte[][] array = matrix.getArray(); - int width = matrix.getWidth(); - int height = matrix.getHeight(); - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - byte[] arrayY = array[y]; // We can at least optimize this access - if (x + 6 < width && - arrayY[x] == 1 && - arrayY[x + 1] == 0 && - arrayY[x + 2] == 1 && - arrayY[x + 3] == 1 && - arrayY[x + 4] == 1 && - arrayY[x + 5] == 0 && - arrayY[x + 6] == 1 && - (isWhiteHorizontal(arrayY, x - 4, x) || isWhiteHorizontal(arrayY, x + 7, x + 11))) { - numPenalties++; - } - if (y + 6 < height && - array[y][x] == 1 && - array[y + 1][x] == 0 && - array[y + 2][x] == 1 && - array[y + 3][x] == 1 && - array[y + 4][x] == 1 && - array[y + 5][x] == 0 && - array[y + 6][x] == 1 && - (isWhiteVertical(array, x, y - 4, y) || isWhiteVertical(array, x, y + 7, y + 11))) { - numPenalties++; - } - } - } - return numPenalties * N3; - } - - private static boolean isWhiteHorizontal(byte[] rowArray, int from, int to) { - from = Math.max(from, 0); - to = Math.min(to, rowArray.length); - for (int i = from; i < to; i++) { - if (rowArray[i] == 1) { - return false; - } - } - return true; - } - - private static boolean isWhiteVertical(byte[][] array, int col, int from, int to) { - from = Math.max(from, 0); - to = Math.min(to, array.length); - for (int i = from; i < to; i++) { - if (array[i][col] == 1) { - return false; - } - } - return true; - } - - /** - * Apply mask penalty rule 4 and return the penalty. Calculate the ratio of dark cells and give - * penalty if the ratio is far from 50%. It gives 10 penalty for 5% distance. - */ - static int applyMaskPenaltyRule4(ByteMatrix matrix) { - int numDarkCells = 0; - byte[][] array = matrix.getArray(); - int width = matrix.getWidth(); - int height = matrix.getHeight(); - for (int y = 0; y < height; y++) { - byte[] arrayY = array[y]; - for (int x = 0; x < width; x++) { - if (arrayY[x] == 1) { - numDarkCells++; - } - } - } - int numTotalCells = matrix.getHeight() * matrix.getWidth(); - int fivePercentVariances = Math.abs(numDarkCells * 2 - numTotalCells) * 10 / numTotalCells; - return fivePercentVariances * N4; - } - - /** - * Return the mask bit for "getMaskPattern" at "x" and "y". See 8.8 of JISX0510:2004 for mask - * pattern conditions. - */ - static boolean getDataMaskBit(int maskPattern, int x, int y) { - int intermediate; - int temp; - switch (maskPattern) { - case 0: - intermediate = (y + x) & 0x1; - break; - case 1: - intermediate = y & 0x1; - break; - case 2: - intermediate = x % 3; - break; - case 3: - intermediate = (y + x) % 3; - break; - case 4: - intermediate = ((y / 2) + (x / 3)) & 0x1; - break; - case 5: - temp = y * x; - intermediate = (temp & 0x1) + (temp % 3); - break; - case 6: - temp = y * x; - intermediate = ((temp & 0x1) + (temp % 3)) & 0x1; - break; - case 7: - temp = y * x; - intermediate = ((temp % 3) + ((y + x) & 0x1)) & 0x1; - break; - default: - throw new IllegalArgumentException("Invalid mask pattern: " + maskPattern); - } - return intermediate == 0; - } - - /** - * Helper function for applyMaskPenaltyRule1. We need this for doing this calculation in both - * vertical and horizontal orders respectively. - */ - private static int applyMaskPenaltyRule1Internal(ByteMatrix matrix, boolean isHorizontal) { - int penalty = 0; - int iLimit = isHorizontal ? matrix.getHeight() : matrix.getWidth(); - int jLimit = isHorizontal ? matrix.getWidth() : matrix.getHeight(); - byte[][] array = matrix.getArray(); - for (int i = 0; i < iLimit; i++) { - int numSameBitCells = 0; - int prevBit = -1; - for (int j = 0; j < jLimit; j++) { - int bit = isHorizontal ? array[i][j] : array[j][i]; - if (bit == prevBit) { - numSameBitCells++; - } else { - if (numSameBitCells >= 5) { - penalty += N1 + (numSameBitCells - 5); - } - numSameBitCells = 1; // Include the cell itself. - prevBit = bit; - } - } - if (numSameBitCells >= 5) { - penalty += N1 + (numSameBitCells - 5); - } - } - return penalty; - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/qrcode/encoder/MatrixUtil.java b/TMessagesProj/src/main/java/com/google/zxing/qrcode/encoder/MatrixUtil.java deleted file mode 100755 index 561284a35..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/qrcode/encoder/MatrixUtil.java +++ /dev/null @@ -1,476 +0,0 @@ -/* - * Copyright 2008 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.qrcode.encoder; - -import com.google.zxing.WriterException; -import com.google.zxing.common.BitArray; -import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; -import com.google.zxing.qrcode.decoder.Version; - -/** - * @author satorux@google.com (Satoru Takabayashi) - creator - * @author dswitkin@google.com (Daniel Switkin) - ported from C++ - */ -final class MatrixUtil { - - private static final int[][] POSITION_DETECTION_PATTERN = { - {1, 1, 1, 1, 1, 1, 1}, - {1, 0, 0, 0, 0, 0, 1}, - {1, 0, 1, 1, 1, 0, 1}, - {1, 0, 1, 1, 1, 0, 1}, - {1, 0, 1, 1, 1, 0, 1}, - {1, 0, 0, 0, 0, 0, 1}, - {1, 1, 1, 1, 1, 1, 1}, - }; - - private static final int[][] POSITION_ADJUSTMENT_PATTERN = { - {1, 1, 1, 1, 1}, - {1, 0, 0, 0, 1}, - {1, 0, 1, 0, 1}, - {1, 0, 0, 0, 1}, - {1, 1, 1, 1, 1}, - }; - - // From Appendix E. Table 1, JIS0510X:2004 (p 71). The table was double-checked by komatsu. - private static final int[][] POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE = { - {-1, -1, -1, -1, -1, -1, -1}, // Version 1 - { 6, 18, -1, -1, -1, -1, -1}, // Version 2 - { 6, 22, -1, -1, -1, -1, -1}, // Version 3 - { 6, 26, -1, -1, -1, -1, -1}, // Version 4 - { 6, 30, -1, -1, -1, -1, -1}, // Version 5 - { 6, 34, -1, -1, -1, -1, -1}, // Version 6 - { 6, 22, 38, -1, -1, -1, -1}, // Version 7 - { 6, 24, 42, -1, -1, -1, -1}, // Version 8 - { 6, 26, 46, -1, -1, -1, -1}, // Version 9 - { 6, 28, 50, -1, -1, -1, -1}, // Version 10 - { 6, 30, 54, -1, -1, -1, -1}, // Version 11 - { 6, 32, 58, -1, -1, -1, -1}, // Version 12 - { 6, 34, 62, -1, -1, -1, -1}, // Version 13 - { 6, 26, 46, 66, -1, -1, -1}, // Version 14 - { 6, 26, 48, 70, -1, -1, -1}, // Version 15 - { 6, 26, 50, 74, -1, -1, -1}, // Version 16 - { 6, 30, 54, 78, -1, -1, -1}, // Version 17 - { 6, 30, 56, 82, -1, -1, -1}, // Version 18 - { 6, 30, 58, 86, -1, -1, -1}, // Version 19 - { 6, 34, 62, 90, -1, -1, -1}, // Version 20 - { 6, 28, 50, 72, 94, -1, -1}, // Version 21 - { 6, 26, 50, 74, 98, -1, -1}, // Version 22 - { 6, 30, 54, 78, 102, -1, -1}, // Version 23 - { 6, 28, 54, 80, 106, -1, -1}, // Version 24 - { 6, 32, 58, 84, 110, -1, -1}, // Version 25 - { 6, 30, 58, 86, 114, -1, -1}, // Version 26 - { 6, 34, 62, 90, 118, -1, -1}, // Version 27 - { 6, 26, 50, 74, 98, 122, -1}, // Version 28 - { 6, 30, 54, 78, 102, 126, -1}, // Version 29 - { 6, 26, 52, 78, 104, 130, -1}, // Version 30 - { 6, 30, 56, 82, 108, 134, -1}, // Version 31 - { 6, 34, 60, 86, 112, 138, -1}, // Version 32 - { 6, 30, 58, 86, 114, 142, -1}, // Version 33 - { 6, 34, 62, 90, 118, 146, -1}, // Version 34 - { 6, 30, 54, 78, 102, 126, 150}, // Version 35 - { 6, 24, 50, 76, 102, 128, 154}, // Version 36 - { 6, 28, 54, 80, 106, 132, 158}, // Version 37 - { 6, 32, 58, 84, 110, 136, 162}, // Version 38 - { 6, 26, 54, 82, 110, 138, 166}, // Version 39 - { 6, 30, 58, 86, 114, 142, 170}, // Version 40 - }; - - // Type info cells at the left top corner. - private static final int[][] TYPE_INFO_COORDINATES = { - {8, 0}, - {8, 1}, - {8, 2}, - {8, 3}, - {8, 4}, - {8, 5}, - {8, 7}, - {8, 8}, - {7, 8}, - {5, 8}, - {4, 8}, - {3, 8}, - {2, 8}, - {1, 8}, - {0, 8}, - }; - - // From Appendix D in JISX0510:2004 (p. 67) - private static final int VERSION_INFO_POLY = 0x1f25; // 1 1111 0010 0101 - - // From Appendix C in JISX0510:2004 (p.65). - private static final int TYPE_INFO_POLY = 0x537; - private static final int TYPE_INFO_MASK_PATTERN = 0x5412; - - private MatrixUtil() { - // do nothing - } - - // Set all cells to -1. -1 means that the cell is empty (not set yet). - // - // JAVAPORT: We shouldn't need to do this at all. The code should be rewritten to begin encoding - // with the ByteMatrix initialized all to zero. - static void clearMatrix(ByteMatrix matrix) { - matrix.clear((byte) -1); - } - - // Build 2D matrix of QR Code from "dataBits" with "ecLevel", "version" and "getMaskPattern". On - // success, store the result in "matrix" and return true. - static void buildMatrix(BitArray dataBits, - ErrorCorrectionLevel ecLevel, - Version version, - int maskPattern, - ByteMatrix matrix) throws WriterException { - clearMatrix(matrix); - embedBasicPatterns(version, matrix); - // Type information appear with any version. - embedTypeInfo(ecLevel, maskPattern, matrix); - // Version info appear if version >= 7. - maybeEmbedVersionInfo(version, matrix); - // Data should be embedded at end. - embedDataBits(dataBits, maskPattern, matrix); - } - - // Embed basic patterns. On success, modify the matrix and return true. - // The basic patterns are: - // - Position detection patterns - // - Timing patterns - // - Dark dot at the left bottom corner - // - Position adjustment patterns, if need be - static void embedBasicPatterns(Version version, ByteMatrix matrix) throws WriterException { - // Let's get started with embedding big squares at corners. - embedPositionDetectionPatternsAndSeparators(matrix); - // Then, embed the dark dot at the left bottom corner. - embedDarkDotAtLeftBottomCorner(matrix); - - // Position adjustment patterns appear if version >= 2. - maybeEmbedPositionAdjustmentPatterns(version, matrix); - // Timing patterns should be embedded after position adj. patterns. - embedTimingPatterns(matrix); - } - - // Embed type information. On success, modify the matrix. - static void embedTypeInfo(ErrorCorrectionLevel ecLevel, int maskPattern, ByteMatrix matrix) - throws WriterException { - BitArray typeInfoBits = new BitArray(); - makeTypeInfoBits(ecLevel, maskPattern, typeInfoBits); - - for (int i = 0; i < typeInfoBits.getSize(); ++i) { - // Place bits in LSB to MSB order. LSB (least significant bit) is the last value in - // "typeInfoBits". - boolean bit = typeInfoBits.get(typeInfoBits.getSize() - 1 - i); - - // Type info bits at the left top corner. See 8.9 of JISX0510:2004 (p.46). - int[] coordinates = TYPE_INFO_COORDINATES[i]; - int x1 = coordinates[0]; - int y1 = coordinates[1]; - matrix.set(x1, y1, bit); - - if (i < 8) { - // Right top corner. - int x2 = matrix.getWidth() - i - 1; - int y2 = 8; - matrix.set(x2, y2, bit); - } else { - // Left bottom corner. - int x2 = 8; - int y2 = matrix.getHeight() - 7 + (i - 8); - matrix.set(x2, y2, bit); - } - } - } - - // Embed version information if need be. On success, modify the matrix and return true. - // See 8.10 of JISX0510:2004 (p.47) for how to embed version information. - static void maybeEmbedVersionInfo(Version version, ByteMatrix matrix) throws WriterException { - if (version.getVersionNumber() < 7) { // Version info is necessary if version >= 7. - return; // Don't need version info. - } - BitArray versionInfoBits = new BitArray(); - makeVersionInfoBits(version, versionInfoBits); - - int bitIndex = 6 * 3 - 1; // It will decrease from 17 to 0. - for (int i = 0; i < 6; ++i) { - for (int j = 0; j < 3; ++j) { - // Place bits in LSB (least significant bit) to MSB order. - boolean bit = versionInfoBits.get(bitIndex); - bitIndex--; - // Left bottom corner. - matrix.set(i, matrix.getHeight() - 11 + j, bit); - // Right bottom corner. - matrix.set(matrix.getHeight() - 11 + j, i, bit); - } - } - } - - // Embed "dataBits" using "getMaskPattern". On success, modify the matrix and return true. - // For debugging purposes, it skips masking process if "getMaskPattern" is -1. - // See 8.7 of JISX0510:2004 (p.38) for how to embed data bits. - static void embedDataBits(BitArray dataBits, int maskPattern, ByteMatrix matrix) - throws WriterException { - int bitIndex = 0; - int direction = -1; - // Start from the right bottom cell. - int x = matrix.getWidth() - 1; - int y = matrix.getHeight() - 1; - while (x > 0) { - // Skip the vertical timing pattern. - if (x == 6) { - x -= 1; - } - while (y >= 0 && y < matrix.getHeight()) { - for (int i = 0; i < 2; ++i) { - int xx = x - i; - // Skip the cell if it's not empty. - if (!isEmpty(matrix.get(xx, y))) { - continue; - } - boolean bit; - if (bitIndex < dataBits.getSize()) { - bit = dataBits.get(bitIndex); - ++bitIndex; - } else { - // Padding bit. If there is no bit left, we'll fill the left cells with 0, as described - // in 8.4.9 of JISX0510:2004 (p. 24). - bit = false; - } - - // Skip masking if mask_pattern is -1. - if (maskPattern != -1 && MaskUtil.getDataMaskBit(maskPattern, xx, y)) { - bit = !bit; - } - matrix.set(xx, y, bit); - } - y += direction; - } - direction = -direction; // Reverse the direction. - y += direction; - x -= 2; // Move to the left. - } - // All bits should be consumed. - if (bitIndex != dataBits.getSize()) { - throw new WriterException("Not all bits consumed: " + bitIndex + '/' + dataBits.getSize()); - } - } - - // Return the position of the most significant bit set (to one) in the "value". The most - // significant bit is position 32. If there is no bit set, return 0. Examples: - // - findMSBSet(0) => 0 - // - findMSBSet(1) => 1 - // - findMSBSet(255) => 8 - static int findMSBSet(int value) { - return 32 - Integer.numberOfLeadingZeros(value); - } - - // Calculate BCH (Bose-Chaudhuri-Hocquenghem) code for "value" using polynomial "poly". The BCH - // code is used for encoding type information and version information. - // Example: Calculation of version information of 7. - // f(x) is created from 7. - // - 7 = 000111 in 6 bits - // - f(x) = x^2 + x^1 + x^0 - // g(x) is given by the standard (p. 67) - // - g(x) = x^12 + x^11 + x^10 + x^9 + x^8 + x^5 + x^2 + 1 - // Multiply f(x) by x^(18 - 6) - // - f'(x) = f(x) * x^(18 - 6) - // - f'(x) = x^14 + x^13 + x^12 - // Calculate the remainder of f'(x) / g(x) - // x^2 - // __________________________________________________ - // g(x) )x^14 + x^13 + x^12 - // x^14 + x^13 + x^12 + x^11 + x^10 + x^7 + x^4 + x^2 - // -------------------------------------------------- - // x^11 + x^10 + x^7 + x^4 + x^2 - // - // The remainder is x^11 + x^10 + x^7 + x^4 + x^2 - // Encode it in binary: 110010010100 - // The return value is 0xc94 (1100 1001 0100) - // - // Since all coefficients in the polynomials are 1 or 0, we can do the calculation by bit - // operations. We don't care if coefficients are positive or negative. - static int calculateBCHCode(int value, int poly) { - if (poly == 0) { - throw new IllegalArgumentException("0 polynomial"); - } - // If poly is "1 1111 0010 0101" (version info poly), msbSetInPoly is 13. We'll subtract 1 - // from 13 to make it 12. - int msbSetInPoly = findMSBSet(poly); - value <<= msbSetInPoly - 1; - // Do the division business using exclusive-or operations. - while (findMSBSet(value) >= msbSetInPoly) { - value ^= poly << (findMSBSet(value) - msbSetInPoly); - } - // Now the "value" is the remainder (i.e. the BCH code) - return value; - } - - // Make bit vector of type information. On success, store the result in "bits" and return true. - // Encode error correction level and mask pattern. See 8.9 of - // JISX0510:2004 (p.45) for details. - static void makeTypeInfoBits(ErrorCorrectionLevel ecLevel, int maskPattern, BitArray bits) - throws WriterException { - if (!QRCode.isValidMaskPattern(maskPattern)) { - throw new WriterException("Invalid mask pattern"); - } - int typeInfo = (ecLevel.getBits() << 3) | maskPattern; - bits.appendBits(typeInfo, 5); - - int bchCode = calculateBCHCode(typeInfo, TYPE_INFO_POLY); - bits.appendBits(bchCode, 10); - - BitArray maskBits = new BitArray(); - maskBits.appendBits(TYPE_INFO_MASK_PATTERN, 15); - bits.xor(maskBits); - - if (bits.getSize() != 15) { // Just in case. - throw new WriterException("should not happen but we got: " + bits.getSize()); - } - } - - // Make bit vector of version information. On success, store the result in "bits" and return true. - // See 8.10 of JISX0510:2004 (p.45) for details. - static void makeVersionInfoBits(Version version, BitArray bits) throws WriterException { - bits.appendBits(version.getVersionNumber(), 6); - int bchCode = calculateBCHCode(version.getVersionNumber(), VERSION_INFO_POLY); - bits.appendBits(bchCode, 12); - - if (bits.getSize() != 18) { // Just in case. - throw new WriterException("should not happen but we got: " + bits.getSize()); - } - } - - // Check if "value" is empty. - private static boolean isEmpty(int value) { - return value == -1; - } - - private static void embedTimingPatterns(ByteMatrix matrix) { - // -8 is for skipping position detection patterns (size 7), and two horizontal/vertical - // separation patterns (size 1). Thus, 8 = 7 + 1. - for (int i = 8; i < matrix.getWidth() - 8; ++i) { - int bit = (i + 1) % 2; - // Horizontal line. - if (isEmpty(matrix.get(i, 6))) { - matrix.set(i, 6, bit); - } - // Vertical line. - if (isEmpty(matrix.get(6, i))) { - matrix.set(6, i, bit); - } - } - } - - // Embed the lonely dark dot at left bottom corner. JISX0510:2004 (p.46) - private static void embedDarkDotAtLeftBottomCorner(ByteMatrix matrix) throws WriterException { - if (matrix.get(8, matrix.getHeight() - 8) == 0) { - throw new WriterException(); - } - matrix.set(8, matrix.getHeight() - 8, 1); - } - - private static void embedHorizontalSeparationPattern(int xStart, - int yStart, - ByteMatrix matrix) throws WriterException { - for (int x = 0; x < 8; ++x) { - if (!isEmpty(matrix.get(xStart + x, yStart))) { - throw new WriterException(); - } - matrix.set(xStart + x, yStart, 0); - } - } - - private static void embedVerticalSeparationPattern(int xStart, - int yStart, - ByteMatrix matrix) throws WriterException { - for (int y = 0; y < 7; ++y) { - if (!isEmpty(matrix.get(xStart, yStart + y))) { - throw new WriterException(); - } - matrix.set(xStart, yStart + y, 0); - } - } - - private static void embedPositionAdjustmentPattern(int xStart, int yStart, ByteMatrix matrix) { - for (int y = 0; y < 5; ++y) { - int[] patternY = POSITION_ADJUSTMENT_PATTERN[y]; - for (int x = 0; x < 5; ++x) { - matrix.set(xStart + x, yStart + y, patternY[x]); - } - } - } - - private static void embedPositionDetectionPattern(int xStart, int yStart, ByteMatrix matrix) { - for (int y = 0; y < 7; ++y) { - int[] patternY = POSITION_DETECTION_PATTERN[y]; - for (int x = 0; x < 7; ++x) { - matrix.set(xStart + x, yStart + y, patternY[x]); - } - } - } - - // Embed position detection patterns and surrounding vertical/horizontal separators. - private static void embedPositionDetectionPatternsAndSeparators(ByteMatrix matrix) throws WriterException { - // Embed three big squares at corners. - int pdpWidth = POSITION_DETECTION_PATTERN[0].length; - // Left top corner. - embedPositionDetectionPattern(0, 0, matrix); - // Right top corner. - embedPositionDetectionPattern(matrix.getWidth() - pdpWidth, 0, matrix); - // Left bottom corner. - embedPositionDetectionPattern(0, matrix.getWidth() - pdpWidth, matrix); - - // Embed horizontal separation patterns around the squares. - int hspWidth = 8; - // Left top corner. - embedHorizontalSeparationPattern(0, hspWidth - 1, matrix); - // Right top corner. - embedHorizontalSeparationPattern(matrix.getWidth() - hspWidth, - hspWidth - 1, matrix); - // Left bottom corner. - embedHorizontalSeparationPattern(0, matrix.getWidth() - hspWidth, matrix); - - // Embed vertical separation patterns around the squares. - int vspSize = 7; - // Left top corner. - embedVerticalSeparationPattern(vspSize, 0, matrix); - // Right top corner. - embedVerticalSeparationPattern(matrix.getHeight() - vspSize - 1, 0, matrix); - // Left bottom corner. - embedVerticalSeparationPattern(vspSize, matrix.getHeight() - vspSize, - matrix); - } - - // Embed position adjustment patterns if need be. - private static void maybeEmbedPositionAdjustmentPatterns(Version version, ByteMatrix matrix) { - if (version.getVersionNumber() < 2) { // The patterns appear if version >= 2 - return; - } - int index = version.getVersionNumber() - 1; - int[] coordinates = POSITION_ADJUSTMENT_PATTERN_COORDINATE_TABLE[index]; - for (int y : coordinates) { - if (y >= 0) { - for (int x : coordinates) { - if (x >= 0 && isEmpty(matrix.get(x, y))) { - // If the cell is unset, we embed the position adjustment pattern here. - // -2 is necessary since the x/y coordinates point to the center of the pattern, not the - // left top corner. - embedPositionAdjustmentPattern(x - 2, y - 2, matrix); - } - } - } - } - } - -} diff --git a/TMessagesProj/src/main/java/com/google/zxing/qrcode/encoder/QRCode.java b/TMessagesProj/src/main/java/com/google/zxing/qrcode/encoder/QRCode.java deleted file mode 100755 index b560de92f..000000000 --- a/TMessagesProj/src/main/java/com/google/zxing/qrcode/encoder/QRCode.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2008 ZXing authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.zxing.qrcode.encoder; - -import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; -import com.google.zxing.qrcode.decoder.Mode; -import com.google.zxing.qrcode.decoder.Version; - -/** - * @author satorux@google.com (Satoru Takabayashi) - creator - * @author dswitkin@google.com (Daniel Switkin) - ported from C++ - */ -public final class QRCode { - - public static final int NUM_MASK_PATTERNS = 8; - - private Mode mode; - private ErrorCorrectionLevel ecLevel; - private Version version; - private int maskPattern; - private ByteMatrix matrix; - - public QRCode() { - maskPattern = -1; - } - - public Mode getMode() { - return mode; - } - - public ErrorCorrectionLevel getECLevel() { - return ecLevel; - } - - public Version getVersion() { - return version; - } - - public int getMaskPattern() { - return maskPattern; - } - - public ByteMatrix getMatrix() { - return matrix; - } - - @Override - public String toString() { - StringBuilder result = new StringBuilder(200); - result.append("<<\n"); - result.append(" mode: "); - result.append(mode); - result.append("\n ecLevel: "); - result.append(ecLevel); - result.append("\n version: "); - result.append(version); - result.append("\n maskPattern: "); - result.append(maskPattern); - if (matrix == null) { - result.append("\n matrix: null\n"); - } else { - result.append("\n matrix:\n"); - result.append(matrix); - } - result.append(">>\n"); - return result.toString(); - } - - public void setMode(Mode value) { - mode = value; - } - - public void setECLevel(ErrorCorrectionLevel value) { - ecLevel = value; - } - - public void setVersion(Version version) { - this.version = version; - } - - public void setMaskPattern(int value) { - maskPattern = value; - } - - public void setMatrix(ByteMatrix value) { - matrix = value; - } - - // Check if "mask_pattern" is valid. - public static boolean isValidMaskPattern(int maskPattern) { - return maskPattern >= 0 && maskPattern < NUM_MASK_PATTERNS; - } - -} diff --git a/TMessagesProj/src/main/java/com/v2ray/ang/V2RayConfig.kt b/TMessagesProj/src/main/java/com/v2ray/ang/V2RayConfig.kt new file mode 100644 index 000000000..ed516e44d --- /dev/null +++ b/TMessagesProj/src/main/java/com/v2ray/ang/V2RayConfig.kt @@ -0,0 +1,67 @@ +package com.v2ray.ang + +/** + * + * App Config Const + */ +object V2RayConfig { + const val ANG_PACKAGE = "com.v2ray.ang" + const val ANG_CONFIG = "ang_config" + const val PREF_CURR_CONFIG = "pref_v2ray_config" + const val PREF_CURR_CONFIG_GUID = "pref_v2ray_config_guid" + const val PREF_CURR_CONFIG_NAME = "pref_v2ray_config_name" + const val PREF_CURR_CONFIG_DOMAIN = "pref_v2ray_config_domain" + const val PREF_INAPP_BUY_IS_PREMIUM = "pref_inapp_buy_is_premium" + const val VMESS_PROTOCOL: String = "vmess://" + const val VMESS1_PROTOCOL = "vmess1://" + const val SS_PROTOCOL: String = "ss://" + const val SSR_PROTOCOL: String = "ssr://" + + const val SOCKS_PROTOCOL: String = "socks://" + const val BROADCAST_ACTION_SERVICE = "com.v2ray.ang.action.service" + const val BROADCAST_ACTION_ACTIVITY = "com.v2ray.ang.action.activity" + const val BROADCAST_ACTION_WIDGET_CLICK = "com.v2ray.ang.action.widget.click" + + const val TASKER_EXTRA_BUNDLE = "com.twofortyfouram.locale.intent.extra.BUNDLE" + const val TASKER_EXTRA_STRING_BLURB = "com.twofortyfouram.locale.intent.extra.BLURB" + const val TASKER_EXTRA_BUNDLE_SWITCH = "tasker_extra_bundle_switch" + const val TASKER_EXTRA_BUNDLE_GUID = "tasker_extra_bundle_guid" + const val TASKER_DEFAULT_GUID = "Default" + + const val PREF_V2RAY_ROUTING_AGENT = "pref_v2ray_routing_agent" + const val PREF_V2RAY_ROUTING_DIRECT = "pref_v2ray_routing_direct" + const val PREF_V2RAY_ROUTING_BLOCKED = "pref_v2ray_routing_blocked" + const val TAG_AGENT = "proxy" + const val TAG_DIRECT = "direct" + const val TAG_BLOCKED = "block" + + const val androidpackagenamelistUrl = "https://raw.githubusercontent.com/2dust/androidpackagenamelist/master/proxy.txt" + const val v2rayCustomRoutingListUrl = "https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/" + const val v2rayNGIssues = "https://github.com/2dust/v2rayNG/issues" + const val promotionUrl = "https://1.2345345.xyz/ads.html" + + const val DNS_AGENT = "1.1.1.1" + const val DNS_DIRECT = "223.5.5.5" + + const val MSG_REGISTER_CLIENT = 1 + const val MSG_STATE_RUNNING = 11 + const val MSG_STATE_NOT_RUNNING = 12 + const val MSG_UNREGISTER_CLIENT = 2 + const val MSG_STATE_START = 3 + const val MSG_STATE_START_SUCCESS = 31 + const val MSG_STATE_START_FAILURE = 32 + const val MSG_STATE_STOP = 4 + const val MSG_STATE_STOP_SUCCESS = 41 + const val MSG_STATE_RESTART = 5 + + object EConfigType { + + @JvmField + val Vmess = 1 + + @JvmField + val Shadowsocks = 3 + + } + +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/com/v2ray/ang/dto/AngConfig.kt b/TMessagesProj/src/main/java/com/v2ray/ang/dto/AngConfig.kt new file mode 100644 index 000000000..8d5786f5e --- /dev/null +++ b/TMessagesProj/src/main/java/com/v2ray/ang/dto/AngConfig.kt @@ -0,0 +1,72 @@ +package com.v2ray.ang.dto + +import android.util.Base64 +import com.google.gson.Gson +import com.v2ray.ang.V2RayConfig +import com.v2ray.ang.V2RayConfig.SS_PROTOCOL +import com.v2ray.ang.V2RayConfig.VMESS_PROTOCOL +import com.v2ray.ang.util.Utils + +data class AngConfig( + var index: Int, + var vmess: ArrayList, + var subItem: ArrayList +) { + data class VmessBean(var guid: String = "123456", + var address: String = "", + var port: Int = 443, + var id: String = "", + var alterId: Int = 64, + var security: String = "auto", + var network: String = "tcp", + var remarks: String = "", + var headerType: String = "none", + var requestHost: String = "", + var path: String = "", + var streamSecurity: String = "", + var configType: Int = 1, + var configVersion: Int = 2, + var testResult: String = "") { + + override fun toString(): String { + + if (configType == V2RayConfig.EConfigType.Vmess) { + + val vmessQRCode = VmessQRCode() + + vmessQRCode.v = configVersion.toString() + vmessQRCode.ps = remarks + vmessQRCode.add = address + vmessQRCode.port = port.toString() + vmessQRCode.id = id + vmessQRCode.aid = alterId.toString() + vmessQRCode.net = network + vmessQRCode.type = headerType + vmessQRCode.host = requestHost + vmessQRCode.path = path + vmessQRCode.tls = streamSecurity + + return VMESS_PROTOCOL + Utils.encode(Gson().toJson(vmessQRCode)) + + } else if (configType == V2RayConfig.EConfigType.Shadowsocks) { + + val remark = "#" + Utils.urlEncode(remarks) + + val url = String.format("%s:%s@%s:%s",security, id, address, port) + + return SS_PROTOCOL + Base64.encodeToString(url.toByteArray(charset("UTF-8")), Base64.NO_WRAP) + remark + + } else { + + error("invalid vmess bean type") + + } + + } + + } + + data class SubItemBean(var id: String = "", + var remarks: String = "", + var url: String = "") +} diff --git a/TMessagesProj/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt b/TMessagesProj/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt new file mode 100644 index 000000000..ec1a87eca --- /dev/null +++ b/TMessagesProj/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt @@ -0,0 +1,142 @@ +package com.v2ray.ang.dto + +data class V2rayConfig( + val stats: Any?=null, + val log: LogBean, + val policy: PolicyBean, + val inbounds: ArrayList, + var outbounds: ArrayList, + var dns: DnsBean, + val routing: RoutingBean) { + + data class LogBean(val access: String, + val error: String, + val loglevel: String) + + data class InboundBean( + var tag: String, + var port: Int, + var protocol: String, + var listen: String?=null, + val settings: InSettingsBean, + val sniffing: SniffingBean?) { + + data class InSettingsBean(val auth: String? = null, + val udp: Boolean? = null, + val userLevel: Int? =null, + val address: String? = null, + val port: Int? = null, + val network: String? = null) + + data class SniffingBean(var enabled: Boolean, + val destOverride: List) + } + + data class OutboundBean(val tag: String, + var protocol: String, + var settings: OutSettingsBean?, + var streamSettings: StreamSettingsBean?, + var mux: MuxBean?) { + + data class OutSettingsBean(var vnext: List?, + var servers: List?, + var response: Response) { + + data class VnextBean(var address: String, + var port: Int, + var users: List) { + + data class UsersBean(var id: String, + var alterId: Int, + var security: String, + var level: Int) + } + + data class ServersBean(var address: String, + var method: String, + var ota: Boolean, + var password: String, + var port: Int, + var level: Int) + + data class Response(var type: String) + } + + data class StreamSettingsBean(var network: String, + var security: String, + var tcpSettings: TcpsettingsBean?, + var kcpsettings: KcpsettingsBean?, + var wssettings: WssettingsBean?, + var httpsettings: HttpsettingsBean?, + var tlssettings: TlssettingsBean?, + var quicsettings: QuicsettingBean? + ) { + + data class TcpsettingsBean(var connectionReuse: Boolean = true, + var header: HeaderBean = HeaderBean()) { + data class HeaderBean(var type: String = "none", + var request: Any? = null, + var response: Any? = null) + } + + data class KcpsettingsBean(var mtu: Int = 1350, + var tti: Int = 20, + var uplinkCapacity: Int = 12, + var downlinkCapacity: Int = 100, + var congestion: Boolean = false, + var readBufferSize: Int = 1, + var writeBufferSize: Int = 1, + var header: HeaderBean = HeaderBean()) { + data class HeaderBean(var type: String = "none") + } + + data class WssettingsBean(var connectionReuse: Boolean = true, + var path: String = "", + var headers: HeadersBean = HeadersBean()) { + data class HeadersBean(var Host: String = "") + } + + data class HttpsettingsBean(var host: List = ArrayList(), var path: String = "") + + data class TlssettingsBean(var allowInsecure: Boolean = true, + var serverName: String = "") + + data class QuicsettingBean(var security: String = "none", + var key: String = "", + var header: HeaderBean = HeaderBean()) { + data class HeaderBean(var type: String = "none") + } + } + + data class MuxBean(var enabled: Boolean) + } + + //data class DnsBean(var servers: List) + data class DnsBean(var servers: List?=null, + var hosts: Map?=null + ) { + data class ServersBean(var address: String = "", + var port: Int = 0, + var domains: List?) + } + + data class RoutingBean(var domainStrategy: String, + var rules: ArrayList) { + + data class RulesBean(var type: String = "", + var ip: ArrayList? = null, + var domain: ArrayList? = null, + var outboundTag: String = "", + var port: String? = null, + var inboundTag: ArrayList? = null) + } + + data class PolicyBean(var levels: Map, + var system: Any?=null) { + data class LevelBean( + var handshake: Int? = null, + var connIdle: Int? = null, + var uplinkOnly: Int? = null, + var downlinkOnly: Int? = null) + } +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/com/v2ray/ang/dto/VmessQRCode.kt b/TMessagesProj/src/main/java/com/v2ray/ang/dto/VmessQRCode.kt new file mode 100644 index 000000000..30618017b --- /dev/null +++ b/TMessagesProj/src/main/java/com/v2ray/ang/dto/VmessQRCode.kt @@ -0,0 +1,13 @@ +package com.v2ray.ang.dto + +data class VmessQRCode(var v: String = "", + var ps: String = "", + var add: String = "", + var port: String = "", + var id: String = "", + var aid: String = "", + var net: String = "", + var type: String = "", + var host: String = "", + var path: String = "", + var tls: String = "") \ No newline at end of file diff --git a/TMessagesProj/src/main/java/com/v2ray/ang/util/Utils.kt b/TMessagesProj/src/main/java/com/v2ray/ang/util/Utils.kt new file mode 100644 index 000000000..a30ea9259 --- /dev/null +++ b/TMessagesProj/src/main/java/com/v2ray/ang/util/Utils.kt @@ -0,0 +1,204 @@ +package com.v2ray.ang.util + +import android.app.ActivityManager +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.text.TextUtils +import android.util.Base64 +import android.util.Patterns +import android.webkit.URLUtil +import com.google.zxing.WriterException +import org.telegram.messenger.ApplicationLoader +import java.io.IOException +import java.net.Socket +import java.net.URLDecoder +import java.net.URLEncoder +import java.net.UnknownHostException +import java.util.* + + +object Utils { + + + /** + * parseInt + */ + fun parseInt(str: String): Int { + try { + return Integer.parseInt(str) + } catch (e: Exception) { + e.printStackTrace() + return 0 + } + } + + + /** + * base64 decode + */ + fun decode(text: String): String { + try { + return Base64.decode(text, Base64.NO_WRAP).toString(charset("UTF-8")) + } catch (e: Exception) { + e.printStackTrace() + return "" + } + } + + /** + * base64 encode + */ + fun encode(text: String): String { + try { + return Base64.encodeToString(text.toByteArray(charset("UTF-8")), Base64.NO_WRAP or Base64.URL_SAFE) + } catch (e: Exception) { + e.printStackTrace() + return "" + } + } + + /** + * is ip address + */ + fun isIpAddress(value: String): Boolean { + try { + var addr = value + if (addr.isEmpty() || addr.isBlank()) { + return false + } + //CIDR + if (addr.indexOf("/") > 0) { + val arr = addr.split("/") + if (arr.count() == 2 && Integer.parseInt(arr[1]) > 0) { + addr = arr[0] + } + } + + // "::ffff:192.168.173.22" + // "[::ffff:192.168.173.22]:80" + if (addr.startsWith("::ffff:") && '.' in addr) { + addr = addr.drop(7) + } else if (addr.startsWith("[::ffff:") && '.' in addr) { + addr = addr.drop(8).replace("]", "") + } + + // addr = addr.toLowerCase() + var octets = addr.split('.').toTypedArray() + if (octets.size == 4) { + if (octets[3].indexOf(":") > 0) { + addr = addr.substring(0, addr.indexOf(":")) + } + return isIpv4Address(addr) + } + + // Ipv6addr [2001:abc::123]:8080 + return isIpv6Address(addr) + } catch (e: Exception) { + e.printStackTrace() + return false + } + } + + fun isPureIpAddress(value: String): Boolean { + return (isIpv4Address(value) || isIpv6Address(value)) + } + + fun isIpv4Address(value: String): Boolean { + val regV4 = Regex("^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$") + return regV4.matches(value) + } + + @JvmStatic + fun isIpv6Address(value: String): Boolean { + var addr = value + if (addr.indexOf("[") == 0 && addr.lastIndexOf("]") > 0) { + addr = addr.drop(1) + addr = addr.dropLast(addr.count() - addr.lastIndexOf("]")) + } + val regV6 = Regex("^((?:[0-9A-Fa-f]{1,4}))?((?::[0-9A-Fa-f]{1,4}))*::((?:[0-9A-Fa-f]{1,4}))?((?::[0-9A-Fa-f]{1,4}))*|((?:[0-9A-Fa-f]{1,4}))((?::[0-9A-Fa-f]{1,4})){7}$") + return regV6.matches(addr) + } + + fun urlDecode(url: String): String { + try { + return URLDecoder.decode(url, "UTF-8") + } catch (e: Exception) { + e.printStackTrace() + return url + } + } + + fun urlEncode(url: String): String { + try { + return URLEncoder.encode(url, "UTF-8") + } catch (e: Exception) { + e.printStackTrace() + return url + } + } + + /** + * readTextFromAssets + */ + fun readTextFromAssets(fileName: String): String { + val content = ApplicationLoader.applicationContext.assets.open(fileName).bufferedReader().use { + it.readText() + } + return content + } + + /** + * ping + */ + fun ping(url: String): String { + try { + val command = "/system/bin/ping -c 3 $url" + val process = Runtime.getRuntime().exec(command) + val allText = process.inputStream.bufferedReader().use { it.readText() } + if (!TextUtils.isEmpty(allText)) { + val tempInfo = allText.substring(allText.indexOf("min/avg/max/mdev") + 19) + val temps = tempInfo.split("/".toRegex()).dropLastWhile({ it.isEmpty() }).toTypedArray() + if (temps.count() > 0 && temps[0].length < 10) { + return temps[0].toFloat().toInt().toString() + "ms" + } + } + } catch (e: Exception) { + e.printStackTrace() + } + return "-1ms" + } + + /** + * tcping + */ + fun tcping(url: String, port: Int): String { + var time = -1L + for (k in 0 until 2) { + val one = socketConnectTime(url, port) + if (one != -1L) + if (time == -1L || one < time) { + time = one + } + } + return time.toString() + "ms" + } + + fun socketConnectTime(url: String, port: Int): Long { + try { + val start = System.currentTimeMillis() + val socket = Socket(url, port) + val time = System.currentTimeMillis() - start + socket.close() + return time + } catch (e: UnknownHostException) { + e.printStackTrace() + } catch (e: IOException) { + e.printStackTrace() + } catch (e: Exception) { + e.printStackTrace() + } + return -1 + } +} + diff --git a/TMessagesProj/src/main/java/com/v2ray/ang/util/V2rayConfigUtil.kt b/TMessagesProj/src/main/java/com/v2ray/ang/util/V2rayConfigUtil.kt new file mode 100644 index 000000000..f36d10abf --- /dev/null +++ b/TMessagesProj/src/main/java/com/v2ray/ang/util/V2rayConfigUtil.kt @@ -0,0 +1,371 @@ +package com.v2ray.ang.util + +import android.text.TextUtils +import com.google.gson.Gson +import com.google.gson.GsonBuilder +import com.google.gson.JsonArray +import com.google.gson.JsonObject +import com.v2ray.ang.V2RayConfig +import com.v2ray.ang.dto.AngConfig.VmessBean +import com.v2ray.ang.dto.V2rayConfig +import org.json.JSONArray +import org.json.JSONException +import org.json.JSONObject + +object V2rayConfigUtil { + private val requestObj: JsonObject by lazy { + Gson().fromJson("""{"version":"1.1","method":"GET","path":["/"],"headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36","Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/53.0.2785.109 Mobile/14A456 Safari/601.1.46"],"Accept-Encoding":["gzip, deflate"],"Connection":["keep-alive"],"Pragma":"no-cache"}}""", JsonObject::class.java) + } + +// private val responseObj: JSONObject by lazy { +// JSONObject("""{"version":"1.1","status":"200","reason":"OK","headers":{"Content-Type":["application/octet-stream","video/mpeg"],"Transfer-Encoding":["chunked"],"Connection":["keep-alive"],"Pragma":"no-cache"}}""") +// } + + data class Result(var status: Boolean, var content: String) + + @JvmStatic + var currDomain: String = "" + + /** + * 生成v2ray的客户端配置文件 + */ + @JvmStatic + fun getV2rayConfig(vmess: VmessBean, port: Int): Result { + val result = Result(false, "") + try { + //取得默认配置 + val assets = Utils.readTextFromAssets("v2ray_config.json") + if (TextUtils.isEmpty(assets)) { + return result + } + + //转成Json + val v2rayConfig = Gson().fromJson(assets, V2rayConfig::class.java) ?: return result +// if (v2rayConfig == null) { +// return result +// } + + inbounds(vmess, v2rayConfig, port) + + outbounds(vmess, v2rayConfig) + + routing(vmess, v2rayConfig) + + val finalConfig = GsonBuilder().setPrettyPrinting().create().toJson(v2rayConfig) + + result.status = true + result.content = finalConfig + return result + + } catch (e: Exception) { + e.printStackTrace() + return result + } + } + + /** + * + */ + private fun inbounds(vmess: VmessBean, v2rayConfig: V2rayConfig, port: Int): Boolean { + try { + v2rayConfig.inbounds.forEach { curInbound -> + curInbound.listen = "127.0.0.1" + } + v2rayConfig.inbounds[0].port = port +// val socksPort = Utils.parseInt(app.defaultDPreference.getPrefString(SettingsActivity.PREF_SOCKS_PORT, "10808")) +// val lanconnPort = Utils.parseInt(app.defaultDPreference.getPrefString(SettingsActivity.PREF_HTTP_PORT, "")) + +// if (socksPort > 0) { +// v2rayConfig.inbounds[0].port = socksPort +// } +// if (lanconnPort > 0) { +// val httpCopy = v2rayConfig.inbounds[0].copy() +// httpCopy.port = lanconnPort +// httpCopy.protocol = "http" +// v2rayConfig.inbounds.add(httpCopy) +// } + v2rayConfig.inbounds[0].sniffing?.enabled = false + + } catch (e: Exception) { + e.printStackTrace() + return false + } + return true + } + + /** + * vmess协议服务器配置 + */ + private fun outbounds(vmess: VmessBean, v2rayConfig: V2rayConfig): Boolean { + try { + val outbound = v2rayConfig.outbounds[0] + + when (vmess.configType) { + V2RayConfig.EConfigType.Vmess -> { + outbound.settings?.servers = null + + val vnext = v2rayConfig.outbounds[0].settings?.vnext?.get(0) + vnext?.address = vmess.address + vnext?.port = vmess.port + val user = vnext?.users?.get(0) + user?.id = vmess.id + user?.alterId = vmess.alterId + user?.security = vmess.security + user?.level = 8 + + //Mux + val muxEnabled = false//app.defaultDPreference.getPrefBoolean(SettingsActivity.PREF_MUX_ENABLED, false) + outbound.mux?.enabled = muxEnabled + + //远程服务器底层传输配置 + outbound.streamSettings = boundStreamSettings(vmess) + + outbound.protocol = "vmess" + } + V2RayConfig.EConfigType.Shadowsocks -> { + outbound.settings?.vnext = null + + val server = outbound.settings?.servers?.get(0) + server?.address = vmess.address + server?.method = vmess.security + server?.ota = false + server?.password = vmess.id + server?.port = vmess.port + server?.level = 8 + + //Mux + outbound.mux?.enabled = false + + outbound.protocol = "shadowsocks" + } + else -> { + } + } + + var serverDomain: String + if (Utils.isIpv6Address(vmess.address)) { + serverDomain = String.format("[%s]:%s", vmess.address, vmess.port) + } else { + serverDomain = String.format("%s:%s", vmess.address, vmess.port) + } + currDomain = serverDomain + } catch (e: Exception) { + e.printStackTrace() + return false + } + return true + } + + /** + * 远程服务器底层传输配置 + */ + private fun boundStreamSettings(vmess: VmessBean): V2rayConfig.OutboundBean.StreamSettingsBean { + val streamSettings = V2rayConfig.OutboundBean.StreamSettingsBean("", "", null, null, null, null, null, null) + try { + //远程服务器底层传输配置 + streamSettings.network = vmess.network + streamSettings.security = vmess.streamSecurity + + //streamSettings + when (streamSettings.network) { + "kcp" -> { + val kcpsettings = V2rayConfig.OutboundBean.StreamSettingsBean.KcpsettingsBean() + kcpsettings.mtu = 1350 + kcpsettings.tti = 50 + kcpsettings.uplinkCapacity = 12 + kcpsettings.downlinkCapacity = 100 + kcpsettings.congestion = false + kcpsettings.readBufferSize = 1 + kcpsettings.writeBufferSize = 1 + kcpsettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.KcpsettingsBean.HeaderBean() + kcpsettings.header.type = vmess.headerType + streamSettings.kcpsettings = kcpsettings + } + "ws" -> { + val wssettings = V2rayConfig.OutboundBean.StreamSettingsBean.WssettingsBean() + wssettings.connectionReuse = true + val host = vmess.requestHost.trim() + val path = vmess.path.trim() + + if (!TextUtils.isEmpty(host)) { + wssettings.headers = V2rayConfig.OutboundBean.StreamSettingsBean.WssettingsBean.HeadersBean() + wssettings.headers.Host = host + } + if (!TextUtils.isEmpty(path)) { + wssettings.path = path + } + streamSettings.wssettings = wssettings + + val tlssettings = V2rayConfig.OutboundBean.StreamSettingsBean.TlssettingsBean() + tlssettings.allowInsecure = true + if (!TextUtils.isEmpty(host)) { + tlssettings.serverName = host + } + streamSettings.tlssettings = tlssettings + } + "h2" -> { + val httpsettings = V2rayConfig.OutboundBean.StreamSettingsBean.HttpsettingsBean() + val host = vmess.requestHost.trim() + val path = vmess.path.trim() + + if (!TextUtils.isEmpty(host)) { + httpsettings.host = host.split(",").map { it.trim() } + } + httpsettings.path = path + streamSettings.httpsettings = httpsettings + + val tlssettings = V2rayConfig.OutboundBean.StreamSettingsBean.TlssettingsBean() + tlssettings.allowInsecure = true + streamSettings.tlssettings = tlssettings + } + "quic" -> { + val quicsettings = V2rayConfig.OutboundBean.StreamSettingsBean.QuicsettingBean() + val host = vmess.requestHost.trim() + val path = vmess.path.trim() + + quicsettings.security = host + quicsettings.key = path + + quicsettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.QuicsettingBean.HeaderBean() + quicsettings.header.type = vmess.headerType + + streamSettings.quicsettings = quicsettings + } + else -> { + //tcp带http伪装 + if (vmess.headerType == "http") { + val tcpSettings = V2rayConfig.OutboundBean.StreamSettingsBean.TcpsettingsBean() + tcpSettings.connectionReuse = true + tcpSettings.header = V2rayConfig.OutboundBean.StreamSettingsBean.TcpsettingsBean.HeaderBean() + tcpSettings.header.type = vmess.headerType + +// if (requestObj.has("headers") +// || requestObj.optJSONObject("headers").has("Pragma")) { +// val arrHost = ArrayList() +// vmess.requestHost +// .split(",") +// .forEach { +// arrHost.add(it) +// } +// requestObj.optJSONObject("headers") +// .put("Host", arrHost) +// +// } + if (!TextUtils.isEmpty(vmess.requestHost)) { + val arrHost = ArrayList() + vmess.requestHost + .split(",") + .forEach { + arrHost.add("\"$it\"") + } + requestObj.getAsJsonObject("headers") + .add("Host", Gson().fromJson(arrHost.toString(), JsonArray::class.java)) + } + if (!TextUtils.isEmpty(vmess.path)) { + val arrPath = ArrayList() + vmess.path + .split(",") + .forEach { + arrPath.add("\"$it\"") + } + requestObj.add("path", Gson().fromJson(arrPath.toString(), JsonArray::class.java)) + } + tcpSettings.header.request = requestObj + //tcpSettings.header.response = responseObj + streamSettings.tcpSettings = tcpSettings + } + } + } + } catch (e: Exception) { + e.printStackTrace() + return streamSettings + } + return streamSettings + } + + /** + * routing + */ + private fun routing(vmess: VmessBean, v2rayConfig: V2rayConfig) { + + v2rayConfig.routing.domainStrategy = "IPIfNonMatch" + + } + + /** + * is valid config + */ + fun isValidConfig(conf: String): Boolean { + try { + val jObj = JSONObject(conf) + var hasBound = false + //hasBound = (jObj.has("outbounds") and jObj.has("inbounds")) or (jObj.has("outbound") and jObj.has("inbound")) + hasBound = (jObj.has("outbounds")) or (jObj.has("outbound")) + return hasBound + } catch (e: JSONException) { + return false + } + } + + private fun parseDomainName(jsonConfig: String): String { + try { + val jObj = JSONObject(jsonConfig) + var domainName: String + if (jObj.has("outbound")) { + domainName = parseDomainName(jObj.optJSONObject("outbound")) + if (!TextUtils.isEmpty(domainName)) { + return domainName + } + } + if (jObj.has("outbounds")) { + for (i in 0..(jObj.optJSONArray("outbounds").length() - 1)) { + domainName = parseDomainName(jObj.optJSONArray("outbounds").getJSONObject(i)) + if (!TextUtils.isEmpty(domainName)) { + return domainName + } + } + } + if (jObj.has("outboundDetour")) { + for (i in 0..(jObj.optJSONArray("outboundDetour").length() - 1)) { + domainName = parseDomainName(jObj.optJSONArray("outboundDetour").getJSONObject(i)) + if (!TextUtils.isEmpty(domainName)) { + return domainName + } + } + } + } catch (e: Exception) { + e.printStackTrace() + } + return "" + } + + private fun parseDomainName(outbound: JSONObject): String { + try { + if (outbound.has("settings")) { + var vnext: JSONArray? + if (outbound.optJSONObject("settings").has("vnext")) { + // vmess + vnext = outbound.optJSONObject("settings").optJSONArray("vnext") + } else if (outbound.optJSONObject("settings").has("servers")) { + // shadowsocks or socks + vnext = outbound.optJSONObject("settings").optJSONArray("servers") + } else { + return "" + } + for (i in 0..(vnext.length() - 1)) { + val item = vnext.getJSONObject(i) + val address = item.getString("address") + val port = item.getString("port") + if (Utils.isIpv6Address(address)) { + return String.format("[%s]:%s", address, port) + } else { + return String.format("%s:%s", address, port) + } + } + } + } catch (e: Exception) { + e.printStackTrace() + } + return "" + } +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/io/github/trojan_gfw/igniter/TrojanConfig.java b/TMessagesProj/src/main/java/io/github/trojan_gfw/igniter/TrojanConfig.java new file mode 100644 index 000000000..cc2cb31a6 --- /dev/null +++ b/TMessagesProj/src/main/java/io/github/trojan_gfw/igniter/TrojanConfig.java @@ -0,0 +1,265 @@ +package io.github.trojan_gfw.igniter; + +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; + +import org.json.JSONArray; +import org.json.JSONObject; + +public class TrojanConfig implements Parcelable { + + private String localAddr; + private int localPort; + private String remoteAddr; + private int remotePort; + private String password; + private boolean verifyCert; + private String caCertPath; + private boolean enableIpv6; + private String cipherList; + private String tls13CipherList; + + public TrojanConfig() { + // defaults + this.localAddr = "127.0.0.1"; + this.localPort = 1081; + this.remotePort = 443; + this.verifyCert = true; + this.cipherList = "ECDHE-ECDSA-AES128-GCM-SHA256:" + + "ECDHE-RSA-AES128-GCM-SHA256:" + + "ECDHE-ECDSA-CHACHA20-POLY1305:" + + "ECDHE-RSA-CHACHA20-POLY1305:" + + "ECDHE-ECDSA-AES256-GCM-SHA384:" + + "ECDHE-RSA-AES256-GCM-SHA384:" + + "ECDHE-ECDSA-AES256-SHA:" + + "ECDHE-ECDSA-AES128-SHA:" + + "ECDHE-RSA-AES128-SHA:" + + "ECDHE-RSA-AES256-SHA:" + + "DHE-RSA-AES128-SHA:" + + "DHE-RSA-AES256-SHA:" + + "AES128-SHA:" + + "AES256-SHA:" + + "DES-CBC3-SHA"; + this.tls13CipherList = "TLS_AES_128_GCM_SHA256:" + + "TLS_CHACHA20_POLY1305_SHA256:" + + "TLS_AES_256_GCM_SHA384"; + } + + protected TrojanConfig(Parcel in) { + localAddr = in.readString(); + localPort = in.readInt(); + remoteAddr = in.readString(); + remotePort = in.readInt(); + password = in.readString(); + verifyCert = in.readByte() != 0; + caCertPath = in.readString(); + enableIpv6 = in.readByte() != 0; + cipherList = in.readString(); + tls13CipherList = in.readString(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public TrojanConfig createFromParcel(Parcel in) { + return new TrojanConfig(in); + } + + @Override + public TrojanConfig[] newArray(int size) { + return new TrojanConfig[size]; + } + }; + + public String generateTrojanConfigJSON() { + try { + return new JSONObject() + .put("local_addr", this.localAddr) + .put("local_port", this.localPort) + .put("remote_addr", this.remoteAddr) + .put("remote_port", this.remotePort) + .put("password", new JSONArray().put(password)) + .put("log_level", 2) // WARN + .put("ssl", new JSONObject() + .put("verify", this.verifyCert) + .put("cert", this.caCertPath) + .put("cipher", this.cipherList) + .put("cipher_tls13", this.tls13CipherList) + .put("alpn", new JSONArray().put("h2").put("http/1.1"))) + .put("enable_ipv6", this.enableIpv6) + .toString(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + public void fromJSON(String jsonStr) { + try { + JSONObject json = new JSONObject(jsonStr); + this.setLocalAddr(json.getString("local_addr")) + .setLocalPort(json.getInt("local_port")) + .setRemoteAddr(json.getString("remote_addr")) + .setRemotePort(json.getInt("remote_port")) + .setPassword(json.getJSONArray("password").getString(0)) + .setEnableIpv6(json.getBoolean("enable_ipv6")) + .setVerifyCert(json.getJSONObject("ssl").getBoolean("verify")); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void copyFrom(TrojanConfig that) { + this + .setLocalAddr(that.localAddr) + .setLocalPort(that.localPort) + .setRemoteAddr(that.remoteAddr) + .setRemotePort(that.remotePort) + .setPassword(that.password) + .setEnableIpv6(that.enableIpv6) + .setVerifyCert(that.verifyCert) + .setCaCertPath(that.caCertPath) + .setCipherList(that.cipherList) + .setTls13CipherList(that.tls13CipherList); + + } + + public boolean isValidRunningConfig() { + return !TextUtils.isEmpty(this.caCertPath) + && !TextUtils.isEmpty(this.remoteAddr) + && !TextUtils.isEmpty(this.password); + } + + public String getLocalAddr() { + return localAddr; + } + + public TrojanConfig setLocalAddr(String localAddr) { + this.localAddr = localAddr; + return this; + } + + public int getLocalPort() { + return localPort; + } + + public TrojanConfig setLocalPort(int localPort) { + this.localPort = localPort; + return this; + } + + public String getRemoteAddr() { + return remoteAddr; + } + + public TrojanConfig setRemoteAddr(String remoteAddr) { + this.remoteAddr = remoteAddr; + return this; + } + + public int getRemotePort() { + return remotePort; + } + + public TrojanConfig setRemotePort(int remotePort) { + this.remotePort = remotePort; + return this; + } + + public String getPassword() { + return password; + } + + public TrojanConfig setPassword(String password) { + this.password = password; + return this; + } + + public boolean getVerifyCert() { + return verifyCert; + } + + public TrojanConfig setVerifyCert(boolean verifyCert) { + this.verifyCert = verifyCert; + return this; + } + + public String getCaCertPath() { + return caCertPath; + } + + public TrojanConfig setCaCertPath(String caCertPath) { + this.caCertPath = caCertPath; + return this; + } + + public boolean getEnableIpv6() { + return enableIpv6; + } + + public TrojanConfig setEnableIpv6(boolean enableIpv6) { + this.enableIpv6 = enableIpv6; + return this; + } + + public String getCipherList() { + return cipherList; + } + + public TrojanConfig setCipherList(String cipherList) { + this.cipherList = cipherList; + return this; + } + + public String getTls13CipherList() { + return tls13CipherList; + } + + public TrojanConfig setTls13CipherList(String tls13CipherList) { + this.tls13CipherList = tls13CipherList; + return this; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof TrojanConfig)) { + return false; + } + TrojanConfig that = (TrojanConfig) obj; + return (paramEquals(remoteAddr, that.remoteAddr) && paramEquals(remotePort, that.remotePort) + && paramEquals(localAddr, that.localAddr) && paramEquals(localPort, that.localPort)) + && paramEquals(password, that.password) && paramEquals(verifyCert, that.verifyCert) + && paramEquals(caCertPath, that.caCertPath) && paramEquals(enableIpv6, that.enableIpv6) + && paramEquals(cipherList, that.cipherList) && paramEquals(tls13CipherList, that.tls13CipherList); + } + + private static boolean paramEquals(Object a, Object b) { + if (a == b) { + return true; + } + if (a == null || b == null) { + return false; + } + return a.equals(b); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(localAddr); + dest.writeInt(localPort); + dest.writeString(remoteAddr); + dest.writeInt(remotePort); + dest.writeString(password); + dest.writeByte((byte) (verifyCert ? 1 : 0)); + dest.writeString(caCertPath); + dest.writeByte((byte) (enableIpv6 ? 1 : 0)); + dest.writeString(cipherList); + dest.writeString(tls13CipherList); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java b/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java index e60a1a707..c5695c694 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java @@ -19,7 +19,6 @@ import android.content.ContentUris; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.SharedPreferences; import android.content.pm.ActivityInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -48,10 +47,6 @@ import android.provider.CallLog; import android.provider.DocumentsContract; import android.provider.MediaStore; import android.provider.Settings; - -import androidx.core.content.FileProvider; -import androidx.viewpager.widget.ViewPager; - import android.telephony.TelephonyManager; import android.text.Selection; import android.text.Spannable; @@ -67,8 +62,8 @@ import android.util.DisplayMetrics; import android.util.StateSet; import android.util.TypedValue; import android.view.Display; -import android.view.MotionEvent; import android.view.Gravity; +import android.view.MotionEvent; import android.view.Surface; import android.view.View; import android.view.ViewGroup; @@ -89,10 +84,14 @@ import android.widget.ListView; import android.widget.ScrollView; import android.widget.TextView; +import androidx.core.content.FileProvider; +import androidx.viewpager.widget.ViewPager; + import com.android.internal.telephony.ITelephony; import org.telegram.PhoneFormat.PhoneFormat; import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.RequestTimeDelegate; import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.AlertDialog; @@ -134,10 +133,18 @@ import java.util.HashSet; import java.util.Hashtable; import java.util.List; import java.util.Locale; +import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Matcher; import java.util.regex.Pattern; +import kotlin.text.StringsKt; import tw.nekomimi.nekogram.NekoConfig; +import tw.nekomimi.nekogram.utils.AlertUtil; + +import static com.v2ray.ang.V2RayConfig.SSR_PROTOCOL; +import static com.v2ray.ang.V2RayConfig.SS_PROTOCOL; +import static com.v2ray.ang.V2RayConfig.VMESS1_PROTOCOL; +import static com.v2ray.ang.V2RayConfig.VMESS_PROTOCOL; public class AndroidUtilities { @@ -296,9 +303,9 @@ public class AndroidUtilities { if ((mask & Linkify.PHONE_NUMBERS) != 0) { Linkify.addLinks(text, Linkify.PHONE_NUMBERS); } - if ((mask & Linkify.WEB_URLS) != 0) { - gatherLinks(links, text, LinkifyPort.WEB_URL, new String[]{"http://", "https://", "ton://", "tg://"}, sUrlMatchFilter); - } + //if ((mask & Linkify.WEB_URLS) != 0) { + gatherLinks(links, text, LinkifyPort.WEB_URL, new String[]{"http://", "https://", "ton://", "tg://",VMESS_PROTOCOL,VMESS1_PROTOCOL,SS_PROTOCOL,SSR_PROTOCOL}, sUrlMatchFilter); + //} pruneOverlaps(links); if (links.size() == 0) { return false; @@ -565,10 +572,10 @@ public class AndroidUtilities { if (pathString.matches(Pattern.quote(new File(ApplicationLoader.applicationContext.getCacheDir(), "voip_logs").getAbsolutePath()) + "/\\d+\\.log")) { return false; } - if (NekoConfig.saveCacheToPrivateDirectory && pathString.startsWith(new File(ApplicationLoader.applicationContext.getCacheDir(), "sdcard").getAbsolutePath())) { + if (!NekoConfig.saveCacheToSdcard && pathString.startsWith(new File(ApplicationLoader.applicationContext.getCacheDir(), "sdcard").getAbsolutePath())) { return false; } - if (NekoConfig.saveCacheToPrivateDirectory && pathString.startsWith(new File(ApplicationLoader.applicationContext.getFilesDir(), "Telegram").getAbsolutePath())) { + if (!NekoConfig.saveCacheToSdcard && pathString.startsWith(new File(ApplicationLoader.applicationContext.getFilesDir(), "Telegram").getAbsolutePath())) { return false; } int tries = 0; @@ -1067,12 +1074,12 @@ public class AndroidUtilities { public static Typeface getTypeface(String assetPath) { synchronized (typefaceCache) { - if (NekoConfig.typeface == 1) { + if (NekoConfig.typeface == 1 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (assetPath.contains("medium") && assetPath.contains("italic")) { - return Typeface.create(Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP ? "sans-serif-medium" : "sans-serif", Typeface.ITALIC); + return Typeface.create("sans-serif-medium", Typeface.ITALIC); } if (assetPath.contains("medium")) { - return Typeface.create(Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP ? "sans-serif-medium" : "sans-serif", Typeface.NORMAL); + return Typeface.create("sans-serif-medium", Typeface.NORMAL); } if (assetPath.contains("italic")) { return Typeface.create((Typeface) null, Typeface.ITALIC); @@ -1264,7 +1271,7 @@ public class AndroidUtilities { } catch (Exception e) { FileLog.e(e); } - if (!NekoConfig.saveCacheToPrivateDirectory && (state == null || state.startsWith(Environment.MEDIA_MOUNTED))) { + if (NekoConfig.saveCacheToSdcard && (state == null || state.startsWith(Environment.MEDIA_MOUNTED))) { try { File file = ApplicationLoader.applicationContext.getExternalCacheDir(); if (file != null) { @@ -1277,7 +1284,7 @@ public class AndroidUtilities { try { File file = ApplicationLoader.applicationContext.getCacheDir(); if (file != null) { - if(NekoConfig.saveCacheToPrivateDirectory) { + if (!NekoConfig.saveCacheToSdcard) { file = new File(file, "sdcard"); file.mkdirs(); } @@ -1610,8 +1617,7 @@ public class AndroidUtilities { Rect insets = (Rect) mStableInsetsField.get(mAttachInfo); return insets.bottom; } - } catch (Exception e) { - FileLog.e(e); + } catch (Exception ignored) { } return 0; } @@ -2276,8 +2282,7 @@ public class AndroidUtilities { return String.format(Locale.US, "%d:%02d:%02d / %02d:%02d", ph, pm, ps, m, s); } else if (ph == 0) { return String.format(Locale.US, "%02d:%02d / %d:%02d:%02d", pm, ps, h, m, s); - } - else { + } else { return String.format(Locale.US, "%d:%02d:%02d / %d:%02d:%02d", ph, pm, ps, h, m, s); } } @@ -2643,6 +2648,27 @@ public class AndroidUtilities { password = data.getQueryParameter("pass"); secret = data.getQueryParameter("secret"); } + } else if (scheme.equals("vmess")) { + try { + showVmessAlert(activity, new SharedConfig.VmessProxy(data.toString())); + } catch (Exception ex) { + AlertUtil.showToast(LocaleController.getString("BrokenLink",R.string.BrokenLink)); + } + return true; + } else if (scheme.equals("ss")) { + try { + showShadowsocksAlert(activity, new SharedConfig.ShadowsocksProxy(data.toString())); + } catch (Exception ex) { + AlertUtil.showToast(LocaleController.getString("BrokenLink",R.string.BrokenLink)); + } + return true; + } else if (scheme.equals("ssr")) { + try { + showShadowsocksRAlert(activity, new SharedConfig.ShadowsocksRProxy(data.toString())); + } catch (Exception ex) { + AlertUtil.showToast(LocaleController.getString("BrokenLink",R.string.BrokenLink)); + } + return true; } } if (!TextUtils.isEmpty(address) && !TextUtils.isEmpty(port)) { @@ -2680,7 +2706,7 @@ public class AndroidUtilities { return true; } - public static void showProxyAlert(Activity activity, final String address, final String port, final String user, final String password, final String secret) { + public static void showProxyAlert(Context activity, final String address, final String port, final String user, final String password, final String secret) { BottomSheet.Builder builder = new BottomSheet.Builder(activity); final Runnable dismissRunnable = builder.getDismissRunnable(); @@ -2758,43 +2784,330 @@ public class AndroidUtilities { pickerBottomLayout.doneButtonBadgeTextView.setVisibility(View.GONE); pickerBottomLayout.doneButtonTextView.setText(LocaleController.getString("ConnectingConnectProxy", R.string.ConnectingConnectProxy).toUpperCase()); pickerBottomLayout.doneButton.setOnClickListener(v -> { - SharedPreferences.Editor editor = MessagesController.getGlobalMainSettings().edit(); - editor.putBoolean("proxy_enabled", true); - editor.putString("proxy_ip", address); int p = Utilities.parseInt(port); - editor.putInt("proxy_port", p); SharedConfig.ProxyInfo info; + if (TextUtils.isEmpty(secret)) { - editor.remove("proxy_secret"); - if (TextUtils.isEmpty(password)) { - editor.remove("proxy_pass"); - } else { - editor.putString("proxy_pass", password); - } - if (TextUtils.isEmpty(user)) { - editor.remove("proxy_user"); - } else { - editor.putString("proxy_user", user); - } + info = new SharedConfig.ProxyInfo(address, p, user, password, ""); + } else { - editor.remove("proxy_pass"); - editor.remove("proxy_user"); - editor.putString("proxy_secret", secret); + info = new SharedConfig.ProxyInfo(address, p, "", "", secret); + } - editor.commit(); - SharedConfig.currentProxy = SharedConfig.addProxy(info); + SharedConfig.setCurrentProxy(SharedConfig.addProxy(info)); - ConnectionsManager.setProxySettings(true, address, p, user, password, secret); NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.proxySettingsChanged); + dismissRunnable.run(); }); builder.show(); } + public static void showVmessAlert(Context activity, final SharedConfig.VmessProxy info) { + BottomSheet.Builder builder = new BottomSheet.Builder(activity); + final Runnable dismissRunnable = builder.getDismissRunnable(); + + builder.setApplyTopPadding(false); + builder.setApplyBottomPadding(false); + LinearLayout linearLayout = new LinearLayout(activity); + builder.setCustomView(linearLayout); + linearLayout.setOrientation(LinearLayout.VERTICAL); + for (int a = 0; a < 8; a++) { + String text = null; + String detail = null; + if (a == 0) { + text = info.bean.getAddress(); + detail = LocaleController.getString("UseProxyAddress", R.string.UseProxyAddress); + } else if (a == 1) { + text = "" + info.bean.getPort(); + detail = LocaleController.getString("UseProxyPort", R.string.UseProxyPort); + } else if (a == 2) { + text = info.bean.getId(); + detail = LocaleController.getString("VmessUserId", R.string.VmessUserId); + } else if (a == 3) { + text = info.bean.getSecurity(); + if ("none".equals(text)) continue; + detail = LocaleController.getString("VmessSecurity", R.string.VmessSecurity); + } else if (a == 4) { + text = info.bean.getNetwork() + (StringsKt.isBlank(info.bean.getStreamSecurity()) ? "" : ", tls"); + detail = LocaleController.getString("VmessNetwork", R.string.VmessNetwork); + } else if (a == 5) { + text = info.bean.getHeaderType(); + if ("none".equals(text)) continue; + detail = LocaleController.getString("VmessHeadType", R.string.VmessHeadType); + } else if (a == 6) { + text =info.bean.getRequestHost(); + detail = LocaleController.getString("VmessRequestHost", R.string.VmessRequestHost); + } else { + text = LocaleController.getString("Checking", R.string.Checking); + detail = LocaleController.getString("Checking", R.string.Checking); + } + if (TextUtils.isEmpty(text)) { + continue; + } + TextDetailSettingsCell cell = new TextDetailSettingsCell(activity); + cell.setTextAndValue(text, detail, true); + cell.getTextView().setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + cell.getValueTextView().setTextColor(Theme.getColor(Theme.key_dialogTextGray3)); + linearLayout.addView(cell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + AtomicInteger count = new AtomicInteger(); + if (a == 7) { + info.start(); + + RequestTimeDelegate callback = new RequestTimeDelegate() { + @Override + public void run(long time) { + int c = count.getAndIncrement(); + String colorKey; + if (time != -1) { + info.stop(); + cell.setTextAndValue(LocaleController.getString("Available", R.string.Available), LocaleController.formatString("Ping", R.string.Ping, time), true); + colorKey = Theme.key_windowBackgroundWhiteGreenText; + } else if (c < 2) { + ConnectionsManager.getInstance(UserConfig.selectedAccount).checkProxy(info.address, info.port, "", "", "", t -> AndroidUtilities.runOnUIThread(() -> run(t),500)); + colorKey = Theme.key_windowBackgroundWhiteGreenText; + } else { + info.stop(); + cell.setTextAndValue(LocaleController.getString("Unavailable", R.string.Unavailable), LocaleController.getString("Unavailable", R.string.Unavailable), true); + colorKey = Theme.key_windowBackgroundWhiteRedText4; + } + cell.getValueTextView().setTextColor(Theme.getColor(colorKey)); + } + + }; + + ConnectionsManager.getInstance(UserConfig.selectedAccount).checkProxy(info.address, info.port, "","","",time ->AndroidUtilities.runOnUIThread(()->callback.run(time))); + + } + } + + PickerBottomLayout pickerBottomLayout = new PickerBottomLayout(activity, false); + pickerBottomLayout.setBackgroundColor(Theme.getColor(Theme.key_dialogBackground)); + linearLayout.addView(pickerBottomLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.BOTTOM)); + pickerBottomLayout.cancelButton.setPadding(AndroidUtilities.dp(18), 0, AndroidUtilities.dp(18), 0); + pickerBottomLayout.cancelButton.setTextColor(Theme.getColor(Theme.key_dialogTextBlue2)); + pickerBottomLayout.cancelButton.setText(LocaleController.getString("Cancel", R.string.Cancel).toUpperCase()); + pickerBottomLayout.cancelButton.setOnClickListener(view -> { + info.stop(); + dismissRunnable.run(); + }); + pickerBottomLayout.doneButtonTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlue2)); + pickerBottomLayout.doneButton.setPadding(AndroidUtilities.dp(18), 0, AndroidUtilities.dp(18), 0); + pickerBottomLayout.doneButtonBadgeTextView.setVisibility(View.GONE); + pickerBottomLayout.doneButtonTextView.setText(LocaleController.getString("ConnectingConnectProxy", R.string.ConnectingConnectProxy).toUpperCase()); + pickerBottomLayout.doneButton.setOnClickListener(v -> { + + SharedConfig.setCurrentProxy(SharedConfig.addProxy(info)); + + NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.proxySettingsChanged); + + dismissRunnable.run(); + + }); + builder.show(); + } + + public static void showShadowsocksAlert(Context activity, final SharedConfig.ShadowsocksProxy info) { + BottomSheet.Builder builder = new BottomSheet.Builder(activity); + final Runnable dismissRunnable = builder.getDismissRunnable(); + + builder.setApplyTopPadding(false); + builder.setApplyBottomPadding(false); + LinearLayout linearLayout = new LinearLayout(activity); + builder.setCustomView(linearLayout); + linearLayout.setOrientation(LinearLayout.VERTICAL); + for (int a = 0; a < 5; a++) { + String text = null; + String detail = null; + if (a == 0) { + text = info.bean.getHost(); + detail = LocaleController.getString("UseProxyAddress", R.string.UseProxyAddress); + } else if (a == 1) { + text = "" + info.bean.getRemotePort(); + detail = LocaleController.getString("UseProxyPort", R.string.UseProxyPort); + } else if (a == 2) { + text = info.bean.getPassword(); + detail = LocaleController.getString("SSPassword", R.string.SSPassword); + } else if (a == 3) { + text = info.bean.getMethod(); + detail = LocaleController.getString("SSMethod", R.string.SSMethod); + } else { + text = LocaleController.getString("Checking", R.string.Checking); + detail = LocaleController.getString("Checking", R.string.Checking); + } + if (TextUtils.isEmpty(text)) { + continue; + } + TextDetailSettingsCell cell = new TextDetailSettingsCell(activity); + cell.setTextAndValue(text, detail, true); + cell.getTextView().setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + cell.getValueTextView().setTextColor(Theme.getColor(Theme.key_dialogTextGray3)); + linearLayout.addView(cell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + AtomicInteger count = new AtomicInteger(); + if (a == 4) { + info.start(); + RequestTimeDelegate callback = new RequestTimeDelegate() { + @Override + public void run(long time) { + int c = count.getAndIncrement(); + String colorKey; + if (time != -1) { + info.stop(); + cell.setTextAndValue(LocaleController.getString("Available", R.string.Available), LocaleController.formatString("Ping", R.string.Ping, time), true); + colorKey = Theme.key_windowBackgroundWhiteGreenText; + } else if (c < 2) { + ConnectionsManager.getInstance(UserConfig.selectedAccount).checkProxy(info.address, info.port, "", "", "", t -> AndroidUtilities.runOnUIThread(() -> run(t),500)); + colorKey = Theme.key_windowBackgroundWhiteGreenText; + } else { + info.stop(); + cell.setTextAndValue(LocaleController.getString("Unavailable", R.string.Unavailable), LocaleController.getString("Unavailable", R.string.Unavailable), true); + colorKey = Theme.key_windowBackgroundWhiteRedText4; + } + cell.getValueTextView().setTextColor(Theme.getColor(colorKey)); + } + + }; + + ConnectionsManager.getInstance(UserConfig.selectedAccount).checkProxy(info.address, info.port, "","","",time ->AndroidUtilities.runOnUIThread(()->callback.run(time))); + + } + } + + PickerBottomLayout pickerBottomLayout = new PickerBottomLayout(activity, false); + pickerBottomLayout.setBackgroundColor(Theme.getColor(Theme.key_dialogBackground)); + linearLayout.addView(pickerBottomLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.BOTTOM)); + pickerBottomLayout.cancelButton.setPadding(AndroidUtilities.dp(18), 0, AndroidUtilities.dp(18), 0); + pickerBottomLayout.cancelButton.setTextColor(Theme.getColor(Theme.key_dialogTextBlue2)); + pickerBottomLayout.cancelButton.setText(LocaleController.getString("Cancel", R.string.Cancel).toUpperCase()); + pickerBottomLayout.cancelButton.setOnClickListener(view -> { + info.stop(); + dismissRunnable.run(); + }); + pickerBottomLayout.doneButtonTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlue2)); + pickerBottomLayout.doneButton.setPadding(AndroidUtilities.dp(18), 0, AndroidUtilities.dp(18), 0); + pickerBottomLayout.doneButtonBadgeTextView.setVisibility(View.GONE); + pickerBottomLayout.doneButtonTextView.setText(LocaleController.getString("ConnectingConnectProxy", R.string.ConnectingConnectProxy).toUpperCase()); + pickerBottomLayout.doneButton.setOnClickListener(v -> { + + SharedConfig.setCurrentProxy(SharedConfig.addProxy(info)); + + NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.proxySettingsChanged); + + dismissRunnable.run(); + + }); + builder.show(); + } + + + public static void showShadowsocksRAlert(Context activity, final SharedConfig.ShadowsocksRProxy info) { + BottomSheet.Builder builder = new BottomSheet.Builder(activity); + final Runnable dismissRunnable = builder.getDismissRunnable(); + + builder.setApplyTopPadding(false); + builder.setApplyBottomPadding(false); + LinearLayout linearLayout = new LinearLayout(activity); + builder.setCustomView(linearLayout); + linearLayout.setOrientation(LinearLayout.VERTICAL); + for (int a = 0; a < 7; a++) { + String text = null; + String detail = null; + if (a == 0) { + text = info.bean.getHost(); + detail = LocaleController.getString("UseProxyAddress", R.string.UseProxyAddress); + } else if (a == 1) { + text = "" + info.bean.getRemotePort(); + detail = LocaleController.getString("UseProxyPort", R.string.UseProxyPort); + } else if (a == 2) { + text = info.bean.getPassword(); + detail = LocaleController.getString("SSPassword", R.string.SSPassword); + } else if (a == 3) { + text = info.bean.getMethod(); + detail = LocaleController.getString("SSMethod", R.string.SSMethod); + } else if (a == 4) { + text = info.bean.getProtocol(); + if (!StringsKt.isBlank(info.bean.getProtocol_param())) { + text += ", " + info.bean.getProtocol_param(); + } + detail = LocaleController.getString("SSRProtocol", R.string.SSRProtocol); + } else if (a == 5) { + text = info.bean.getObfs(); + if (!StringsKt.isBlank(info.bean.getObfs_param())) { + text += ", " + info.bean.getObfs_param(); + } + detail = LocaleController.getString("SSRObfs", R.string.SSRObfs); + } else { + text = LocaleController.getString("Checking", R.string.Checking); + detail = LocaleController.getString("Checking", R.string.Checking); + } + if (TextUtils.isEmpty(text)) { + continue; + } + TextDetailSettingsCell cell = new TextDetailSettingsCell(activity); + cell.setTextAndValue(text, detail, true); + cell.getTextView().setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + cell.getValueTextView().setTextColor(Theme.getColor(Theme.key_dialogTextGray3)); + linearLayout.addView(cell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + AtomicInteger count = new AtomicInteger(); + if (a == 6) { + info.start(); + RequestTimeDelegate callback = new RequestTimeDelegate() { + @Override + public void run(long time) { + int c = count.getAndIncrement(); + String colorKey; + if (time != -1) { + info.stop(); + cell.setTextAndValue(LocaleController.getString("Available", R.string.Available), LocaleController.formatString("Ping", R.string.Ping, time), true); + colorKey = Theme.key_windowBackgroundWhiteGreenText; + } else if (c < 2) { + ConnectionsManager.getInstance(UserConfig.selectedAccount).checkProxy(info.address, info.port, "", "", "", t -> AndroidUtilities.runOnUIThread(() -> run(t),500)); + colorKey = Theme.key_windowBackgroundWhiteGreenText; + } else { + info.stop(); + cell.setTextAndValue(LocaleController.getString("Unavailable", R.string.Unavailable), LocaleController.getString("Unavailable", R.string.Unavailable), true); + colorKey = Theme.key_windowBackgroundWhiteRedText4; + } + cell.getValueTextView().setTextColor(Theme.getColor(colorKey)); + } + + }; + + ConnectionsManager.getInstance(UserConfig.selectedAccount).checkProxy(info.address, info.port, "","","",time ->AndroidUtilities.runOnUIThread(()->callback.run(time))); + + } + } + + PickerBottomLayout pickerBottomLayout = new PickerBottomLayout(activity, false); + pickerBottomLayout.setBackgroundColor(Theme.getColor(Theme.key_dialogBackground)); + linearLayout.addView(pickerBottomLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.BOTTOM)); + pickerBottomLayout.cancelButton.setPadding(AndroidUtilities.dp(18), 0, AndroidUtilities.dp(18), 0); + pickerBottomLayout.cancelButton.setTextColor(Theme.getColor(Theme.key_dialogTextBlue2)); + pickerBottomLayout.cancelButton.setText(LocaleController.getString("Cancel", R.string.Cancel).toUpperCase()); + pickerBottomLayout.cancelButton.setOnClickListener(view -> { + info.stop(); + dismissRunnable.run(); + }); + pickerBottomLayout.doneButtonTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlue2)); + pickerBottomLayout.doneButton.setPadding(AndroidUtilities.dp(18), 0, AndroidUtilities.dp(18), 0); + pickerBottomLayout.doneButtonBadgeTextView.setVisibility(View.GONE); + pickerBottomLayout.doneButtonTextView.setText(LocaleController.getString("ConnectingConnectProxy", R.string.ConnectingConnectProxy).toUpperCase()); + pickerBottomLayout.doneButton.setOnClickListener(v -> { + + SharedConfig.setCurrentProxy(SharedConfig.addProxy(info)); + + NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.proxySettingsChanged); + + dismissRunnable.run(); + + }); + builder.show(); + } + @SuppressLint("PrivateApi") public static String getSystemProperty(String key) { try { @@ -2950,7 +3263,7 @@ public class AndroidUtilities { hsb[1] = Math.min(1.0f, hsb[1] + 0.05f); if (hsb[2] > 0.5f) { hsb[2] = Math.max(0.0f, hsb[2] * 0.90f); - } else{ + } else { hsb[2] = Math.max(0.0f, hsb[2] * 0.90f); } return HSBtoRGB(hsb[0], hsb[1], hsb[2]) | 0xff000000; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java index 476d95ad2..86de0df45 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java @@ -33,7 +33,11 @@ import org.telegram.ui.Components.ForegroundDetector; import java.io.File; +import tw.nekomimi.nekogram.ExternalGcm; import tw.nekomimi.nekogram.NekoConfig; +import tw.nekomimi.nekogram.NekoXConfig; +import tw.nekomimi.nekogram.utils.ProxyUtil; +import tw.nekomimi.nekogram.utils.ZipUtil; public class ApplicationLoader extends Application { @@ -69,7 +73,7 @@ public class ApplicationLoader extends Application { } catch (Exception e) { FileLog.e(e); } - return new File("/data/data/org.telegram.messenger/files"); + return new File("/data/data/" + BuildConfig.APPLICATION_ID + "/files"); } public static void postInitApplication() { @@ -79,6 +83,20 @@ public class ApplicationLoader extends Application { applicationInited = true; + if (!new File(applicationContext.getFilesDir(), "unoffical_base_classic_zh_cn.xml").isFile()) { + + try { + + ZipUtil.unzip(applicationContext.getAssets().open("languages.zip"), applicationContext.getFilesDir()); + + } catch (Exception e) { + + FileLog.e("load languages error", e); + + } + + } + try { LocaleController.getInstance(); } catch (Exception e) { @@ -145,7 +163,30 @@ public class ApplicationLoader extends Application { } } + if (ProxyUtil.isVPNEnabled()) { + + if (NekoXConfig.disableProxyWhenVpnEnabled) { + + SharedConfig.setProxyEnable(false); + + } + + } else if (MessagesController.getGlobalMainSettings().getBoolean("first_open", true)) { + + MessagesController.getGlobalMainSettings().edit().putBoolean("first_open", false).apply(); + + if (!UserConfig.getInstance(0).isClientActivated()) { + + SharedConfig.setCurrentProxy(SharedConfig.proxyList.get(0)); + + } + + } + ApplicationLoader app = (ApplicationLoader) ApplicationLoader.applicationContext; + if (ExternalGcm.INSTANCE != null) { + ExternalGcm.INSTANCE.initPlayServices(); + } if (BuildVars.LOGS_ENABLED) { FileLog.d("app initied"); } @@ -185,6 +226,9 @@ public class ApplicationLoader extends Application { } public static void startPushService() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + return; // USE NOTIF LISTENER + } SharedPreferences preferences = MessagesController.getGlobalNotificationsSettings(); boolean enabled; if (preferences.contains("pushService")) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java b/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java index cd5d7459a..4ec1f3cff 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java @@ -10,6 +10,8 @@ package org.telegram.messenger; import android.content.Context; import android.content.SharedPreferences; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; public class BuildVars { @@ -17,21 +19,29 @@ public class BuildVars { public static boolean DEBUG_PRIVATE_VERSION = false; public static boolean LOGS_ENABLED = false; public static boolean USE_CLOUD_STRINGS = true; - public static boolean CHECK_UPDATES = true; - public static boolean TON_WALLET_STANDALONE = false; - public static int BUILD_VERSION = 1875; - public static String BUILD_VERSION_STRING = "5.15.0.3"; - public static int APP_ID = 336779; //obtain your own APP_ID at https://core.telegram.org/api/obtaining_api_id - public static String APP_HASH = "b91eefacc86747c068c8d8a16b41500d"; //obtain your own APP_HASH at https://core.telegram.org/api/obtaining_api_id - public static String HOCKEY_APP_HASH = "your-hockeyapp-api-key-here"; - public static String HOCKEY_APP_HASH_DEBUG = "your-hockeyapp-api-key-here"; - public static String SMS_HASH = ""; //https://developers.google.com/identity/sms-retriever/overview - public static String PLAYSTORE_APP_URL = "https://play.google.com/store/apps/details?id=tw.nekomimi.nekogram"; + + public static int BUILD_VERSION; + public static String BUILD_VERSION_STRING; + + static { + + try { + PackageInfo info = ApplicationLoader.applicationContext.getPackageManager().getPackageInfo(ApplicationLoader.applicationContext.getPackageName(), 0); + BUILD_VERSION = info.versionCode; + BUILD_VERSION_STRING = info.packageName; + } catch (PackageManager.NameNotFoundException e) { + BUILD_VERSION = BuildConfig.VERSION_CODE; + BUILD_VERSION_STRING = BuildConfig.VERSION_NAME; + } + + } static { if (ApplicationLoader.applicationContext != null) { SharedPreferences sharedPreferences = ApplicationLoader.applicationContext.getSharedPreferences("systemConfig", Context.MODE_PRIVATE); LOGS_ENABLED = sharedPreferences.getBoolean("logsEnabled", DEBUG_VERSION); + DEBUG_VERSION = LOGS_ENABLED; + DEBUG_PRIVATE_VERSION = LOGS_ENABLED; } } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java b/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java index 37a6d93f1..f029ed890 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java @@ -36,6 +36,8 @@ import java.util.Collections; import java.util.HashMap; import java.util.concurrent.ConcurrentHashMap; +import tw.nekomimi.nekogram.NekoXConfig; + public class ContactsController extends BaseController { private Account systemAccount; @@ -309,9 +311,12 @@ public class ContactsController extends BaseController { } public void checkAppAccount() { + if (NekoXConfig.disableSystemAccount) { + return; + } AccountManager am = AccountManager.get(ApplicationLoader.applicationContext); try { - Account[] accounts = am.getAccountsByType("tw.nekomimi.nekogram"); + Account[] accounts = am.getAccountsByType(BuildConfig.APPLICATION_ID); systemAccount = null; for (int a = 0; a < accounts.length; a++) { Account acc = accounts[a]; @@ -319,7 +324,7 @@ public class ContactsController extends BaseController { for (int b = 0; b < UserConfig.MAX_ACCOUNT_COUNT; b++) { TLRPC.User user = UserConfig.getInstance(b).getCurrentUser(); if (user != null) { - if (acc.name.equals("" + user.id)) { + if (acc.name.equals(formatName(user.first_name,user.last_name))) { if (b == currentAccount) { systemAccount = acc; } @@ -344,7 +349,8 @@ public class ContactsController extends BaseController { readContacts(); if (systemAccount == null) { try { - systemAccount = new Account("" + UserConfig.getInstance(currentAccount).getClientUserId(), "tw.nekomimi.nekogram"); + TLRPC.User user = UserConfig.getInstance(currentAccount).getCurrentUser(); + systemAccount = new Account(formatName(user.first_name,user.last_name), BuildConfig.APPLICATION_ID); am.addAccountExplicitly(systemAccount, "", null); } catch (Exception ignore) { @@ -357,25 +363,33 @@ public class ContactsController extends BaseController { try { systemAccount = null; AccountManager am = AccountManager.get(ApplicationLoader.applicationContext); - Account[] accounts = am.getAccountsByType("tw.nekomimi.nekogram"); + Account[] accounts = am.getAccountsByType(BuildConfig.APPLICATION_ID); for (int a = 0; a < accounts.length; a++) { Account acc = accounts[a]; - boolean found = false; - for (int b = 0; b < UserConfig.MAX_ACCOUNT_COUNT; b++) { - TLRPC.User user = UserConfig.getInstance(b).getCurrentUser(); - if (user != null) { - if (acc.name.equals("" + user.id)) { - found = true; - break; - } - } - } - if (!found) { + if (NekoXConfig.disableSystemAccount) { try { am.removeAccount(accounts[a], null, null); } catch (Exception ignore) { } + } else { + boolean found = false; + for (int b = 0; b < UserConfig.MAX_ACCOUNT_COUNT; b++) { + TLRPC.User user = UserConfig.getInstance(b).getCurrentUser(); + if (user != null) { + if (acc.name.equals(formatName(user.first_name, user.last_name))) { + found = true; + break; + } + } + } + if (!found) { + try { + am.removeAccount(accounts[a], null, null); + } catch (Exception ignore) { + + } + } } } } catch (Exception e) { @@ -433,7 +447,7 @@ public class ContactsController extends BaseController { AndroidUtilities.runOnUIThread(() -> { AccountManager am = AccountManager.get(ApplicationLoader.applicationContext); try { - Account[] accounts = am.getAccountsByType("tw.nekomimi.nekogram"); + Account[] accounts = am.getAccountsByType(BuildConfig.APPLICATION_ID); systemAccount = null; for (int a = 0; a < accounts.length; a++) { Account acc = accounts[a]; @@ -451,7 +465,7 @@ public class ContactsController extends BaseController { } try { - systemAccount = new Account("" + UserConfig.getInstance(currentAccount).getClientUserId(), "tw.nekomimi.nekogram"); + systemAccount = new Account("" + UserConfig.getInstance(currentAccount).getClientUserId(), BuildConfig.APPLICATION_ID); am.addAccountExplicitly(systemAccount, "", null); } catch (Exception ignore) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLog.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLog.java index d91a002fe..1cbf0fa8d 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileLog.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLog.java @@ -26,7 +26,7 @@ public class FileLog { private File tonlibFile = null; private boolean initied; - private final static String tag = "tmessages"; + private final static String tag = BuildConfig.APPLICATION_ID; private static volatile FileLog Instance = null; public static FileLog getInstance() { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ImageLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/ImageLoader.java index 7fe5381d2..54d1eb910 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ImageLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ImageLoader.java @@ -1780,9 +1780,9 @@ public class ImageLoader { } try { - if (NekoConfig.saveCacheToPrivateDirectory || Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { - if (NekoConfig.saveCacheToPrivateDirectory) { - telegramPath = new File(ApplicationLoader.applicationContext.getFilesDir(), "Telegram"); + if (!NekoConfig.saveCacheToSdcard || Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { + if (!NekoConfig.saveCacheToSdcard) { + telegramPath = ApplicationLoader.applicationContext.getExternalFilesDir("Telegram"); } else { telegramPath = new File(Environment.getExternalStorageDirectory(), "Telegram"); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/LinkifyPort.java b/TMessagesProj/src/main/java/org/telegram/messenger/LinkifyPort.java index 1646b7e68..b24b830d1 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/LinkifyPort.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/LinkifyPort.java @@ -156,12 +156,15 @@ public class LinkifyPort { private static final String DOMAIN_NAME_STR = "(" + HOST_NAME + "|" + IP_ADDRESS_STRING + ")"; private static final Pattern DOMAIN_NAME = Pattern.compile(DOMAIN_NAME_STR); private static final String PROTOCOL = "(?i:http|https|ton|tg)://"; + private static final String PROXY_PROTOCOL = "(?i:vmess|vmess1|ss|ssr)://"; private static final String WORD_BOUNDARY = "(?:\\b|$|^)"; private static final String USER_INFO = "(?:[a-zA-Z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)" + "\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,64}(?:\\:(?:[a-zA-Z0-9\\$\\-\\_" + "\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,25})?\\@"; private static final String PORT_NUMBER = "\\:\\d{1,5}"; private static final String PATH_AND_QUERY = "[/\\?](?:(?:[" + LABEL_CHAR + ";/\\?:@&=#~" + "\\-\\.\\+!\\*'\\(\\),_\\$])|(?:%[a-fA-F0-9]{2}))*"; + private static final String BASE64 = "(?:[A-Za-z0-9+\\/]{4}\\\\n?)*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=)"; + private static final String PATH_AND_QUERY_BASE64 = "[/\\?]?(?:(?:[" + LABEL_CHAR + ";/\\?:@&=#~" + "\\-\\.\\+!\\*'\\(\\),_\\$])|(?:%[a-fA-F0-9]{2})|" + BASE64 + ")*"; private static final String RELAXED_DOMAIN_NAME = "(?:" + "(?:" + IRI_LABEL + "(?:\\.(?=\\S))" + "?)+" + "|" + IP_ADDRESS_STRING + ")"; private static final String WEB_URL_WITHOUT_PROTOCOL = "(" @@ -185,11 +188,23 @@ public class LinkifyPort { + "(?:" + PATH_AND_QUERY + ")?" + WORD_BOUNDARY + ")"; + + private static final String PROXY_URL = "(" + + WORD_BOUNDARY + + "(?:" + + "(?:" + PROXY_PROTOCOL + "(?:" + USER_INFO + ")?" + ")" + + "(?:" + RELAXED_DOMAIN_NAME + ")?" + + "(?:" + PORT_NUMBER + ")?" + + ")" + + "(?:" + PATH_AND_QUERY_BASE64 + ")?" + + WORD_BOUNDARY + + ")"; + public static Pattern WEB_URL = null; static { try { - WEB_URL = Pattern.compile("(" + WEB_URL_WITH_PROTOCOL + "|" + WEB_URL_WITHOUT_PROTOCOL + ")"); + WEB_URL = Pattern.compile("(" + WEB_URL_WITH_PROTOCOL + "|" + WEB_URL_WITHOUT_PROTOCOL+ "|" + PROXY_URL + ")"); } catch (Exception e) { FileLog.e(e); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/LocaleController.java b/TMessagesProj/src/main/java/org/telegram/messenger/LocaleController.java index b85396406..16df8800f 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/LocaleController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/LocaleController.java @@ -336,23 +336,6 @@ public class LocaleController { localeInfo.builtIn = true; languages.add(localeInfo); languagesDict.put(localeInfo.getKey(), localeInfo); - languagesDict.put("zh_cn", localeInfo); - languagesDict.put("zh_sg", localeInfo); - - localeInfo = new LocaleInfo(); - localeInfo.name = "瓜體中文"; - localeInfo.nameEnglish = "Duangified Chinese (Traditional)"; - localeInfo.shortName = "duang_zh_hant"; - localeInfo.baseLangCode = "zh_hant_raw"; - localeInfo.isRtl = false; - localeInfo.pathToFile = "unofficial"; - localeInfo.pluralLangCode = "zh_tw"; - localeInfo.builtIn = true; - languages.add(localeInfo); - languagesDict.put(localeInfo.getKey(), localeInfo); - languagesDict.put("zh_tw", localeInfo); - languagesDict.put("zh_hk", localeInfo); - languagesDict.put("zh_mo", localeInfo); localeInfo = new LocaleInfo(); localeInfo.name = "简体中文"; @@ -365,6 +348,8 @@ public class LocaleController { localeInfo.builtIn = true; languages.add(localeInfo); languagesDict.put(localeInfo.getKey(), localeInfo); + languagesDict.put("zh_cn", localeInfo); + languagesDict.put("zh_sg", localeInfo); localeInfo = new LocaleInfo(); localeInfo.name = "正體中文"; @@ -377,6 +362,33 @@ public class LocaleController { localeInfo.builtIn = true; languages.add(localeInfo); languagesDict.put(localeInfo.getKey(), localeInfo); + languagesDict.put("zh_tw", localeInfo); + + localeInfo = new LocaleInfo(); + localeInfo.name = "中文 (香港)"; + localeInfo.nameEnglish = "Chinese (hongkong)"; + localeInfo.shortName = "hongkong"; + localeInfo.baseLangCode = "zh_hant_hk"; + localeInfo.isRtl = false; + localeInfo.pathToFile = "unofficial"; + localeInfo.pluralLangCode = "zh_hk"; + localeInfo.builtIn = true; + languages.add(localeInfo); + languagesDict.put(localeInfo.getKey(), localeInfo); + languagesDict.put("zh_hk", localeInfo); + languagesDict.put("zh_mo", localeInfo); + + localeInfo = new LocaleInfo(); + localeInfo.name = "香港人口語"; + localeInfo.nameEnglish = "Chinese (hongkonger)"; + localeInfo.shortName = "hongkonger"; + localeInfo.baseLangCode = "zh_hant_hk"; + localeInfo.isRtl = false; + localeInfo.pathToFile = "unofficial"; + localeInfo.pluralLangCode = "zh_hk"; + localeInfo.builtIn = true; + languages.add(localeInfo); + languagesDict.put(localeInfo.getKey(), localeInfo); localeInfo = new LocaleInfo(); localeInfo.name = "日本語"; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java index fed24ef8d..96aad13ff 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java @@ -85,6 +85,8 @@ import java.util.Locale; import java.util.Timer; import java.util.TimerTask; +import tw.nekomimi.nekogram.NekoXConfig; + public class MediaController implements AudioManager.OnAudioFocusChangeListener, NotificationCenter.NotificationCenterDelegate, SensorEventListener { private native int startRecord(String path); @@ -1077,7 +1079,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, } } - private void checkScreenshots(ArrayList dates) { + public void checkScreenshots(ArrayList dates) { if (dates == null || dates.isEmpty() || lastChatEnterTime == 0 || (lastUser == null && !(lastSecretChat instanceof TLRPC.TL_encryptedChat))) { return; } @@ -1096,7 +1098,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, } } } - if (send) { + if (send && !NekoXConfig.disableScreenshotDetection) { if (lastSecretChat != null) { SecretChatHelper.getInstance(lastChatAccount).sendScreenshotMessage(lastSecretChat, lastChatVisibleMessages, null); } else { @@ -1105,6 +1107,10 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, } } + public ArrayList getLastVisibleMessageIds() { + return lastChatVisibleMessages; + } + public void setLastVisibleMessageIds(int account, long enterTime, long leaveTime, TLRPC.User user, TLRPC.EncryptedChat encryptedChat, ArrayList visibleMessages, int visibleMessage) { lastChatEnterTime = enterTime; lastChatLeaveTime = leaveTime; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java index 6207b2474..86f6ec470 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java @@ -3133,7 +3133,7 @@ public class MessageObject { hasEntities = !messageOwner.entities.isEmpty(); } - boolean useManualParse = !hasEntities && ( + boolean useManualParse = true;/*!hasEntities && ( eventId != 0 || messageOwner.media instanceof TLRPC.TL_messageMediaPhoto_old || messageOwner.media instanceof TLRPC.TL_messageMediaPhoto_layer68 || @@ -3142,7 +3142,7 @@ public class MessageObject { messageOwner.media instanceof TLRPC.TL_messageMediaDocument_layer68 || messageOwner.media instanceof TLRPC.TL_messageMediaDocument_layer74 || isOut() && messageOwner.send_state != MESSAGE_SEND_STATE_SENT || - messageOwner.id < 0); + messageOwner.id < 0);*/ if (useManualParse) { if (containsUrls(caption)) { @@ -3686,7 +3686,7 @@ public class MessageObject { hasEntities = !messageOwner.entities.isEmpty(); } - boolean useManualParse = !hasEntities && ( + boolean useManualParse = true;/*!hasEntities && ( eventId != 0 || messageOwner instanceof TLRPC.TL_message_old || messageOwner instanceof TLRPC.TL_message_old2 || @@ -3697,7 +3697,7 @@ public class MessageObject { messageOwner instanceof TLRPC.TL_message_secret || messageOwner.media instanceof TLRPC.TL_messageMediaInvoice || isOut() && messageOwner.send_state != MESSAGE_SEND_STATE_SENT || - messageOwner.id < 0 || messageOwner.media instanceof TLRPC.TL_messageMediaUnsupported); + messageOwner.id < 0 || messageOwner.media instanceof TLRPC.TL_messageMediaUnsupported);*/ if (useManualParse) { addLinks(isOutOwner(), messageText, true, true); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java index afbd46831..5fa7b75de 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java @@ -53,8 +53,14 @@ import java.util.concurrent.CountDownLatch; import androidx.core.app.NotificationManagerCompat; +import kotlin.collections.ArraysKt; +import kotlin.concurrent.ThreadsKt; +import tw.nekomimi.nekogram.ExternalGcm; import tw.nekomimi.nekogram.FilterPopup; import tw.nekomimi.nekogram.NekoConfig; +import tw.nekomimi.nekogram.NekoXConfig; +import tw.nekomimi.nekogram.utils.ThreadUtil; +import tw.nekomimi.nekogram.utils.UIUtil; public class MessagesController extends BaseController implements NotificationCenter.NotificationCenterDelegate { @@ -324,6 +330,78 @@ public class MessagesController extends BaseController implements NotificationCe return 0; } } + + boolean is1user = !DialogObject.isChannel(dialog1) && dialog1.id > 0; + boolean is2user = !DialogObject.isChannel(dialog2) && dialog2.id > 0; + + if (NekoXConfig.sortByUnread) { + if (dialog1.unread_count == 0 && dialog2.unread_count > 0) { + return 1; + } else if (dialog1.unread_count > 0 && dialog2.unread_count == 0) { + return -1; + } else if (dialog1.unread_count > 0 && dialog2.unread_count > 0) { + if (is2user && !ArraysKt.contains(NekoXConfig.DEVELOPER_IDS, (int) dialog1.id) && + ArraysKt.contains(NekoXConfig.DEVELOPER_IDS, (int) dialog2.id) + ) { + return 1; + } else if (is1user && ArraysKt.contains(NekoXConfig.DEVELOPER_IDS, (int) dialog1.id) && + !ArraysKt.contains(NekoXConfig.DEVELOPER_IDS, (int) dialog2.id)) { + return -1; + } else if (is1user && is2user && + ArraysKt.contains(NekoXConfig.DEVELOPER_IDS, (int) dialog1.id) && + ArraysKt.contains(NekoXConfig.DEVELOPER_IDS, (int) dialog2.id)) + if (NekoXConfig.sortByUnmuted) { + if (isDialogMuted(dialog1.id) && !isDialogMuted(dialog2.id)) { + return 1; + } else if (!isDialogMuted(dialog1.id) && isDialogMuted(dialog2.id)) { + return -1; + } else if (isDialogMuted(dialog1.id) && isDialogMuted(dialog2.id)) { + if (NekoXConfig.sortByUser) { + if (!is1user && is2user) { + return 1; + } else if (is1user && !is2user) { + return -1; + } else if (is1user && is2user) { + if (NekoXConfig.sortByContacts) { + boolean is1contact = is1user && getContactsController().isContact((int) dialog1.id); + boolean is2contact = is2user && getContactsController().isContact((int) dialog2.id); + if (!is1contact && is2contact) { + return 1; + } else if (is1contact && !is2contact) { + return -1; + } + } + } + } + } + } + } + } else if (NekoXConfig.sortByUnmuted) { + if (dialog1.unread_count == 0 && dialog2.unread_count > 0 && !isDialogMuted(dialog2.id)) { + return 1; + } else if (dialog1.unread_count > 0 && dialog2.unread_count == 0 && !isDialogMuted(dialog1.id)) { + return -1; + } else if (dialog1.unread_count > 0 && dialog2.unread_count > 0 && !isDialogMuted(dialog1.id) && !isDialogMuted(dialog2.id)) { + if (NekoXConfig.sortByUser) { + if (!is1user && is2user) { + return 1; + } else if (is1user && !is2user) { + return -1; + } else if (is1user && is2user) { + if (NekoXConfig.sortByContacts) { + boolean is1contact = is1user && getContactsController().isContact((int) dialog1.id); + boolean is2contact = is2user && getContactsController().isContact((int) dialog2.id); + if (!is1contact && is2contact) { + return 1; + } else if (is1contact && !is2contact) { + return -1; + } + } + } + } + } + } + final MediaDataController mediaDataController = getMediaDataController(); final long date1 = DialogObject.getLastMessageOrDraftDate(dialog1, mediaDataController.getDraft(dialog1.id)); final long date2 = DialogObject.getLastMessageOrDraftDate(dialog2, mediaDataController.getDraft(dialog2.id)); @@ -332,6 +410,7 @@ public class MessagesController extends BaseController implements NotificationCe } else if (date1 > date2) { return -1; } + return 0; }; @@ -357,6 +436,7 @@ public class MessagesController extends BaseController implements NotificationCe }; private static volatile MessagesController[] Instance = new MessagesController[UserConfig.MAX_ACCOUNT_COUNT]; + public static MessagesController getInstance(int num) { MessagesController localInstance = Instance[num]; if (localInstance == null) { @@ -447,8 +527,8 @@ public class MessagesController extends BaseController implements NotificationCe canRevokePmInbox = mainPreferences.getBoolean("canRevokePmInbox", canRevokePmInbox); preloadFeaturedStickers = mainPreferences.getBoolean("preloadFeaturedStickers", false); youtubePipType = mainPreferences.getString("youtubePipType", "disabled"); - keepAliveService = mainPreferences.getBoolean("keepAliveService", true); - backgroundConnection = mainPreferences.getBoolean("keepAliveService", true); + keepAliveService = mainPreferences.getBoolean("keepAliveService", false); + backgroundConnection = mainPreferences.getBoolean("keepAliveService", false); proxyDialogId = mainPreferences.getLong("proxy_dialog", 0); proxyDialogAddress = mainPreferences.getString("proxyDialogAddress", null); nextTosCheckTime = notificationsPreferences.getInt("nextTosCheckTime", 0); @@ -1366,7 +1446,7 @@ public class MessagesController extends BaseController implements NotificationCe oldUser.photo = user.photo; oldUser.flags |= 32; } else { - oldUser.flags = oldUser.flags &~ 32; + oldUser.flags = oldUser.flags & ~32; oldUser.photo = null; } } @@ -1400,7 +1480,7 @@ public class MessagesController extends BaseController implements NotificationCe user.photo = oldUser.photo; user.flags |= 32; } else { - user.flags = user.flags &~ 32; + user.flags = user.flags & ~32; user.photo = null; } users.put(user.id, user); @@ -1464,7 +1544,7 @@ public class MessagesController extends BaseController implements NotificationCe oldChat.username = chat.username; oldChat.flags |= 64; } else { - oldChat.flags = oldChat.flags &~ 64; + oldChat.flags = oldChat.flags & ~64; oldChat.username = null; } if (chat.participants_count != 0) { @@ -1491,19 +1571,19 @@ public class MessagesController extends BaseController implements NotificationCe int newFlags2 = chat.default_banned_rights != null ? chat.default_banned_rights.flags : 0; oldChat.default_banned_rights = chat.default_banned_rights; if (oldChat.default_banned_rights == null) { - oldChat.flags &=~ 262144; + oldChat.flags &= ~262144; } else { oldChat.flags |= 262144; } oldChat.banned_rights = chat.banned_rights; if (oldChat.banned_rights == null) { - oldChat.flags &=~ 32768; + oldChat.flags &= ~32768; } else { oldChat.flags |= 32768; } oldChat.admin_rights = chat.admin_rights; if (oldChat.admin_rights == null) { - oldChat.flags &=~ 16384; + oldChat.flags &= ~16384; } else { oldChat.flags |= 16384; } @@ -1538,7 +1618,7 @@ public class MessagesController extends BaseController implements NotificationCe chat.username = oldChat.username; chat.flags |= 64; } else { - chat.flags = chat.flags &~ 64; + chat.flags = chat.flags & ~64; chat.username = null; } if (oldChat.participants_count != 0 && chat.participants_count == 0) { @@ -2086,7 +2166,7 @@ public class MessagesController extends BaseController implements NotificationCe SharedPreferences.Editor editor = notificationsPreferences.edit(); boolean bar_hidden = !settings.report_spam && !settings.add_contact && !settings.block_contact && !settings.share_contact && !settings.report_geo; if (BuildVars.LOGS_ENABLED) { - FileLog.d("peer settings loaded for " + dialogId + " add = " + settings.add_contact + " block = " + settings.block_contact + " spam = " + settings.report_spam + " share = " + settings.share_contact + " geo = " + settings.report_geo + " hide = " + bar_hidden); + FileLog.d("peer settings loaded for " + dialogId + " add = " + settings.add_contact + " block = " + settings.block_contact + " spam = " + settings.report_spam + " share = " + settings.share_contact + " geo = " + settings.report_geo + " hide = " + bar_hidden); } editor.putInt("dialog_bar_vis3" + dialogId, bar_hidden ? 1 : 2); editor.putBoolean("dialog_bar_share" + dialogId, settings.share_contact); @@ -2137,7 +2217,7 @@ public class MessagesController extends BaseController implements NotificationCe })); } - protected void processNewChannelDifferenceParams(int pts, int pts_count, int channelId) { + public void processNewChannelDifferenceParams(int pts, int pts_count, int channelId) { if (BuildVars.LOGS_ENABLED) { FileLog.d("processNewChannelDifferenceParams pts = " + pts + " pts_count = " + pts_count + " channeldId = " + channelId); } @@ -2483,7 +2563,7 @@ public class MessagesController extends BaseController implements NotificationCe loadingBlockedUsers = true; TLRPC.TL_contacts_getBlocked req = new TLRPC.TL_contacts_getBlocked(); req.offset = reset ? 0 : blockedUsers.size(); - req.limit = reset ? 20 : 100; + req.limit = 100; getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { if (response != null) { TLRPC.contacts_Blocked res = (TLRPC.contacts_Blocked) response; @@ -2504,6 +2584,38 @@ public class MessagesController extends BaseController implements NotificationCe })); } + public void unblockAllUsers() { + + if (totalBlockedCount == 0) return; + + if (blockedUsers.size() == 0) getBlockedUsers(true); + + SparseIntArray blockedCopy = blockedUsers.clone(); + + if (blockedCopy.size() == 0) return; + + for (int index = 0;index < blockedCopy.size();index ++) { + + TLRPC.TL_contacts_unblock req = new TLRPC.TL_contacts_unblock(); + int user_id = blockedCopy.keyAt(index); + req.id = getInputUser(user_id); + getConnectionsManager().sendRequest(req, (response, error) -> { + + totalBlockedCount--; + blockedUsers.delete(user_id); + + UIUtil.runOnUIThread(() -> getNotificationCenter().postNotificationName(NotificationCenter.blockedUsersDidLoad)); + + }); + + ThreadUtil.sleep(233); + + } + + unblockAllUsers(); + + } + public void deleteUserPhoto(TLRPC.InputPhoto photo) { if (photo == null) { TLRPC.TL_photos_updateProfilePhoto req = new TLRPC.TL_photos_updateProfilePhoto(); @@ -3600,6 +3712,11 @@ public class MessagesController extends BaseController implements NotificationCe getLocationController().update(); checkProxyInfoInternal(false); checkTosUpdate(); + if (lastPushRegisterSendTime != 0 && Math.abs(SystemClock.elapsedRealtime() - lastPushRegisterSendTime) >= 3 * 60 * 60 * 1000) { + if (ExternalGcm.INSTANCE != null) { + ExternalGcm.INSTANCE.sendRegistrationToServer(); + } + } } private void checkTosUpdate() { @@ -3648,7 +3765,7 @@ public class MessagesController extends BaseController implements NotificationCe removeCurrent = 1; } lastCheckProxyId++; - if (!NekoConfig.hideProxySponsorChannel && enabled && !TextUtils.isEmpty(proxyAddress) && !TextUtils.isEmpty(proxySecret)) { + if (!NekoConfig.hideProxySponsorChannel && (SharedConfig.currentProxy == null || !SharedConfig.currentProxy.isInternal) && enabled && !TextUtils.isEmpty(proxyAddress) && !TextUtils.isEmpty(proxySecret)) { checkingProxyInfo = true; int checkProxyId = lastCheckProxyId; TLRPC.TL_help_getProxyData req = new TLRPC.TL_help_getProxyData(); @@ -4025,6 +4142,7 @@ public class MessagesController extends BaseController implements NotificationCe } public void sendTyping(final long dialog_id, final int action, int classGuid) { + if (NekoXConfig.disableChatAction) return; if (dialog_id == 0) { return; } @@ -5751,7 +5869,7 @@ public class MessagesController extends BaseController implements NotificationCe allDialogs.add(dialogs_dict.valueAt(a)); } sortDialogs(migrate ? chatsDict : null); - + putAllNeededDraftDialogs(); if (loadType != DIALOGS_LOAD_TYPE_CHANNEL && loadType != DIALOGS_LOAD_TYPE_UNKNOWN) { @@ -7229,6 +7347,7 @@ public class MessagesController extends BaseController implements NotificationCe } private boolean gettingAppChangelog; + public void generateUpdateMessage() { if (gettingAppChangelog || BuildVars.DEBUG_VERSION || SharedConfig.lastUpdateVersion == null || SharedConfig.lastUpdateVersion.equals(BuildVars.BUILD_VERSION_STRING)) { return; @@ -11288,7 +11407,24 @@ public class MessagesController extends BaseController implements NotificationCe unreadUnmutedDialogs = 0; boolean selfAdded = false; int selfId = getUserConfig().getClientUserId(); - Collections.sort(allDialogs, dialogComparator); + + try { + + Collections.sort(allDialogs, dialogComparator); + + } catch (Exception e) { + + FileLog.e(e); + + NekoXConfig.sortByUnread = false; + NekoXConfig.sortByUnmuted = false; + NekoXConfig.sortByUser = false; + NekoXConfig.sortByContacts = false; + + Collections.sort(allDialogs, dialogComparator); + + } + isLeftProxyChannel = true; if (proxyDialog != null && proxyDialog.id < 0) { TLRPC.Chat chat = getChat(-(int) proxyDialog.id); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsService.java b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsService.java index 8fc4f07ec..d96a070d4 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsService.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsService.java @@ -71,7 +71,11 @@ public class NotificationsService extends Service { SharedPreferences preferences = MessagesController.getGlobalNotificationsSettings(); if (preferences.getBoolean("pushService", true)) { Intent intent = new Intent("org.telegram.start"); - sendBroadcast(intent); + try { + sendBroadcast(intent); + } catch (Exception ex) { + // 辣鷄miui 就你事最多.jpg + } } } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java b/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java index 610174cf7..fa1306dd3 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java @@ -50,7 +50,6 @@ import org.telegram.ui.ChatActivity; import org.telegram.ui.Components.AlertsCreator; import org.telegram.ui.Components.AnimatedFileDrawable; import org.telegram.ui.Components.Point; -import org.telegram.ui.PaymentFormActivity; import java.io.File; import java.io.FileInputStream; @@ -2172,14 +2171,6 @@ public class SendMessagesHelper extends BaseController implements NotificationCe TLRPC.TL_urlAuthResultDefault res = (TLRPC.TL_urlAuthResultDefault) response; parentFragment.showOpenUrlAlert(button.url, true); } - } else if (button instanceof TLRPC.TL_keyboardButtonBuy) { - if (response instanceof TLRPC.TL_payments_paymentForm) { - final TLRPC.TL_payments_paymentForm form = (TLRPC.TL_payments_paymentForm) response; - getMessagesController().putUsers(form.users, false); - parentFragment.presentFragment(new PaymentFormActivity(form, messageObject)); - } else if (response instanceof TLRPC.TL_payments_paymentReceipt) { - parentFragment.presentFragment(new PaymentFormActivity(messageObject, (TLRPC.TL_payments_paymentReceipt) response)); - } } else { TLRPC.TL_messages_botCallbackAnswer res = (TLRPC.TL_messages_botCallbackAnswer) response; if (!cacheFinal && res.cache_time != 0) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java b/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java index d153691c4..470448576 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java @@ -10,8 +10,11 @@ package org.telegram.messenger; import android.app.Activity; import android.app.ActivityManager; +import android.app.Fragment; import android.content.Context; import android.content.SharedPreferences; +import android.net.Proxy; +import android.net.Uri; import android.os.Build; import android.os.Environment; import android.os.SystemClock; @@ -19,17 +22,30 @@ import android.text.TextUtils; import android.util.Base64; import android.util.SparseArray; +import com.v2ray.ang.V2RayConfig; +import com.v2ray.ang.dto.AngConfig; + +import org.json.JSONArray; +import org.json.JSONException; import org.json.JSONObject; import org.telegram.tgnet.ConnectionsManager; -import org.telegram.tgnet.SerializedData; import java.io.File; import java.io.RandomAccessFile; -import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedList; +import tw.nekomimi.nekogram.ShadowsocksRLoader; import tw.nekomimi.nekogram.NekoConfig; +import tw.nekomimi.nekogram.ProxyManager; +import tw.nekomimi.nekogram.ShadowsocksLoader; +import tw.nekomimi.nekogram.VmessLoader; +import tw.nekomimi.nekogram.utils.FileUtil; +import tw.nekomimi.nekogram.utils.ProxyUtil; + +import static com.v2ray.ang.V2RayConfig.SSR_PROTOCOL; +import static com.v2ray.ang.V2RayConfig.SS_PROTOCOL; public class SharedConfig { @@ -97,9 +113,9 @@ public class SharedConfig { public static int repeatMode; public static boolean allowBigEmoji; public static boolean useSystemEmoji; - public static int fontSize = 16; - public static int bubbleRadius = 10; - public static int ivFontSize = 16; + public static int fontSize = 12; + public static int bubbleRadius = 3; + public static int ivFontSize = 12; private static int devicePerformanceClass; public static boolean drawDialogIcons; @@ -126,6 +142,16 @@ public class SharedConfig { public boolean available; public long availableCheckTime; + public boolean isInternal = false; + public String descripton; + + public ProxyInfo() { + address = ""; + password = ""; + username = ""; + secret = ""; + } + public ProxyInfo(String a, int p, String u, String pw, String s) { address = a; port = p; @@ -145,9 +171,285 @@ public class SharedConfig { secret = ""; } } + + public JSONObject toJson() throws JSONException { + + JSONObject obj = new JSONObject(); + obj.put("address", address); + obj.put("port", port); + if (secret.isEmpty()) { + obj.put("type", "socks5"); + if (!username.isEmpty()) { + obj.put("username", username); + } + if (!password.isEmpty()) { + obj.put("password", password); + } + } else { + obj.put("type", "socks5"); + obj.put("secret", secret); + } + + return obj; + + } + + public static ProxyInfo fromJson(JSONObject obj) { + + ProxyInfo info; + + switch (obj.optString("type", "null")) { + + case "socks5": { + + info = new ProxyInfo(); + + info.address = obj.optString("address", ""); + info.port = obj.optInt("port", 443); + info.username = obj.optString("username", ""); + info.password = obj.optString("password", ""); + + break; + + } + + case "mtproto": { + + info = new ProxyInfo(); + + info.address = obj.optString("address", ""); + info.port = obj.optInt("port", 443); + info.secret = obj.optString("secret", ""); + + break; + + } + + case "vmess": { + + info = new VmessProxy(obj.optString("link")); + + break; + + } + + case "shadowsocks": { + + info = new ShadowsocksProxy(obj.optString("link")); + + break; + + } + + case "shadowsocksr": { + + info = new ShadowsocksRProxy(obj.optString("link")); + + break; + + } + + default: { + + throw new IllegalStateException("invalid proxy type " + obj.optString("type", "null")); + + } + + } + + return info; + + } + } - public static ArrayList proxyList = new ArrayList<>(); + public abstract static class ExternalSocks5Proxy extends ProxyInfo { + + public ExternalSocks5Proxy() { + + address = "127.0.0.1"; + username = ""; + password = ""; + secret = ""; + + } + + public abstract void start(); + + public abstract void stop(); + + @Override + public abstract JSONObject toJson() throws JSONException; + + } + + public static class VmessProxy extends ExternalSocks5Proxy { + + public AngConfig.VmessBean bean; + public VmessLoader loader; + + public VmessProxy(String vmessLink) { + + this(VmessLoader.parseVmessLink(vmessLink)); + + } + + public VmessProxy(AngConfig.VmessBean bean) { + + this.bean = bean; + + } + + @Override + public void start() { + + stop(); + + port = ProxyManager.getPortForBean(bean); + + loader = new VmessLoader(); + loader.initConfig(bean, port); + loader.start(); + + } + + @Override + public void stop() { + + if (loader != null) { + + loader.stop(); + + loader = null; + + } + + } + + @Override + public JSONObject toJson() throws JSONException { + + JSONObject obj = new JSONObject(); + obj.put("type", "vmess"); + obj.put("link", bean.toString()); + return obj; + + } + + } + + public static class ShadowsocksProxy extends ExternalSocks5Proxy { + + public ShadowsocksLoader.Bean bean; + public ShadowsocksLoader loader; + + public ShadowsocksProxy(String ssLink) { + + this(ShadowsocksLoader.Bean.Companion.parse(ssLink)); + + } + + public ShadowsocksProxy(ShadowsocksLoader.Bean bean) { + + this.bean = bean; + + } + + @Override + public void start() { + + stop(); + + port = ProxyManager.getPortForBean(bean); + + loader = new ShadowsocksLoader(); + loader.initConfig(bean, port); + loader.start(); + + } + + @Override + public void stop() { + + if (loader != null) { + + loader.stop(); + + loader = null; + + } + + } + + @Override + public JSONObject toJson() throws JSONException { + + JSONObject obj = new JSONObject(); + obj.put("type", "shadowsocks"); + obj.put("link", bean.toString()); + return obj; + + } + + } + + public static class ShadowsocksRProxy extends ExternalSocks5Proxy { + + public ShadowsocksRLoader.Bean bean; + public ShadowsocksRLoader loader; + + public ShadowsocksRProxy(String ssLink) { + + this(ShadowsocksRLoader.Bean.Companion.parse(ssLink)); + + } + + public ShadowsocksRProxy(ShadowsocksRLoader.Bean bean) { + + this.bean = bean; + + } + + @Override + public void start() { + + stop(); + + port = ProxyManager.getPortForBean(bean); + + loader = new ShadowsocksRLoader(); + loader.initConfig(bean, port); + loader.start(); + + } + + @Override + public void stop() { + + if (loader != null) { + + loader.stop(); + + loader = null; + + } + + } + + @Override + public JSONObject toJson() throws JSONException { + + JSONObject obj = new JSONObject(); + obj.put("type", "shadowsocksr"); + obj.put("link", bean.toString()); + return obj; + + } + + } + + public static LinkedList proxyList = new LinkedList<>(); + private static boolean proxyListLoaded; public static ProxyInfo currentProxy; @@ -178,7 +480,7 @@ public class SharedConfig { editor.putBoolean("sortFilesByName", sortFilesByName); editor.putInt("textSelectionHintShows", textSelectionHintShows); editor.putInt("scheduledOrNoSoundHintShows", scheduledOrNoSoundHintShows); - editor.commit(); + editor.apply(); } catch (Exception e) { FileLog.e(e); } @@ -246,8 +548,8 @@ public class SharedConfig { hasCameraCache = preferences.contains("cameraCache"); roundCamera16to9 = true;//preferences.getBoolean("roundCamera16to9", false); repeatMode = preferences.getInt("repeatMode", 0); - fontSize = preferences.getInt("fons_size", AndroidUtilities.isTablet() ? 18 : 16); - bubbleRadius = preferences.getInt("bubbleRadius", 10); + fontSize = preferences.getInt("fons_size", AndroidUtilities.isTablet() ? 14 : 12); + bubbleRadius = preferences.getInt("bubbleRadius", 3); ivFontSize = preferences.getInt("iv_font_size", fontSize); allowBigEmoji = preferences.getBoolean("allowBigEmoji", true); useSystemEmoji = preferences.getBoolean("useSystemEmoji", false); @@ -398,7 +700,7 @@ public class SharedConfig { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putInt("suggestStickers", suggestStickers); - editor.commit(); + editor.apply(); } public static void setSearchMessagesAsListUsed(boolean searchMessagesAsListUsed) { @@ -406,42 +708,42 @@ public class SharedConfig { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("searchMessagesAsListUsed", searchMessagesAsListUsed); - editor.commit(); + editor.apply(); } public static void increaseTextSelectionHintShowed() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putInt("textSelectionHintShows", ++textSelectionHintShows); - editor.commit(); + editor.apply(); } public static void removeTextSelectionHint() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putInt("textSelectionHintShows", 3); - editor.commit(); + editor.apply(); } public static void increaseScheduledOrNoSuoundHintShowed() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putInt("scheduledOrNoSoundHintShows", ++scheduledOrNoSoundHintShows); - editor.commit(); + editor.apply(); } public static void removeScheduledOrNoSuoundHint() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putInt("scheduledOrNoSoundHintShows", 3); - editor.commit(); + editor.apply(); } public static void increaseSearchAsListHintShows() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putInt("searchMessagesAsListHintShows", ++searchMessagesAsListHintShows); - editor.commit(); + editor.apply(); } public static void setKeepMedia(int value) { @@ -449,7 +751,7 @@ public class SharedConfig { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putInt("keep_media", keepMedia); - editor.commit(); + editor.apply(); } public static void checkKeepMedia() { @@ -494,7 +796,7 @@ public class SharedConfig { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putInt("lastKeepMediaCheckTime", lastKeepMediaCheckTime); - editor.commit(); + editor.apply(); }); } @@ -503,7 +805,7 @@ public class SharedConfig { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("loopStickers", loopStickers); - editor.commit(); + editor.apply(); } public static void toggleBigEmoji() { @@ -511,7 +813,7 @@ public class SharedConfig { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("allowBigEmoji", allowBigEmoji); - editor.commit(); + editor.apply(); } public static void toggleShuffleMusic(int type) { @@ -525,7 +827,7 @@ public class SharedConfig { SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("shuffleMusic", shuffleMusic); editor.putBoolean("playOrderReversed", playOrderReversed); - editor.commit(); + editor.apply(); } public static void toggleRepeatMode() { @@ -536,7 +838,7 @@ public class SharedConfig { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putInt("repeatMode", repeatMode); - editor.commit(); + editor.apply(); } public static void toggleSaveToGallery() { @@ -544,7 +846,7 @@ public class SharedConfig { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("save_gallery", saveToGallery); - editor.commit(); + editor.apply(); checkSaveToGalleryFiles(); } @@ -553,7 +855,7 @@ public class SharedConfig { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("autoplay_gif", autoplayGifs); - editor.commit(); + editor.apply(); } public static void setUseThreeLinesLayout(boolean value) { @@ -561,7 +863,7 @@ public class SharedConfig { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("useThreeLinesLayout", useThreeLinesLayout); - editor.commit(); + editor.apply(); NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.dialogsNeedReload, true); } @@ -570,7 +872,7 @@ public class SharedConfig { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("archiveHidden", archiveHidden); - editor.commit(); + editor.apply(); } public static void toggleAutoplayVideo() { @@ -578,7 +880,7 @@ public class SharedConfig { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("autoplay_video", autoplayVideo); - editor.commit(); + editor.apply(); } public static boolean isSecretMapPreviewSet() { @@ -591,7 +893,7 @@ public class SharedConfig { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putInt("mapPreviewType", mapPreviewType); - editor.commit(); + editor.apply(); } public static void setNoSoundHintShowed(boolean value) { @@ -602,7 +904,7 @@ public class SharedConfig { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("noSoundHintShowed", noSoundHintShowed); - editor.commit(); + editor.apply(); } public static void toogleRaiseToSpeak() { @@ -610,7 +912,7 @@ public class SharedConfig { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("raise_to_speak", raiseToSpeak); - editor.commit(); + editor.apply(); } public static void toggleCustomTabs() { @@ -618,7 +920,7 @@ public class SharedConfig { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("custom_tabs", customTabs); - editor.commit(); + editor.apply(); } public static void toggleDirectShare() { @@ -626,7 +928,7 @@ public class SharedConfig { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("direct_share", directShare); - editor.commit(); + editor.apply(); } public static void toggleStreamMedia() { @@ -634,7 +936,7 @@ public class SharedConfig { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("streamMedia", streamMedia); - editor.commit(); + editor.apply(); } public static void toggleSortContactsByName() { @@ -642,7 +944,7 @@ public class SharedConfig { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("sortContactsByName", sortContactsByName); - editor.commit(); + editor.apply(); } public static void toggleSortFilesByName() { @@ -650,7 +952,7 @@ public class SharedConfig { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("sortFilesByName", sortFilesByName); - editor.commit(); + editor.apply(); } public static void toggleStreamAllVideo() { @@ -658,7 +960,7 @@ public class SharedConfig { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("streamAllVideo", streamAllVideo); - editor.commit(); + editor.apply(); } public static void toggleStreamMkv() { @@ -666,7 +968,7 @@ public class SharedConfig { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("streamMkv", streamMkv); - editor.commit(); + editor.apply(); } public static void toggleSaveStreamMedia() { @@ -674,7 +976,7 @@ public class SharedConfig { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("saveStreamMedia", saveStreamMedia); - editor.commit(); + editor.apply(); } public static void toggleSmoothKeyboard() { @@ -682,7 +984,7 @@ public class SharedConfig { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("smoothKeyboard", smoothKeyboard); - editor.commit(); + editor.apply(); } public static void togglePauseMusicOnRecord() { @@ -690,7 +992,7 @@ public class SharedConfig { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("pauseMusicOnRecord", pauseMusicOnRecord); - editor.commit(); + editor.apply(); } public static void toggleInappCamera() { @@ -698,7 +1000,7 @@ public class SharedConfig { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("inappCamera", inappCamera); - editor.commit(); + editor.apply(); } public static void toggleRoundCamera16to9() { @@ -706,7 +1008,7 @@ public class SharedConfig { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("roundCamera16to9", roundCamera16to9); - editor.commit(); + editor.apply(); } public static void setDistanceSystemType(int type) { @@ -714,75 +1016,274 @@ public class SharedConfig { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putInt("distanceSystemType", distanceSystemType); - editor.commit(); + editor.apply(); LocaleController.resetImperialSystemType(); } + public static boolean proxyEnabled; + + public static VmessProxy publicProxy; static { + + publicProxy = new VmessProxy(VmessLoader.getPublic()); + publicProxy.isInternal = true; + + } + + static { + + loadProxyList(); + + SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); + + boolean proxyEnabledValue = preferences.getBoolean("proxy_enabled", false); + + if (currentProxy == null) { + + currentProxy = publicProxy; + + publicProxy.start(); + + } + + proxyEnabled = proxyEnabledValue; + + } + + public static void setProxyEnable(boolean enable) { + + proxyEnabled = enable; + + SharedPreferences preferences = MessagesController.getGlobalMainSettings(); + + preferences.edit().putBoolean("proxy_enabled", enable).apply(); + + ProxyInfo info = currentProxy; + + if (info == null) { + + info = new ProxyInfo(); + + } + + if (enable && info instanceof ExternalSocks5Proxy) { + + ((ExternalSocks5Proxy) info).start(); + + } else if (!enable && info instanceof ExternalSocks5Proxy) { + + ((ExternalSocks5Proxy) info).stop(); + + } + + ConnectionsManager.setProxySettings(enable, info.address, info.port, info.username, info.password, info.secret); + + } + + public static void setCurrentProxy(ProxyInfo info) { + + currentProxy = info; + + setProxyEnable(true); + + } + public static void loadProxyList() { if (proxyListLoaded) { return; } - SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); - String proxyAddress = preferences.getString("proxy_ip", ""); - String proxyUsername = preferences.getString("proxy_user", ""); - String proxyPassword = preferences.getString("proxy_pass", ""); - String proxySecret = preferences.getString("proxy_secret", ""); - int proxyPort = preferences.getInt("proxy_port", 1080); proxyListLoaded = true; proxyList.clear(); currentProxy = null; - String list = preferences.getString("proxy_list", null); - if (!TextUtils.isEmpty(list)) { - byte[] bytes = Base64.decode(list, Base64.DEFAULT); - SerializedData data = new SerializedData(bytes); - int count = data.readInt32(false); - for (int a = 0; a < count; a++) { - ProxyInfo info = new ProxyInfo( - data.readString(false), - data.readInt32(false), - data.readString(false), - data.readString(false), - data.readString(false)); - proxyList.add(info); - if (currentProxy == null && !TextUtils.isEmpty(proxyAddress)) { - if (proxyAddress.equals(info.address) && proxyPort == info.port && proxyUsername.equals(info.username) && proxyPassword.equals(info.password)) { - currentProxy = info; + + proxyList.add(publicProxy); + + File proxyListFile = new File(ApplicationLoader.applicationContext.getFilesDir().getParentFile(), "nekox/proxy_list.json"); + + if (proxyListFile.isFile()) { + + try { + + JSONArray proxyArray = new JSONArray(FileUtil.readUtf8String(proxyListFile)); + + for (int a = 0; a < proxyArray.length(); a++) { + + JSONObject proxyObj = proxyArray.getJSONObject(a); + + ProxyInfo info; + + try { + + info = ProxyInfo.fromJson(proxyObj); + + } catch (Exception ex) { + + FileLog.e("load proxy failed", ex); + + continue; + } + + if (!proxyObj.optBoolean("internal",false)) { + + proxyList.add(info); + + if (proxyObj.optBoolean("current", false)) { + + currentProxy = publicProxy; + + if (info instanceof ExternalSocks5Proxy) { + + ((ExternalSocks5Proxy) info).start(); + + } + + } + + } + + } + + } catch (Exception ex) { + + FileLog.e("invalid proxy list json format", ex); + } - data.cleanup(); + } - if (currentProxy == null && !TextUtils.isEmpty(proxyAddress)) { - ProxyInfo info = currentProxy = new ProxyInfo(proxyAddress, proxyPort, proxyUsername, proxyPassword, proxySecret); - proxyList.add(0, info); + + } + + public static ProxyInfo parseProxyInfo(String url) throws InvalidProxyException { + + if (url.startsWith(V2RayConfig.VMESS_PROTOCOL) || url.startsWith(V2RayConfig.VMESS1_PROTOCOL)) { + + try { + + return new VmessProxy(url); + + } catch (Exception ex) { + + throw new InvalidProxyException(ex); + + } + + } else if (url.startsWith(SS_PROTOCOL)) { + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + + throw new InvalidProxyException("shadowsocks requires min api 21"); + + } + + try { + + return new ShadowsocksProxy(url); + + } catch (Exception ex) { + + throw new InvalidProxyException(ex); + + } + + } else if (url.startsWith(SSR_PROTOCOL)) { + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + + throw new InvalidProxyException("shadowsocksR requires min api 21"); + + } + + try { + + return new ShadowsocksRProxy(url); + + } catch (Exception ex) { + + throw new InvalidProxyException(ex); + + } + } + + if (url.startsWith("tg:proxy") || + url.startsWith("tg://proxy") || + url.startsWith("tg:socks") || + url.startsWith("tg://socks") || + url.startsWith("https://t.me/proxy") || + url.startsWith("https://t.me/socks")) { + url = url + .replace("tg:proxy", "tg://telegram.org") + .replace("tg://proxy", "tg://telegram.org") + .replace("tg://socks", "tg://telegram.org") + .replace("tg:socks", "tg://telegram.org"); + Uri data = Uri.parse(url); + return new ProxyInfo(data.getQueryParameter("server"), + Utilities.parseInt(data.getQueryParameter("port")), + data.getQueryParameter("user"), + data.getQueryParameter("pass"), + data.getQueryParameter("secret")); + } + + throw new InvalidProxyException(); + + } + + public static class InvalidProxyException extends Exception { + + public InvalidProxyException() { + } + + public InvalidProxyException(String messsage) { + super(messsage); + } + + public InvalidProxyException(Throwable cause) { + + super(cause); + + } + } public static void saveProxyList() { - SerializedData serializedData = new SerializedData(); - int count = proxyList.size(); - serializedData.writeInt32(count); - for (int a = 0; a < count; a++) { - ProxyInfo info = proxyList.get(a); - serializedData.writeString(info.address != null ? info.address : ""); - serializedData.writeInt32(info.port); - serializedData.writeString(info.username != null ? info.username : ""); - serializedData.writeString(info.password != null ? info.password : ""); - serializedData.writeString(info.secret != null ? info.secret : ""); + JSONArray proxyArray = new JSONArray(); + for (ProxyInfo info : proxyList) { + try { + JSONObject obj = info.toJson(); + if (info == currentProxy) { + obj.put("current", true); + if (info.isInternal) { + obj.put("internal",true); + } + } else if (info.isInternal) { + continue; + } + proxyArray.put(obj); + } catch (JSONException e) { + FileLog.e(e); + } + } + + File proxyListFile = new File(ApplicationLoader.applicationContext.getFilesDir().getParentFile(), "nekox/proxy_list.json"); + + try { + FileUtil.writeUtf8String(proxyArray.toString(4),proxyListFile); + } catch (JSONException e) { + FileLog.e(e); } - SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); - preferences.edit().putString("proxy_list", Base64.encodeToString(serializedData.toByteArray(), Base64.NO_WRAP)).commit(); - serializedData.cleanup(); } public static ProxyInfo addProxy(ProxyInfo proxyInfo) { - loadProxyList(); int count = proxyList.size(); for (int a = 0; a < count; a++) { ProxyInfo info = proxyList.get(a); - if (proxyInfo.address.equals(info.address) && proxyInfo.port == info.port && proxyInfo.username.equals(info.username) && proxyInfo.password.equals(info.password) && proxyInfo.secret.equals(info.secret)) { - return info; + try { + if (info.toJson().equals(proxyInfo.toJson())) { + return info; + } + } catch (JSONException e) { } } proxyList.add(proxyInfo); @@ -793,19 +1294,8 @@ public class SharedConfig { public static void deleteProxy(ProxyInfo proxyInfo) { if (currentProxy == proxyInfo) { currentProxy = null; - SharedPreferences preferences = MessagesController.getGlobalMainSettings(); - boolean enabled = preferences.getBoolean("proxy_enabled", false); - SharedPreferences.Editor editor = preferences.edit(); - editor.putString("proxy_ip", ""); - editor.putString("proxy_pass", ""); - editor.putString("proxy_user", ""); - editor.putString("proxy_secret", ""); - editor.putInt("proxy_port", 1080); - editor.putBoolean("proxy_enabled", false); - editor.putBoolean("proxy_enabled_calls", false); - editor.commit(); - if (enabled) { - ConnectionsManager.setProxySettings(false, "", 0, "", "", ""); + if (proxyEnabled) { + setProxyEnable(false); } } proxyList.remove(proxyInfo); @@ -815,7 +1305,7 @@ public class SharedConfig { public static void checkSaveToGalleryFiles() { try { File telegramPath; - if (NekoConfig.saveCacheToPrivateDirectory) { + if (!NekoConfig.saveCacheToSdcard) { telegramPath = new File(ApplicationLoader.applicationContext.getFilesDir(), "Telegram"); } else { telegramPath = new File(Environment.getExternalStorageDirectory(), "Telegram"); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/audioinfo/m4a/M4AInfo.java b/TMessagesProj/src/main/java/org/telegram/messenger/audioinfo/m4a/M4AInfo.java index 9765b4748..51813bea7 100755 --- a/TMessagesProj/src/main/java/org/telegram/messenger/audioinfo/m4a/M4AInfo.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/audioinfo/m4a/M4AInfo.java @@ -1,325 +1,325 @@ -/* - * Copyright 2013-2014 Odysseus Software GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.telegram.messenger.audioinfo.m4a; - -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; - -import org.telegram.messenger.audioinfo.AudioInfo; -import org.telegram.messenger.audioinfo.mp3.ID3v1Genre; - -import java.io.IOException; -import java.io.InputStream; -import java.math.BigDecimal; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class M4AInfo extends AudioInfo { - static final Logger LOGGER = Logger.getLogger(M4AInfo.class.getName()); - - private static final String ASCII = "ISO8859_1"; - private static final String UTF_8 = "UTF-8"; - - private BigDecimal volume; // normal = 1.0 - private BigDecimal speed; // normal = 1.0 - - private short tempo; - private byte rating; // none = 0, clean = 2, explicit = 4 - - private final Level debugLevel; - - public M4AInfo(InputStream input) throws IOException { - this(input, Level.FINEST); - } - - public M4AInfo(InputStream input, Level debugLevel) throws IOException { - this.debugLevel = debugLevel; - MP4Input mp4 = new MP4Input(input); - if (LOGGER.isLoggable(debugLevel)) { - LOGGER.log(debugLevel, mp4.toString()); - } - ftyp(mp4.nextChild("ftyp")); - moov(mp4.nextChildUpTo("moov")); - } - - void ftyp(MP4Atom atom) throws IOException { - if (LOGGER.isLoggable(debugLevel)) { - LOGGER.log(debugLevel, atom.toString()); - } - brand = atom.readString(4, ASCII).trim(); - if (brand.matches("M4V|MP4|mp42|isom")) { // experimental file types - LOGGER.warning(atom.getPath() + ": brand=" + brand + " (experimental)"); - } else if (!brand.matches("M4A|M4P")) { - LOGGER.warning(atom.getPath() + ": brand=" + brand + " (expected M4A or M4P)"); - } - version = String.valueOf(atom.readInt()); - } - - void moov(MP4Atom atom) throws IOException { - if (LOGGER.isLoggable(debugLevel)) { - LOGGER.log(debugLevel, atom.toString()); - } - while (atom.hasMoreChildren()) { - MP4Atom child = atom.nextChild(); - switch (child.getType()) { - case "mvhd": - mvhd(child); - break; - case "trak": - trak(child); - break; - case "udta": - udta(child); - break; - default: - break; - } - } - } - - void mvhd(MP4Atom atom) throws IOException { - if (LOGGER.isLoggable(debugLevel)) { - LOGGER.log(debugLevel, atom.toString()); - } - byte version = atom.readByte(); - atom.skip(3); // flags - atom.skip(version == 1 ? 16 : 8); // created/modified date - int scale = atom.readInt(); - long units = version == 1 ? atom.readLong() : atom.readInt(); - if (duration == 0) { - duration = 1000 * units / scale; - } else if (LOGGER.isLoggable(debugLevel) && Math.abs(duration - 1000 * units / scale) > 2) { - LOGGER.log(debugLevel, "mvhd: duration " + duration + " -> " + (1000 * units / scale)); - } - speed = atom.readIntegerFixedPoint(); - volume = atom.readShortFixedPoint(); - } - - void trak(MP4Atom atom) throws IOException { - if (LOGGER.isLoggable(debugLevel)) { - LOGGER.log(debugLevel, atom.toString()); - } - mdia(atom.nextChildUpTo("mdia")); - } - - void mdia(MP4Atom atom) throws IOException { - if (LOGGER.isLoggable(debugLevel)) { - LOGGER.log(debugLevel, atom.toString()); - } - mdhd(atom.nextChild("mdhd")); - } - - void mdhd(MP4Atom atom) throws IOException { - if (LOGGER.isLoggable(debugLevel)) { - LOGGER.log(debugLevel, atom.toString()); - } - byte version = atom.readByte(); - atom.skip(3); - atom.skip(version == 1 ? 16 : 8); // created/modified date - int sampleRate = atom.readInt(); - long samples = version == 1 ? atom.readLong() : atom.readInt(); - if (duration == 0) { - duration = 1000 * samples / sampleRate; - } else if (LOGGER.isLoggable(debugLevel) && Math.abs(duration - 1000 * samples / sampleRate) > 2) { - LOGGER.log(debugLevel, "mdhd: duration " + duration + " -> " + (1000 * samples / sampleRate)); - } - } - - void udta(MP4Atom atom) throws IOException { - if (LOGGER.isLoggable(debugLevel)) { - LOGGER.log(debugLevel, atom.toString()); - } - while (atom.hasMoreChildren()) { - MP4Atom child = atom.nextChild(); - if ("meta".equals(child.getType())) { - meta(child); - break; - } - } - } - - void meta(MP4Atom atom) throws IOException { - if (LOGGER.isLoggable(debugLevel)) { - LOGGER.log(debugLevel, atom.toString()); - } - atom.skip(4); // version/flags - while (atom.hasMoreChildren()) { - MP4Atom child = atom.nextChild(); - if ("ilst".equals(child.getType())) { - ilst(child); - break; - } - } - } - - void ilst(MP4Atom atom) throws IOException { - if (LOGGER.isLoggable(debugLevel)) { - LOGGER.log(debugLevel, atom.toString()); - } - while (atom.hasMoreChildren()) { - MP4Atom child = atom.nextChild(); - if (LOGGER.isLoggable(debugLevel)) { - LOGGER.log(debugLevel, child.toString()); - } - if (child.getRemaining() == 0) { - if (LOGGER.isLoggable(debugLevel)) { - LOGGER.log(debugLevel, child.getPath() + ": contains no value"); - } - continue; - } - data(child.nextChildUpTo("data")); - } - } - - void data(MP4Atom atom) throws IOException { - if (LOGGER.isLoggable(debugLevel)) { - LOGGER.log(debugLevel, atom.toString()); - } - atom.skip(4); // version & flags - atom.skip(4); // reserved - switch (atom.getParent().getType()) { - case "©alb": - album = atom.readString(UTF_8); - break; - case "aART": - albumArtist = atom.readString(UTF_8); - break; - case "©ART": - artist = atom.readString(UTF_8); - break; - case "©cmt": - comment = atom.readString(UTF_8); - break; - case "©com": - case "©wrt": - if (composer == null || composer.trim().length() == 0) { - composer = atom.readString(UTF_8); - } - break; - case "covr": - try { - byte[] bytes = atom.readBytes(); - BitmapFactory.Options opts = new BitmapFactory.Options(); - opts.inJustDecodeBounds = true; - opts.inSampleSize = 1; - BitmapFactory.decodeByteArray(bytes, 0, bytes.length, opts); - if (opts.outWidth > 800 || opts.outHeight > 800) { - int size = Math.max(opts.outWidth, opts.outHeight); - while (size > 800) { - opts.inSampleSize *= 2; - size /= 2; - } - } - opts.inJustDecodeBounds = false; - cover = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, opts); - if (cover != null) { - float scale = Math.max(cover.getWidth(), cover.getHeight()) / 120.0f; - if (scale > 0) { - smallCover = Bitmap.createScaledBitmap(cover, (int) (cover.getWidth() / scale), (int) (cover.getHeight() / scale), true); - } else { - smallCover = cover; - } - if (smallCover == null) { - smallCover = cover; - } - } - } catch (Exception e) { - e.printStackTrace(); - } - break; - case "cpil": - compilation = atom.readBoolean(); - break; - case "cprt": - case "©cpy": - if (copyright == null || copyright.trim().length() == 0) { - copyright = atom.readString(UTF_8); - } - break; - case "©day": - String day = atom.readString(UTF_8).trim(); - if (day.length() >= 4) { - try { - year = Short.valueOf(day.substring(0, 4)); - } catch (NumberFormatException e) { - // ignore - } - } - break; - case "disk": - atom.skip(2); // padding? - disc = atom.readShort(); - discs = atom.readShort(); - break; - case "gnre": - if (genre == null || genre.trim().length() == 0) { - if (atom.getRemaining() == 2) { // id3v1 genre? - int index = atom.readShort() - 1; - ID3v1Genre id3v1Genre = ID3v1Genre.getGenre(index); - if (id3v1Genre != null) { - genre = id3v1Genre.getDescription(); - } - } else { - genre = atom.readString(UTF_8); - } - } - break; - case "©gen": - if (genre == null || genre.trim().length() == 0) { - genre = atom.readString(UTF_8); - } - break; - case "©grp": - grouping = atom.readString(UTF_8); - break; - case "©lyr": - lyrics = atom.readString(UTF_8); - break; - case "©nam": - title = atom.readString(UTF_8); - break; - case "rtng": - rating = atom.readByte(); - break; - case "tmpo": - tempo = atom.readShort(); - break; - case "trkn": - atom.skip(2); // padding? - track = atom.readShort(); - tracks = atom.readShort(); - break; - default: - break; - } - } - - public short getTempo() { - return tempo; - } - - public byte getRating() { - return rating; - } - - public BigDecimal getSpeed() { - return speed; - } - - public BigDecimal getVolume() { - return volume; - } -} +/* + * Copyright 2013-2014 Odysseus Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.telegram.messenger.audioinfo.m4a; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; + +import org.telegram.messenger.audioinfo.AudioInfo; +import org.telegram.messenger.audioinfo.mp3.ID3v1Genre; + +import java.io.IOException; +import java.io.InputStream; +import java.math.BigDecimal; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class M4AInfo extends AudioInfo { + static final Logger LOGGER = Logger.getLogger(M4AInfo.class.getName()); + + private static final String ASCII = "ISO8859_1"; + private static final String UTF_8 = "UTF-8"; + + private BigDecimal volume; // normal = 1.0 + private BigDecimal speed; // normal = 1.0 + + private short tempo; + private byte rating; // none = 0, clean = 2, explicit = 4 + + private final Level debugLevel; + + public M4AInfo(InputStream input) throws IOException { + this(input, Level.FINEST); + } + + public M4AInfo(InputStream input, Level debugLevel) throws IOException { + this.debugLevel = debugLevel; + MP4Input mp4 = new MP4Input(input); + if (LOGGER.isLoggable(debugLevel)) { + LOGGER.log(debugLevel, mp4.toString()); + } + ftyp(mp4.nextChild("ftyp")); + moov(mp4.nextChildUpTo("moov")); + } + + void ftyp(MP4Atom atom) throws IOException { + if (LOGGER.isLoggable(debugLevel)) { + LOGGER.log(debugLevel, atom.toString()); + } + brand = atom.readString(4, ASCII).trim(); + if (brand.matches("M4V|MP4|mp42|isom")) { // experimental file types + LOGGER.warning(atom.getPath() + ": brand=" + brand + " (experimental)"); + } else if (!brand.matches("M4A|M4P")) { + LOGGER.warning(atom.getPath() + ": brand=" + brand + " (expected M4A or M4P)"); + } + version = String.valueOf(atom.readInt()); + } + + void moov(MP4Atom atom) throws IOException { + if (LOGGER.isLoggable(debugLevel)) { + LOGGER.log(debugLevel, atom.toString()); + } + while (atom.hasMoreChildren()) { + MP4Atom child = atom.nextChild(); + switch (child.getType()) { + case "mvhd": + mvhd(child); + break; + case "trak": + trak(child); + break; + case "udta": + udta(child); + break; + default: + break; + } + } + } + + void mvhd(MP4Atom atom) throws IOException { + if (LOGGER.isLoggable(debugLevel)) { + LOGGER.log(debugLevel, atom.toString()); + } + byte version = atom.readByte(); + atom.skip(3); // flags + atom.skip(version == 1 ? 16 : 8); // created/modified date + int scale = atom.readInt(); + long units = version == 1 ? atom.readLong() : atom.readInt(); + if (duration == 0) { + duration = 1000 * units / scale; + } else if (LOGGER.isLoggable(debugLevel) && Math.abs(duration - 1000 * units / scale) > 2) { + LOGGER.log(debugLevel, "mvhd: duration " + duration + " -> " + (1000 * units / scale)); + } + speed = atom.readIntegerFixedPoint(); + volume = atom.readShortFixedPoint(); + } + + void trak(MP4Atom atom) throws IOException { + if (LOGGER.isLoggable(debugLevel)) { + LOGGER.log(debugLevel, atom.toString()); + } + mdia(atom.nextChildUpTo("mdia")); + } + + void mdia(MP4Atom atom) throws IOException { + if (LOGGER.isLoggable(debugLevel)) { + LOGGER.log(debugLevel, atom.toString()); + } + mdhd(atom.nextChild("mdhd")); + } + + void mdhd(MP4Atom atom) throws IOException { + if (LOGGER.isLoggable(debugLevel)) { + LOGGER.log(debugLevel, atom.toString()); + } + byte version = atom.readByte(); + atom.skip(3); + atom.skip(version == 1 ? 16 : 8); // created/modified date + int sampleRate = atom.readInt(); + long samples = version == 1 ? atom.readLong() : atom.readInt(); + if (duration == 0) { + duration = 1000 * samples / sampleRate; + } else if (LOGGER.isLoggable(debugLevel) && Math.abs(duration - 1000 * samples / sampleRate) > 2) { + LOGGER.log(debugLevel, "mdhd: duration " + duration + " -> " + (1000 * samples / sampleRate)); + } + } + + void udta(MP4Atom atom) throws IOException { + if (LOGGER.isLoggable(debugLevel)) { + LOGGER.log(debugLevel, atom.toString()); + } + while (atom.hasMoreChildren()) { + MP4Atom child = atom.nextChild(); + if ("meta".equals(child.getType())) { + meta(child); + break; + } + } + } + + void meta(MP4Atom atom) throws IOException { + if (LOGGER.isLoggable(debugLevel)) { + LOGGER.log(debugLevel, atom.toString()); + } + atom.skip(4); // version/flags + while (atom.hasMoreChildren()) { + MP4Atom child = atom.nextChild(); + if ("ilst".equals(child.getType())) { + ilst(child); + break; + } + } + } + + void ilst(MP4Atom atom) throws IOException { + if (LOGGER.isLoggable(debugLevel)) { + LOGGER.log(debugLevel, atom.toString()); + } + while (atom.hasMoreChildren()) { + MP4Atom child = atom.nextChild(); + if (LOGGER.isLoggable(debugLevel)) { + LOGGER.log(debugLevel, child.toString()); + } + if (child.getRemaining() == 0) { + if (LOGGER.isLoggable(debugLevel)) { + LOGGER.log(debugLevel, child.getPath() + ": contains no value"); + } + continue; + } + data(child.nextChildUpTo("data")); + } + } + + void data(MP4Atom atom) throws IOException { + if (LOGGER.isLoggable(debugLevel)) { + LOGGER.log(debugLevel, atom.toString()); + } + atom.skip(4); // version & flags + atom.skip(4); // reserved + switch (atom.getParent().getType()) { + case "©alb": + album = atom.readString(UTF_8); + break; + case "aART": + albumArtist = atom.readString(UTF_8); + break; + case "©ART": + artist = atom.readString(UTF_8); + break; + case "©cmt": + comment = atom.readString(UTF_8); + break; + case "©com": + case "©wrt": + if (composer == null || composer.trim().length() == 0) { + composer = atom.readString(UTF_8); + } + break; + case "covr": + try { + byte[] bytes = atom.readBytes(); + BitmapFactory.Options opts = new BitmapFactory.Options(); + opts.inJustDecodeBounds = true; + opts.inSampleSize = 1; + BitmapFactory.decodeByteArray(bytes, 0, bytes.length, opts); + if (opts.outWidth > 800 || opts.outHeight > 800) { + int size = Math.max(opts.outWidth, opts.outHeight); + while (size > 800) { + opts.inSampleSize *= 2; + size /= 2; + } + } + opts.inJustDecodeBounds = false; + cover = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, opts); + if (cover != null) { + float scale = Math.max(cover.getWidth(), cover.getHeight()) / 120.0f; + if (scale > 0) { + smallCover = Bitmap.createScaledBitmap(cover, (int) (cover.getWidth() / scale), (int) (cover.getHeight() / scale), true); + } else { + smallCover = cover; + } + if (smallCover == null) { + smallCover = cover; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + break; + case "cpil": + compilation = atom.readBoolean(); + break; + case "cprt": + case "©cpy": + if (copyright == null || copyright.trim().length() == 0) { + copyright = atom.readString(UTF_8); + } + break; + case "©day": + String day = atom.readString(UTF_8).trim(); + if (day.length() >= 4) { + try { + year = Short.valueOf(day.substring(0, 4)); + } catch (NumberFormatException e) { + // ignore + } + } + break; + case "disk": + atom.skip(2); // padding? + disc = atom.readShort(); + discs = atom.readShort(); + break; + case "gnre": + if (genre == null || genre.trim().length() == 0) { + if (atom.getRemaining() == 2) { // id3v1 genre? + int index = atom.readShort() - 1; + ID3v1Genre id3v1Genre = ID3v1Genre.getGenre(index); + if (id3v1Genre != null) { + genre = id3v1Genre.getDescription(); + } + } else { + genre = atom.readString(UTF_8); + } + } + break; + case "©gen": + if (genre == null || genre.trim().length() == 0) { + genre = atom.readString(UTF_8); + } + break; + case "©grp": + grouping = atom.readString(UTF_8); + break; + case "©lyr": + lyrics = atom.readString(UTF_8); + break; + case "©nam": + title = atom.readString(UTF_8); + break; + case "rtng": + rating = atom.readByte(); + break; + case "tmpo": + tempo = atom.readShort(); + break; + case "trkn": + atom.skip(2); // padding? + track = atom.readShort(); + tracks = atom.readShort(); + break; + default: + break; + } + } + + public short getTempo() { + return tempo; + } + + public byte getRating() { + return rating; + } + + public BigDecimal getSpeed() { + return speed; + } + + public BigDecimal getVolume() { + return volume; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/audioinfo/m4a/MP4Atom.java b/TMessagesProj/src/main/java/org/telegram/messenger/audioinfo/m4a/MP4Atom.java index dc31fab8e..1520b8b18 100755 --- a/TMessagesProj/src/main/java/org/telegram/messenger/audioinfo/m4a/MP4Atom.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/audioinfo/m4a/MP4Atom.java @@ -1,151 +1,151 @@ -/* - * Copyright 2013-2014 Odysseus Software GmbH - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.telegram.messenger.audioinfo.m4a; - -import org.telegram.messenger.audioinfo.util.RangeInputStream; - -import java.io.EOFException; -import java.io.IOException; -import java.math.BigDecimal; - -public class MP4Atom extends MP4Box { - public MP4Atom(RangeInputStream input, MP4Box parent, String type) { - super(input, parent, type); - } - - public long getLength() { - return getInput().getPosition() + getInput().getRemainingLength(); - } - - public long getOffset() { - return getParent().getPosition() - getPosition(); - } - - public long getRemaining() { - return getInput().getRemainingLength(); - } - - public boolean hasMoreChildren() { - return (getChild() != null ? getChild().getRemaining() : 0) < getRemaining(); - } - - public MP4Atom nextChildUpTo(String expectedTypeExpression) throws IOException { - while (getRemaining() > 0) { - MP4Atom atom = nextChild(); - if (atom.getType().matches(expectedTypeExpression)) { - return atom; - } - } - throw new IOException("atom type mismatch, not found: " + expectedTypeExpression); - } - - public boolean readBoolean() throws IOException { - return data.readBoolean(); - } - - public byte readByte() throws IOException { - return data.readByte(); - } - - public short readShort() throws IOException { - return data.readShort(); - } - - public int readInt() throws IOException { - return data.readInt(); - } - - public long readLong() throws IOException { - return data.readLong(); - } - - public byte[] readBytes(int len) throws IOException { - byte[] bytes = new byte[len]; - data.readFully(bytes); - return bytes; - } - - public byte[] readBytes() throws IOException { - return readBytes((int) getRemaining()); - } - - public BigDecimal readShortFixedPoint() throws IOException { - int integer = data.readByte(); - int decimal = data.readUnsignedByte(); - return new BigDecimal(String.valueOf(integer) + "" + String.valueOf(decimal)); - } - - public BigDecimal readIntegerFixedPoint() throws IOException { - int integer = data.readShort(); - int decimal = data.readUnsignedShort(); - return new BigDecimal(String.valueOf(integer) + "" + String.valueOf(decimal)); - } - - public String readString(int len, String enc) throws IOException { - String s = new String(readBytes(len), enc); - int end = s.indexOf(0); - return end < 0 ? s : s.substring(0, end); - } - - public String readString(String enc) throws IOException { - return readString((int) getRemaining(), enc); - } - - public void skip(int len) throws IOException { - int total = 0; - while (total < len) { - int current = data.skipBytes(len - total); - if (current > 0) { - total += current; - } else { - throw new EOFException(); - } - } - } - - public void skip() throws IOException { - while (getRemaining() > 0) { - if (getInput().skip(getRemaining()) == 0) { - throw new EOFException("Cannot skip atom"); - } - } - } - - private StringBuffer appendPath(StringBuffer s, MP4Box box) { - if (box.getParent() != null) { - appendPath(s, box.getParent()); - s.append("/"); - } - return s.append(box.getType()); - } - - public String getPath() { - return appendPath(new StringBuffer(), this).toString(); - } - - public String toString() { - StringBuffer s = new StringBuffer(); - appendPath(s, this); - s.append("[off="); - s.append(getOffset()); - s.append(",pos="); - s.append(getPosition()); - s.append(",len="); - s.append(getLength()); - s.append("]"); - return s.toString(); - } -} +/* + * Copyright 2013-2014 Odysseus Software GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.telegram.messenger.audioinfo.m4a; + +import org.telegram.messenger.audioinfo.util.RangeInputStream; + +import java.io.EOFException; +import java.io.IOException; +import java.math.BigDecimal; + +public class MP4Atom extends MP4Box { + public MP4Atom(RangeInputStream input, MP4Box parent, String type) { + super(input, parent, type); + } + + public long getLength() { + return getInput().getPosition() + getInput().getRemainingLength(); + } + + public long getOffset() { + return getParent().getPosition() - getPosition(); + } + + public long getRemaining() { + return getInput().getRemainingLength(); + } + + public boolean hasMoreChildren() { + return (getChild() != null ? getChild().getRemaining() : 0) < getRemaining(); + } + + public MP4Atom nextChildUpTo(String expectedTypeExpression) throws IOException { + while (getRemaining() > 0) { + MP4Atom atom = nextChild(); + if (atom.getType().matches(expectedTypeExpression)) { + return atom; + } + } + throw new IOException("atom type mismatch, not found: " + expectedTypeExpression); + } + + public boolean readBoolean() throws IOException { + return data.readBoolean(); + } + + public byte readByte() throws IOException { + return data.readByte(); + } + + public short readShort() throws IOException { + return data.readShort(); + } + + public int readInt() throws IOException { + return data.readInt(); + } + + public long readLong() throws IOException { + return data.readLong(); + } + + public byte[] readBytes(int len) throws IOException { + byte[] bytes = new byte[len]; + data.readFully(bytes); + return bytes; + } + + public byte[] readBytes() throws IOException { + return readBytes((int) getRemaining()); + } + + public BigDecimal readShortFixedPoint() throws IOException { + int integer = data.readByte(); + int decimal = data.readUnsignedByte(); + return new BigDecimal(String.valueOf(integer) + "" + String.valueOf(decimal)); + } + + public BigDecimal readIntegerFixedPoint() throws IOException { + int integer = data.readShort(); + int decimal = data.readUnsignedShort(); + return new BigDecimal(String.valueOf(integer) + "" + String.valueOf(decimal)); + } + + public String readString(int len, String enc) throws IOException { + String s = new String(readBytes(len), enc); + int end = s.indexOf(0); + return end < 0 ? s : s.substring(0, end); + } + + public String readString(String enc) throws IOException { + return readString((int) getRemaining(), enc); + } + + public void skip(int len) throws IOException { + int total = 0; + while (total < len) { + int current = data.skipBytes(len - total); + if (current > 0) { + total += current; + } else { + throw new EOFException(); + } + } + } + + public void skip() throws IOException { + while (getRemaining() > 0) { + if (getInput().skip(getRemaining()) == 0) { + throw new EOFException("Cannot skip atom"); + } + } + } + + private StringBuffer appendPath(StringBuffer s, MP4Box box) { + if (box.getParent() != null) { + appendPath(s, box.getParent()); + s.append("/"); + } + return s.append(box.getType()); + } + + public String getPath() { + return appendPath(new StringBuffer(), this).toString(); + } + + public String toString() { + StringBuffer s = new StringBuffer(); + appendPath(s, this); + s.append("[off="); + s.append(getOffset()); + s.append(",pos="); + s.append(getPosition()); + s.append(",len="); + s.append(getLength()); + s.append("]"); + return s.toString(); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/browser/Browser.java b/TMessagesProj/src/main/java/org/telegram/messenger/browser/Browser.java index f7b6f32cd..d6098c56a 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/browser/Browser.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/browser/Browser.java @@ -348,7 +348,7 @@ public class Browser { } return true; - } else if ("tg".equals(uri.getScheme())) { + } else if ("tg".equals(uri.getScheme()) || "vmess".equals(uri.getScheme()) || "ss".equals(uri.getScheme()) || "ssr".equals(uri.getScheme())) { return true; } else if ("telegram.dog".equals(host)) { String path = uri.getPath(); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPBaseService.java b/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPBaseService.java index dac5c51e1..b859d1d3b 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPBaseService.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPBaseService.java @@ -94,416 +94,416 @@ import java.util.List; * Created by grishka on 21.07.17. */ -public abstract class VoIPBaseService extends Service implements SensorEventListener, AudioManager.OnAudioFocusChangeListener, VoIPController.ConnectionStateListener, NotificationCenter.NotificationCenterDelegate{ +public abstract class VoIPBaseService extends Service implements SensorEventListener, AudioManager.OnAudioFocusChangeListener, VoIPController.ConnectionStateListener, NotificationCenter.NotificationCenterDelegate { - protected int currentAccount = -1; - public static final int STATE_WAIT_INIT = 1; - public static final int STATE_WAIT_INIT_ACK = 2; - public static final int STATE_ESTABLISHED = 3; - public static final int STATE_FAILED = 4; - public static final int STATE_RECONNECTING = 5; - public static final int STATE_ENDED = 11; - public static final String ACTION_HEADSET_PLUG = "android.intent.action.HEADSET_PLUG"; + protected int currentAccount = -1; + public static final int STATE_WAIT_INIT = 1; + public static final int STATE_WAIT_INIT_ACK = 2; + public static final int STATE_ESTABLISHED = 3; + public static final int STATE_FAILED = 4; + public static final int STATE_RECONNECTING = 5; + public static final int STATE_ENDED = 11; + public static final String ACTION_HEADSET_PLUG = "android.intent.action.HEADSET_PLUG"; - protected static final int ID_ONGOING_CALL_NOTIFICATION = 201; - protected static final int ID_INCOMING_CALL_NOTIFICATION = 202; + protected static final int ID_ONGOING_CALL_NOTIFICATION = 201; + protected static final int ID_INCOMING_CALL_NOTIFICATION = 202; - public static final int DISCARD_REASON_HANGUP = 1; - public static final int DISCARD_REASON_DISCONNECT = 2; - public static final int DISCARD_REASON_MISSED = 3; - public static final int DISCARD_REASON_LINE_BUSY = 4; + public static final int DISCARD_REASON_HANGUP = 1; + public static final int DISCARD_REASON_DISCONNECT = 2; + public static final int DISCARD_REASON_MISSED = 3; + public static final int DISCARD_REASON_LINE_BUSY = 4; - public static final int AUDIO_ROUTE_EARPIECE=0; - public static final int AUDIO_ROUTE_SPEAKER=1; - public static final int AUDIO_ROUTE_BLUETOOTH=2; + public static final int AUDIO_ROUTE_EARPIECE = 0; + public static final int AUDIO_ROUTE_SPEAKER = 1; + public static final int AUDIO_ROUTE_BLUETOOTH = 2; - protected static final boolean USE_CONNECTION_SERVICE=isDeviceCompatibleWithConnectionServiceAPI(); + protected static final boolean USE_CONNECTION_SERVICE = isDeviceCompatibleWithConnectionServiceAPI(); - protected static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK = 32; - protected static VoIPBaseService sharedInstance; - protected NetworkInfo lastNetInfo; - protected int currentState = 0; - protected Notification ongoingCallNotification; - protected VoIPController controller; - protected int lastError; - protected PowerManager.WakeLock proximityWakelock; - protected PowerManager.WakeLock cpuWakelock; - protected boolean isProximityNear; - protected boolean isHeadsetPlugged; - protected ArrayList stateListeners = new ArrayList<>(); - protected MediaPlayer ringtonePlayer; - protected Vibrator vibrator; - protected SoundPool soundPool; - protected int spRingbackID; - protected int spFailedID; - protected int spEndId; - protected int spBusyId; - protected int spConnectingId; - protected int spPlayID; - protected boolean needPlayEndSound; - protected boolean haveAudioFocus; - protected boolean micMute; - protected boolean controllerStarted; - protected BluetoothAdapter btAdapter; - protected VoIPController.Stats stats = new VoIPController.Stats(); - protected VoIPController.Stats prevStats = new VoIPController.Stats(); - protected boolean isBtHeadsetConnected; - protected Runnable afterSoundRunnable=new Runnable(){ - @Override - public void run(){ - soundPool.release(); - if(USE_CONNECTION_SERVICE) - return; - if(isBtHeadsetConnected) - ((AudioManager) ApplicationLoader.applicationContext.getSystemService(AUDIO_SERVICE)).stopBluetoothSco(); - ((AudioManager)ApplicationLoader.applicationContext.getSystemService(AUDIO_SERVICE)).setSpeakerphoneOn(false); - } - }; - protected long lastKnownDuration = 0; - protected boolean playingSound; - protected boolean isOutgoing; - protected Runnable timeoutRunnable; - protected BroadcastReceiver receiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (ACTION_HEADSET_PLUG.equals(intent.getAction())) { - isHeadsetPlugged = intent.getIntExtra("state", 0) == 1; - if (isHeadsetPlugged && proximityWakelock != null && proximityWakelock.isHeld()) { - proximityWakelock.release(); - } - isProximityNear = false; - updateOutputGainControlState(); - } else if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) { - updateNetworkType(); - } else if(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())){ - if(BuildVars.LOGS_ENABLED) - FileLog.e("bt headset state = "+intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0)); - updateBluetoothHeadsetState(intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED)==BluetoothProfile.STATE_CONNECTED); - }else if(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED.equals(intent.getAction())){ - int state=intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, AudioManager.SCO_AUDIO_STATE_DISCONNECTED); - if(BuildVars.LOGS_ENABLED) - FileLog.e("Bluetooth SCO state updated: "+state); - if(state==AudioManager.SCO_AUDIO_STATE_DISCONNECTED && isBtHeadsetConnected){ - if(!btAdapter.isEnabled() || btAdapter.getProfileConnectionState(BluetoothProfile.HEADSET)!=BluetoothProfile.STATE_CONNECTED){ - updateBluetoothHeadsetState(false); - return; - } - } - bluetoothScoActive=state==AudioManager.SCO_AUDIO_STATE_CONNECTED; - if(bluetoothScoActive && needSwitchToBluetoothAfterScoActivates){ - needSwitchToBluetoothAfterScoActivates=false; - AudioManager am=(AudioManager) getSystemService(AUDIO_SERVICE); - am.setSpeakerphoneOn(false); - am.setBluetoothScoOn(true); - } - for(StateListener l : stateListeners) - l.onAudioSettingsChanged(); - }else if(TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(intent.getAction())){ - String state=intent.getStringExtra(TelephonyManager.EXTRA_STATE); - if(TelephonyManager.EXTRA_STATE_OFFHOOK.equals(state)){ - hangUp(); - } - } - } - }; - private Boolean mHasEarpiece = null; - private boolean wasEstablished; - protected int signalBarCount; - protected boolean audioConfigured; - protected int audioRouteToSet=AUDIO_ROUTE_BLUETOOTH; - protected boolean speakerphoneStateToSet; - protected CallConnection systemCallConnection; - protected int callDiscardReason; - protected boolean bluetoothScoActive=false; - protected boolean needSwitchToBluetoothAfterScoActivates=false; - protected boolean didDeleteConnectionServiceContact=false; - protected Runnable connectingSoundRunnable; + protected static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK = 32; + protected static VoIPBaseService sharedInstance; + protected NetworkInfo lastNetInfo; + protected int currentState = 0; + protected Notification ongoingCallNotification; + protected VoIPController controller; + protected int lastError; + protected PowerManager.WakeLock proximityWakelock; + protected PowerManager.WakeLock cpuWakelock; + protected boolean isProximityNear; + protected boolean isHeadsetPlugged; + protected ArrayList stateListeners = new ArrayList<>(); + protected MediaPlayer ringtonePlayer; + protected Vibrator vibrator; + protected SoundPool soundPool; + protected int spRingbackID; + protected int spFailedID; + protected int spEndId; + protected int spBusyId; + protected int spConnectingId; + protected int spPlayID; + protected boolean needPlayEndSound; + protected boolean haveAudioFocus; + protected boolean micMute; + protected boolean controllerStarted; + protected BluetoothAdapter btAdapter; + protected VoIPController.Stats stats = new VoIPController.Stats(); + protected VoIPController.Stats prevStats = new VoIPController.Stats(); + protected boolean isBtHeadsetConnected; + protected Runnable afterSoundRunnable = new Runnable() { + @Override + public void run() { + soundPool.release(); + if (USE_CONNECTION_SERVICE) + return; + if (isBtHeadsetConnected) + ((AudioManager) ApplicationLoader.applicationContext.getSystemService(AUDIO_SERVICE)).stopBluetoothSco(); + ((AudioManager) ApplicationLoader.applicationContext.getSystemService(AUDIO_SERVICE)).setSpeakerphoneOn(false); + } + }; + protected long lastKnownDuration = 0; + protected boolean playingSound; + protected boolean isOutgoing; + protected Runnable timeoutRunnable; + protected BroadcastReceiver receiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (ACTION_HEADSET_PLUG.equals(intent.getAction())) { + isHeadsetPlugged = intent.getIntExtra("state", 0) == 1; + if (isHeadsetPlugged && proximityWakelock != null && proximityWakelock.isHeld()) { + proximityWakelock.release(); + } + isProximityNear = false; + updateOutputGainControlState(); + } else if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) { + updateNetworkType(); + } else if (BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) { + if (BuildVars.LOGS_ENABLED) + FileLog.e("bt headset state = " + intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0)); + updateBluetoothHeadsetState(intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED) == BluetoothProfile.STATE_CONNECTED); + } else if (AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED.equals(intent.getAction())) { + int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, AudioManager.SCO_AUDIO_STATE_DISCONNECTED); + if (BuildVars.LOGS_ENABLED) + FileLog.e("Bluetooth SCO state updated: " + state); + if (state == AudioManager.SCO_AUDIO_STATE_DISCONNECTED && isBtHeadsetConnected) { + if (!btAdapter.isEnabled() || btAdapter.getProfileConnectionState(BluetoothProfile.HEADSET) != BluetoothProfile.STATE_CONNECTED) { + updateBluetoothHeadsetState(false); + return; + } + } + bluetoothScoActive = state == AudioManager.SCO_AUDIO_STATE_CONNECTED; + if (bluetoothScoActive && needSwitchToBluetoothAfterScoActivates) { + needSwitchToBluetoothAfterScoActivates = false; + AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); + am.setSpeakerphoneOn(false); + am.setBluetoothScoOn(true); + } + for (StateListener l : stateListeners) + l.onAudioSettingsChanged(); + } else if (TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(intent.getAction())) { + String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE); + if (TelephonyManager.EXTRA_STATE_OFFHOOK.equals(state)) { + hangUp(); + } + } + } + }; + private Boolean mHasEarpiece = null; + private boolean wasEstablished; + protected int signalBarCount; + protected boolean audioConfigured; + protected int audioRouteToSet = AUDIO_ROUTE_BLUETOOTH; + protected boolean speakerphoneStateToSet; + protected CallConnection systemCallConnection; + protected int callDiscardReason; + protected boolean bluetoothScoActive = false; + protected boolean needSwitchToBluetoothAfterScoActivates = false; + protected boolean didDeleteConnectionServiceContact = false; + protected Runnable connectingSoundRunnable; - public boolean hasEarpiece() { - if(USE_CONNECTION_SERVICE){ - if(systemCallConnection!=null && systemCallConnection.getCallAudioState()!=null){ - int routeMask=systemCallConnection.getCallAudioState().getSupportedRouteMask(); - return (routeMask & (CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_WIRED_HEADSET))!=0; - } - } - if(((TelephonyManager)getSystemService(TELEPHONY_SERVICE)).getPhoneType()!=TelephonyManager.PHONE_TYPE_NONE) - return true; - if (mHasEarpiece != null) { - return mHasEarpiece; - } + public boolean hasEarpiece() { + if (USE_CONNECTION_SERVICE) { + if (systemCallConnection != null && systemCallConnection.getCallAudioState() != null) { + int routeMask = systemCallConnection.getCallAudioState().getSupportedRouteMask(); + return (routeMask & (CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_WIRED_HEADSET)) != 0; + } + } + if (((TelephonyManager) getSystemService(TELEPHONY_SERVICE)).getPhoneType() != TelephonyManager.PHONE_TYPE_NONE) + return true; + if (mHasEarpiece != null) { + return mHasEarpiece; + } - // not calculated yet, do it now - try { - AudioManager am=(AudioManager)getSystemService(AUDIO_SERVICE); - Method method = AudioManager.class.getMethod("getDevicesForStream", Integer.TYPE); - Field field = AudioManager.class.getField("DEVICE_OUT_EARPIECE"); - int earpieceFlag = field.getInt(null); - int bitmaskResult = (int) method.invoke(am, AudioManager.STREAM_VOICE_CALL); + // not calculated yet, do it now + try { + AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); + Method method = AudioManager.class.getMethod("getDevicesForStream", Integer.TYPE); + Field field = AudioManager.class.getField("DEVICE_OUT_EARPIECE"); + int earpieceFlag = field.getInt(null); + int bitmaskResult = (int) method.invoke(am, AudioManager.STREAM_VOICE_CALL); - // check if masked by the earpiece flag - if ((bitmaskResult & earpieceFlag) == earpieceFlag) { - mHasEarpiece = Boolean.TRUE; - } else { - mHasEarpiece = Boolean.FALSE; - } - } catch (Throwable error) { - if (BuildVars.LOGS_ENABLED) { - FileLog.e("Error while checking earpiece! ", error); - } - mHasEarpiece = Boolean.TRUE; - } + // check if masked by the earpiece flag + if ((bitmaskResult & earpieceFlag) == earpieceFlag) { + mHasEarpiece = Boolean.TRUE; + } else { + mHasEarpiece = Boolean.FALSE; + } + } catch (Throwable error) { + if (BuildVars.LOGS_ENABLED) { + FileLog.e("Error while checking earpiece! ", error); + } + mHasEarpiece = Boolean.TRUE; + } - return mHasEarpiece; - } + return mHasEarpiece; + } - protected int getStatsNetworkType() { - int netType = StatsController.TYPE_WIFI; - if (lastNetInfo != null) { - if (lastNetInfo.getType() == ConnectivityManager.TYPE_MOBILE) - netType = lastNetInfo.isRoaming() ? StatsController.TYPE_ROAMING : StatsController.TYPE_MOBILE; - } - return netType; - } + protected int getStatsNetworkType() { + int netType = StatsController.TYPE_WIFI; + if (lastNetInfo != null) { + if (lastNetInfo.getType() == ConnectivityManager.TYPE_MOBILE) + netType = lastNetInfo.isRoaming() ? StatsController.TYPE_ROAMING : StatsController.TYPE_MOBILE; + } + return netType; + } - public void registerStateListener(StateListener l) { - stateListeners.add(l); - if (currentState != 0) - l.onStateChanged(currentState); - if(signalBarCount!=0) - l.onSignalBarsCountChanged(signalBarCount); - } + public void registerStateListener(StateListener l) { + stateListeners.add(l); + if (currentState != 0) + l.onStateChanged(currentState); + if (signalBarCount != 0) + l.onSignalBarsCountChanged(signalBarCount); + } - public void unregisterStateListener(StateListener l) { - stateListeners.remove(l); - } + public void unregisterStateListener(StateListener l) { + stateListeners.remove(l); + } - public void setMicMute(boolean mute) { - micMute=mute; - if(controller!=null) - controller.setMicMute(mute); - } + public void setMicMute(boolean mute) { + micMute = mute; + if (controller != null) + controller.setMicMute(mute); + } - public boolean isMicMute() { - return micMute; - } + public boolean isMicMute() { + return micMute; + } - public void toggleSpeakerphoneOrShowRouteSheet(Activity activity){ - if(isBluetoothHeadsetConnected() && hasEarpiece()){ - BottomSheet.Builder bldr=new BottomSheet.Builder(activity) - .setItems(new CharSequence[]{LocaleController.getString("VoipAudioRoutingBluetooth", R.string.VoipAudioRoutingBluetooth), - LocaleController.getString("VoipAudioRoutingEarpiece", R.string.VoipAudioRoutingEarpiece), - LocaleController.getString("VoipAudioRoutingSpeaker", R.string.VoipAudioRoutingSpeaker)}, - new int[]{R.drawable.ic_bluetooth_white_24dp, - R.drawable.ic_phone_in_talk_white_24dp, - R.drawable.ic_volume_up_white_24dp}, new DialogInterface.OnClickListener(){ - @Override - public void onClick(DialogInterface dialog, int which){ - AudioManager am=(AudioManager)getSystemService(AUDIO_SERVICE); - if(getSharedInstance()==null) - return; - if(USE_CONNECTION_SERVICE && systemCallConnection!=null){ - switch(which){ - case 0: - systemCallConnection.setAudioRoute(CallAudioState.ROUTE_BLUETOOTH); - break; - case 1: - systemCallConnection.setAudioRoute(CallAudioState.ROUTE_WIRED_OR_EARPIECE); - break; - case 2: - systemCallConnection.setAudioRoute(CallAudioState.ROUTE_SPEAKER); - break; - } - }else if(audioConfigured && !USE_CONNECTION_SERVICE){ - switch(which){ - case 0: - if(!bluetoothScoActive){ - needSwitchToBluetoothAfterScoActivates=true; - try { - am.startBluetoothSco(); - } catch (Throwable ignore) { + public void toggleSpeakerphoneOrShowRouteSheet(Activity activity) { + if (isBluetoothHeadsetConnected() && hasEarpiece()) { + BottomSheet.Builder bldr = new BottomSheet.Builder(activity) + .setItems(new CharSequence[]{LocaleController.getString("VoipAudioRoutingBluetooth", R.string.VoipAudioRoutingBluetooth), + LocaleController.getString("VoipAudioRoutingEarpiece", R.string.VoipAudioRoutingEarpiece), + LocaleController.getString("VoipAudioRoutingSpeaker", R.string.VoipAudioRoutingSpeaker)}, + new int[]{R.drawable.ic_bluetooth_white_24dp, + R.drawable.ic_phone_in_talk_white_24dp, + R.drawable.ic_volume_up_white_24dp}, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); + if (getSharedInstance() == null) + return; + if (USE_CONNECTION_SERVICE && systemCallConnection != null) { + switch (which) { + case 0: + systemCallConnection.setAudioRoute(CallAudioState.ROUTE_BLUETOOTH); + break; + case 1: + systemCallConnection.setAudioRoute(CallAudioState.ROUTE_WIRED_OR_EARPIECE); + break; + case 2: + systemCallConnection.setAudioRoute(CallAudioState.ROUTE_SPEAKER); + break; + } + } else if (audioConfigured && !USE_CONNECTION_SERVICE) { + switch (which) { + case 0: + if (!bluetoothScoActive) { + needSwitchToBluetoothAfterScoActivates = true; + try { + am.startBluetoothSco(); + } catch (Throwable ignore) { - } - }else{ - am.setBluetoothScoOn(true); - am.setSpeakerphoneOn(false); - } - break; - case 1: - if(bluetoothScoActive) - am.stopBluetoothSco(); - am.setSpeakerphoneOn(false); - am.setBluetoothScoOn(false); - break; - case 2: - if(bluetoothScoActive) - am.stopBluetoothSco(); - am.setBluetoothScoOn(false); - am.setSpeakerphoneOn(true); - break; - } - updateOutputGainControlState(); - }else{ - switch(which){ - case 0: - audioRouteToSet=AUDIO_ROUTE_BLUETOOTH; - break; - case 1: - audioRouteToSet=AUDIO_ROUTE_EARPIECE; - break; - case 2: - audioRouteToSet=AUDIO_ROUTE_SPEAKER; - break; - } - } - for(StateListener l:stateListeners) - l.onAudioSettingsChanged(); - } - }); - BottomSheet sheet=bldr.create(); - sheet.setBackgroundColor(0xff2b2b2b); - sheet.show(); - ViewGroup container=sheet.getSheetContainer(); - for(int i=0;i activity) { - Intent intent = new Intent(this, activity); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP); - Notification.Builder builder = new Notification.Builder(this) - .setContentTitle(LocaleController.getString("VoipOutgoingCall", R.string.VoipOutgoingCall)) - .setContentText(name) - .setSmallIcon(R.drawable.notification) - .setContentIntent(PendingIntent.getActivity(this, 0, intent, 0)); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - Intent endIntent = new Intent(this, VoIPActionsReceiver.class); - endIntent.setAction(getPackageName() + ".END_CALL"); - builder.addAction(R.drawable.ic_call_end_white_24dp, LocaleController.getString("VoipEndCall", R.string.VoipEndCall), PendingIntent.getBroadcast(this, 0, endIntent, PendingIntent.FLAG_UPDATE_CURRENT)); - builder.setPriority(Notification.PRIORITY_MAX); - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - builder.setShowWhen(false); - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - builder.setColor(0xff2ca5e0); - } - if (Build.VERSION.SDK_INT >= 26) { - NotificationsController.checkOtherNotificationsChannel(); - builder.setChannelId(NotificationsController.OTHER_NOTIFICATIONS_CHANNEL); - } - if (photo!= null) { - BitmapDrawable img = ImageLoader.getInstance().getImageFromMemory(photo, null, "50_50"); - if (img != null) { - builder.setLargeIcon(img.getBitmap()); - } else { - try { - float scaleFactor = 160.0f / AndroidUtilities.dp(50); - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inSampleSize = scaleFactor < 1 ? 1 : (int) scaleFactor; - Bitmap bitmap = BitmapFactory.decodeFile(FileLoader.getPathToAttach(photo, true).toString(), options); - if (bitmap != null) { - builder.setLargeIcon(bitmap); - } - } catch (Throwable e) { - FileLog.e(e); - } - } - } - ongoingCallNotification = builder.getNotification(); - startForeground(ID_ONGOING_CALL_NOTIFICATION, ongoingCallNotification); - } + protected void showNotification(String name, TLRPC.FileLocation photo, Class activity) { + Intent intent = new Intent(this, activity); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP); + Notification.Builder builder = new Notification.Builder(this) + .setContentTitle(LocaleController.getString("VoipOutgoingCall", R.string.VoipOutgoingCall)) + .setContentText(name) + .setSmallIcon(R.drawable.notification) + .setContentIntent(PendingIntent.getActivity(this, 0, intent, 0)); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + Intent endIntent = new Intent(this, VoIPActionsReceiver.class); + endIntent.setAction(getPackageName() + ".END_CALL"); + builder.addAction(R.drawable.ic_call_end_white_24dp, LocaleController.getString("VoipEndCall", R.string.VoipEndCall), PendingIntent.getBroadcast(this, 0, endIntent, PendingIntent.FLAG_UPDATE_CURRENT)); + builder.setPriority(Notification.PRIORITY_MAX); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + builder.setShowWhen(false); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + builder.setColor(0xff2ca5e0); + } + if (Build.VERSION.SDK_INT >= 26) { + NotificationsController.checkOtherNotificationsChannel(); + builder.setChannelId(NotificationsController.OTHER_NOTIFICATIONS_CHANNEL); + } + if (photo != null) { + BitmapDrawable img = ImageLoader.getInstance().getImageFromMemory(photo, null, "50_50"); + if (img != null) { + builder.setLargeIcon(img.getBitmap()); + } else { + try { + float scaleFactor = 160.0f / AndroidUtilities.dp(50); + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inSampleSize = scaleFactor < 1 ? 1 : (int) scaleFactor; + Bitmap bitmap = BitmapFactory.decodeFile(FileLoader.getPathToAttach(photo, true).toString(), options); + if (bitmap != null) { + builder.setLargeIcon(bitmap); + } + } catch (Throwable e) { + FileLog.e(e); + } + } + } + ongoingCallNotification = builder.getNotification(); + startForeground(ID_ONGOING_CALL_NOTIFICATION, ongoingCallNotification); + } - protected void startRingtoneAndVibration(int chatID){ - SharedPreferences prefs = MessagesController.getNotificationsSettings(currentAccount); - AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); - boolean needRing=am.getRingerMode()!=AudioManager.RINGER_MODE_SILENT; + protected void startRingtoneAndVibration(int chatID) { + SharedPreferences prefs = MessagesController.getNotificationsSettings(currentAccount); + AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); + boolean needRing = am.getRingerMode() != AudioManager.RINGER_MODE_SILENT; /*if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP){ try{ int mode=Settings.Global.getInt(getContentResolver(), "zen_mode"); @@ -511,282 +511,292 @@ public abstract class VoIPBaseService extends Service implements SensorEventList needRing=mode==0; }catch(Exception ignore){} }*/ - if(needRing){ - if(!USE_CONNECTION_SERVICE){ - am.requestAudioFocus(this, AudioManager.STREAM_RING, AudioManager.AUDIOFOCUS_GAIN); - } - ringtonePlayer=new MediaPlayer(); - ringtonePlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener(){ - @Override - public void onPrepared(MediaPlayer mediaPlayer){ - ringtonePlayer.start(); - } - }); - ringtonePlayer.setLooping(true); - ringtonePlayer.setAudioStreamType(AudioManager.STREAM_RING); - try{ - String notificationUri; - if(prefs.getBoolean("custom_"+chatID, false)) - notificationUri=prefs.getString("ringtone_path_"+chatID, RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE).toString()); - else - notificationUri=prefs.getString("CallsRingtonePath", RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE).toString()); - ringtonePlayer.setDataSource(this, Uri.parse(notificationUri)); - ringtonePlayer.prepareAsync(); - }catch(Exception e){ - FileLog.e(e); - if(ringtonePlayer!=null){ - ringtonePlayer.release(); - ringtonePlayer=null; - } - } - int vibrate; - if(prefs.getBoolean("custom_"+chatID, false)) - vibrate=prefs.getInt("calls_vibrate_"+chatID, 0); - else - vibrate=prefs.getInt("vibrate_calls", 0); - if((vibrate!=2 && vibrate!=4 && (am.getRingerMode()==AudioManager.RINGER_MODE_VIBRATE || am.getRingerMode()==AudioManager.RINGER_MODE_NORMAL)) || - (vibrate==4 && am.getRingerMode()==AudioManager.RINGER_MODE_VIBRATE)){ - vibrator=(Vibrator) getSystemService(VIBRATOR_SERVICE); - long duration=700; - if(vibrate==1) - duration/=2; - else if(vibrate==3) - duration*=2; - vibrator.vibrate(new long[]{0, duration, 500}, 0); - } - } - } + if (needRing) { + if (!USE_CONNECTION_SERVICE) { + am.requestAudioFocus(this, AudioManager.STREAM_RING, AudioManager.AUDIOFOCUS_GAIN); + } + ringtonePlayer = new MediaPlayer(); + ringtonePlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { + @Override + public void onPrepared(MediaPlayer mediaPlayer) { + ringtonePlayer.start(); + } + }); + ringtonePlayer.setLooping(true); + ringtonePlayer.setAudioStreamType(AudioManager.STREAM_RING); + try { + String notificationUri; + if (prefs.getBoolean("custom_" + chatID, false)) + notificationUri = prefs.getString("ringtone_path_" + chatID, RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE).toString()); + else + notificationUri = prefs.getString("CallsRingtonePath", RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE).toString()); + ringtonePlayer.setDataSource(this, Uri.parse(notificationUri)); + ringtonePlayer.prepareAsync(); + } catch (Exception e) { + FileLog.e(e); + if (ringtonePlayer != null) { + ringtonePlayer.release(); + ringtonePlayer = null; + } + } + int vibrate; + if (prefs.getBoolean("custom_" + chatID, false)) + vibrate = prefs.getInt("calls_vibrate_" + chatID, 0); + else + vibrate = prefs.getInt("vibrate_calls", 0); + if ((vibrate != 2 && vibrate != 4 && (am.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE || am.getRingerMode() == AudioManager.RINGER_MODE_NORMAL)) || + (vibrate == 4 && am.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE)) { + vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE); + long duration = 700; + if (vibrate == 1) + duration /= 2; + else if (vibrate == 3) + duration *= 2; + vibrator.vibrate(new long[]{0, duration, 500}, 0); + } + } + } - public abstract long getCallID(); - public abstract void hangUp(); - public abstract void hangUp(Runnable onDone); - public abstract void acceptIncomingCall(); - public abstract void declineIncomingCall(int reason, Runnable onDone); - public abstract void declineIncomingCall(); - protected abstract Class getUIActivityClass(); - public abstract CallConnection getConnectionAndStartCall(); - protected abstract void startRinging(); - public abstract void startRingtoneAndVibration(); + public abstract long getCallID(); - @Override - public void onDestroy() { - if (BuildVars.LOGS_ENABLED) { - FileLog.d("=============== VoIPService STOPPING ==============="); - } - stopForeground(true); - stopRinging(); - NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.appDidLogout); - SensorManager sm = (SensorManager) getSystemService(SENSOR_SERVICE); - Sensor proximity = sm.getDefaultSensor(Sensor.TYPE_PROXIMITY); - if (proximity != null) { - sm.unregisterListener(this); - } - if (proximityWakelock != null && proximityWakelock.isHeld()) { - proximityWakelock.release(); - } - unregisterReceiver(receiver); - if(timeoutRunnable!=null){ - AndroidUtilities.cancelRunOnUIThread(timeoutRunnable); - timeoutRunnable=null; - } - super.onDestroy(); - sharedInstance = null; - AndroidUtilities.runOnUIThread(new Runnable(){ - @Override - public void run(){ - NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.didEndCall); - } - }); - if (controller != null && controllerStarted) { - lastKnownDuration = controller.getCallDuration(); - updateStats(); - StatsController.getInstance(currentAccount).incrementTotalCallsTime(getStatsNetworkType(), (int) (lastKnownDuration / 1000) % 5); - onControllerPreRelease(); - controller.release(); - controller = null; - } - cpuWakelock.release(); - AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); - if(!USE_CONNECTION_SERVICE){ - if(isBtHeadsetConnected && !playingSound){ - am.stopBluetoothSco(); - am.setSpeakerphoneOn(false); - } - try{ - am.setMode(AudioManager.MODE_NORMAL); - }catch(SecurityException x){ - if(BuildVars.LOGS_ENABLED){ - FileLog.e("Error setting audio more to normal", x); - } - } - am.abandonAudioFocus(this); - } - am.unregisterMediaButtonEventReceiver(new ComponentName(this, VoIPMediaButtonReceiver.class)); - if (haveAudioFocus) - am.abandonAudioFocus(this); + public abstract void hangUp(); - if (!playingSound) - soundPool.release(); + public abstract void hangUp(Runnable onDone); - if(USE_CONNECTION_SERVICE){ - if(!didDeleteConnectionServiceContact) - ContactsController.getInstance(currentAccount).deleteConnectionServiceContact(); - if(systemCallConnection!=null && !playingSound){ - systemCallConnection.destroy(); - } - } + public abstract void acceptIncomingCall(); - ConnectionsManager.getInstance(currentAccount).setAppPaused(true, false); - VoIPHelper.lastCallTime=System.currentTimeMillis(); - } + public abstract void declineIncomingCall(int reason, Runnable onDone); - protected void onControllerPreRelease(){ + public abstract void declineIncomingCall(); - } + protected abstract Class getUIActivityClass(); - protected VoIPController createController(){ - return new VoIPController(); - } + public abstract CallConnection getConnectionAndStartCall(); - protected void initializeAccountRelatedThings(){ - updateServerConfig(); - NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.appDidLogout); - ConnectionsManager.getInstance(currentAccount).setAppPaused(false, false); - controller = createController(); - controller.setConnectionStateListener(this); - } + protected abstract void startRinging(); - @Override - public void onCreate() { - super.onCreate(); - if (BuildVars.LOGS_ENABLED) { - FileLog.d("=============== VoIPService STARTING ==============="); - } - AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER)!=null) { - int outFramesPerBuffer = Integer.parseInt(am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER)); - VoIPController.setNativeBufferSize(outFramesPerBuffer); - } else { - VoIPController.setNativeBufferSize(AudioTrack.getMinBufferSize(48000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT) / 2); - } - try { - cpuWakelock = ((PowerManager) getSystemService(POWER_SERVICE)).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "telegram-voip"); - cpuWakelock.acquire(); + public abstract void startRingtoneAndVibration(); - btAdapter=am.isBluetoothScoAvailableOffCall() ? BluetoothAdapter.getDefaultAdapter() : null; + @Override + public void onDestroy() { + if (BuildVars.LOGS_ENABLED) { + FileLog.d("=============== VoIPService STOPPING ==============="); + } + stopForeground(true); + stopRinging(); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.appDidLogout); + SensorManager sm = (SensorManager) getSystemService(SENSOR_SERVICE); + Sensor proximity = sm.getDefaultSensor(Sensor.TYPE_PROXIMITY); + if (proximity != null) { + sm.unregisterListener(this); + } + if (proximityWakelock != null && proximityWakelock.isHeld()) { + proximityWakelock.release(); + } + unregisterReceiver(receiver); + if (timeoutRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(timeoutRunnable); + timeoutRunnable = null; + } + super.onDestroy(); + sharedInstance = null; + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.didEndCall); + } + }); + if (controller != null && controllerStarted) { + lastKnownDuration = controller.getCallDuration(); + updateStats(); + StatsController.getInstance(currentAccount).incrementTotalCallsTime(getStatsNetworkType(), (int) (lastKnownDuration / 1000) % 5); + onControllerPreRelease(); + controller.release(); + controller = null; + } + cpuWakelock.release(); + AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); + if (!USE_CONNECTION_SERVICE) { + if (isBtHeadsetConnected && !playingSound) { + am.stopBluetoothSco(); + am.setSpeakerphoneOn(false); + } + try { + am.setMode(AudioManager.MODE_NORMAL); + } catch (SecurityException x) { + if (BuildVars.LOGS_ENABLED) { + FileLog.e("Error setting audio more to normal", x); + } + } + am.abandonAudioFocus(this); + } + am.unregisterMediaButtonEventReceiver(new ComponentName(this, VoIPMediaButtonReceiver.class)); + if (haveAudioFocus) + am.abandonAudioFocus(this); - IntentFilter filter = new IntentFilter(); - filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); - if(!USE_CONNECTION_SERVICE){ - filter.addAction(ACTION_HEADSET_PLUG); - if(btAdapter!=null){ - filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); - filter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED); - } - filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); - } - registerReceiver(receiver, filter); + if (!playingSound) + soundPool.release(); - soundPool = new SoundPool(1, AudioManager.STREAM_VOICE_CALL, 0); - spConnectingId = soundPool.load(this, R.raw.voip_connecting, 1); - spRingbackID = soundPool.load(this, R.raw.voip_ringback, 1); - spFailedID = soundPool.load(this, R.raw.voip_failed, 1); - spEndId = soundPool.load(this, R.raw.voip_end, 1); - spBusyId = soundPool.load(this, R.raw.voip_busy, 1); + if (USE_CONNECTION_SERVICE) { + if (!didDeleteConnectionServiceContact) + ContactsController.getInstance(currentAccount).deleteConnectionServiceContact(); + if (systemCallConnection != null && !playingSound) { + systemCallConnection.destroy(); + } + } - am.registerMediaButtonEventReceiver(new ComponentName(this, VoIPMediaButtonReceiver.class)); + ConnectionsManager.getInstance(currentAccount).setAppPaused(true, false); + VoIPHelper.lastCallTime = System.currentTimeMillis(); + } - if(!USE_CONNECTION_SERVICE && btAdapter!=null && btAdapter.isEnabled()){ - int headsetState=btAdapter.getProfileConnectionState(BluetoothProfile.HEADSET); - updateBluetoothHeadsetState(headsetState==BluetoothProfile.STATE_CONNECTED); - //if(headsetState==BluetoothProfile.STATE_CONNECTED) - // am.setBluetoothScoOn(true); - for (StateListener l : stateListeners) - l.onAudioSettingsChanged(); - } + protected void onControllerPreRelease() { - } catch (Exception x) { - if (BuildVars.LOGS_ENABLED) { - FileLog.e("error initializing voip controller", x); - } - callFailed(); - } - } + } - protected abstract void updateServerConfig(); - protected abstract void showNotification(); + protected VoIPController createController() { + return new VoIPController(); + } - protected void dispatchStateChanged(int state) { - if (BuildVars.LOGS_ENABLED) { - FileLog.d("== Call " + getCallID() + " state changed to " + state + " =="); - } - currentState = state; - if(USE_CONNECTION_SERVICE && state==STATE_ESTABLISHED /*&& !wasEstablished*/ && systemCallConnection!=null){ - systemCallConnection.setActive(); - } - for (int a = 0; a < stateListeners.size(); a++) { - StateListener l = stateListeners.get(a); - l.onStateChanged(state); - } - } + protected void initializeAccountRelatedThings() { + updateServerConfig(); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.appDidLogout); + ConnectionsManager.getInstance(currentAccount).setAppPaused(false, false); + controller = createController(); + controller.setConnectionStateListener(this); + } - protected void updateStats() { - controller.getStats(stats); - long wifiSentDiff = stats.bytesSentWifi - prevStats.bytesSentWifi; - long wifiRecvdDiff = stats.bytesRecvdWifi - prevStats.bytesRecvdWifi; - long mobileSentDiff = stats.bytesSentMobile - prevStats.bytesSentMobile; - long mobileRecvdDiff = stats.bytesRecvdMobile - prevStats.bytesRecvdMobile; - VoIPController.Stats tmp = stats; - stats = prevStats; - prevStats = tmp; - if (wifiSentDiff > 0) - StatsController.getInstance(currentAccount).incrementSentBytesCount(StatsController.TYPE_WIFI, StatsController.TYPE_CALLS, wifiSentDiff); - if (wifiRecvdDiff > 0) - StatsController.getInstance(currentAccount).incrementReceivedBytesCount(StatsController.TYPE_WIFI, StatsController.TYPE_CALLS, wifiRecvdDiff); - if (mobileSentDiff > 0) - StatsController.getInstance(currentAccount).incrementSentBytesCount(lastNetInfo != null && lastNetInfo.isRoaming() ? StatsController.TYPE_ROAMING : StatsController.TYPE_MOBILE, - StatsController.TYPE_CALLS, mobileSentDiff); - if (mobileRecvdDiff > 0) - StatsController.getInstance(currentAccount).incrementReceivedBytesCount(lastNetInfo != null && lastNetInfo.isRoaming() ? StatsController.TYPE_ROAMING : StatsController.TYPE_MOBILE, - StatsController.TYPE_CALLS, mobileRecvdDiff); - } + @Override + public void onCreate() { + super.onCreate(); + if (BuildVars.LOGS_ENABLED) { + FileLog.d("=============== VoIPService STARTING ==============="); + } + AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER) != null) { + int outFramesPerBuffer = Integer.parseInt(am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER)); + VoIPController.setNativeBufferSize(outFramesPerBuffer); + } else { + VoIPController.setNativeBufferSize(AudioTrack.getMinBufferSize(48000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT) / 2); + } + try { + cpuWakelock = ((PowerManager) getSystemService(POWER_SERVICE)).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "telegram-voip"); + cpuWakelock.acquire(); - protected void configureDeviceForCall() { - needPlayEndSound = true; - AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); - if(!USE_CONNECTION_SERVICE){ - am.setMode(AudioManager.MODE_IN_COMMUNICATION); - am.requestAudioFocus(this, AudioManager.STREAM_VOICE_CALL, AudioManager.AUDIOFOCUS_GAIN); - if(isBluetoothHeadsetConnected() && hasEarpiece()){ - switch(audioRouteToSet){ - case AUDIO_ROUTE_BLUETOOTH: - if(!bluetoothScoActive){ - needSwitchToBluetoothAfterScoActivates=true; - try { - am.startBluetoothSco(); - } catch (Throwable ignore) { + btAdapter = am.isBluetoothScoAvailableOffCall() ? BluetoothAdapter.getDefaultAdapter() : null; - } - }else{ - am.setBluetoothScoOn(true); - am.setSpeakerphoneOn(false); - } - break; - case AUDIO_ROUTE_EARPIECE: - am.setBluetoothScoOn(false); - am.setSpeakerphoneOn(false); - break; - case AUDIO_ROUTE_SPEAKER: - am.setBluetoothScoOn(false); - am.setSpeakerphoneOn(true); - break; - } - }else if(isBluetoothHeadsetConnected()){ - am.setBluetoothScoOn(speakerphoneStateToSet); - }else{ - am.setSpeakerphoneOn(speakerphoneStateToSet); - } - }/*else{ + IntentFilter filter = new IntentFilter(); + filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); + if (!USE_CONNECTION_SERVICE) { + filter.addAction(ACTION_HEADSET_PLUG); + if (btAdapter != null) { + filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); + filter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED); + } + filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); + } + registerReceiver(receiver, filter); + + soundPool = new SoundPool(1, AudioManager.STREAM_VOICE_CALL, 0); + spConnectingId = soundPool.load(this, R.raw.voip_connecting, 1); + spRingbackID = soundPool.load(this, R.raw.voip_ringback, 1); + spFailedID = soundPool.load(this, R.raw.voip_failed, 1); + spEndId = soundPool.load(this, R.raw.voip_end, 1); + spBusyId = soundPool.load(this, R.raw.voip_busy, 1); + + am.registerMediaButtonEventReceiver(new ComponentName(this, VoIPMediaButtonReceiver.class)); + + if (!USE_CONNECTION_SERVICE && btAdapter != null && btAdapter.isEnabled()) { + int headsetState = btAdapter.getProfileConnectionState(BluetoothProfile.HEADSET); + updateBluetoothHeadsetState(headsetState == BluetoothProfile.STATE_CONNECTED); + //if(headsetState==BluetoothProfile.STATE_CONNECTED) + // am.setBluetoothScoOn(true); + for (StateListener l : stateListeners) + l.onAudioSettingsChanged(); + } + + } catch (Exception x) { + if (BuildVars.LOGS_ENABLED) { + FileLog.e("error initializing voip controller", x); + } + callFailed(); + } + } + + protected abstract void updateServerConfig(); + + protected abstract void showNotification(); + + protected void dispatchStateChanged(int state) { + if (BuildVars.LOGS_ENABLED) { + FileLog.d("== Call " + getCallID() + " state changed to " + state + " =="); + } + currentState = state; + if (USE_CONNECTION_SERVICE && state == STATE_ESTABLISHED /*&& !wasEstablished*/ && systemCallConnection != null) { + systemCallConnection.setActive(); + } + for (int a = 0; a < stateListeners.size(); a++) { + StateListener l = stateListeners.get(a); + l.onStateChanged(state); + } + } + + protected void updateStats() { + controller.getStats(stats); + long wifiSentDiff = stats.bytesSentWifi - prevStats.bytesSentWifi; + long wifiRecvdDiff = stats.bytesRecvdWifi - prevStats.bytesRecvdWifi; + long mobileSentDiff = stats.bytesSentMobile - prevStats.bytesSentMobile; + long mobileRecvdDiff = stats.bytesRecvdMobile - prevStats.bytesRecvdMobile; + VoIPController.Stats tmp = stats; + stats = prevStats; + prevStats = tmp; + if (wifiSentDiff > 0) + StatsController.getInstance(currentAccount).incrementSentBytesCount(StatsController.TYPE_WIFI, StatsController.TYPE_CALLS, wifiSentDiff); + if (wifiRecvdDiff > 0) + StatsController.getInstance(currentAccount).incrementReceivedBytesCount(StatsController.TYPE_WIFI, StatsController.TYPE_CALLS, wifiRecvdDiff); + if (mobileSentDiff > 0) + StatsController.getInstance(currentAccount).incrementSentBytesCount(lastNetInfo != null && lastNetInfo.isRoaming() ? StatsController.TYPE_ROAMING : StatsController.TYPE_MOBILE, + StatsController.TYPE_CALLS, mobileSentDiff); + if (mobileRecvdDiff > 0) + StatsController.getInstance(currentAccount).incrementReceivedBytesCount(lastNetInfo != null && lastNetInfo.isRoaming() ? StatsController.TYPE_ROAMING : StatsController.TYPE_MOBILE, + StatsController.TYPE_CALLS, mobileRecvdDiff); + } + + protected void configureDeviceForCall() { + needPlayEndSound = true; + AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); + if (!USE_CONNECTION_SERVICE) { + am.setMode(AudioManager.MODE_IN_COMMUNICATION); + am.requestAudioFocus(this, AudioManager.STREAM_VOICE_CALL, AudioManager.AUDIOFOCUS_GAIN); + if (isBluetoothHeadsetConnected() && hasEarpiece()) { + switch (audioRouteToSet) { + case AUDIO_ROUTE_BLUETOOTH: + if (!bluetoothScoActive) { + needSwitchToBluetoothAfterScoActivates = true; + try { + am.startBluetoothSco(); + } catch (Throwable ignore) { + + } + } else { + am.setBluetoothScoOn(true); + am.setSpeakerphoneOn(false); + } + break; + case AUDIO_ROUTE_EARPIECE: + am.setBluetoothScoOn(false); + am.setSpeakerphoneOn(false); + break; + case AUDIO_ROUTE_SPEAKER: + am.setBluetoothScoOn(false); + am.setSpeakerphoneOn(true); + break; + } + } else if (isBluetoothHeadsetConnected()) { + am.setBluetoothScoOn(speakerphoneStateToSet); + } else { + am.setSpeakerphoneOn(speakerphoneStateToSet); + } + }/*else{ if(isBluetoothHeadsetConnected() && hasEarpiece()){ switch(audioRouteToSet){ case AUDIO_ROUTE_BLUETOOTH: @@ -806,303 +816,303 @@ public abstract class VoIPBaseService extends Service implements SensorEventList systemCallConnection.setAudioRoute(!speakerphoneStateToSet ? CallAudioState.ROUTE_WIRED_OR_EARPIECE : CallAudioState.ROUTE_BLUETOOTH); } }*/ - updateOutputGainControlState(); - audioConfigured=true; + updateOutputGainControlState(); + audioConfigured = true; - SensorManager sm = (SensorManager) getSystemService(SENSOR_SERVICE); - Sensor proximity = sm.getDefaultSensor(Sensor.TYPE_PROXIMITY); - try{ - if(proximity!=null){ - proximityWakelock=((PowerManager) getSystemService(Context.POWER_SERVICE)).newWakeLock(PROXIMITY_SCREEN_OFF_WAKE_LOCK, "telegram-voip-prx"); - sm.registerListener(this, proximity, SensorManager.SENSOR_DELAY_NORMAL); - } - }catch(Exception x){ - if (BuildVars.LOGS_ENABLED) { - FileLog.e("Error initializing proximity sensor", x); - } - } - } + SensorManager sm = (SensorManager) getSystemService(SENSOR_SERVICE); + Sensor proximity = sm.getDefaultSensor(Sensor.TYPE_PROXIMITY); + try { + if (proximity != null) { + proximityWakelock = ((PowerManager) getSystemService(Context.POWER_SERVICE)).newWakeLock(PROXIMITY_SCREEN_OFF_WAKE_LOCK, "telegram-voip-prx"); + sm.registerListener(this, proximity, SensorManager.SENSOR_DELAY_NORMAL); + } + } catch (Exception x) { + if (BuildVars.LOGS_ENABLED) { + FileLog.e("Error initializing proximity sensor", x); + } + } + } - @SuppressLint("NewApi") - @Override - public void onSensorChanged(SensorEvent event) { - if (event.sensor.getType() == Sensor.TYPE_PROXIMITY) { - AudioManager am=(AudioManager) getSystemService(AUDIO_SERVICE); - if (isHeadsetPlugged || am.isSpeakerphoneOn() || (isBluetoothHeadsetConnected() && am.isBluetoothScoOn())) { - return; - } - boolean newIsNear = event.values[0] < Math.min(event.sensor.getMaximumRange(), 3); - if (newIsNear != isProximityNear) { - if (BuildVars.LOGS_ENABLED) { - FileLog.d("proximity " + newIsNear); - } - isProximityNear = newIsNear; - try{ - if(isProximityNear){ - proximityWakelock.acquire(); - }else{ - proximityWakelock.release(1); // this is non-public API before L - } - }catch(Exception x){ - FileLog.e(x); - } - } - } - } + @SuppressLint("NewApi") + @Override + public void onSensorChanged(SensorEvent event) { + if (event.sensor.getType() == Sensor.TYPE_PROXIMITY) { + AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); + if (isHeadsetPlugged || am.isSpeakerphoneOn() || (isBluetoothHeadsetConnected() && am.isBluetoothScoOn())) { + return; + } + boolean newIsNear = event.values[0] < Math.min(event.sensor.getMaximumRange(), 3); + if (newIsNear != isProximityNear) { + if (BuildVars.LOGS_ENABLED) { + FileLog.d("proximity " + newIsNear); + } + isProximityNear = newIsNear; + try { + if (isProximityNear) { + proximityWakelock.acquire(); + } else { + proximityWakelock.release(1); // this is non-public API before L + } + } catch (Exception x) { + FileLog.e(x); + } + } + } + } - @Override - public void onAccuracyChanged(Sensor sensor, int accuracy) { + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { - } + } - public boolean isBluetoothHeadsetConnected(){ - if(USE_CONNECTION_SERVICE && systemCallConnection!=null && systemCallConnection.getCallAudioState()!=null) - return (systemCallConnection.getCallAudioState().getSupportedRouteMask() & CallAudioState.ROUTE_BLUETOOTH)!=0; - return isBtHeadsetConnected; - } + public boolean isBluetoothHeadsetConnected() { + if (USE_CONNECTION_SERVICE && systemCallConnection != null && systemCallConnection.getCallAudioState() != null) + return (systemCallConnection.getCallAudioState().getSupportedRouteMask() & CallAudioState.ROUTE_BLUETOOTH) != 0; + return isBtHeadsetConnected; + } - public void onAudioFocusChange(int focusChange) { - if (focusChange == AudioManager.AUDIOFOCUS_GAIN) { - haveAudioFocus = true; - } else { - haveAudioFocus = false; - } - } + public void onAudioFocusChange(int focusChange) { + if (focusChange == AudioManager.AUDIOFOCUS_GAIN) { + haveAudioFocus = true; + } else { + haveAudioFocus = false; + } + } - protected void updateBluetoothHeadsetState(boolean connected){ - if(connected==isBtHeadsetConnected) - return; - if(BuildVars.LOGS_ENABLED) - FileLog.d("updateBluetoothHeadsetState: "+connected); - isBtHeadsetConnected=connected; - final AudioManager am=(AudioManager)getSystemService(AUDIO_SERVICE); - if(connected && !isRinging() && currentState!=0){ - if(bluetoothScoActive){ - if(BuildVars.LOGS_ENABLED) - FileLog.d("SCO already active, setting audio routing"); - am.setSpeakerphoneOn(false); - am.setBluetoothScoOn(true); - }else{ - if(BuildVars.LOGS_ENABLED) - FileLog.d("startBluetoothSco"); - needSwitchToBluetoothAfterScoActivates=true; - // some devices ignore startBluetoothSco when called immediately after the headset is connected, so delay it - AndroidUtilities.runOnUIThread(new Runnable(){ - @Override - public void run(){ - try { - am.startBluetoothSco(); - } catch (Throwable ignore) { + protected void updateBluetoothHeadsetState(boolean connected) { + if (connected == isBtHeadsetConnected) + return; + if (BuildVars.LOGS_ENABLED) + FileLog.d("updateBluetoothHeadsetState: " + connected); + isBtHeadsetConnected = connected; + final AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); + if (connected && !isRinging() && currentState != 0) { + if (bluetoothScoActive) { + if (BuildVars.LOGS_ENABLED) + FileLog.d("SCO already active, setting audio routing"); + am.setSpeakerphoneOn(false); + am.setBluetoothScoOn(true); + } else { + if (BuildVars.LOGS_ENABLED) + FileLog.d("startBluetoothSco"); + needSwitchToBluetoothAfterScoActivates = true; + // some devices ignore startBluetoothSco when called immediately after the headset is connected, so delay it + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + try { + am.startBluetoothSco(); + } catch (Throwable ignore) { - } - } - }, 500); - } - }else{ - bluetoothScoActive=false; - } - for (StateListener l : stateListeners) - l.onAudioSettingsChanged(); - } + } + } + }, 500); + } + } else { + bluetoothScoActive = false; + } + for (StateListener l : stateListeners) + l.onAudioSettingsChanged(); + } - public int getLastError() { - return lastError; - } + public int getLastError() { + return lastError; + } - public int getCallState(){ - return currentState; - } + public int getCallState() { + return currentState; + } - protected void updateNetworkType() { - ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); - NetworkInfo info = cm.getActiveNetworkInfo(); - lastNetInfo = info; - int type = VoIPController.NET_TYPE_UNKNOWN; - if (info != null) { - switch (info.getType()) { - case ConnectivityManager.TYPE_MOBILE: - switch (info.getSubtype()) { - case TelephonyManager.NETWORK_TYPE_GPRS: - type = VoIPController.NET_TYPE_GPRS; - break; - case TelephonyManager.NETWORK_TYPE_EDGE: - case TelephonyManager.NETWORK_TYPE_1xRTT: - type = VoIPController.NET_TYPE_EDGE; - break; - case TelephonyManager.NETWORK_TYPE_UMTS: - case TelephonyManager.NETWORK_TYPE_EVDO_0: - type = VoIPController.NET_TYPE_3G; - break; - case TelephonyManager.NETWORK_TYPE_HSDPA: - case TelephonyManager.NETWORK_TYPE_HSPA: - case TelephonyManager.NETWORK_TYPE_HSPAP: - case TelephonyManager.NETWORK_TYPE_HSUPA: - case TelephonyManager.NETWORK_TYPE_EVDO_A: - case TelephonyManager.NETWORK_TYPE_EVDO_B: - type = VoIPController.NET_TYPE_HSPA; - break; - case TelephonyManager.NETWORK_TYPE_LTE: - type = VoIPController.NET_TYPE_LTE; - break; - default: - type = VoIPController.NET_TYPE_OTHER_MOBILE; - break; - } - break; - case ConnectivityManager.TYPE_WIFI: - type = VoIPController.NET_TYPE_WIFI; - break; - case ConnectivityManager.TYPE_ETHERNET: - type = VoIPController.NET_TYPE_ETHERNET; - break; - } - } - if (controller != null) { - controller.setNetworkType(type); - } - } + protected void updateNetworkType() { + ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE); + NetworkInfo info = cm.getActiveNetworkInfo(); + lastNetInfo = info; + int type = VoIPController.NET_TYPE_UNKNOWN; + if (info != null) { + switch (info.getType()) { + case ConnectivityManager.TYPE_MOBILE: + switch (info.getSubtype()) { + case TelephonyManager.NETWORK_TYPE_GPRS: + type = VoIPController.NET_TYPE_GPRS; + break; + case TelephonyManager.NETWORK_TYPE_EDGE: + case TelephonyManager.NETWORK_TYPE_1xRTT: + type = VoIPController.NET_TYPE_EDGE; + break; + case TelephonyManager.NETWORK_TYPE_UMTS: + case TelephonyManager.NETWORK_TYPE_EVDO_0: + type = VoIPController.NET_TYPE_3G; + break; + case TelephonyManager.NETWORK_TYPE_HSDPA: + case TelephonyManager.NETWORK_TYPE_HSPA: + case TelephonyManager.NETWORK_TYPE_HSPAP: + case TelephonyManager.NETWORK_TYPE_HSUPA: + case TelephonyManager.NETWORK_TYPE_EVDO_A: + case TelephonyManager.NETWORK_TYPE_EVDO_B: + type = VoIPController.NET_TYPE_HSPA; + break; + case TelephonyManager.NETWORK_TYPE_LTE: + type = VoIPController.NET_TYPE_LTE; + break; + default: + type = VoIPController.NET_TYPE_OTHER_MOBILE; + break; + } + break; + case ConnectivityManager.TYPE_WIFI: + type = VoIPController.NET_TYPE_WIFI; + break; + case ConnectivityManager.TYPE_ETHERNET: + type = VoIPController.NET_TYPE_ETHERNET; + break; + } + } + if (controller != null) { + controller.setNetworkType(type); + } + } - protected void callFailed() { - callFailed(controller != null && controllerStarted ? controller.getLastError() : VoIPController.ERROR_UNKNOWN); - } + protected void callFailed() { + callFailed(controller != null && controllerStarted ? controller.getLastError() : VoIPController.ERROR_UNKNOWN); + } - protected Bitmap getRoundAvatarBitmap(TLObject userOrChat){ - Bitmap bitmap=null; - if(userOrChat instanceof TLRPC.User){ - TLRPC.User user=(TLRPC.User) userOrChat; - if(user.photo!=null && user.photo.photo_small!=null){ - BitmapDrawable img=ImageLoader.getInstance().getImageFromMemory(user.photo.photo_small, null, "50_50"); - if(img!=null){ - bitmap=img.getBitmap().copy(Bitmap.Config.ARGB_8888, true); - }else{ - try{ - BitmapFactory.Options opts=new BitmapFactory.Options(); - opts.inMutable=true; - bitmap=BitmapFactory.decodeFile(FileLoader.getPathToAttach(user.photo.photo_small, true).toString(), opts); - }catch(Throwable e){ - FileLog.e(e); - } - } - } - }else{ - TLRPC.Chat chat=(TLRPC.Chat) userOrChat; - if(chat.photo!=null && chat.photo.photo_small!=null){ - BitmapDrawable img=ImageLoader.getInstance().getImageFromMemory(chat.photo.photo_small, null, "50_50"); - if(img!=null){ - bitmap=img.getBitmap().copy(Bitmap.Config.ARGB_8888, true); - }else{ - try{ - BitmapFactory.Options opts=new BitmapFactory.Options(); - opts.inMutable=true; - bitmap=BitmapFactory.decodeFile(FileLoader.getPathToAttach(chat.photo.photo_small, true).toString(), opts); - }catch(Throwable e){ - FileLog.e(e); - } - } - } - } - if(bitmap==null){ - Theme.createDialogsResources(this); - AvatarDrawable placeholder; - if(userOrChat instanceof TLRPC.User) - placeholder=new AvatarDrawable((TLRPC.User)userOrChat); - else - placeholder=new AvatarDrawable((TLRPC.Chat)userOrChat); - bitmap=Bitmap.createBitmap(AndroidUtilities.dp(42), AndroidUtilities.dp(42), Bitmap.Config.ARGB_8888); - placeholder.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight()); - placeholder.draw(new Canvas(bitmap)); - } + protected Bitmap getRoundAvatarBitmap(TLObject userOrChat) { + Bitmap bitmap = null; + if (userOrChat instanceof TLRPC.User) { + TLRPC.User user = (TLRPC.User) userOrChat; + if (user.photo != null && user.photo.photo_small != null) { + BitmapDrawable img = ImageLoader.getInstance().getImageFromMemory(user.photo.photo_small, null, "50_50"); + if (img != null) { + bitmap = img.getBitmap().copy(Bitmap.Config.ARGB_8888, true); + } else { + try { + BitmapFactory.Options opts = new BitmapFactory.Options(); + opts.inMutable = true; + bitmap = BitmapFactory.decodeFile(FileLoader.getPathToAttach(user.photo.photo_small, true).toString(), opts); + } catch (Throwable e) { + FileLog.e(e); + } + } + } + } else { + TLRPC.Chat chat = (TLRPC.Chat) userOrChat; + if (chat.photo != null && chat.photo.photo_small != null) { + BitmapDrawable img = ImageLoader.getInstance().getImageFromMemory(chat.photo.photo_small, null, "50_50"); + if (img != null) { + bitmap = img.getBitmap().copy(Bitmap.Config.ARGB_8888, true); + } else { + try { + BitmapFactory.Options opts = new BitmapFactory.Options(); + opts.inMutable = true; + bitmap = BitmapFactory.decodeFile(FileLoader.getPathToAttach(chat.photo.photo_small, true).toString(), opts); + } catch (Throwable e) { + FileLog.e(e); + } + } + } + } + if (bitmap == null) { + Theme.createDialogsResources(this); + AvatarDrawable placeholder; + if (userOrChat instanceof TLRPC.User) + placeholder = new AvatarDrawable((TLRPC.User) userOrChat); + else + placeholder = new AvatarDrawable((TLRPC.Chat) userOrChat); + bitmap = Bitmap.createBitmap(AndroidUtilities.dp(42), AndroidUtilities.dp(42), Bitmap.Config.ARGB_8888); + placeholder.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight()); + placeholder.draw(new Canvas(bitmap)); + } - Canvas canvas=new Canvas(bitmap); - Path circlePath=new Path(); - circlePath.addCircle(bitmap.getWidth()/2, bitmap.getHeight()/2, bitmap.getWidth()/2, Path.Direction.CW); - circlePath.toggleInverseFillType(); - Paint paint=new Paint(Paint.ANTI_ALIAS_FLAG); - paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); - canvas.drawPath(circlePath, paint); - return bitmap; - } + Canvas canvas = new Canvas(bitmap); + Path circlePath = new Path(); + circlePath.addCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2, bitmap.getWidth() / 2, Path.Direction.CW); + circlePath.toggleInverseFillType(); + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + canvas.drawPath(circlePath, paint); + return bitmap; + } - protected void showIncomingNotification(String name, CharSequence subText, TLObject userOrChat, List groupUsers, int additionalMemberCount, Class activityOnClick) { - Intent intent = new Intent(this, activityOnClick); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP); - Notification.Builder builder = new Notification.Builder(this) - .setContentTitle(LocaleController.getString("VoipInCallBranding", R.string.VoipInCallBranding)) - .setContentText(name) - .setSmallIcon(R.drawable.notification) - .setSubText(subText) - .setContentIntent(PendingIntent.getActivity(this, 0, intent, 0)); - Uri soundProviderUri=Uri.parse("content://"+BuildConfig.APPLICATION_ID+".call_sound_provider/start_ringing"); - if (Build.VERSION.SDK_INT >= 26) { - SharedPreferences nprefs=MessagesController.getGlobalNotificationsSettings(); - int chanIndex=nprefs.getInt("calls_notification_channel", 0); - NotificationManager nm=(NotificationManager) getSystemService(NOTIFICATION_SERVICE); - NotificationChannel oldChannel=nm.getNotificationChannel("incoming_calls"+chanIndex); - if(oldChannel!=null) - nm.deleteNotificationChannel(oldChannel.getId()); - NotificationChannel existingChannel=nm.getNotificationChannel("incoming_calls2"+chanIndex); - boolean needCreate=true; - if(existingChannel!=null){ - if(existingChannel.getImportance()=Build.VERSION_CODES.LOLLIPOP){ - builder.setSound(soundProviderUri, AudioManager.STREAM_RING); - } - Intent endIntent = new Intent(this, VoIPActionsReceiver.class); - endIntent.setAction(getPackageName() + ".DECLINE_CALL"); - endIntent.putExtra("call_id", getCallID()); - CharSequence endTitle=LocaleController.getString("VoipDeclineCall", R.string.VoipDeclineCall); - if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){ - endTitle=new SpannableString(endTitle); - ((SpannableString)endTitle).setSpan(new ForegroundColorSpan(0xFFF44336), 0, endTitle.length(), 0); - } - PendingIntent endPendingIntent=PendingIntent.getBroadcast(this, 0, endIntent, PendingIntent.FLAG_CANCEL_CURRENT); - builder.addAction(R.drawable.ic_call_end_white_24dp, endTitle, endPendingIntent); - Intent answerIntent = new Intent(this, VoIPActionsReceiver.class); - answerIntent.setAction(getPackageName() + ".ANSWER_CALL"); - answerIntent.putExtra("call_id", getCallID()); - CharSequence answerTitle=LocaleController.getString("VoipAnswerCall", R.string.VoipAnswerCall); - if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){ - answerTitle=new SpannableString(answerTitle); - ((SpannableString)answerTitle).setSpan(new ForegroundColorSpan(0xFF00AA00), 0, answerTitle.length(), 0); - } - PendingIntent answerPendingIntent=PendingIntent.getBroadcast(this, 0, answerIntent, PendingIntent.FLAG_CANCEL_CURRENT); - builder.addAction(R.drawable.ic_call, answerTitle, answerPendingIntent); - builder.setPriority(Notification.PRIORITY_MAX); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - builder.setShowWhen(false); - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - builder.setColor(0xff2ca5e0); - builder.setVibrate(new long[0]); - builder.setCategory(Notification.CATEGORY_CALL); - builder.setFullScreenIntent(PendingIntent.getActivity(this, 0, intent, 0), true); - if(userOrChat instanceof TLRPC.User){ - TLRPC.User user=(TLRPC.User) userOrChat; - if(!TextUtils.isEmpty(user.phone)){ - builder.addPerson("tel:"+user.phone); - } - } - } + protected void showIncomingNotification(String name, CharSequence subText, TLObject userOrChat, List groupUsers, int additionalMemberCount, Class activityOnClick) { + Intent intent = new Intent(this, activityOnClick); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP); + Notification.Builder builder = new Notification.Builder(this) + .setContentTitle(LocaleController.getString("VoipInCallBranding", R.string.VoipInCallBranding)) + .setContentText(name) + .setSmallIcon(R.drawable.notification) + .setSubText(subText) + .setContentIntent(PendingIntent.getActivity(this, 0, intent, 0)); + Uri soundProviderUri = Uri.parse("content://" + BuildConfig.APPLICATION_ID + ".call_sound_provider/start_ringing"); + if (Build.VERSION.SDK_INT >= 26) { + SharedPreferences nprefs = MessagesController.getGlobalNotificationsSettings(); + int chanIndex = nprefs.getInt("calls_notification_channel", 0); + NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + NotificationChannel oldChannel = nm.getNotificationChannel("incoming_calls" + chanIndex); + if (oldChannel != null) + nm.deleteNotificationChannel(oldChannel.getId()); + NotificationChannel existingChannel = nm.getNotificationChannel("incoming_calls2" + chanIndex); + boolean needCreate = true; + if (existingChannel != null) { + if (existingChannel.getImportance() < NotificationManager.IMPORTANCE_HIGH || !soundProviderUri.equals(existingChannel.getSound()) || existingChannel.getVibrationPattern() != null || existingChannel.shouldVibrate()) { + if (BuildVars.LOGS_ENABLED) + FileLog.d("User messed up the notification channel; deleting it and creating a proper one"); + nm.deleteNotificationChannel("incoming_calls2" + chanIndex); + chanIndex++; + nprefs.edit().putInt("calls_notification_channel", chanIndex).commit(); + } else { + needCreate = false; + } + } + if (needCreate) { + AudioAttributes attrs = new AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE) + .build(); + NotificationChannel chan = new NotificationChannel("incoming_calls2" + chanIndex, LocaleController.getString("IncomingCalls", R.string.IncomingCalls), NotificationManager.IMPORTANCE_HIGH); + chan.setSound(soundProviderUri, attrs); + chan.enableVibration(false); + chan.enableLights(false); + nm.createNotificationChannel(chan); + } + builder.setChannelId("incoming_calls2" + chanIndex); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + builder.setSound(soundProviderUri, AudioManager.STREAM_RING); + } + Intent endIntent = new Intent(this, VoIPActionsReceiver.class); + endIntent.setAction(getPackageName() + ".DECLINE_CALL"); + endIntent.putExtra("call_id", getCallID()); + CharSequence endTitle = LocaleController.getString("VoipDeclineCall", R.string.VoipDeclineCall); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + endTitle = new SpannableString(endTitle); + ((SpannableString) endTitle).setSpan(new ForegroundColorSpan(0xFFF44336), 0, endTitle.length(), 0); + } + PendingIntent endPendingIntent = PendingIntent.getBroadcast(this, 0, endIntent, PendingIntent.FLAG_CANCEL_CURRENT); + builder.addAction(R.drawable.ic_call_end_white_24dp, endTitle, endPendingIntent); + Intent answerIntent = new Intent(this, VoIPActionsReceiver.class); + answerIntent.setAction(getPackageName() + ".ANSWER_CALL"); + answerIntent.putExtra("call_id", getCallID()); + CharSequence answerTitle = LocaleController.getString("VoipAnswerCall", R.string.VoipAnswerCall); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + answerTitle = new SpannableString(answerTitle); + ((SpannableString) answerTitle).setSpan(new ForegroundColorSpan(0xFF00AA00), 0, answerTitle.length(), 0); + } + PendingIntent answerPendingIntent = PendingIntent.getBroadcast(this, 0, answerIntent, PendingIntent.FLAG_CANCEL_CURRENT); + builder.addAction(R.drawable.ic_call, answerTitle, answerPendingIntent); + builder.setPriority(Notification.PRIORITY_MAX); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + builder.setShowWhen(false); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + builder.setColor(0xff2ca5e0); + builder.setVibrate(new long[0]); + builder.setCategory(Notification.CATEGORY_CALL); + builder.setFullScreenIntent(PendingIntent.getActivity(this, 0, intent, 0), true); + if (userOrChat instanceof TLRPC.User) { + TLRPC.User user = (TLRPC.User) userOrChat; + if (!TextUtils.isEmpty(user.phone)) { + builder.addPerson("tel:" + user.phone); + } + } + } /*Bitmap photoBitmap=null; if (photo != null) { BitmapDrawable img = ImageLoader.getInstance().getImageFromMemory(photo, null, "50_50"); @@ -1122,37 +1132,37 @@ public abstract class VoIPBaseService extends Service implements SensorEventList } } }*/ - Notification incomingNotification = builder.getNotification(); - if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP){ - RemoteViews customView=new RemoteViews(getPackageName(), LocaleController.isRTL ? R.layout.call_notification_rtl : R.layout.call_notification); - customView.setTextViewText(R.id.name, name); - boolean subtitleVisible=true; - if(TextUtils.isEmpty(subText)){ - customView.setViewVisibility(R.id.subtitle, View.GONE); - subtitleVisible=false; - if(UserConfig.getActivatedAccountsCount()>1){ - TLRPC.User self=UserConfig.getInstance(currentAccount).getCurrentUser(); - customView.setTextViewText(R.id.title, LocaleController.formatString("VoipInCallBrandingWithName", R.string.VoipInCallBrandingWithName, ContactsController.formatName(self.first_name, self.last_name))); - }else{ - customView.setTextViewText(R.id.title, LocaleController.getString("VoipInCallBranding", R.string.VoipInCallBranding)); - } - }else{ - if(UserConfig.getActivatedAccountsCount()>1){ - TLRPC.User self=UserConfig.getInstance(currentAccount).getCurrentUser(); - customView.setTextViewText(R.id.subtitle, LocaleController.formatString("VoipAnsweringAsAccount", R.string.VoipAnsweringAsAccount, ContactsController.formatName(self.first_name, self.last_name))); - }else{ - customView.setViewVisibility(R.id.subtitle, View.GONE); - subtitleVisible=false; - } - customView.setTextViewText(R.id.title, subText); - } - Bitmap avatar=getRoundAvatarBitmap(userOrChat); - customView.setTextViewText(R.id.answer_text, LocaleController.getString("VoipAnswerCall", R.string.VoipAnswerCall)); - customView.setTextViewText(R.id.decline_text, LocaleController.getString("VoipDeclineCall", R.string.VoipDeclineCall)); - customView.setImageViewBitmap(R.id.photo, avatar); - customView.setOnClickPendingIntent(R.id.answer_btn, answerPendingIntent); - customView.setOnClickPendingIntent(R.id.decline_btn, endPendingIntent); - builder.setLargeIcon(avatar); + Notification incomingNotification = builder.getNotification(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + RemoteViews customView = new RemoteViews(getPackageName(), LocaleController.isRTL ? R.layout.call_notification_rtl : R.layout.call_notification); + customView.setTextViewText(R.id.name, name); + boolean subtitleVisible = true; + if (TextUtils.isEmpty(subText)) { + customView.setViewVisibility(R.id.subtitle, View.GONE); + subtitleVisible = false; + if (UserConfig.getActivatedAccountsCount() > 1) { + TLRPC.User self = UserConfig.getInstance(currentAccount).getCurrentUser(); + customView.setTextViewText(R.id.title, LocaleController.formatString("VoipInCallBrandingWithName", R.string.VoipInCallBrandingWithName, ContactsController.formatName(self.first_name, self.last_name))); + } else { + customView.setTextViewText(R.id.title, LocaleController.getString("VoipInCallBranding", R.string.VoipInCallBranding)); + } + } else { + if (UserConfig.getActivatedAccountsCount() > 1) { + TLRPC.User self = UserConfig.getInstance(currentAccount).getCurrentUser(); + customView.setTextViewText(R.id.subtitle, LocaleController.formatString("VoipAnsweringAsAccount", R.string.VoipAnsweringAsAccount, ContactsController.formatName(self.first_name, self.last_name))); + } else { + customView.setViewVisibility(R.id.subtitle, View.GONE); + subtitleVisible = false; + } + customView.setTextViewText(R.id.title, subText); + } + Bitmap avatar = getRoundAvatarBitmap(userOrChat); + customView.setTextViewText(R.id.answer_text, LocaleController.getString("VoipAnswerCall", R.string.VoipAnswerCall)); + customView.setTextViewText(R.id.decline_text, LocaleController.getString("VoipDeclineCall", R.string.VoipDeclineCall)); + customView.setImageViewBitmap(R.id.photo, avatar); + customView.setOnClickPendingIntent(R.id.answer_btn, answerPendingIntent); + customView.setOnClickPendingIntent(R.id.decline_btn, endPendingIntent); + builder.setLargeIcon(avatar); /*if(groupUsers==null || groupUsers.size()==0){ customView.setViewVisibility(R.id.group_photos, View.GONE); @@ -1178,330 +1188,333 @@ public abstract class VoIPBaseService extends Service implements SensorEventList } }*/ - incomingNotification.headsUpContentView=incomingNotification.bigContentView=customView; - } - startForeground(ID_INCOMING_CALL_NOTIFICATION, incomingNotification); - } + incomingNotification.headsUpContentView = incomingNotification.bigContentView = customView; + } + startForeground(ID_INCOMING_CALL_NOTIFICATION, incomingNotification); + } - protected void callFailed(int errorCode){ - try{ - throw new Exception("Call "+getCallID()+" failed with error code "+errorCode); - }catch(Exception x){ - FileLog.e(x); - } - lastError = errorCode; - dispatchStateChanged(STATE_FAILED); - if(errorCode!=VoIPController.ERROR_LOCALIZED && soundPool!=null){ - playingSound=true; - soundPool.play(spFailedID, 1, 1, 0, 0, 1); - AndroidUtilities.runOnUIThread(afterSoundRunnable, 1000); - } - if(USE_CONNECTION_SERVICE && systemCallConnection!=null){ - systemCallConnection.setDisconnected(new DisconnectCause(DisconnectCause.ERROR)); - systemCallConnection.destroy(); - systemCallConnection=null; - } - stopSelf(); - } + protected void callFailed(int errorCode) { + try { + throw new Exception("Call " + getCallID() + " failed with error code " + errorCode); + } catch (Exception x) { + FileLog.e(x); + } + lastError = errorCode; + dispatchStateChanged(STATE_FAILED); + if (errorCode != VoIPController.ERROR_LOCALIZED && soundPool != null) { + playingSound = true; + soundPool.play(spFailedID, 1, 1, 0, 0, 1); + AndroidUtilities.runOnUIThread(afterSoundRunnable, 1000); + } + if (USE_CONNECTION_SERVICE && systemCallConnection != null) { + systemCallConnection.setDisconnected(new DisconnectCause(DisconnectCause.ERROR)); + systemCallConnection.destroy(); + systemCallConnection = null; + } + stopSelf(); + } - /*package*/ void callFailedFromConnectionService(){ - if(isOutgoing) - callFailed(VoIPController.ERROR_CONNECTION_SERVICE); - else - hangUp(); - } + /*package*/ void callFailedFromConnectionService() { + if (isOutgoing) + callFailed(VoIPController.ERROR_CONNECTION_SERVICE); + else + hangUp(); + } - @Override - public void onConnectionStateChanged(int newState) { - if (newState == STATE_FAILED) { - callFailed(); - return; - } - if (newState == STATE_ESTABLISHED) { - if(connectingSoundRunnable!=null){ - AndroidUtilities.cancelRunOnUIThread(connectingSoundRunnable); - connectingSoundRunnable=null; - } - if (spPlayID != 0) { - soundPool.stop(spPlayID); - spPlayID = 0; - } - if(!wasEstablished){ - wasEstablished=true; - if(!isProximityNear){ - Vibrator vibrator=(Vibrator) getSystemService(VIBRATOR_SERVICE); - if(vibrator.hasVibrator()) - vibrator.vibrate(100); - } - AndroidUtilities.runOnUIThread(new Runnable(){ - @Override - public void run(){ - if(controller==null) - return; - int netType=getStatsNetworkType(); - StatsController.getInstance(currentAccount).incrementTotalCallsTime(netType, 5); - AndroidUtilities.runOnUIThread(this, 5000); - } - }, 5000); - if(isOutgoing) - StatsController.getInstance(currentAccount).incrementSentItemsCount(getStatsNetworkType(), StatsController.TYPE_CALLS, 1); - else - StatsController.getInstance(currentAccount).incrementReceivedItemsCount(getStatsNetworkType(), StatsController.TYPE_CALLS, 1); - } - } - if(newState==STATE_RECONNECTING){ - if(spPlayID!=0) - soundPool.stop(spPlayID); - spPlayID=soundPool.play(spConnectingId, 1, 1, 0, -1, 1); - } - dispatchStateChanged(newState); - } + @Override + public void onConnectionStateChanged(int newState) { + if (newState == STATE_FAILED) { + callFailed(); + return; + } + if (newState == STATE_ESTABLISHED) { + if (connectingSoundRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(connectingSoundRunnable); + connectingSoundRunnable = null; + } + if (spPlayID != 0) { + soundPool.stop(spPlayID); + spPlayID = 0; + } + if (!wasEstablished) { + wasEstablished = true; + if (!isProximityNear) { + Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE); + if (vibrator.hasVibrator()) + vibrator.vibrate(100); + } + AndroidUtilities.runOnUIThread(new Runnable() { + @Override + public void run() { + if (controller == null) + return; + int netType = getStatsNetworkType(); + StatsController.getInstance(currentAccount).incrementTotalCallsTime(netType, 5); + AndroidUtilities.runOnUIThread(this, 5000); + } + }, 5000); + if (isOutgoing) + StatsController.getInstance(currentAccount).incrementSentItemsCount(getStatsNetworkType(), StatsController.TYPE_CALLS, 1); + else + StatsController.getInstance(currentAccount).incrementReceivedItemsCount(getStatsNetworkType(), StatsController.TYPE_CALLS, 1); + } + } + if (newState == STATE_RECONNECTING) { + if (spPlayID != 0) + soundPool.stop(spPlayID); + spPlayID = soundPool.play(spConnectingId, 1, 1, 0, -1, 1); + } + dispatchStateChanged(newState); + } - @Override - public void onSignalBarCountChanged(int newCount){ - signalBarCount=newCount; - for (int a = 0; a < stateListeners.size(); a++) { - StateListener l = stateListeners.get(a); - l.onSignalBarsCountChanged(newCount); - } - } + @Override + public void onSignalBarCountChanged(int newCount) { + signalBarCount = newCount; + for (int a = 0; a < stateListeners.size(); a++) { + StateListener l = stateListeners.get(a); + l.onSignalBarsCountChanged(newCount); + } + } - protected void callEnded() { - if (BuildVars.LOGS_ENABLED) { - FileLog.d("Call " + getCallID() + " ended"); - } - dispatchStateChanged(STATE_ENDED); - if (needPlayEndSound) { - playingSound = true; - soundPool.play(spEndId, 1, 1, 0, 0, 1); - AndroidUtilities.runOnUIThread(afterSoundRunnable, 700); - } - if(timeoutRunnable!=null){ - AndroidUtilities.cancelRunOnUIThread(timeoutRunnable); - timeoutRunnable=null; - } - endConnectionServiceCall(needPlayEndSound ? 700 : 0); - stopSelf(); - } + protected void callEnded() { + if (BuildVars.LOGS_ENABLED) { + FileLog.d("Call " + getCallID() + " ended"); + } + dispatchStateChanged(STATE_ENDED); + if (needPlayEndSound) { + playingSound = true; + soundPool.play(spEndId, 1, 1, 0, 0, 1); + AndroidUtilities.runOnUIThread(afterSoundRunnable, 700); + } + if (timeoutRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(timeoutRunnable); + timeoutRunnable = null; + } + endConnectionServiceCall(needPlayEndSound ? 700 : 0); + stopSelf(); + } - protected void endConnectionServiceCall(long delay){ - if(USE_CONNECTION_SERVICE){ - Runnable r=new Runnable(){ - @Override - public void run(){ - if(systemCallConnection!=null){ - switch(callDiscardReason){ - case DISCARD_REASON_HANGUP: - systemCallConnection.setDisconnected(new DisconnectCause(isOutgoing ? DisconnectCause.LOCAL : DisconnectCause.REJECTED)); - break; - case DISCARD_REASON_DISCONNECT: - systemCallConnection.setDisconnected(new DisconnectCause(DisconnectCause.ERROR)); - break; - case DISCARD_REASON_LINE_BUSY: - systemCallConnection.setDisconnected(new DisconnectCause(DisconnectCause.BUSY)); - break; - case DISCARD_REASON_MISSED: - systemCallConnection.setDisconnected(new DisconnectCause(isOutgoing ? DisconnectCause.CANCELED : DisconnectCause.MISSED)); - break; - default: - systemCallConnection.setDisconnected(new DisconnectCause(DisconnectCause.REMOTE)); - break; - } - systemCallConnection.destroy(); - systemCallConnection=null; - } - } - }; - if(delay>0) - AndroidUtilities.runOnUIThread(r, delay); - else - r.run(); - } - } + protected void endConnectionServiceCall(long delay) { + if (USE_CONNECTION_SERVICE) { + Runnable r = new Runnable() { + @Override + public void run() { + if (systemCallConnection != null) { + switch (callDiscardReason) { + case DISCARD_REASON_HANGUP: + systemCallConnection.setDisconnected(new DisconnectCause(isOutgoing ? DisconnectCause.LOCAL : DisconnectCause.REJECTED)); + break; + case DISCARD_REASON_DISCONNECT: + systemCallConnection.setDisconnected(new DisconnectCause(DisconnectCause.ERROR)); + break; + case DISCARD_REASON_LINE_BUSY: + systemCallConnection.setDisconnected(new DisconnectCause(DisconnectCause.BUSY)); + break; + case DISCARD_REASON_MISSED: + systemCallConnection.setDisconnected(new DisconnectCause(isOutgoing ? DisconnectCause.CANCELED : DisconnectCause.MISSED)); + break; + default: + systemCallConnection.setDisconnected(new DisconnectCause(DisconnectCause.REMOTE)); + break; + } + systemCallConnection.destroy(); + systemCallConnection = null; + } + } + }; + if (delay > 0) + AndroidUtilities.runOnUIThread(r, delay); + else + r.run(); + } + } - public boolean isOutgoing(){ - return isOutgoing; - } + public boolean isOutgoing() { + return isOutgoing; + } - public void handleNotificationAction(Intent intent){ - if ((getPackageName() + ".END_CALL").equals(intent.getAction())) { - stopForeground(true); - hangUp(); - } else if ((getPackageName() + ".DECLINE_CALL").equals(intent.getAction())) { - stopForeground(true); - declineIncomingCall(DISCARD_REASON_LINE_BUSY, null); - } else if ((getPackageName() + ".ANSWER_CALL").equals(intent.getAction())) { - acceptIncomingCallFromNotification(); - } - } + public void handleNotificationAction(Intent intent) { + if ((getPackageName() + ".END_CALL").equals(intent.getAction())) { + stopForeground(true); + hangUp(); + } else if ((getPackageName() + ".DECLINE_CALL").equals(intent.getAction())) { + stopForeground(true); + declineIncomingCall(DISCARD_REASON_LINE_BUSY, null); + } else if ((getPackageName() + ".ANSWER_CALL").equals(intent.getAction())) { + acceptIncomingCallFromNotification(); + } + } - private void acceptIncomingCallFromNotification(){ - showNotification(); - if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.M && checkSelfPermission(Manifest.permission.RECORD_AUDIO)!=PackageManager.PERMISSION_GRANTED){ - try{ - PendingIntent.getActivity(VoIPBaseService.this, 0, new Intent(VoIPBaseService.this, VoIPPermissionActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), 0).send(); - }catch(Exception x){ - if (BuildVars.LOGS_ENABLED) { - FileLog.e("Error starting permission activity", x); - } - } - return; - } - acceptIncomingCall(); - try{ - PendingIntent.getActivity(VoIPBaseService.this, 0, new Intent(VoIPBaseService.this, getUIActivityClass()).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_SINGLE_TOP), 0).send(); - }catch(Exception x){ - if (BuildVars.LOGS_ENABLED) { - FileLog.e("Error starting incall activity", x); - } - } - } + private void acceptIncomingCallFromNotification() { + showNotification(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission(Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { + try { + PendingIntent.getActivity(VoIPBaseService.this, 0, new Intent(VoIPBaseService.this, VoIPPermissionActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), 0).send(); + } catch (Exception x) { + if (BuildVars.LOGS_ENABLED) { + FileLog.e("Error starting permission activity", x); + } + } + return; + } + acceptIncomingCall(); + try { + PendingIntent.getActivity(VoIPBaseService.this, 0, new Intent(VoIPBaseService.this, getUIActivityClass()).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP), 0).send(); + } catch (Exception x) { + if (BuildVars.LOGS_ENABLED) { + FileLog.e("Error starting incall activity", x); + } + } + } - public void updateOutputGainControlState(){ - if(controller==null || !controllerStarted) - return; - if(!USE_CONNECTION_SERVICE){ - AudioManager am=(AudioManager) getSystemService(AUDIO_SERVICE); - controller.setAudioOutputGainControlEnabled(hasEarpiece() && !am.isSpeakerphoneOn() && !am.isBluetoothScoOn() && !isHeadsetPlugged); - controller.setEchoCancellationStrength(isHeadsetPlugged || (hasEarpiece() && !am.isSpeakerphoneOn() && !am.isBluetoothScoOn() && !isHeadsetPlugged) ? 0 : 1); - }else{ - boolean isEarpiece=systemCallConnection.getCallAudioState().getRoute()==CallAudioState.ROUTE_EARPIECE; - controller.setAudioOutputGainControlEnabled(isEarpiece); - controller.setEchoCancellationStrength(isEarpiece ? 0 : 1); - } - } + public void updateOutputGainControlState() { + if (controller == null || !controllerStarted) + return; + if (!USE_CONNECTION_SERVICE) { + AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); + controller.setAudioOutputGainControlEnabled(hasEarpiece() && !am.isSpeakerphoneOn() && !am.isBluetoothScoOn() && !isHeadsetPlugged); + controller.setEchoCancellationStrength(isHeadsetPlugged || (hasEarpiece() && !am.isSpeakerphoneOn() && !am.isBluetoothScoOn() && !isHeadsetPlugged) ? 0 : 1); + } else { + boolean isEarpiece = systemCallConnection.getCallAudioState().getRoute() == CallAudioState.ROUTE_EARPIECE; + controller.setAudioOutputGainControlEnabled(isEarpiece); + controller.setEchoCancellationStrength(isEarpiece ? 0 : 1); + } + } - public int getAccount(){ - return currentAccount; - } + public int getAccount() { + return currentAccount; + } - @Override - public void didReceivedNotification(int id, int account, Object... args){ - if(id==NotificationCenter.appDidLogout){ - callEnded(); - } - } + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.appDidLogout) { + callEnded(); + } + } - public static boolean isAnyKindOfCallActive(){ - //if(VoIPGroupService.getSharedInstance()!=null){ - // return VoIPGroupService.getSharedInstance().getCallState()!=VoIPGroupService.STATE_INVITED; - /*}else*/ if(VoIPService.getSharedInstance()!=null){ - return VoIPService.getSharedInstance().getCallState()!=VoIPService.STATE_WAITING_INCOMING; - } - return false; - } + public static boolean isAnyKindOfCallActive() { + //if(VoIPGroupService.getSharedInstance()!=null){ + // return VoIPGroupService.getSharedInstance().getCallState()!=VoIPGroupService.STATE_INVITED; + /*}else*/ + if (VoIPService.getSharedInstance() != null) { + return VoIPService.getSharedInstance().getCallState() != VoIPService.STATE_WAITING_INCOMING; + } + return false; + } - protected boolean isFinished(){ - return currentState==STATE_ENDED || currentState==STATE_FAILED; - } + protected boolean isFinished() { + return currentState == STATE_ENDED || currentState == STATE_FAILED; + } - protected boolean isRinging(){ - return false; - } + protected boolean isRinging() { + return false; + } - @TargetApi(Build.VERSION_CODES.O) - protected PhoneAccountHandle addAccountToTelecomManager(){ - TelecomManager tm=(TelecomManager) getSystemService(TELECOM_SERVICE); - TLRPC.User self=UserConfig.getInstance(currentAccount).getCurrentUser(); - PhoneAccountHandle handle=new PhoneAccountHandle(new ComponentName(this, TelegramConnectionService.class), ""+self.id); - PhoneAccount account=new PhoneAccount.Builder(handle, ContactsController.formatName(self.first_name, self.last_name)) - .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED) - .setIcon(Icon.createWithResource(this, R.mipmap.ic_launcher)) - .setHighlightColor(0xff2ca5e0) - .addSupportedUriScheme("sip") - .build(); - tm.registerPhoneAccount(account); - return handle; - } + @TargetApi(Build.VERSION_CODES.O) + protected PhoneAccountHandle addAccountToTelecomManager() { + TelecomManager tm = (TelecomManager) getSystemService(TELECOM_SERVICE); + TLRPC.User self = UserConfig.getInstance(currentAccount).getCurrentUser(); + PhoneAccountHandle handle = new PhoneAccountHandle(new ComponentName(this, TelegramConnectionService.class), "" + self.id); + PhoneAccount account = new PhoneAccount.Builder(handle, ContactsController.formatName(self.first_name, self.last_name)) + .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED) + .setIcon(Icon.createWithResource(this, R.mipmap.ic_launcher)) + .setHighlightColor(0xff2ca5e0) + .addSupportedUriScheme("sip") + .build(); + tm.registerPhoneAccount(account); + return handle; + } - private static boolean isDeviceCompatibleWithConnectionServiceAPI(){ - if(Build.VERSION.SDK_INT addresses; + public InetAddress[] addresses; long ttl; - public ResolvedDomain(ArrayList a, long t) { + public ResolvedDomain(InetAddress[] a, long t) { addresses = a; ttl = t; } public String getAddress() { - return addresses.get(Utilities.random.nextInt(addresses.size())); + return addresses[Utilities.random.nextInt(addresses.length)].getHostAddress(); } } private static HashMap dnsCache = new HashMap<>(); private static int lastClassGuid = 1; - + private static volatile ConnectionsManager[] Instance = new ConnectionsManager[UserConfig.MAX_ACCOUNT_COUNT]; + public static ConnectionsManager getInstance(int num) { ConnectionsManager localInstance = Instance[num]; if (localInstance == null) { @@ -197,9 +202,9 @@ public class ConnectionsManager extends BaseController { pushString = SharedConfig.pushStringStatus; } String fingerprint = AndroidUtilities.getCertificateSHA256Fingerprint(); - init(BuildVars.BUILD_VERSION, TLRPC.LAYER, BuildVars.APP_ID, deviceModel, systemVersion, appVersion, langCode, systemLangCode, configPath, FileLog.getNetworkLogPath(), pushString, fingerprint, getUserConfig().getClientUserId(), enablePushConnection); + init(BuildVars.BUILD_VERSION, TLRPC.LAYER, BuildConfig.APP_ID, deviceModel, systemVersion, appVersion, langCode, systemLangCode, configPath, FileLog.getNetworkLogPath(), pushString, fingerprint, getUserConfig().getClientUserId(), enablePushConnection); if (currentAccount == 0 && BuildVars.DEBUG_PRIVATE_VERSION) { - MozillaDnsLoadTask task = new MozillaDnsLoadTask(currentAccount); + DnsTxtLoadTask task = new DnsTxtLoadTask(currentAccount); task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null, null, null); } } @@ -334,18 +339,17 @@ public class ConnectionsManager extends BaseController { } public void init(int version, int layer, int apiId, String deviceModel, String systemVersion, String appVersion, String langCode, String systemLangCode, String configPath, String logPath, String regId, String cFingerprint, int userId, boolean enablePushConnection) { - SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); - String proxyAddress = preferences.getString("proxy_ip", ""); - String proxyUsername = preferences.getString("proxy_user", ""); - String proxyPassword = preferences.getString("proxy_pass", ""); - String proxySecret = preferences.getString("proxy_secret", ""); - int proxyPort = preferences.getInt("proxy_port", 1080); - if (preferences.getBoolean("proxy_enabled", false) && !TextUtils.isEmpty(proxyAddress)) { - native_setProxySettings(currentAccount, proxyAddress, proxyPort, proxyUsername, proxyPassword, proxySecret); + + if (SharedConfig.proxyEnabled && SharedConfig.currentProxy != null) { + + native_setProxySettings(currentAccount, SharedConfig.currentProxy.address, SharedConfig.currentProxy.port, SharedConfig.currentProxy.username, SharedConfig.currentProxy.password, SharedConfig.currentProxy.secret); + } native_init(currentAccount, version, layer, apiId, deviceModel, systemVersion, appVersion, langCode, systemLangCode, configPath, logPath, regId, cFingerprint, userId, enablePushConnection, ApplicationLoader.isNetworkOnline(), ApplicationLoader.getCurrentNetworkType()); + checkConnection(); + } public static void setLangCode(String langCode) { @@ -528,35 +532,14 @@ public class ConnectionsManager extends BaseController { return; } lastDnsRequestTime = System.currentTimeMillis(); - if (second == 3) { - if (BuildVars.LOGS_ENABLED) { - FileLog.d("start mozilla txt task"); - } - MozillaDnsLoadTask task = new MozillaDnsLoadTask(currentAccount); - task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null, null, null); - currentTask = task; - } else if (second == 2) { - if (BuildVars.LOGS_ENABLED) { - FileLog.d("start google txt task"); - } - GoogleDnsLoadTask task = new GoogleDnsLoadTask(currentAccount); - task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null, null, null); - currentTask = task; - } else if (second == 1) { - if (BuildVars.LOGS_ENABLED) { - FileLog.d("start dns txt task"); - } - DnsTxtLoadTask task = new DnsTxtLoadTask(currentAccount); - task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null, null, null); - currentTask = task; - } else { - if (BuildVars.LOGS_ENABLED) { - FileLog.d("start firebase task"); - } - FirebaseTask task = new FirebaseTask(currentAccount); - task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null, null, null); - currentTask = task; + + if (BuildVars.LOGS_ENABLED) { + FileLog.d("start dns txt task"); } + DnsTxtLoadTask task = new DnsTxtLoadTask(currentAccount); + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null, null, null); + currentTask = task; + }); } @@ -568,7 +551,8 @@ public class ConnectionsManager extends BaseController { AndroidUtilities.runOnUIThread(() -> { ResolvedDomain resolvedDomain = dnsCache.get(hostName); if (resolvedDomain != null && SystemClock.elapsedRealtime() - resolvedDomain.ttl < 5 * 60 * 1000) { - native_onHostNameResolved(hostName, address, resolvedDomain.getAddress()); + String addr = resolvedDomain.getAddress(); + native_onHostNameResolved(hostName, address, addr, Utils.isIpv6Address(addr)); } else { ResolveHostByNameTask task = resolvingHostnameTasks.get(hostName); if (task == null) { @@ -577,7 +561,7 @@ public class ConnectionsManager extends BaseController { task.executeOnExecutor(DNS_THREAD_POOL_EXECUTOR, null, null, null); } catch (Throwable e) { FileLog.e(e); - native_onHostNameResolved(hostName, address, ""); + native_onHostNameResolved(hostName, address, "", false); return; } resolvingHostnameTasks.put(hostName, task); @@ -639,34 +623,62 @@ public class ConnectionsManager extends BaseController { } public static native void native_switchBackend(int currentAccount); + public static native int native_isTestBackend(int currentAccount); + public static native void native_pauseNetwork(int currentAccount); + public static native void native_setUseIpv6(int currentAccount, boolean value); + public static native void native_updateDcSettings(int currentAccount); + public static native void native_setNetworkAvailable(int currentAccount, boolean value, int networkType, boolean slow); + public static native void native_resumeNetwork(int currentAccount, boolean partial); + public static native long native_getCurrentTimeMillis(int currentAccount); + public static native int native_getCurrentTime(int currentAccount); + public static native int native_getTimeDifference(int currentAccount); + public static native void native_sendRequest(int currentAccount, long object, RequestDelegateInternal onComplete, QuickAckDelegate onQuickAck, WriteToSocketDelegate onWriteToSocket, int flags, int datacenterId, int connetionType, boolean immediate, int requestToken); + public static native void native_cancelRequest(int currentAccount, int token, boolean notifyServer); + public static native void native_cleanUp(int currentAccount, boolean resetKeys); + public static native void native_cancelRequestsForGuid(int currentAccount, int guid); + public static native void native_bindRequestToGuid(int currentAccount, int requestToken, int guid); + public static native void native_applyDatacenterAddress(int currentAccount, int datacenterId, String ipAddress, int port); + public static native int native_getConnectionState(int currentAccount); + public static native void native_setUserId(int currentAccount, int id); + public static native void native_init(int currentAccount, int version, int layer, int apiId, String deviceModel, String systemVersion, String appVersion, String langCode, String systemLangCode, String configPath, String logPath, String regId, String cFingerprint, int userId, boolean enablePushConnection, boolean hasNetwork, int networkType); + public static native void native_setProxySettings(int currentAccount, String address, int port, String username, String password, String secret); + public static native void native_setLangCode(int currentAccount, String langCode); + public static native void native_setRegId(int currentAccount, String regId); + public static native void native_setSystemLangCode(int currentAccount, String langCode); + public static native void native_seSystemLangCode(int currentAccount, String langCode); + public static native void native_setJava(boolean useJavaByteBuffers); + public static native void native_setPushConnectionEnabled(int currentAccount, boolean value); + public static native void native_applyDnsConfig(int currentAccount, long address, String phone, int date); + public static native long native_checkProxy(int currentAccount, String address, int port, String username, String password, String secret, RequestTimeDelegate requestTimeDelegate); - public static native void native_onHostNameResolved(String host, long address, String ip); + + public static native void native_onHostNameResolved(String host, long address, String ip, boolean ipv6); public static int generateClassGuid() { return lastClassGuid++; @@ -685,8 +697,8 @@ public class ConnectionsManager extends BaseController { } @SuppressLint("NewApi") - protected static boolean useIpv6Address() { - if (Build.VERSION.SDK_INT < 19) { + public static boolean useIpv6Address() { + if (Build.VERSION.SDK_INT < 19 || !(SharedConfig.proxyEnabled && !SharedConfig.currentProxy.secret.isEmpty())) { return false; } if (BuildVars.LOGS_ENABLED) { @@ -747,7 +759,7 @@ public class ConnectionsManager extends BaseController { } } } - if(NekoConfig.useIPv6){ + if (NekoConfig.useIPv6) { return hasIpv6; } else { return !hasIpv4 && hasIpv6; @@ -759,6 +771,7 @@ public class ConnectionsManager extends BaseController { return false; } + private static class ResolveHostByNameTask extends AsyncTask { private ArrayList addresses = new ArrayList<>(); @@ -777,75 +790,11 @@ public class ConnectionsManager extends BaseController { } protected ResolvedDomain doInBackground(Void... voids) { - ByteArrayOutputStream outbuf = null; - InputStream httpConnectionStream = null; - boolean done = false; - try { - URL downloadUrl = new URL("https://www.google.com/resolve?name=" + currentHostName + "&type=A"); - URLConnection httpConnection = downloadUrl.openConnection(); - httpConnection.addRequestProperty("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 10_0 like Mac OS X) AppleWebKit/602.1.38 (KHTML, like Gecko) Version/10.0 Mobile/14A5297c Safari/602.1"); - httpConnection.addRequestProperty("Host", "dns.google.com"); - httpConnection.setConnectTimeout(1000); - httpConnection.setReadTimeout(2000); - httpConnection.connect(); - httpConnectionStream = httpConnection.getInputStream(); - outbuf = new ByteArrayOutputStream(); + InetAddress[] result = DnsFactory.Companion.lookUp(currentHostName); - byte[] data = new byte[1024 * 32]; - while (true) { - int read = httpConnectionStream.read(data); - if (read > 0) { - outbuf.write(data, 0, read); - } else if (read == -1) { - break; - } else { - break; - } - } + return new ResolvedDomain(result, 10 * 60 * 1000L); - JSONObject jsonObject = new JSONObject(new String(outbuf.toByteArray())); - if (jsonObject.has("Answer")) { - JSONArray array = jsonObject.getJSONArray("Answer"); - int len = array.length(); - if (len > 0) { - ArrayList addresses = new ArrayList<>(len); - for (int a = 0; a < len; a++) { - addresses.add(array.getJSONObject(a).getString("data")); - } - return new ResolvedDomain(addresses, SystemClock.elapsedRealtime()); - } - } - done = true; - } catch (Throwable e) { - FileLog.e(e); - } finally { - try { - if (httpConnectionStream != null) { - httpConnectionStream.close(); - } - } catch (Throwable e) { - FileLog.e(e); - } - try { - if (outbuf != null) { - outbuf.close(); - } - } catch (Exception ignore) { - - } - } - if (!done) { - try { - InetAddress address = InetAddress.getByName(currentHostName); - ArrayList addresses = new ArrayList<>(1); - addresses.add(address.getHostAddress()); - return new ResolvedDomain(addresses, SystemClock.elapsedRealtime()); - } catch (Exception e) { - FileLog.e(e); - } - } - return null; } @Override @@ -853,11 +802,12 @@ public class ConnectionsManager extends BaseController { if (result != null) { dnsCache.put(currentHostName, result); for (int a = 0, N = addresses.size(); a < N; a++) { - native_onHostNameResolved(currentHostName, addresses.get(a), result.getAddress()); + String address = result.getAddress(); + native_onHostNameResolved(currentHostName, addresses.get(a), address, Utils.isIpv6Address(address)); } } else { for (int a = 0, N = addresses.size(); a < N; a++) { - native_onHostNameResolved(currentHostName, addresses.get(a), ""); + native_onHostNameResolved(currentHostName, addresses.get(a), "", false); } } resolvingHostnameTasks.remove(currentHostName); @@ -877,15 +827,19 @@ public class ConnectionsManager extends BaseController { protected NativeByteBuffer doInBackground(Void... voids) { ByteArrayOutputStream outbuf = null; InputStream httpConnectionStream = null; - for (int i = 0; i < 3; i++) { + for (int i = 0; i < 5; i++) { try { - String googleDomain; + String dohUrl; if (i == 0) { - googleDomain = "www.google.com"; + dohUrl = "https://mozilla.cloudflare-dns.com/dns-query"; } else if (i == 1) { - googleDomain = "www.google.ru"; + dohUrl = "https://cloudflare-dns.com/dns-query"; + } else if (i == 2) { + dohUrl = "https://dns.google/dns-query"; + } else if (i == 3) { + dohUrl = "https://dns.twnic.tw/dns-query"; } else { - googleDomain = "google.com"; + dohUrl = "https://dns.adguard.com/dns-query"; } String domain = native_isTestBackend(currentAccount) != 0 ? "tapv3.stel.com" : AccountInstance.getInstance(currentAccount).getMessagesController().dcDomainName; int len = Utilities.random.nextInt(116) + 13; @@ -895,10 +849,9 @@ public class ConnectionsManager extends BaseController { for (int a = 0; a < len; a++) { padding.append(characters.charAt(Utilities.random.nextInt(characters.length()))); } - URL downloadUrl = new URL("https://" + googleDomain + "/resolve?name=" + domain + "&type=ANY&random_padding=" + padding); + URL downloadUrl = new URL(dohUrl + "?name=" + domain + "&type=ANY&random_padding=" + padding); URLConnection httpConnection = downloadUrl.openConnection(); httpConnection.addRequestProperty("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 10_0 like Mac OS X) AppleWebKit/602.1.38 (KHTML, like Gecko) Version/10.0 Mobile/14A5297c Safari/602.1"); - httpConnection.addRequestProperty("Host", "dns.google.com"); httpConnection.setConnectTimeout(5000); httpConnection.setReadTimeout(5000); httpConnection.connect(); @@ -983,290 +936,14 @@ public class ConnectionsManager extends BaseController { } else { if (BuildVars.LOGS_ENABLED) { FileLog.d("failed to get dns txt result"); - FileLog.d("start google task"); - } - GoogleDnsLoadTask task = new GoogleDnsLoadTask(currentAccount); - task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null, null, null); - currentTask = task; - } - }); - } - } - - private static class GoogleDnsLoadTask extends AsyncTask { - - private int currentAccount; - private int responseDate; - - public GoogleDnsLoadTask(int instance) { - super(); - currentAccount = instance; - } - - protected NativeByteBuffer doInBackground(Void... voids) { - ByteArrayOutputStream outbuf = null; - InputStream httpConnectionStream = null; - try { - String domain = native_isTestBackend(currentAccount) != 0 ? "tapv3.stel.com" : AccountInstance.getInstance(currentAccount).getMessagesController().dcDomainName; - int len = Utilities.random.nextInt(116) + 13; - final String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - - StringBuilder padding = new StringBuilder(len); - for (int a = 0; a < len; a++) { - padding.append(characters.charAt(Utilities.random.nextInt(characters.length()))); - } - URL downloadUrl = new URL("https://dns.google.com/resolve?name=" + domain + "&type=ANY&random_padding=" + padding); - URLConnection httpConnection = downloadUrl.openConnection(); - httpConnection.addRequestProperty("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 10_0 like Mac OS X) AppleWebKit/602.1.38 (KHTML, like Gecko) Version/10.0 Mobile/14A5297c Safari/602.1"); - httpConnection.setConnectTimeout(5000); - httpConnection.setReadTimeout(5000); - httpConnection.connect(); - httpConnectionStream = httpConnection.getInputStream(); - responseDate = (int) (httpConnection.getDate() / 1000); - - outbuf = new ByteArrayOutputStream(); - - byte[] data = new byte[1024 * 32]; - while (true) { - if (isCancelled()) { - break; - } - int read = httpConnectionStream.read(data); - if (read > 0) { - outbuf.write(data, 0, read); - } else if (read == -1) { - break; - } else { - break; - } - } - - JSONObject jsonObject = new JSONObject(new String(outbuf.toByteArray())); - JSONArray array = jsonObject.getJSONArray("Answer"); - len = array.length(); - ArrayList arrayList = new ArrayList<>(len); - for (int a = 0; a < len; a++) { - JSONObject object = array.getJSONObject(a); - int type = object.getInt("type"); - if (type != 16) { - continue; - } - arrayList.add(object.getString("data")); - } - Collections.sort(arrayList, (o1, o2) -> { - int l1 = o1.length(); - int l2 = o2.length(); - if (l1 > l2) { - return -1; - } else if (l1 < l2) { - return 1; - } - return 0; - }); - StringBuilder builder = new StringBuilder(); - for (int a = 0; a < arrayList.size(); a++) { - builder.append(arrayList.get(a).replace("\"", "")); - } - byte[] bytes = Base64.decode(builder.toString(), Base64.DEFAULT); - NativeByteBuffer buffer = new NativeByteBuffer(bytes.length); - buffer.writeBytes(bytes); - return buffer; - } catch (Throwable e) { - FileLog.e(e); - } finally { - try { - if (httpConnectionStream != null) { - httpConnectionStream.close(); - } - } catch (Throwable e) { - FileLog.e(e); - } - try { - if (outbuf != null) { - outbuf.close(); - } - } catch (Exception ignore) { - - } - } - return null; - } - - @Override - protected void onPostExecute(final NativeByteBuffer result) { - Utilities.stageQueue.postRunnable(() -> { - currentTask = null; - if (result != null) { - native_applyDnsConfig(currentAccount, result.address, AccountInstance.getInstance(currentAccount).getUserConfig().getClientPhone(), responseDate); - } else { - if (BuildVars.LOGS_ENABLED) { - FileLog.d("failed to get google result"); - FileLog.d("start mozilla task"); - } - MozillaDnsLoadTask task = new MozillaDnsLoadTask(currentAccount); - task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null, null, null); - currentTask = task; - } - }); - } - } - - private static class MozillaDnsLoadTask extends AsyncTask { - - private int currentAccount; - private int responseDate; - - public MozillaDnsLoadTask(int instance) { - super(); - currentAccount = instance; - } - - protected NativeByteBuffer doInBackground(Void... voids) { - ByteArrayOutputStream outbuf = null; - InputStream httpConnectionStream = null; - try { - String domain = native_isTestBackend(currentAccount) != 0 ? "tapv3.stel.com" : AccountInstance.getInstance(currentAccount).getMessagesController().dcDomainName; - int len = Utilities.random.nextInt(116) + 13; - final String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - - StringBuilder padding = new StringBuilder(len); - for (int a = 0; a < len; a++) { - padding.append(characters.charAt(Utilities.random.nextInt(characters.length()))); - } - URL downloadUrl = new URL("https://mozilla.cloudflare-dns.com/dns-query?name=" + domain + "&type=16&random_padding=" + padding); - URLConnection httpConnection = downloadUrl.openConnection(); - httpConnection.addRequestProperty("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 10_0 like Mac OS X) AppleWebKit/602.1.38 (KHTML, like Gecko) Version/10.0 Mobile/14A5297c Safari/602.1"); - httpConnection.addRequestProperty("accept", "application/dns-json"); - httpConnection.setConnectTimeout(5000); - httpConnection.setReadTimeout(5000); - httpConnection.connect(); - httpConnectionStream = httpConnection.getInputStream(); - responseDate = (int) (httpConnection.getDate() / 1000); - - outbuf = new ByteArrayOutputStream(); - - byte[] data = new byte[1024 * 32]; - while (true) { - if (isCancelled()) { - break; - } - int read = httpConnectionStream.read(data); - if (read > 0) { - outbuf.write(data, 0, read); - } else if (read == -1) { - break; - } else { - break; - } - } - - JSONObject jsonObject = new JSONObject(new String(outbuf.toByteArray())); - JSONArray array = jsonObject.getJSONArray("Answer"); - len = array.length(); - ArrayList arrayList = new ArrayList<>(len); - for (int a = 0; a < len; a++) { - JSONObject object = array.getJSONObject(a); - int type = object.getInt("type"); - if (type != 16) { - continue; - } - arrayList.add(object.getString("data")); - } - Collections.sort(arrayList, (o1, o2) -> { - int l1 = o1.length(); - int l2 = o2.length(); - if (l1 > l2) { - return -1; - } else if (l1 < l2) { - return 1; - } - return 0; - }); - StringBuilder builder = new StringBuilder(); - for (int a = 0; a < arrayList.size(); a++) { - builder.append(arrayList.get(a).replace("\"", "")); - } - byte[] bytes = Base64.decode(builder.toString(), Base64.DEFAULT); - NativeByteBuffer buffer = new NativeByteBuffer(bytes.length); - buffer.writeBytes(bytes); - return buffer; - } catch (Throwable e) { - FileLog.e(e); - } finally { - try { - if (httpConnectionStream != null) { - httpConnectionStream.close(); - } - } catch (Throwable e) { - FileLog.e(e); - } - try { - if (outbuf != null) { - outbuf.close(); - } - } catch (Exception ignore) { - - } - } - return null; - } - - @Override - protected void onPostExecute(final NativeByteBuffer result) { - Utilities.stageQueue.postRunnable(() -> { - currentTask = null; - if (result != null) { - native_applyDnsConfig(currentAccount, result.address, AccountInstance.getInstance(currentAccount).getUserConfig().getClientPhone(), responseDate); - } else { - if (BuildVars.LOGS_ENABLED) { - FileLog.d("failed to get mozilla txt result"); - } - } - }); - } - } - - private static class FirebaseTask extends AsyncTask { - - private int currentAccount; - - public FirebaseTask(int instance) { - super(); - currentAccount = instance; - } - - protected NativeByteBuffer doInBackground(Void... voids) { - try { - if (native_isTestBackend(currentAccount) != 0) { - throw new Exception("test backend"); - } - Utilities.stageQueue.postRunnable(() -> { - if (BuildVars.LOGS_ENABLED) { - FileLog.d("failed to get firebase result"); - FileLog.d("start dns txt task"); + FileLog.d("restart load task"); } DnsTxtLoadTask task = new DnsTxtLoadTask(currentAccount); task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null, null, null); currentTask = task; - }); - } catch (Throwable e) { - Utilities.stageQueue.postRunnable(() -> { - if (BuildVars.LOGS_ENABLED) { - FileLog.d("failed to get firebase result"); - FileLog.d("start dns txt task"); - } - DnsTxtLoadTask task = new DnsTxtLoadTask(currentAccount); - task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null, null, null); - currentTask = task; - }); - FileLog.e(e); - } - return null; - } - - @Override - protected void onPostExecute(NativeByteBuffer result) { - + } + }); } } + } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheet.java index bc5e1f7c1..59e2f9e32 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheet.java @@ -40,6 +40,8 @@ import android.view.WindowManager; import android.view.animation.Interpolator; import android.widget.FrameLayout; import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ScrollView; import android.widget.TextView; import org.telegram.messenger.AndroidUtilities; @@ -105,8 +107,8 @@ public class BottomSheet extends Dialog { protected int backgroundPaddingTop; protected int backgroundPaddingLeft; - private boolean applyTopPadding = true; - private boolean applyBottomPadding = true; + private boolean applyTopPadding = false; + private boolean applyBottomPadding = false; private ArrayList itemViews = new ArrayList<>(); @@ -491,9 +493,8 @@ public class BottomSheet extends Dialog { public BottomSheetCell(Context context, int type) { super(context); - setBackground(null); - setBackgroundDrawable(Theme.getSelectorDrawable(false)); - //setPadding(AndroidUtilities.dp(16), 0, AndroidUtilities.dp(16), 0); + setBackground(Theme.getSelectorDrawable(false)); + setPadding(0, 0, AndroidUtilities.dp(16), 0); imageView = new ImageView(context); imageView.setScaleType(ImageView.ScaleType.CENTER); @@ -576,7 +577,7 @@ public class BottomSheet extends Dialog { touchSlop = vc.getScaledTouchSlop(); Rect padding = new Rect(); - shadowDrawable = context.getResources().getDrawable(R.drawable.sheet_shadow_round).mutate(); + shadowDrawable = context.getResources().getDrawable(R.drawable.sheet_shadow).mutate(); shadowDrawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_dialogBackground), PorterDuff.Mode.MULTIPLY)); shadowDrawable.getPadding(padding); backgroundPaddingLeft = padding.left; @@ -644,13 +645,12 @@ public class BottomSheet extends Dialog { containerView.setVisibility(View.INVISIBLE); container.addView(containerView, 0, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM)); - int topOffset = 0; if (title != null) { titleView = new TextView(getContext()); - titleView.setLines(1); - titleView.setSingleLine(true); titleView.setText(title); if (bigTitle) { + titleView.setSingleLine(true); + titleView.setEllipsize(TextUtils.TruncateAt.MIDDLE); titleView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); titleView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); @@ -660,30 +660,31 @@ public class BottomSheet extends Dialog { titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); titleView.setPadding(AndroidUtilities.dp(16), 0, AndroidUtilities.dp(16), AndroidUtilities.dp(8)); } - titleView.setEllipsize(TextUtils.TruncateAt.MIDDLE); titleView.setGravity(Gravity.CENTER_VERTICAL); - containerView.addView(titleView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48)); + containerView.addView(titleView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); titleView.setOnTouchListener((v, event) -> true); - topOffset += 48; } if (customView != null) { if (customView.getParent() != null) { ViewGroup viewGroup = (ViewGroup) customView.getParent(); viewGroup.removeView(customView); } - containerView.addView(customView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 0, topOffset, 0, 0)); + containerView.addView(customView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 0, 0, 0, 0)); } else { if (items != null) { - FrameLayout rowLayout = null; - int lastRowLayoutNum = 0; + FrameLayout rootView = new ScrollView(getContext()); + LinearLayout rowView = new LinearLayout(getContext()); + rowView.setOrientation(LinearLayout.VERTICAL); + rootView.addView(rowView,new ScrollView.LayoutParams(-1,-1)); + containerView.addView(rootView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, 0, 0, 0)); + for (int a = 0; a < items.length; a++) { if (items[a] == null) { continue; } BottomSheetCell cell = new BottomSheetCell(getContext(), 0); cell.setTextAndIcon(items[a], itemIcons != null ? itemIcons[a] : 0, bigTitle); - containerView.addView(cell, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.TOP, 0, topOffset, 0, 0)); - topOffset += 48; + rowView.addView(cell,LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.TOP, 0, 0, 0, 0)); cell.setTag(a); cell.setOnClickListener(v -> dismissWithButtonClick((Integer) v.getTag())); itemViews.add(cell); @@ -773,7 +774,7 @@ public class BottomSheet extends Dialog { startAnimationRunnable = null; startOpenAnimation(); } - }, 150); + }); } else { startOpenAnimation(); } @@ -810,6 +811,7 @@ public class BottomSheet extends Dialog { public void setTitle(CharSequence value, boolean big) { title = value; bigTitle = big; + setApplyTopPadding(true); } public void setApplyTopPadding(boolean value) { @@ -865,7 +867,6 @@ public class BottomSheet extends Dialog { ObjectAnimator.ofFloat(containerView, View.TRANSLATION_Y, 0), ObjectAnimator.ofInt(backDrawable, AnimationProperties.COLOR_DRAWABLE_ALPHA, dimBehind ? 51 : 0)); currentSheetAnimation.setDuration(400); - currentSheetAnimation.setStartDelay(20); currentSheetAnimation.setInterpolator(openInterpolator); currentSheetAnimation.addListener(new AnimatorListenerAdapter() { @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/Theme.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/Theme.java index 4de39785a..98f1948ca 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/Theme.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/Theme.java @@ -1854,7 +1854,7 @@ public class Theme { } }; - public static int DEFALT_THEME_ACCENT_ID = 99; + public static int DEFALT_THEME_ACCENT_ID = 0; public static int selectedAutoNightType = AUTO_NIGHT_TYPE_NONE; public static boolean autoNightScheduleByLocation; public static float autoNightBrighnessThreshold = 0.25f; @@ -3701,11 +3701,10 @@ public class Theme { ThemeInfo themeInfo = new ThemeInfo(); themeInfo.name = "Blue"; - themeInfo.assetName = "bluebubbles.attheme"; - themeInfo.previewBackgroundColor = 0xff95beec; - themeInfo.previewInColor = 0xffffffff; - themeInfo.previewOutColor = 0xffd0e6ff; - themeInfo.firstAccentIsDefault = true; + themeInfo.assetName = "indigo.attheme"; + themeInfo.previewBackgroundColor = -657931; + themeInfo.previewInColor = Color.parseColor("#c0ffffff"); + themeInfo.previewOutColor = Color.parseColor("#3f51b5"); themeInfo.currentAccentId = DEFALT_THEME_ACCENT_ID; themeInfo.sortIndex = 1; themeInfo.setAccentColorOptions( @@ -3715,7 +3714,7 @@ public class Theme { new int[] { 0x00000000, 0xFFDFE2A0, 0xFFE2B991, 0xFFD7C1E9, 0xFFDCD1C0, 0xFFEFB576, 0xFFC0A2D1, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, new int[] { 0x00000000, 0xFFC1E1A3, 0xFFEBE2BA, 0xFFE8CDD6, 0xFFE0DFC6, 0xFFECE771, 0xFFDECCDE, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, new int[] { 99, 9, 10, 11, 12, 13, 14, 0, 1, 2, 3, 4, 5, 6, 7, 8 }, - new String[] { "", "p-pXcflrmFIBAAAAvXYQk-mCwZU", "JqSUrO0-mFIBAAAAWwTvLzoWGQI", "O-wmAfBPSFADAAAA4zINVfD_bro", "RepJ5uE_SVABAAAAr4d0YhgB850", "-Xc-np9y2VMCAAAARKr0yNNPYW0", "dhf9pceaQVACAAAAbzdVo4SCiZA", "", "", "", "", "", "", "", "", "" }, + new String[] { "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }, new int[] { 0, 180, 45, 0, 45, 180, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, new int[] { 0, 52, 46, 57, 45, 64, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0 } ); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DrawerLayoutAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DrawerLayoutAdapter.java index b6fab19a2..526a70cf4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DrawerLayoutAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DrawerLayoutAdapter.java @@ -47,7 +47,7 @@ public class DrawerLayoutAdapter extends RecyclerListView.SelectionAdapter { public DrawerLayoutAdapter(Context context, RecyclerView.ItemAnimator animator) { mContext = context; itemAnimator = animator; - accountsShowed = UserConfig.getActivatedAccountsCount() > 1 && MessagesController.getGlobalMainSettings().getBoolean("accountsShowed", true); + accountsShowed = true;//UserConfig.getActivatedAccountsCount() > 1 && MessagesController.getGlobalMainSettings().getBoolean("accountsShowed", true); Theme.createDialogsResources(context); resetItems(); } @@ -220,36 +220,36 @@ public class DrawerLayoutAdapter extends RecyclerListView.SelectionAdapter { } int eventType = Theme.getEventType(); if (eventType == 0) { - items.add(new Item(2, LocaleController.getString("NewGroup", R.string.NewGroup), R.drawable.menu_groups_ny)); - items.add(new Item(3, LocaleController.getString("NewSecretChat", R.string.NewSecretChat), R.drawable.menu_secret_ny)); - items.add(new Item(4, LocaleController.getString("NewChannel", R.string.NewChannel), R.drawable.menu_channel_ny)); + //items.add(new Item(2, LocaleController.getString("NewGroup", R.string.NewGroup), R.drawable.menu_groups_ny)); + //items.add(new Item(3, LocaleController.getString("NewSecretChat", R.string.NewSecretChat), R.drawable.menu_secret_ny)); + //items.add(new Item(4, LocaleController.getString("NewChannel", R.string.NewChannel), R.drawable.menu_channel_ny)); items.add(new Item(6, LocaleController.getString("Contacts", R.string.Contacts), R.drawable.menu_contacts_ny)); - items.add(new Item(10, LocaleController.getString("Calls", R.string.Calls), R.drawable.menu_calls_ny)); + //items.add(new Item(10, LocaleController.getString("Calls", R.string.Calls), R.drawable.menu_calls_ny)); items.add(new Item(11, LocaleController.getString("SavedMessages", R.string.SavedMessages), R.drawable.menu_bookmarks_ny)); items.add(new Item(8, LocaleController.getString("Settings", R.string.Settings), R.drawable.menu_settings_ny)); - items.add(null); // divider + //items.add(null); // divider items.add(new Item(7, LocaleController.getString("InviteFriends", R.string.InviteFriends), R.drawable.menu_invite_ny)); items.add(new Item(9, LocaleController.getString("TelegramFAQ", R.string.TelegramFAQ), R.drawable.menu_help_ny)); } else if (eventType == 1) { - items.add(new Item(2, LocaleController.getString("NewGroup", R.string.NewGroup), R.drawable.menu_groups_14)); - items.add(new Item(3, LocaleController.getString("NewSecretChat", R.string.NewSecretChat), R.drawable.menu_secret_14)); - items.add(new Item(4, LocaleController.getString("NewChannel", R.string.NewChannel), R.drawable.menu_broadcast_14)); + //items.add(new Item(2, LocaleController.getString("NewGroup", R.string.NewGroup), R.drawable.menu_groups_14)); + //items.add(new Item(3, LocaleController.getString("NewSecretChat", R.string.NewSecretChat), R.drawable.menu_secret_14)); + //items.add(new Item(4, LocaleController.getString("NewChannel", R.string.NewChannel), R.drawable.menu_broadcast_14)); items.add(new Item(6, LocaleController.getString("Contacts", R.string.Contacts), R.drawable.menu_contacts_14)); - items.add(new Item(10, LocaleController.getString("Calls", R.string.Calls), R.drawable.menu_calls_14)); + //items.add(new Item(10, LocaleController.getString("Calls", R.string.Calls), R.drawable.menu_calls_14)); items.add(new Item(11, LocaleController.getString("SavedMessages", R.string.SavedMessages), R.drawable.menu_bookmarks_14)); items.add(new Item(8, LocaleController.getString("Settings", R.string.Settings), R.drawable.menu_settings_14)); - items.add(null); // divider + //items.add(null); // divider items.add(new Item(7, LocaleController.getString("InviteFriends", R.string.InviteFriends), R.drawable.menu_secret_ny)); items.add(new Item(9, LocaleController.getString("TelegramFAQ", R.string.TelegramFAQ), R.drawable.menu_help)); } else { - items.add(new Item(2, LocaleController.getString("NewGroup", R.string.NewGroup), R.drawable.menu_groups)); - items.add(new Item(3, LocaleController.getString("NewSecretChat", R.string.NewSecretChat), R.drawable.menu_secret)); - items.add(new Item(4, LocaleController.getString("NewChannel", R.string.NewChannel), R.drawable.menu_broadcast)); + //items.add(new Item(2, LocaleController.getString("NewGroup", R.string.NewGroup), R.drawable.menu_groups)); + //items.add(new Item(3, LocaleController.getString("NewSecretChat", R.string.NewSecretChat), R.drawable.menu_secret)); + //items.add(new Item(4, LocaleController.getString("NewChannel", R.string.NewChannel), R.drawable.menu_broadcast)); items.add(new Item(6, LocaleController.getString("Contacts", R.string.Contacts), R.drawable.menu_contacts)); - items.add(new Item(10, LocaleController.getString("Calls", R.string.Calls), R.drawable.menu_calls)); + //items.add(new Item(10, LocaleController.getString("Calls", R.string.Calls), R.drawable.menu_calls)); items.add(new Item(11, LocaleController.getString("SavedMessages", R.string.SavedMessages), R.drawable.menu_saved)); items.add(new Item(8, LocaleController.getString("Settings", R.string.Settings), R.drawable.menu_settings)); - items.add(null); // divider + //items.add(null); // divider items.add(new Item(7, LocaleController.getString("InviteFriends", R.string.InviteFriends), R.drawable.menu_invite)); items.add(new Item(9, LocaleController.getString("TelegramFAQ", R.string.TelegramFAQ), R.drawable.menu_help)); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/CameraScanActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/CameraScanActivity.java index 5157365d1..ae0e599e1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/CameraScanActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/CameraScanActivity.java @@ -90,6 +90,7 @@ public class CameraScanActivity extends BaseFragment implements Camera.PreviewCa //private BarcodeDetector visionQrReader; private boolean needGalleryButton; + private boolean any; private int currentType; @@ -161,6 +162,62 @@ public class CameraScanActivity extends BaseFragment implements Camera.PreviewCa return actionBarLayout; } + public static ActionBarLayout[] showAsSheet(BaseFragment parentFragment, CameraScanActivityDelegate delegate) { + if (parentFragment == null || parentFragment.getParentActivity() == null) { + return null; + } + ActionBarLayout[] actionBarLayout = new ActionBarLayout[]{new ActionBarLayout(parentFragment.getParentActivity())}; + BottomSheet bottomSheet = new BottomSheet(parentFragment.getParentActivity(), false) { + { + actionBarLayout[0].init(new ArrayList<>()); + CameraScanActivity fragment = new CameraScanActivity(TYPE_QR) { + @Override + public void finishFragment() { + dismiss(); + } + + @Override + public void removeSelfFromStack() { + dismiss(); + } + }; + fragment.needGalleryButton = true; + fragment.any = true; + actionBarLayout[0].addFragmentToStack(fragment); + actionBarLayout[0].showLastFragment(); + actionBarLayout[0].setPadding(backgroundPaddingLeft, 0, backgroundPaddingLeft, 0); + fragment.setDelegate(delegate); + containerView = actionBarLayout[0]; + setApplyBottomPadding(false); + setApplyBottomPadding(false); + setOnDismissListener(dialog -> fragment.onFragmentDestroy()); + } + + @Override + protected boolean canDismissWithSwipe() { + return false; + } + + @Override + public void onBackPressed() { + if (actionBarLayout[0] == null || actionBarLayout[0].fragmentsStack.size() <= 1) { + super.onBackPressed(); + } else { + actionBarLayout[0].onBackPressed(); + } + } + + @Override + public void dismiss() { + super.dismiss(); + actionBarLayout[0] = null; + } + }; + + bottomSheet.show(); + return actionBarLayout; + } + public CameraScanActivity(int type) { super(); CameraController.getInstance().initCamera(() -> { @@ -540,7 +597,11 @@ public class CameraScanActivity extends BaseFragment implements Camera.PreviewCa } private void showErrorAlert() { - AlertsCreator.createSimpleAlert(getParentActivity(), LocaleController.getString("WalletQRCode", R.string.WalletQRCode), LocaleController.getString("WalletScanImageNotFound", R.string.WalletScanImageNotFound)).show(); + if (any) { + AlertsCreator.createSimpleAlert(getParentActivity(), LocaleController.getString("WalletQRCode", R.string.WalletQRCode), LocaleController.getString("NoQrFound", R.string.NoQrFound)).show(); + } else { + AlertsCreator.createSimpleAlert(getParentActivity(), LocaleController.getString("WalletQRCode", R.string.WalletQRCode), LocaleController.getString("WalletScanImageNotFound", R.string.WalletScanImageNotFound)).show(); + } } private void onNoQrFound(boolean alert) { @@ -649,6 +710,7 @@ public class CameraScanActivity extends BaseFragment implements Camera.PreviewCa onNoQrFound(bitmap != null); return null; } + if (any) return text; if (needGalleryButton) { if (!text.startsWith("ton://transfer/")) { onNoWalletFound(bitmap != null); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/CancelAccountDeletionActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/CancelAccountDeletionActivity.java index bc4510e47..f6a454ecc 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/CancelAccountDeletionActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/CancelAccountDeletionActivity.java @@ -387,11 +387,6 @@ public class CancelAccountDeletionActivity extends BaseFragment { req.settings.allow_flashcall = false;//simcardAvailable && allowCall; req.settings.allow_app_hash = ApplicationLoader.hasPlayServices; SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); - if (req.settings.allow_app_hash) { - preferences.edit().putString("sms_hash", BuildVars.SMS_HASH).commit(); - } else { - preferences.edit().remove("sms_hash").commit(); - } if (req.settings.allow_flashcall) { try { @SuppressLint("HardwareIds") String number = tm.getLine1Number(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java index f4393ef59..57f7090e5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java @@ -119,6 +119,7 @@ import org.telegram.ui.PhotoViewer; import org.telegram.ui.SecretMediaViewer; import tw.nekomimi.nekogram.NekoConfig; +import tw.nekomimi.nekogram.utils.AlertUtil; import java.io.File; import java.util.ArrayList; @@ -1563,53 +1564,55 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (pressedBotButton != -1) { BotButton button = botButtons.get(pressedBotButton); Gson gson = new Gson(); - if (!TextUtils.isEmpty(button.button.url)) { - AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); - builder.setItems(new CharSequence[]{LocaleController.getString("Copy", R.string.Copy), LocaleController.getString("CopyLink", R.string.CopyLink), LocaleController.getString("CopyDetails", R.string.CopyDetails)}, (dialogInterface, i) -> { - if (i == 0) { - try { - AndroidUtilities.addToClipboard(button.button.text); - Toast.makeText(getContext(), LocaleController.getString("TextCopied", R.string.TextCopied), Toast.LENGTH_SHORT).show(); - } catch (Exception e) { - FileLog.e(e); + if (button.button != null) { + if (!TextUtils.isEmpty(button.button.url)) { + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setItems(new CharSequence[]{LocaleController.getString("Copy", R.string.Copy), LocaleController.getString("CopyLink", R.string.CopyLink), LocaleController.getString("CopyDetails", R.string.CopyDetails)}, (dialogInterface, i) -> { + if (i == 0) { + try { + AndroidUtilities.addToClipboard(button.button.text); + Toast.makeText(getContext(), LocaleController.getString("TextCopied", R.string.TextCopied), Toast.LENGTH_SHORT).show(); + } catch (Exception e) { + FileLog.e(e); + } + } else if (i == 1) { + try { + AndroidUtilities.addToClipboard(button.button.url); + Toast.makeText(getContext(), LocaleController.getString("LinkCopied", R.string.LinkCopied), Toast.LENGTH_SHORT).show(); + } catch (Exception e) { + FileLog.e(e); + } + } else if (i == 2) { + try { + AndroidUtilities.addToClipboard(gson.toJson(button.button)); + Toast.makeText(getContext(), LocaleController.getString("TextCopied", R.string.TextCopied), Toast.LENGTH_SHORT).show(); + } catch (Exception e) { + FileLog.e(e); + } } - } else if (i == 1) { - try { - AndroidUtilities.addToClipboard(button.button.url); - Toast.makeText(getContext(), LocaleController.getString("LinkCopied", R.string.LinkCopied), Toast.LENGTH_SHORT).show(); - } catch (Exception e) { - FileLog.e(e); + }); + builder.show(); + } else { + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setItems(new CharSequence[]{LocaleController.getString("Copy", R.string.Copy), LocaleController.getString("CopyDetails", R.string.CopyDetails)}, (dialogInterface, i) -> { + if (i == 0) { + try { + AndroidUtilities.addToClipboard(button.button.text); + AlertUtil.showToast(LocaleController.getString("TextCopied", R.string.TextCopied)); + } catch (Exception e) { + FileLog.e(e); + } + } else if (i == 1) { + try { + AndroidUtilities.addToClipboard(gson.toJson(button.button)); + AlertUtil.showToast(LocaleController.getString("TextCopied", R.string.TextCopied)); + } catch (Exception e) { + FileLog.e(e); + } } - } else if (i == 2) { - try { - AndroidUtilities.addToClipboard(gson.toJson(button.button)); - Toast.makeText(getContext(), LocaleController.getString("TextCopied", R.string.TextCopied), Toast.LENGTH_SHORT).show(); - } catch (Exception e) { - FileLog.e(e); - } - } - }); - builder.show(); - } else { - AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); - builder.setItems(new CharSequence[]{LocaleController.getString("Copy", R.string.Copy), LocaleController.getString("CopyDetails", R.string.CopyDetails)}, (dialogInterface, i) -> { - if (i == 0) { - try { - AndroidUtilities.addToClipboard(button.button.text); - Toast.makeText(getContext(), LocaleController.getString("TextCopied", R.string.TextCopied), Toast.LENGTH_SHORT).show(); - } catch (Exception e) { - FileLog.e(e); - } - } else if (i == 1) { - try { - AndroidUtilities.addToClipboard(gson.toJson(button.button)); - Toast.makeText(getContext(), LocaleController.getString("TextCopied", R.string.TextCopied), Toast.LENGTH_SHORT).show(); - } catch (Exception e) { - FileLog.e(e); - } - } - }); - builder.show(); + }); + builder.show(); + } } pressedBotButton = -1; invalidate(); @@ -9345,7 +9348,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } else { imageW = 0; } - if (drawLoadingProgress && loadingProgressLayout != null){ + if (drawLoadingProgress && loadingProgressLayout != null) { imageW = 0; infoW = (int) loadingProgressLayout.getLineWidth(0); } else { @@ -10127,7 +10130,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(ChatMessageCell.this); onInitializeAccessibilityNodeInfo(info); StringBuilder sb = new StringBuilder(); - if (isChat && currentUser!=null && !currentMessageObject.isOut()) { + if (isChat && currentUser != null && !currentMessageObject.isOut()) { sb.append(UserObject.getUserName(currentUser)); sb.append('\n'); } @@ -10137,10 +10140,10 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (currentMessageObject.isMusic()) { sb.append("\n"); sb.append(LocaleController.formatString("AccDescrMusicInfo", R.string.AccDescrMusicInfo, currentMessageObject.getMusicAuthor(), currentMessageObject.getMusicTitle())); - } else if (currentMessageObject.isVoice() || currentMessageObject.isRoundVideo()){ + } else if (currentMessageObject.isVoice() || currentMessageObject.isRoundVideo()) { sb.append(", "); sb.append(LocaleController.formatCallDuration(currentMessageObject.getDuration())); - if (currentMessageObject.isContentUnread()){ + if (currentMessageObject.isContentUnread()) { sb.append(", "); sb.append(LocaleController.getString("AccDescrMsgNotPlayed", R.string.AccDescrMsgNotPlayed)); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerProfileCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerProfileCell.java index 13107bde1..ba7dff136 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerProfileCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DrawerProfileCell.java @@ -118,16 +118,16 @@ public class DrawerProfileCell extends FrameLayout { if (Theme.getTheme(dayThemeName) == null) { dayThemeName = "Blue"; } - String nightThemeName = preferences.getString("lastDarkTheme", "Dark Blue"); + String nightThemeName = preferences.getString("lastDarkTheme", "Night"); if (Theme.getTheme(nightThemeName) == null) { - nightThemeName = "Dark Blue"; + nightThemeName = "Night"; } Theme.ThemeInfo themeInfo = Theme.getActiveTheme(); if (dayThemeName.equals(nightThemeName)) { if (themeInfo.isDark()) { dayThemeName = "Blue"; } else { - nightThemeName = "Dark Blue"; + nightThemeName = "Night"; } } @@ -263,6 +263,10 @@ public class DrawerProfileCell extends FrameLayout { nameTextView.setText(UserObject.getUserName(user)); if (!NekoConfig.hidePhone) { phoneTextView.setText(PhoneFormat.getInstance().format("+" + user.phone)); + } else if (!TextUtils.isEmpty(user.username)) { + phoneTextView.setText("@" + user.username); + } else { + phoneTextView.setText(LocaleController.getString("MobileHidden",R.string.MobileHidden)); } AvatarDrawable avatarDrawable = new AvatarDrawable(user); avatarDrawable.setColor(Theme.getColor(Theme.key_avatar_backgroundInProfileBlue)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/RadioColorCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/RadioColorCell.java index 737a879d8..6f861ee46 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/RadioColorCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/RadioColorCell.java @@ -42,6 +42,9 @@ public class RadioColorCell extends FrameLayout { textView.setSingleLine(true); textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, (LocaleController.isRTL ? 21 : 51), 13, (LocaleController.isRTL ? 51 : 21), 0)); + + setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_actionBarActionModeDefaultSelector), 3)); + } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SessionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SessionCell.java index f45fbb681..437d0a626 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SessionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SessionCell.java @@ -92,8 +92,8 @@ public class SessionCell extends FrameLayout { detailTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); detailTextView.setLines(1); detailTextView.setMaxLines(1); - detailTextView.setSingleLine(true); - detailTextView.setEllipsize(TextUtils.TruncateAt.END); + //detailTextView.setSingleLine(true); + //detailTextView.setEllipsize(TextUtils.TruncateAt.END); detailTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); addView(detailTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 21, 36, 21, 0)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckCell.java index f63aee662..4b426656e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextCheckCell.java @@ -14,7 +14,10 @@ import android.animation.ObjectAnimator; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; import android.graphics.Typeface; +import android.graphics.drawable.Drawable; import android.text.TextUtils; import android.util.Property; import android.util.TypedValue; @@ -25,6 +28,7 @@ import android.widget.FrameLayout; import android.widget.TextView; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.FileLog; import org.telegram.messenger.LocaleController; import org.telegram.messenger.R; import org.telegram.ui.ActionBar.Theme; @@ -118,7 +122,22 @@ public class TextCheckCell extends FrameLayout { } public void setTextAndCheck(String text, boolean checked, boolean divider) { + setTextAndCheckAndIcon(text,checked,divider,-1); + } + + public void setTextAndCheckAndIcon(String text, boolean checked, boolean divider,int resId) { textView.setText(text); + if (resId != -1) { + try { + Drawable drawable = getResources().getDrawable(resId).mutate(); + if (drawable != null) { + drawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chats_menuItemIcon), PorterDuff.Mode.MULTIPLY)); + } + textView.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null); + } catch (Throwable e) { + FileLog.e(e); + } + } isMultiline = false; checkBox.setChecked(checked, false); needDivider = divider; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChangePhoneActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChangePhoneActivity.java index a1bfa8b84..a800c4c22 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChangePhoneActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChangePhoneActivity.java @@ -754,11 +754,6 @@ public class ChangePhoneActivity extends BaseFragment { req.settings.allow_flashcall = simcardAvailable && allowCall; req.settings.allow_app_hash = ApplicationLoader.hasPlayServices; SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); - if (req.settings.allow_app_hash) { - preferences.edit().putString("sms_hash", BuildVars.SMS_HASH).commit(); - } else { - preferences.edit().remove("sms_hash").commit(); - } if (req.settings.allow_flashcall) { try { @SuppressLint("HardwareIds") String number = tm.getLine1Number(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChannelAdminLogActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChannelAdminLogActivity.java index b95bc740f..558882ede 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChannelAdminLogActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChannelAdminLogActivity.java @@ -117,6 +117,8 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; +import tw.nekomimi.nekogram.NekoXConfig; + public class ChannelAdminLogActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { protected TLRPC.Chat currentChat; @@ -1836,7 +1838,7 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio } public void showOpenUrlAlert(final String url, boolean ask) { - if (Browser.isInternalUrl(url, null) || !ask) { + if (Browser.isInternalUrl(url, null) || !ask || NekoXConfig.skipOpenLinkConfirm) { Browser.openUrl(getParentActivity(), url, true); } else { AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java index dd690ba84..231acb4c7 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java @@ -9,35 +9,16 @@ package org.telegram.ui; import android.Manifest; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; +import android.animation.*; import android.annotation.TargetApi; import android.app.Activity; import android.app.DatePickerDialog; import android.app.Dialog; -import android.content.ClipData; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.SharedPreferences; +import android.content.*; import android.content.pm.PackageManager; import android.content.res.Configuration; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Outline; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffColorFilter; -import android.graphics.PorterDuffXfermode; import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.Typeface; +import android.graphics.*; import android.graphics.drawable.Drawable; import android.media.ThumbnailUtils; import android.net.Uri; @@ -45,168 +26,46 @@ import android.os.Build; import android.os.Bundle; import android.os.SystemClock; import android.provider.MediaStore; - -import androidx.core.content.FileProvider; -import androidx.recyclerview.widget.GridLayoutManager; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.LinearSmoothScrollerCustom; -import androidx.recyclerview.widget.RecyclerView; - -import android.text.Layout; -import android.text.Spannable; -import android.text.SpannableString; -import android.text.SpannableStringBuilder; -import android.text.Spanned; -import android.text.TextPaint; -import android.text.TextUtils; +import android.text.*; import android.text.style.CharacterStyle; import android.text.style.ClickableSpan; import android.text.style.ForegroundColorSpan; import android.text.style.URLSpan; -import android.util.LongSparseArray; -import android.util.Property; -import android.util.SparseArray; -import android.util.SparseIntArray; -import android.util.TypedValue; -import android.view.Gravity; -import android.view.HapticFeedbackConstants; -import android.view.KeyEvent; -import android.view.Menu; -import android.view.MotionEvent; -import android.view.TextureView; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewOutlineProvider; -import android.view.ViewTreeObserver; -import android.view.WindowManager; +import android.util.*; +import android.view.*; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.DecelerateInterpolator; -import android.widget.DatePicker; -import android.widget.EditText; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.ScrollView; -import android.widget.TextView; -import android.widget.Toast; +import android.widget.*; + +import androidx.core.content.FileProvider; +import androidx.recyclerview.widget.*; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; -import org.telegram.messenger.AndroidUtilities; import org.telegram.PhoneFormat.PhoneFormat; -import org.telegram.messenger.BuildConfig; -import org.telegram.messenger.BuildVars; -import org.telegram.messenger.ChatObject; -import org.telegram.messenger.EmojiData; -import org.telegram.messenger.MediaDataController; -import org.telegram.messenger.Emoji; -import org.telegram.messenger.ImageLocation; -import org.telegram.messenger.LocaleController; import org.telegram.messenger.MediaController; -import org.telegram.messenger.MessagesStorage; -import org.telegram.messenger.SecretChatHelper; -import org.telegram.messenger.SendMessagesHelper; -import org.telegram.messenger.SharedConfig; -import org.telegram.messenger.UserConfig; -import org.telegram.messenger.UserObject; -import org.telegram.messenger.Utilities; -import org.telegram.messenger.VideoEditedInfo; +import org.telegram.messenger.*; import org.telegram.messenger.browser.Browser; import org.telegram.messenger.support.SparseLongArray; -import org.telegram.messenger.ApplicationLoader; -import org.telegram.messenger.FileLoader; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; -import org.telegram.messenger.ContactsController; -import org.telegram.messenger.FileLog; -import org.telegram.messenger.MessageObject; -import org.telegram.messenger.MessagesController; -import org.telegram.messenger.NotificationCenter; -import org.telegram.messenger.R; -import org.telegram.ui.ActionBar.ActionBarLayout; -import org.telegram.ui.ActionBar.ActionBarPopupWindow; -import org.telegram.ui.ActionBar.AlertDialog; -import org.telegram.ui.ActionBar.BackDrawable; -import org.telegram.ui.ActionBar.BottomSheet; -import org.telegram.ui.ActionBar.SimpleTextView; -import org.telegram.ui.ActionBar.ThemeDescription; +import org.telegram.ui.ActionBar.*; import org.telegram.ui.Adapters.MentionsAdapter; import org.telegram.ui.Adapters.MessagesSearchAdapter; import org.telegram.ui.Adapters.StickersAdapter; -import org.telegram.ui.Cells.BotSwitchCell; -import org.telegram.ui.Cells.ChatActionCell; -import org.telegram.ui.Cells.ChatLoadingCell; -import org.telegram.ui.ActionBar.ActionBar; -import org.telegram.ui.ActionBar.ActionBarMenu; -import org.telegram.ui.ActionBar.ActionBarMenuItem; -import org.telegram.ui.Cells.ChatMessageCell; -import org.telegram.ui.Cells.ChatUnreadCell; -import org.telegram.ui.Cells.CheckBoxCell; -import org.telegram.ui.Cells.ContextLinkCell; -import org.telegram.ui.Cells.DialogCell; -import org.telegram.ui.Cells.MentionCell; -import org.telegram.ui.ActionBar.ActionBarMenuSubItem; -import org.telegram.ui.Cells.StickerCell; -import org.telegram.ui.Cells.TextSelectionHelper; -import org.telegram.ui.Components.AlertsCreator; -import org.telegram.ui.Components.AnimatedFileDrawable; -import org.telegram.ui.Components.AnimationProperties; -import org.telegram.ui.Components.AvatarDrawable; -import org.telegram.ui.Components.BackupImageView; -import org.telegram.ui.ActionBar.BaseFragment; -import org.telegram.ui.Cells.BotHelpCell; -import org.telegram.ui.Components.ChatActivityEnterView; -import org.telegram.messenger.ImageReceiver; -import org.telegram.ui.Components.ChatAttachAlert; -import org.telegram.ui.Components.ChatAvatarContainer; -import org.telegram.ui.Components.ChatBigEmptyView; -import org.telegram.ui.Components.ClippingImageView; -import org.telegram.ui.Components.CombinedDrawable; -import org.telegram.ui.Components.CorrectlyMeasuringTextView; -import org.telegram.ui.Components.CubicBezierInterpolator; -import org.telegram.ui.Components.EditTextBoldCursor; -import org.telegram.ui.Components.EditTextCaption; -import org.telegram.ui.Components.EmbedBottomSheet; -import org.telegram.ui.Components.EmojiView; -import org.telegram.ui.Components.ExtendedGridLayoutManager; -import org.telegram.ui.Components.FireworksOverlay; -import org.telegram.ui.Components.FragmentContextView; -import androidx.recyclerview.widget.GridLayoutManagerFixed; -import org.telegram.ui.Components.InstantCameraView; -import org.telegram.ui.Components.LayoutHelper; -import org.telegram.ui.Components.HintView; -import org.telegram.ui.Components.MessageBackgroundDrawable; -import org.telegram.ui.Components.NumberTextView; -import org.telegram.ui.Components.PhonebookSelectShareAlert; -import org.telegram.ui.Components.PhonebookShareAlert; -import org.telegram.ui.Components.PipRoundVideoView; -import org.telegram.ui.Components.PollVotesAlert; -import org.telegram.ui.Components.RLottieDrawable; -import org.telegram.ui.Components.RadialProgressView; -import org.telegram.ui.Components.RecyclerAnimationScrollHelper; -import org.telegram.ui.Components.RecyclerListView; -import org.telegram.ui.Components.ShareAlert; +import org.telegram.ui.Cells.*; import org.telegram.ui.Components.Size; -import org.telegram.ui.Components.SizeNotifierFrameLayout; -import org.telegram.ui.Components.StickersAlert; -import org.telegram.ui.ActionBar.Theme; -import org.telegram.ui.Components.TextStyleSpan; -import org.telegram.ui.Components.TypefaceSpan; -import org.telegram.ui.Components.URLSpanBotCommand; -import org.telegram.ui.Components.URLSpanMono; -import org.telegram.ui.Components.URLSpanNoUnderline; -import org.telegram.ui.Components.URLSpanReplacement; -import org.telegram.ui.Components.URLSpanUserMention; -import org.telegram.ui.Components.UndoView; +import org.telegram.ui.Components.*; import org.telegram.ui.Components.voip.VoIPHelper; import tw.nekomimi.nekogram.MessageDetailsActivity; import tw.nekomimi.nekogram.MessageHelper; import tw.nekomimi.nekogram.NekoConfig; -import tw.nekomimi.nekogram.translator.Translator; +import tw.nekomimi.nekogram.NekoXConfig; import tw.nekomimi.nekogram.translator.TranslateBottomSheet; +import tw.nekomimi.nekogram.translator.Translator; import java.io.BufferedWriter; import java.io.File; @@ -239,6 +98,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private FrameLayout bottomOverlay; protected ChatActivityEnterView chatActivityEnterView; private View timeItem2; + private ActionBarMenuSubItem fakeScreenshotItem; private ActionBarMenuItem attachItem; private ActionBarMenuItem headerItem; private ActionBarMenuItem editTextItem; @@ -558,6 +418,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private interface ChatActivityDelegate { void openReplyMessage(int mid); + void openSearch(String text); } @@ -759,6 +620,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private final static int add_shortcut = 24; private final static int show_pinned = 25; + private final static int fake_screenshot = 27; + private final static int delete_all = 28; + private final static int bot_help = 30; private final static int bot_settings = 31; private final static int call = 32; @@ -853,7 +717,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not scheduledOrNoSoundHint.setText(LocaleController.getString("ScheduledOrNoSoundHint", R.string.ScheduledOrNoSoundHint)); contentView.addView(scheduledOrNoSoundHint, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 10, 0, 10, 0)); } - scheduledOrNoSoundHint.showForView(chatActivityEnterView.getSendButton(),true); + scheduledOrNoSoundHint.showForView(chatActivityEnterView.getSendButton(), true); }; public ChatActivity(Bundle args) { @@ -1331,7 +1195,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not return; } showDialog(AlertsCreator.createTTLAlert(getParentActivity(), currentEncryptedChat).create()); - } else if (id == delete_history) { + } else if (id == delete_history || id == delete_all) { AlertDialog.Builder builder = new AlertDialog.Builder(context); TextView messageTextView = new TextView(context); messageTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); @@ -1355,13 +1219,29 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not textView.setSingleLine(true); textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); textView.setEllipsize(TextUtils.TruncateAt.END); - textView.setText(LocaleController.getString("DeleteAllFromSelf", R.string.DeleteAllFromSelf)); + if (id == delete_history) { + textView.setText(LocaleController.getString("DeleteAllFromSelf", R.string.DeleteAllFromSelf)); + } else { + textView.setText(LocaleController.getString("DeleteAllInChat", R.string.DeleteAllInChat)); + } frameLayout.addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, (LocaleController.isRTL ? 21 : 76), 11, (LocaleController.isRTL ? 76 : 21), 0)); frameLayout.addView(messageTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 24, 57, 24, 9)); - messageTextView.setText(AndroidUtilities.replaceTags(LocaleController.getString("DeleteAllFromSelfAlert", R.string.DeleteAllFromSelfAlert))); - builder.setPositiveButton(LocaleController.getString("DeleteAll", R.string.DeleteAll), (dialogInterface, i) -> { - MessageHelper.getInstance(currentAccount).deleteUserChannelHistoryWithSearch(dialog_id, UserConfig.getInstance(currentAccount).getCurrentUser()); - }); + if (id == delete_history) { + messageTextView.setText(AndroidUtilities.replaceTags(LocaleController.getString("DeleteAllFromSelfAlert", R.string.DeleteAllFromSelfAlert))); + builder.setPositiveButton(LocaleController.getString("DeleteAll", R.string.DeleteAll), (dialogInterface, i) -> { + if (ChatObject.canUserDoAction(currentChat,ChatObject.ACTION_DELETE_MESSAGES)) { + getMessagesController().deleteUserChannelHistory(currentChat, UserConfig.getInstance(currentAccount).getCurrentUser(),0); + } else { + MessageHelper.getInstance(currentAccount).deleteUserChannelHistoryWithSearch(dialog_id, UserConfig.getInstance(currentAccount).getCurrentUser()); + } + }); + } else { + messageTextView.setText(AndroidUtilities.replaceTags(LocaleController.getString("DeleteAllInChatAlert", R.string.DeleteAllInChatAlert))); + builder.setPositiveButton(LocaleController.getString("DeleteAllInChat", R.string.DeleteAllInChat), (dialogInterface, i) -> { + MessageHelper.getInstance(currentAccount).deleteChannelHistory(dialog_id, currentChat,0); + }); + } + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); AlertDialog alertDialog = builder.create(); showDialog(alertDialog); @@ -1535,6 +1415,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not SharedPreferences preferences = MessagesController.getNotificationsSettings(currentAccount); preferences.edit().putInt("pin_" + dialog_id, 0).commit(); updatePinnedMessageView(true); + } else if (id == fake_screenshot) { + getSecretChatHelper().sendScreenshotMessage(currentEncryptedChat, getMediaController().getLastVisibleMessageIds(), null); } } }); @@ -1727,6 +1609,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } if (currentEncryptedChat != null) { timeItem2 = headerItem.addSubItem(chat_enc_timer, R.drawable.msg_timer, LocaleController.getString("SetTimer", R.string.SetTimer)); + fakeScreenshotItem = headerItem.addSubItem(fake_screenshot, R.drawable.msg_fave, LocaleController.getString("FakeScreenshot", R.string.FakeScreenshot)); } boolean allowShowPinned; @@ -1755,6 +1638,11 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } if (ChatObject.isChannel(currentChat) && currentChat.megagroup) { headerItem.addSubItem(delete_history, R.drawable.msg_delete, LocaleController.getString("DeleteAllFromSelf", R.string.DeleteAllFromSelf)); + + } + + if (ChatObject.isChannel(currentChat) && ChatObject.canUserDoAction(currentChat,ChatObject.ACTION_DELETE_MESSAGES)) { + headerItem.addSubItem(delete_all, R.drawable.msg_delete, LocaleController.getString("DeleteAllInChat", R.string.DeleteAllInChat)); } if (ChatObject.isChannel(currentChat)) { @@ -2488,6 +2376,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private long lastReplyButtonAnimationTime; int lastH = 0; + @Override protected void onMeasure(int widthSpec, int heightSpec) { super.onMeasure(widthSpec, heightSpec); @@ -2769,7 +2658,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not updateTextureViewPosition(false); } - if(trackAnimationProgress == 1f || trackAnimationProgress == 0f) { + if (trackAnimationProgress == 1f || trackAnimationProgress == 0f) { slidingView = null; maybeStartTrackingSlidingView = false; startedTrackingSlidingView = false; @@ -3485,7 +3374,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not builder.setMessage(LocaleController.getString("UnpinMessageAlert", R.string.UnpinMessageAlert)); builder.setPositiveButton(LocaleController.getString("UnpinMessage", R.string.UnpinMessage), (dialogInterface, i) -> getMessagesController().pinMessage(currentChat, currentUser, 0, false)); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - builder.setNeutralButton(LocaleController.getString("Hide", R.string.Hide),(dialogInterface, i) -> { + builder.setNeutralButton(LocaleController.getString("Hide", R.string.Hide), (dialogInterface, i) -> { SharedPreferences preferences = MessagesController.getNotificationsSettings(currentAccount); if (chatInfo != null) { preferences.edit().putInt("pin_" + dialog_id, chatInfo.pinned_msg_id).commit(); @@ -4125,7 +4014,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not chatActivityEnterView.replaceWithText(start, len, "@" + user.username + " ", false); } else { String name = UserObject.getFirstName(user, false); - Spannable spannable = new SpannableString(name + " "); + Spannable spannable = new SpannableString("@" + name + " "); spannable.setSpan(new URLSpanUserMention("" + user.id, 3), 0, spannable.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); chatActivityEnterView.replaceWithText(start, len, spannable, false); } @@ -4205,7 +4094,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not TLRPC.User user = (TLRPC.User) object; if (!(searchingForUser && searchContainer.getVisibility() == View.VISIBLE) && user != null) { String name = UserObject.getFirstName(user, false); - Spannable spannable = new SpannableString(name + " "); + Spannable spannable = new SpannableString("@" + name + " "); spannable.setSpan(new URLSpanUserMention("" + user.id, 3), 0, spannable.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); chatActivityEnterView.replaceWithText(start, len, spannable, false); return true; @@ -5137,17 +5026,19 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not bottomOverlayProgress.setAlpha(1.0f); bottomOverlayChat.addView(bottomOverlayProgress, LayoutHelper.createFrame(30, 30, Gravity.CENTER)); + boolean isMaterial = "indigo.attheme".equals(Theme.getCurrentTheme().assetName); + replyButton = new TextView(context); replyButton.setText(LocaleController.getString("Reply", R.string.Reply)); replyButton.setGravity(Gravity.CENTER_VERTICAL); replyButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); replyButton.setPadding(AndroidUtilities.dp(14), 0, AndroidUtilities.dp(21), 0); replyButton.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.getColor(Theme.key_actionBarActionModeDefaultSelector), 3)); - replyButton.setTextColor(Theme.getColor(Theme.key_actionBarActionModeDefaultIcon)); + replyButton.setTextColor(Theme.getColor(isMaterial ? Theme.key_actionBarActionModeDefault : Theme.key_actionBarActionModeDefaultIcon)); replyButton.setCompoundDrawablePadding(AndroidUtilities.dp(7)); replyButton.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); Drawable image = context.getResources().getDrawable(R.drawable.input_reply).mutate(); - image.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_actionBarActionModeDefaultIcon), PorterDuff.Mode.MULTIPLY)); + image.setColorFilter(new PorterDuffColorFilter(Theme.getColor(isMaterial ? Theme.key_actionBarActionModeDefault : Theme.key_actionBarActionModeDefaultIcon), PorterDuff.Mode.MULTIPLY)); replyButton.setCompoundDrawablesWithIntrinsicBounds(image, null, null, null); replyButton.setOnClickListener(v -> { MessageObject messageObject = null; @@ -5175,10 +5066,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not forwardButton.setPadding(AndroidUtilities.dp(21), 0, AndroidUtilities.dp(21), 0); forwardButton.setCompoundDrawablePadding(AndroidUtilities.dp(6)); forwardButton.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.getColor(Theme.key_actionBarActionModeDefaultSelector), 3)); - forwardButton.setTextColor(Theme.getColor(Theme.key_actionBarActionModeDefaultIcon)); + forwardButton.setTextColor(Theme.getColor(isMaterial ? Theme.key_actionBarActionModeDefault : Theme.key_actionBarActionModeDefaultIcon)); forwardButton.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); image = context.getResources().getDrawable(R.drawable.input_forward).mutate(); - image.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_actionBarActionModeDefaultIcon), PorterDuff.Mode.MULTIPLY)); + image.setColorFilter(new PorterDuffColorFilter(Theme.getColor(isMaterial ? Theme.key_actionBarActionModeDefault : Theme.key_actionBarActionModeDefaultIcon), PorterDuff.Mode.MULTIPLY)); forwardButton.setCompoundDrawablesWithIntrinsicBounds(image, null, null, null); forwardButton.setOnClickListener(v -> openForward()); bottomMessagesActionContainer.addView(forwardButton, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT, Gravity.RIGHT | Gravity.TOP)); @@ -5216,7 +5107,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not chatScrollHelper.setAnimationCallback(chatScrollHelperCallback); try { - if (currentEncryptedChat != null && Build.VERSION.SDK_INT >= 23 && (SharedConfig.passcodeHash.length() == 0 || SharedConfig.allowScreenCapture)) { + if (currentEncryptedChat != null && Build.VERSION.SDK_INT >= 23 && (SharedConfig.passcodeHash.length() == 0 || SharedConfig.allowScreenCapture) && !NekoXConfig.disableFlagSecure) { AndroidUtilities.setFlagSecure(this, true); } } catch (Throwable e) { @@ -5578,7 +5469,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } } else if ((int) dialog_id != 0) { - int scrollDirection = RecyclerAnimationScrollHelper.SCROLL_DIRECTION_UNSET; + int scrollDirection = RecyclerAnimationScrollHelper.SCROLL_DIRECTION_UNSET; int end = chatLayoutManager.findLastVisibleItemPosition(); for (int i = chatLayoutManager.findFirstVisibleItemPosition(); i <= end; i++) { if (i >= chatAdapter.messagesStartRow && i <= chatAdapter.messagesEndRow) { @@ -6148,7 +6039,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not contentView.addView(searchAsListHint, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 19, 0, 19, 0)); } searchAsListHint.showForView(searchCountText, true); -} + } private void showScheduledOrNoSoundHint() { boolean disableNoSound = (UserObject.isUserSelf(currentUser) || (chatInfo != null && chatInfo.slowmode_next_send_date > 0) && !isInScheduleMode()); @@ -6156,7 +6047,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not return; } AndroidUtilities.cancelRunOnUIThread(showScheduledOrNoSoundRunnable); - AndroidUtilities.runOnUIThread(showScheduledOrNoSoundRunnable,200); + AndroidUtilities.runOnUIThread(showScheduledOrNoSoundRunnable, 200); } private void showMediaBannedHint() { @@ -6681,7 +6572,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not int checkLoadCount; if (scroll) { checkLoadCount = 25; - } else { + } else { checkLoadCount = 5; } if (totalItemCount - firstVisibleItem - visibleItemCount <= checkLoadCount && !loading) { @@ -7129,10 +7020,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not return; } if (!fromMyName) { - AlertsCreator.showSendMediaAlert(getSendMessagesHelper().sendMessage(arrayList, did == 0 ? dialog_id : did, notify, scheduleDate), this); + AlertsCreator.showSendMediaAlert(getSendMessagesHelper().sendMessage(arrayList, did == 0 ? dialog_id : did, notify, scheduleDate), this); } else { for (MessageObject object : arrayList) { - getSendMessagesHelper().processForwardFromMyName(object, did == 0 ? dialog_id : did, true); + getSendMessagesHelper().processForwardFromMyName(object, did == 0 ? dialog_id : did, true); } } } @@ -8164,7 +8055,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not long lastGroupId = 0; while (h < maxH) { - if (i < chatAdapter.messagesStartRow || i > chatAdapter.messagesEndRow) break; + if (i < chatAdapter.messagesStartRow || i > chatAdapter.messagesEndRow) + break; if (scrollDirection == RecyclerAnimationScrollHelper.SCROLL_DIRECTION_DOWN) { if (position > i) break; } else { @@ -8185,12 +8077,12 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (view != null) { int scrollY; if (scrollDirection == RecyclerAnimationScrollHelper.SCROLL_DIRECTION_UP) { - scrollY= view.getTop() - chatListView.getPaddingTop() - h - yOffset; + scrollY = view.getTop() - chatListView.getPaddingTop() - h - yOffset; } else { MessageObject messageObject = messages.get(position - chatAdapter.messagesStartRow); int scrollToHeight = dummyMessageCell.computeHeight(messageObject, groupedMessagesMap.get(messageObject.getGroupId())); int t = chatListView.getMeasuredHeight() - scrollToHeight; - scrollY= -(chatListView.getMeasuredHeight() - view.getBottom()) + t + h - yOffset; + scrollY = -(chatListView.getMeasuredHeight() - view.getBottom()) + t + h - yOffset; } int maxScrollOffset = chatListView.computeVerticalScrollRange() - chatListView.computeVerticalScrollOffset() - chatListView.computeVerticalScrollExtent(); if (maxScrollOffset < 0) maxScrollOffset = 0; @@ -8212,7 +8104,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not chatScrollHelper.scrollToPosition(position, yOffset, false, true); } canShowPagedownButton = true; - updatePagedownButtonVisibility( true); + updatePagedownButtonVisibility(true); } } else { query = true; @@ -9238,7 +9130,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (id == NotificationCenter.messagesDidLoad) { int guid = (Integer) args[10]; if (guid == classGuid) { - setItemAnimationsEnabled(false); + setItemAnimationsEnabled(false); if (!openAnimationEnded) { getNotificationCenter().setAllowedNotificationsDutingAnimation(new int[]{NotificationCenter.chatInfoDidLoad, NotificationCenter.dialogsNeedReload, NotificationCenter.scheduledMessagesUpdated, NotificationCenter.closeChats, NotificationCenter.botKeyboardDidLoad, NotificationCenter.userInfoDidLoad, NotificationCenter.needDeleteDialog/*, NotificationCenter.botInfoDidLoad*/}); @@ -9356,7 +9248,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (showScrollToMessageError) { boolean found = false; for (int k = 0; k < messArr.size(); k++) { - if(messArr.get(k).getId() == postponedScrollMessageId) { + if (messArr.get(k).getId() == postponedScrollMessageId) { found = true; break; } @@ -9810,7 +9702,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not scrollToMessagePosition = -10000; scrollToMessage = null; } else { - moveScrollToLastMessage(); + moveScrollToLastMessage(); } if (loaded_mentions_count != 0) { showMentionDownButton(true, true); @@ -12598,6 +12490,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not ValueAnimator animator = ValueAnimator.ofFloat(pinnedMessageView.getTranslationY(), 0); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { int position = -1; + @Override public void onAnimationUpdate(ValueAnimator animation) { float translationY = (float) animation.getAnimatedValue(); @@ -13170,7 +13063,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not chatActivityEnterView.setFieldFocused(false); } if (chatAttachAlert != null) { - if (!ignoreAttachOnPause){ + if (!ignoreAttachOnPause) { chatAttachAlert.onPause(); } else { ignoreAttachOnPause = false; @@ -13930,7 +13823,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not icons.add(R.drawable.msg_sticker); TLRPC.Document document = selectedObject.getDocument(); if (!getMediaDataController().isStickerInFavorites(document)) { - if (getMediaDataController().canAddStickerToFavorites() && MessageObject.isStickerHasSet(document)) { + if (getMediaDataController().canAddStickerToFavorites()/* && MessageObject.isStickerHasSet(document)*/) { items.add(LocaleController.getString("AddToFavorites", R.string.AddToFavorites)); options.add(20); icons.add(R.drawable.msg_fave); @@ -13964,11 +13857,11 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } TLRPC.Document document = selectedObject.getDocument(); if (!getMediaDataController().isStickerInFavorites(document)) { - if (MessageObject.isStickerHasSet(document)) { + //if (MessageObject.isStickerHasSet(document)) { items.add(LocaleController.getString("AddToFavorites", R.string.AddToFavorites)); options.add(20); icons.add(R.drawable.msg_fave); - } + //} } else { items.add(LocaleController.getString("DeleteFromFavorites", R.string.DeleteFromFavorites)); options.add(21); @@ -14043,7 +13936,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not icons.add(R.drawable.msg_schedule); } if (!inScheduleMode && selectedObject.contentType == 0 && selectedObject.getId() > 0 && !selectedObject.isOut() && (currentChat != null || currentUser != null && currentUser.bot) - && NekoConfig.showReport) { + && NekoConfig.showReport) { items.add(LocaleController.getString("ReportChat", R.string.ReportChat)); options.add(23); icons.add(R.drawable.msg_report); @@ -14562,6 +14455,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } private void saveStickerToGallery(MessageObject messageObject) { + String path = messageObject.messageOwner.attachPath; if (!TextUtils.isEmpty(path)) { File temp = new File(path); @@ -14582,9 +14476,13 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (!TextUtils.isEmpty(path)) { try { Bitmap image = BitmapFactory.decodeFile(path); + Bitmap outB = image.copy(Bitmap.Config.ARGB_8888, true); + Canvas canvas = new Canvas(outB); + canvas.drawColor(Color.WHITE); + canvas.drawBitmap(image, 0, 0, null); if (image != null) { FileOutputStream stream = new FileOutputStream(path + ".png"); - image.compress(Bitmap.CompressFormat.PNG, 100, stream); + outB.compress(Bitmap.CompressFormat.PNG, 100, stream); stream.close(); MediaController.saveFile(path + ".png", getParentActivity(), 0, null, null); } @@ -15017,7 +14915,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); showDialog(builder.create()); break; - } case 87: { + } + case 87: { if (Build.VERSION.SDK_INT >= 23 && getParentActivity().checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { getParentActivity().requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 4); selectedObject = null; @@ -15027,7 +14926,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } saveStickerToGallery(selectedObject); break; - } case 88: { + } + case 88: { if (NekoConfig.translationProvider < 0) { TranslateBottomSheet.show(getParentActivity(), selectedObject.messageOwner.message); } else { @@ -15045,9 +14945,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } String original = selectedObject.messageOwner.message; Matcher matcher = Pattern.compile("\u200C\u200C\\n\\n--------\\n.*\u200C\u200C", Pattern.DOTALL).matcher(original); - if (matcher.find()){ + if (matcher.find()) { if (messageCell != null) { - MessageHelper.setMessageContent(selectedObject,messageCell,original.replace(matcher.group(),"")); + MessageHelper.setMessageContent(selectedObject, messageCell, original.replace(matcher.group(), "")); chatAdapter.updateRowWithMessageObject(selectedObject, true); } } else { @@ -15057,7 +14957,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not public void onSuccess(String translation) { if (finalMessageCell != null) { MessageObject messageObject = finalMessageCell.getMessageObject(); - MessageHelper.setMessageContent(messageObject,finalMessageCell,original + + MessageHelper.setMessageContent(messageObject, finalMessageCell, original + "\u200C\u200C\n" + "\n" + "--------" + @@ -15089,15 +14989,18 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } break; - } case 89: { + } + case 89: { presentFragment(new MessageDetailsActivity(selectedObject)); break; - } case 90: { + } + case 90: { TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(selectedObject.messageOwner.from_id); getMediaDataController().searchMessagesInChat("", dialog_id, mergeDialogId, classGuid, 0, user); showMessagesSearchListView(true); break; - } case 92: { + } + case 92: { TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(selectedObject.messageOwner.from_id); if (user.username != null) { SendMessagesHelper.getInstance(currentAccount).sendMessage("/prpr@" + user.username, dialog_id, selectedObject, null, false, @@ -15120,7 +15023,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not entities, null, null, true, 0); } break; - } case 91: { + } + case 91: { if (Build.VERSION.SDK_INT >= 23 && getParentActivity().checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { getParentActivity().requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 4); selectedObject = null; @@ -15162,17 +15066,20 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not messageCell.updateButtonState(false, true, false); } break; - } case 93: { - ArrayList messages = new ArrayList<>(); + } + case 93: { + ArrayList messages = new ArrayList<>(); messages.add(selectedObject); forwardMessages(messages, false, true, 0, UserConfig.getInstance(currentAccount).getClientUserId()); break; - } case 94: { - ArrayList messages = new ArrayList<>(); + } + case 94: { + ArrayList messages = new ArrayList<>(); messages.add(selectedObject); forwardMessages(messages, false, true, 0); break; - } case 95: { + } + case 95: { noForwardQuote = true; forwardingMessage = selectedObject; forwardingMessageGroup = selectedObjectGroup; @@ -15183,10 +15090,12 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not fragment.setDelegate(this); presentFragment(fragment); break; - } case 97: { + } + case 97: { doAdminActions(97); break; - } case 98: { + } + case 98: { doAdminActions(98); break; } @@ -15245,7 +15154,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private boolean processSelectedOptionLongClick(int option) { switch (option) { case 94: { - ArrayList messages = new ArrayList<>(); + ArrayList messages = new ArrayList<>(); messages.add(selectedObject); if (selectedObject.type == 0 || selectedObject.isAnimatedEmoji() || getMessageCaption(selectedObject, selectedObjectGroup) != null) { CharSequence caption = getMessageCaption(selectedObject, selectedObjectGroup); @@ -15254,7 +15163,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } if (caption != null) { StringBuilder toSend = new StringBuilder(); - for (int i = 0; i < caption.length(); i++){ + for (int i = 0; i < caption.length(); i++) { char c = caption.charAt(i); if (c == '我') { toSend.append('你'); @@ -15394,7 +15303,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not return false; } else if (checkRecordLocked()) { return false; - } else if(textSelectionHelper.isSelectionMode()) { + } else if (textSelectionHelper.isSelectionMode()) { textSelectionHelper.clear(); return false; } else if (actionBar != null && actionBar.isActionModeShowed()) { @@ -15429,7 +15338,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not for (int i = 0; i < n; i++) { View child = chatListView.getChildAt(i); if (child instanceof ChatMessageCell && ((ChatMessageCell) child).getMessageObject() == unreadMessageObject) { - int unreadMessageIndex = messages.indexOf(unreadMessageObject); + int unreadMessageIndex = messages.indexOf(unreadMessageObject); if (unreadMessageIndex >= 0) { lastVisibleItem = chatAdapter.messagesStartRow + messages.indexOf(unreadMessageObject); top = chatListView.getMeasuredHeight() - child.getBottom() - chatListView.getPaddingBottom(); @@ -15676,7 +15585,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } public void showOpenUrlAlert(final String url, boolean ask) { - if (Browser.isInternalUrl(url, null) || !ask) { + if (Browser.isInternalUrl(url, null) || !ask || NekoXConfig.skipOpenLinkConfirm) { Browser.openUrl(getParentActivity(), url, inlineReturn == 0); } else { if (getParentActivity() == null) { @@ -16825,7 +16734,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } }); } - if (fromUserBlocked){ + if (fromUserBlocked) { messageCell.setVisibility(View.GONE); } else { messageCell.setVisibility(View.VISIBLE); @@ -16955,7 +16864,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not for (int i = 0; i < n; i++) { View child = chatListView.getChildAt(i); if (child instanceof ChatMessageCell && ((ChatMessageCell) child).getMessageObject() == unreadMessageObject) { - int unreadMessageIndex = messages.indexOf(unreadMessageObject); + int unreadMessageIndex = messages.indexOf(unreadMessageObject); if (unreadMessageIndex >= 0) { lastVisibleItem = messagesStartRow + messages.indexOf(unreadMessageObject); top = chatListView.getMeasuredHeight() - child.getBottom() - chatListView.getPaddingBottom(); @@ -17163,8 +17072,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } - public static boolean isClickableLink(String str){ - return str.startsWith("https://") || str.startsWith("@") || str.startsWith("#") || str.startsWith("$"); + public static boolean isClickableLink(String str) { + return str.startsWith("https://") || str.startsWith("vmess://") || str.startsWith("vmess1://") || str.startsWith("ss://") ||str.startsWith("ssr://") || str.startsWith("@") || str.startsWith("#") || str.startsWith("$"); } @Override @@ -17772,13 +17681,13 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not presentFragment(fragment); } - private void doAdminActions(int option){ + private void doAdminActions(int option) { int action = 0; - switch (option){ + switch (option) { case 97: action = 0; break; - case 98 : + case 98: action = 1; break; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatEditActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatEditActivity.java index 23ba850c8..509c50a50 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatEditActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatEditActivity.java @@ -1209,12 +1209,12 @@ public class ChatEditActivity extends BaseFragment implements ImageUpdater.Image } blockCell.setTextAndValueAndIcon(LocaleController.getString("ChannelPermissions", R.string.ChannelPermissions), String.format("%d/%d", count, 8), R.drawable.actions_permissions, true); } - if (ChatObject.isChannel(currentChat) && !ChatObject.hasAdminRights(currentChat) && info.participants.participants.size() != info.participants_count && realAdminCount == 0) { + if (ChatObject.isChannel(currentChat) && !ChatObject.hasAdminRights(currentChat) && info.participants != null && info.participants.participants.size() != info.participants_count && realAdminCount == 0) { adminCell.setTextAndIcon(LocaleController.getString("ChannelAdministrators", R.string.ChannelAdministrators), R.drawable.actions_addadmin, true); } else { adminCell.setTextAndValueAndIcon(LocaleController.getString("ChannelAdministrators", R.string.ChannelAdministrators), String.format("%d", ChatObject.isChannel(currentChat) ? ChatObject.hasAdminRights(currentChat) ? info.admins_count : realAdminCount == 0 ? getChannelAdminCount() : realAdminCount : getAdminCount()), R.drawable.actions_addadmin, true); } - if (ChatObject.isChannel(currentChat) && !ChatObject.hasAdminRights(currentChat) && info.participants.participants.size() != info.participants_count) { + if (ChatObject.isChannel(currentChat) && !ChatObject.hasAdminRights(currentChat) && info.participants != null && info.participants.participants.size() != info.participants_count) { getRealChannelAdminCount(); } } else { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatEditTypeActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatEditTypeActivity.java index 9656c99d4..2f8cdfeed 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatEditTypeActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatEditTypeActivity.java @@ -55,6 +55,8 @@ import org.telegram.ui.Components.LayoutHelper; import java.util.ArrayList; import java.util.concurrent.CountDownLatch; +import tw.nekomimi.nekogram.utils.ProxyUtil; + public class ChatEditTypeActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { private EditTextBoldCursor usernameTextView; @@ -77,6 +79,7 @@ public class ChatEditTypeActivity extends BaseFragment implements NotificationCe private TextSettingsCell copyCell; private TextSettingsCell revokeCell; private TextSettingsCell shareCell; + private TextSettingsCell shareQRCell; private ShadowSectionCell sectionCell2; private TextInfoPrivacyCell infoCell; private TextSettingsCell textCell; @@ -407,6 +410,17 @@ public class ChatEditTypeActivity extends BaseFragment implements NotificationCe } }); + shareQRCell = new TextSettingsCell(context); + shareQRCell.setBackgroundDrawable(Theme.getSelectorDrawable(false)); + shareQRCell.setText(LocaleController.getString("ShareQRCode", R.string.ShareQRCode), false); + privateContainer.addView(shareQRCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + shareQRCell.setOnClickListener(v -> { + if (invite == null) { + return; + } + ProxyUtil.showQrDialog(getParentActivity(),invite.link); + }); + checkTextView = new TextInfoPrivacyCell(context); checkTextView.setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); checkTextView.setBottomPadding(6); @@ -800,6 +814,9 @@ public class ChatEditTypeActivity extends BaseFragment implements NotificationCe new ThemeDescription(shareCell, 0, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText), new ThemeDescription(shareCell, ThemeDescription.FLAG_SELECTOR, null, null, null, null, Theme.key_listSelector), + new ThemeDescription(shareQRCell, 0, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText), + new ThemeDescription(shareQRCell, ThemeDescription.FLAG_SELECTOR, null, null, null, null, Theme.key_listSelector), + new ThemeDescription(adminnedChannelsLayout, ThemeDescription.FLAG_TEXTCOLOR, new Class[]{AdminedChannelCell.class}, new String[]{"nameTextView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText), new ThemeDescription(adminnedChannelsLayout, ThemeDescription.FLAG_TEXTCOLOR, new Class[]{AdminedChannelCell.class}, new String[]{"statusTextView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText), new ThemeDescription(adminnedChannelsLayout, ThemeDescription.FLAG_LINKCOLOR, new Class[]{AdminedChannelCell.class}, new String[]{"statusTextView"}, null, null, null, Theme.key_windowBackgroundWhiteLinkText), diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatRightsEditActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatRightsEditActivity.java index aa134b9ab..2f57ae2c5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatRightsEditActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatRightsEditActivity.java @@ -115,6 +115,7 @@ public class ChatRightsEditActivity extends BaseFragment { private int sendMessagesRow; private int sendMediaRow; private int sendStickersRow; + private int sendGifsRow; private int sendPollsRow; private int embedLinksRow; private int untilSectionRow; @@ -313,7 +314,7 @@ public class ChatRightsEditActivity extends BaseFragment { }); listView.setOnItemClickListener((view, position) -> { - if (!canEdit) { + if (!canEdit && position != 0) { return; } if (position == 0) { @@ -527,7 +528,9 @@ public class ChatRightsEditActivity extends BaseFragment { } else if (position == sendMediaRow) { bannedRights.send_media = !bannedRights.send_media; } else if (position == sendStickersRow) { - bannedRights.send_stickers = bannedRights.send_games = bannedRights.send_gifs = bannedRights.send_inline = !bannedRights.send_stickers; + bannedRights.send_stickers = bannedRights.send_games = bannedRights.send_inline = !bannedRights.send_stickers; + } else if (position == sendGifsRow) { + bannedRights.send_gifs = !bannedRights.send_gifs; } else if (position == embedLinksRow) { bannedRights.embed_links = !bannedRights.embed_links; } else if (position == sendPollsRow) { @@ -556,12 +559,19 @@ public class ChatRightsEditActivity extends BaseFragment { } } if ((bannedRights.view_messages || bannedRights.send_messages) && !bannedRights.send_stickers) { - bannedRights.send_stickers = bannedRights.send_games = bannedRights.send_gifs = bannedRights.send_inline = true; + bannedRights.send_stickers = bannedRights.send_games = bannedRights.send_inline = true; RecyclerListView.ViewHolder holder = listView.findViewHolderForAdapterPosition(sendStickersRow); if (holder != null) { ((TextCheckCell2) holder.itemView).setChecked(false); } } + if ((bannedRights.view_messages || bannedRights.send_messages) && !bannedRights.send_gifs) { + bannedRights.send_gifs = true; + RecyclerListView.ViewHolder holder = listView.findViewHolderForAdapterPosition(sendGifsRow); + if (holder != null) { + ((TextCheckCell2) holder.itemView).setChecked(false); + } + } if ((bannedRights.view_messages || bannedRights.send_messages) && !bannedRights.embed_links) { bannedRights.embed_links = true; RecyclerListView.ViewHolder holder = listView.findViewHolderForAdapterPosition(embedLinksRow); @@ -791,6 +801,7 @@ public class ChatRightsEditActivity extends BaseFragment { sendMessagesRow = -1; sendMediaRow = -1; sendStickersRow = -1; + sendGifsRow = -1; sendPollsRow = -1; embedLinksRow = -1; untilSectionRow = -1; @@ -817,6 +828,7 @@ public class ChatRightsEditActivity extends BaseFragment { sendMessagesRow = rowCount++; sendMediaRow = rowCount++; sendStickersRow = rowCount++; + sendGifsRow = rowCount++; sendPollsRow = rowCount++; embedLinksRow = rowCount++; addUsersRow = rowCount++; @@ -988,7 +1000,7 @@ public class ChatRightsEditActivity extends BaseFragment { @Override public boolean isEnabled(RecyclerView.ViewHolder holder) { - if (!canEdit) { + if (!canEdit && holder.getAdapterPosition() != 0) { return false; } int type = holder.getItemViewType(); @@ -1199,8 +1211,11 @@ public class ChatRightsEditActivity extends BaseFragment { checkCell.setTextAndCheck(LocaleController.getString("UserRestrictionsSendMedia", R.string.UserRestrictionsSendMedia), !bannedRights.send_media && !defaultBannedRights.send_media, true); checkCell.setIcon(defaultBannedRights.send_media ? R.drawable.permission_locked : 0); } else if (position == sendStickersRow) { - checkCell.setTextAndCheck(LocaleController.getString("UserRestrictionsSendStickers", R.string.UserRestrictionsSendStickers), !bannedRights.send_stickers && !defaultBannedRights.send_stickers, true); + checkCell.setTextAndCheck(LocaleController.getString("UserRestrictionsSendStickers2", R.string.UserRestrictionsSendStickers2), !bannedRights.send_stickers && !defaultBannedRights.send_stickers, true); checkCell.setIcon(defaultBannedRights.send_stickers ? R.drawable.permission_locked : 0); + } else if (position == sendGifsRow) { + checkCell.setTextAndCheck(LocaleController.getString("UserRestrictionsSendGifs", R.string.UserRestrictionsSendGifs), !bannedRights.send_gifs && !defaultBannedRights.send_gifs, true); + checkCell.setIcon(defaultBannedRights.send_gifs ? R.drawable.permission_locked : 0); } else if (position == embedLinksRow) { checkCell.setTextAndCheck(LocaleController.getString("UserRestrictionsEmbedLinks", R.string.UserRestrictionsEmbedLinks), !bannedRights.embed_links && !defaultBannedRights.embed_links, true); checkCell.setIcon(defaultBannedRights.embed_links ? R.drawable.permission_locked : 0); @@ -1209,7 +1224,7 @@ public class ChatRightsEditActivity extends BaseFragment { checkCell.setIcon(defaultBannedRights.send_polls ? R.drawable.permission_locked : 0); } - if (position == sendMediaRow || position == sendStickersRow || position == embedLinksRow || position == sendPollsRow) { + if (position == sendMediaRow || position == sendStickersRow || position == sendGifsRow || position == embedLinksRow || position == sendPollsRow) { checkCell.setEnabled(!bannedRights.send_messages && !bannedRights.view_messages && !defaultBannedRights.send_messages && !defaultBannedRights.view_messages); } else if (position == sendMessagesRow) { checkCell.setEnabled(!bannedRights.view_messages && !defaultBannedRights.view_messages); @@ -1281,8 +1296,8 @@ public class ChatRightsEditActivity extends BaseFragment { return 3; } else if (position == changeInfoRow || position == postMessagesRow || position == editMesagesRow || position == deleteMessagesRow || position == addAdminsRow || position == banUsersRow || position == addUsersRow || position == pinMessagesRow || - position == sendMessagesRow || position == sendMediaRow || position == sendStickersRow || position == embedLinksRow || - position == sendPollsRow) { + position == sendMessagesRow || position == sendMediaRow || position == sendStickersRow || position == sendGifsRow || + position == embedLinksRow || position == sendPollsRow) { return 4; } else if (position == cantEditInfoRow || position == rankInfoRow) { return 1; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatUsersActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatUsersActivity.java index ef6e23126..4fbb009e5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatUsersActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatUsersActivity.java @@ -106,6 +106,7 @@ public class ChatUsersActivity extends BaseFragment implements NotificationCente private int sendMessagesRow; private int sendMediaRow; private int sendStickersRow; + private int sendGifsRow; private int sendPollsRow; private int embedLinksRow; private int changeInfoRow; @@ -381,6 +382,7 @@ public class ChatUsersActivity extends BaseFragment implements NotificationCente sendMessagesRow = -1; sendMediaRow = -1; sendStickersRow = -1; + sendGifsRow = -1; sendPollsRow = -1; embedLinksRow = -1; addUsersRow = -1; @@ -404,6 +406,7 @@ public class ChatUsersActivity extends BaseFragment implements NotificationCente sendMessagesRow = rowCount++; sendMediaRow = rowCount++; sendStickersRow = rowCount++; + sendGifsRow = rowCount++; sendPollsRow = rowCount++; embedLinksRow = rowCount++; addUsersRow = rowCount++; @@ -739,7 +742,9 @@ public class ChatUsersActivity extends BaseFragment implements NotificationCente } else if (position == sendMediaRow) { defaultBannedRights.send_media = !defaultBannedRights.send_media; } else if (position == sendStickersRow) { - defaultBannedRights.send_stickers = defaultBannedRights.send_games = defaultBannedRights.send_gifs = defaultBannedRights.send_inline = !defaultBannedRights.send_stickers; + defaultBannedRights.send_stickers = defaultBannedRights.send_games = defaultBannedRights.send_inline = !defaultBannedRights.send_stickers; + } else if (position == sendGifsRow) { + defaultBannedRights.send_gifs = !defaultBannedRights.send_gifs; } else if (position == embedLinksRow) { defaultBannedRights.embed_links = !defaultBannedRights.embed_links; } else if (position == sendPollsRow) { @@ -768,12 +773,19 @@ public class ChatUsersActivity extends BaseFragment implements NotificationCente } } if ((defaultBannedRights.view_messages || defaultBannedRights.send_messages) && !defaultBannedRights.send_stickers) { - defaultBannedRights.send_stickers = defaultBannedRights.send_games = defaultBannedRights.send_gifs = defaultBannedRights.send_inline = true; + defaultBannedRights.send_stickers = defaultBannedRights.send_games = defaultBannedRights.send_inline = true; RecyclerListView.ViewHolder holder = listView.findViewHolderForAdapterPosition(sendStickersRow); if (holder != null) { ((TextCheckCell2) holder.itemView).setChecked(false); } } + if ((defaultBannedRights.view_messages || defaultBannedRights.send_messages) && !defaultBannedRights.send_gifs) { + defaultBannedRights.send_gifs = true; + RecyclerListView.ViewHolder holder = listView.findViewHolderForAdapterPosition(sendGifsRow); + if (holder != null) { + ((TextCheckCell2) holder.itemView).setChecked(false); + } + } if ((defaultBannedRights.view_messages || defaultBannedRights.send_messages) && !defaultBannedRights.embed_links) { defaultBannedRights.embed_links = true; RecyclerListView.ViewHolder holder = listView.findViewHolderForAdapterPosition(embedLinksRow); @@ -2731,14 +2743,16 @@ public class ChatUsersActivity extends BaseFragment implements NotificationCente } else if (position == sendMediaRow) { checkCell.setTextAndCheck(LocaleController.getString("UserRestrictionsSendMedia", R.string.UserRestrictionsSendMedia), !defaultBannedRights.send_media, true); } else if (position == sendStickersRow) { - checkCell.setTextAndCheck(LocaleController.getString("UserRestrictionsSendStickers", R.string.UserRestrictionsSendStickers), !defaultBannedRights.send_stickers, true); + checkCell.setTextAndCheck(LocaleController.getString("UserRestrictionsSendStickers2", R.string.UserRestrictionsSendStickers2), !defaultBannedRights.send_stickers, true); + } else if (position == sendGifsRow) { + checkCell.setTextAndCheck(LocaleController.getString("UserRestrictionsSendGifs", R.string.UserRestrictionsSendGifs), !defaultBannedRights.send_gifs, true); } else if (position == embedLinksRow) { checkCell.setTextAndCheck(LocaleController.getString("UserRestrictionsEmbedLinks", R.string.UserRestrictionsEmbedLinks), !defaultBannedRights.embed_links, true); } else if (position == sendPollsRow) { checkCell.setTextAndCheck(LocaleController.getString("UserRestrictionsSendPolls", R.string.UserRestrictionsSendPolls), !defaultBannedRights.send_polls, true); } - if (position == sendMediaRow || position == sendStickersRow || position == embedLinksRow || position == sendPollsRow) { + if (position == sendMediaRow || position == sendStickersRow || position == sendGifsRow || position == embedLinksRow || position == sendPollsRow) { checkCell.setEnabled(!defaultBannedRights.send_messages && !defaultBannedRights.view_messages); } else if (position == sendMessagesRow) { checkCell.setEnabled(!defaultBannedRights.view_messages); @@ -2803,7 +2817,8 @@ public class ChatUsersActivity extends BaseFragment implements NotificationCente } else if (position == removedUsersRow) { return 6; } else if (position == changeInfoRow || position == addUsersRow || position == pinMessagesRow || position == sendMessagesRow || - position == sendMediaRow || position == sendStickersRow || position == embedLinksRow || position == sendPollsRow) { + position == sendMediaRow || position == sendStickersRow || position == sendGifsRow || position == embedLinksRow || + position == sendPollsRow) { return 7; } else if (position == membersHeaderRow || position == contactsHeaderRow || position == botHeaderRow) { return 8; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/AlertsCreator.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/AlertsCreator.java index d21ab1592..5207bec10 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AlertsCreator.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AlertsCreator.java @@ -73,6 +73,7 @@ import org.telegram.ui.Cells.CheckBoxCell; import org.telegram.ui.Cells.RadioColorCell; import org.telegram.ui.Cells.TextColorCell; import org.telegram.ui.ChatActivity; +import org.telegram.ui.ChatRightsEditActivity; import org.telegram.ui.LanguageSelectActivity; import org.telegram.ui.LaunchActivity; import org.telegram.ui.NotificationsCustomSettingsActivity; @@ -317,9 +318,6 @@ public class AlertsCreator { builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); builder.setMessage(text); builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null); - if (updateApp) { - builder.setNegativeButton(LocaleController.getString("UpdateApp", R.string.UpdateApp), (dialog, which) -> Browser.openUrl(context, BuildVars.PLAYSTORE_APP_URL)); - } return builder.show(); } @@ -2593,7 +2591,7 @@ public class AlertsCreator { cell.setBackgroundDrawable(Theme.getSelectorDrawable(false)); cell.setTag(a); if (a == 0) { - cell.setText(LocaleController.getString("DeleteBanUser", R.string.DeleteBanUser), "", false, false); + cell.setText(LocaleController.getString("ChangePermissions", R.string.ChangePermissions), "", false, false); } else if (a == 1) { cell.setText(LocaleController.getString("DeleteReportSpam", R.string.DeleteReportSpam), "", false, false); } else if (a == 2) { @@ -2739,9 +2737,6 @@ public class AlertsCreator { } } if (userFinal != null) { - if (checks[0]) { - MessagesController.getInstance(currentAccount).deleteUserFromChat(chat.id, userFinal, chatInfo); - } if (checks[1]) { TLRPC.TL_channels_reportSpam req = new TLRPC.TL_channels_reportSpam(); req.channel = MessagesController.getInputChannel(chat); @@ -2754,6 +2749,40 @@ public class AlertsCreator { if (checks[2]) { MessagesController.getInstance(currentAccount).deleteUserChannelHistory(chat, userFinal, 0); } + if (checks[0]) { + + for (int a = 0; a < chatInfo.participants.participants.size(); a++) { + + TLRPC.ChatParticipant participant = chatInfo.participants.participants.get(a); + + if (participant.user_id != userFinal.id || participant.user_id == UserConfig.getInstance(currentAccount).getCurrentUser().id) { + + continue; + + } + + final TLRPC.ChannelParticipant channelParticipant; + + if (ChatObject.isChannel(chat)) { + channelParticipant = ((TLRPC.TL_chatChannelParticipant) participant).channelParticipant; + } else { + channelParticipant = null; + } + + TLRPC.User u = MessagesController.getInstance(currentAccount).getUser(participant.user_id); + + if (channelParticipant != null) { + openRightsEdit(fragment,chatInfo,chat, u.id, participant, channelParticipant.admin_rights, channelParticipant.banned_rights, channelParticipant.rank); + } else { + openRightsEdit(fragment,chatInfo,chat, u.id, participant, null, null, ""); + } + + } + + //MessagesController.getInstance(currentAccount).deleteUserFromChat(chat.id, userFinal, chatInfo); + + } + } if (onDelete != null) { onDelete.run(); @@ -2805,6 +2834,44 @@ public class AlertsCreator { } } + private static void openRightsEdit(BaseFragment f, TLRPC.ChatFull chatInfo, TLRPC.Chat currentChat, int user_id, TLRPC.ChatParticipant participant, TLRPC.TL_chatAdminRights adminRights, TLRPC.TL_chatBannedRights bannedRights, String rank) { + ChatRightsEditActivity fragment = new ChatRightsEditActivity(user_id, currentChat.id, adminRights, currentChat.default_banned_rights, bannedRights, rank, 1, true, false); + fragment.setDelegate(new ChatRightsEditActivity.ChatRightsEditActivityDelegate() { + @Override + public void didChangeOwner(TLRPC.User user) { + } + + @Override + public void didSetRights(int rights, TLRPC.TL_chatAdminRights rightsAdmin, TLRPC.TL_chatBannedRights rightsBanned, String rank) { + if (rights == 0) { + if (currentChat.megagroup && chatInfo != null && chatInfo.participants != null) { + for (int a = 0; a < chatInfo.participants.participants.size(); a++) { + TLRPC.ChannelParticipant p = ((TLRPC.TL_chatChannelParticipant) chatInfo.participants.participants.get(a)).channelParticipant; + if (p.user_id == participant.user_id) { + if (chatInfo != null) { + chatInfo.participants_count--; + } + chatInfo.participants.participants.remove(a); + break; + } + } + if (chatInfo != null && chatInfo.participants != null) { + for (int a = 0; a < chatInfo.participants.participants.size(); a++) { + TLRPC.ChatParticipant p = chatInfo.participants.participants.get(a); + if (p.user_id == participant.user_id) { + chatInfo.participants.participants.remove(a); + break; + } + } + } + } + } + } + + }); + f.presentFragment(fragment); + } + public static void createThemeCreateDialog(BaseFragment fragment, int type, Theme.ThemeInfo switchToTheme, Theme.ThemeAccent switchToAccent) { if (fragment == null || fragment.getParentActivity() == null) { return; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java index 85d409703..b1833d89b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java @@ -116,6 +116,7 @@ import java.util.List; import java.util.Locale; import tw.nekomimi.nekogram.NekoConfig; +import tw.nekomimi.nekogram.utils.AlertUtil; public class ChatActivityEnterView extends FrameLayout implements NotificationCenter.NotificationCenterDelegate, SizeNotifierFrameLayout.SizeNotifierFrameLayoutDelegate, StickersAlert.StickersAlertDelegate { @@ -4115,7 +4116,9 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); parentFragment.showDialog(builder.create()); - } else if (button instanceof TLRPC.TL_keyboardButtonCallback || button instanceof TLRPC.TL_keyboardButtonGame || button instanceof TLRPC.TL_keyboardButtonBuy || button instanceof TLRPC.TL_keyboardButtonUrlAuth) { + } else if (button instanceof TLRPC.TL_keyboardButtonBuy) { + AlertUtil.showToast("Please use offical Telegram application to create payments"); + } else if (button instanceof TLRPC.TL_keyboardButtonCallback || button instanceof TLRPC.TL_keyboardButtonGame || button instanceof TLRPC.TL_keyboardButtonUrlAuth) { SendMessagesHelper.getInstance(currentAccount).sendCallback(true, messageObject, button, parentFragment); } else if (button instanceof TLRPC.TL_keyboardButtonSwitchInline) { if (parentFragment.processSwitchButton((TLRPC.TL_keyboardButtonSwitchInline) button)) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextBoldCursor.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextBoldCursor.java index eb90c93c0..39fc5797c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextBoldCursor.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextBoldCursor.java @@ -190,8 +190,7 @@ public class EditTextBoldCursor extends EditText { mShowCursorField = editorClass.getDeclaredField("mShowCursor"); mShowCursorField.setAccessible(true); } - } catch (Throwable e) { - FileLog.e(e); + } catch (Throwable ignored) { } try { gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, new int[] {0xff54a1db, 0xff54a1db}); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/JoinGroupAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/JoinGroupAlert.java index 6f9813fd7..b17aaaccd 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/JoinGroupAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/JoinGroupAlert.java @@ -61,7 +61,7 @@ public class JoinGroupAlert extends BottomSheet { int participants_count; BackupImageView avatarImageView = new BackupImageView(context); - avatarImageView.setRoundRadius(AndroidUtilities.dp(35)); + //avatarImageView.setRoundRadius(AndroidUtilities.dp(35)); linearLayout.addView(avatarImageView, LayoutHelper.createLinear(70, 70, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 29, 0, 0)); if (invite.chat != null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/UndoView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/UndoView.java index b53503cbb..ee32f169c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/UndoView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/UndoView.java @@ -36,6 +36,8 @@ import org.telegram.messenger.UserObject; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.Theme; +import tw.nekomimi.nekogram.NekoXConfig; + @SuppressWarnings("FieldCanBeLocal") public class UndoView extends FrameLayout { @@ -255,6 +257,10 @@ public class UndoView extends FrameLayout { if (currentActionRunnable != null) { currentActionRunnable.run(); } + if (NekoXConfig.disableUndo && actionRunnable != null) { + actionRunnable.run(); + return; + } isShown = true; currentActionRunnable = actionRunnable; currentCancelRunnable = cancelRunnable; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/WebPlayerView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/WebPlayerView.java index 5cf185150..fff23a0ff 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/WebPlayerView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/WebPlayerView.java @@ -45,6 +45,9 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; +import okhttp3.Headers; +import okhttp3.OkHttpClient; +import okhttp3.Request; import org.json.JSONArray; import org.json.JSONObject; import org.json.JSONTokener; @@ -57,12 +60,18 @@ import org.telegram.messenger.FileLog; import org.telegram.messenger.ImageLocation; import org.telegram.messenger.ImageReceiver; import org.telegram.messenger.R; +import org.telegram.messenger.SharedConfig; import org.telegram.messenger.Utilities; import org.telegram.tgnet.TLRPC; +import tw.nekomimi.nekogram.utils.HttpUtil; +import tw.nekomimi.nekogram.utils.ThreadUtil; import java.io.FileNotFoundException; +import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; +import java.net.InetSocketAddress; +import java.net.Proxy; import java.net.SocketException; import java.net.SocketTimeoutException; import java.net.URL; @@ -74,6 +83,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.Locale; +import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -445,139 +455,45 @@ public class WebPlayerView extends ViewGroup implements VideoPlayer.VideoPlayerD } protected String downloadUrlContent(AsyncTask parentTask, String url, HashMap headers, boolean tryGzip) { - boolean canRetry = true; - InputStream httpConnectionStream = null; - boolean done = false; - StringBuilder result = null; - URLConnection httpConnection = null; - try { - URL downloadUrl = new URL(url); - httpConnection = downloadUrl.openConnection(); - httpConnection.addRequestProperty("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20150101 Firefox/47.0 (Chrome)"); - if (tryGzip) { - httpConnection.addRequestProperty("Accept-Encoding", "gzip, deflate"); - } - httpConnection.addRequestProperty("Accept-Language", "en-us,en;q=0.5"); - httpConnection.addRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); - httpConnection.addRequestProperty("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7"); - if (headers != null) { - for (HashMap.Entry entry : headers.entrySet()) { - httpConnection.addRequestProperty(entry.getKey(), entry.getValue()); - } - } - httpConnection.setConnectTimeout(5000); - httpConnection.setReadTimeout(5000); - if (httpConnection instanceof HttpURLConnection) { - HttpURLConnection httpURLConnection = (HttpURLConnection) httpConnection; - httpURLConnection.setInstanceFollowRedirects(true); - int status = httpURLConnection.getResponseCode(); - if (status == HttpURLConnection.HTTP_MOVED_TEMP || status == HttpURLConnection.HTTP_MOVED_PERM || status == HttpURLConnection.HTTP_SEE_OTHER) { - String newUrl = httpURLConnection.getHeaderField("Location"); - String cookies = httpURLConnection.getHeaderField("Set-Cookie"); - downloadUrl = new URL(newUrl); - httpConnection = downloadUrl.openConnection(); - httpConnection.setRequestProperty("Cookie", cookies); - httpConnection.addRequestProperty("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20150101 Firefox/47.0 (Chrome)"); - if (tryGzip) { - httpConnection.addRequestProperty("Accept-Encoding", "gzip, deflate"); - } - httpConnection.addRequestProperty("Accept-Language", "en-us,en;q=0.5"); - httpConnection.addRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); - httpConnection.addRequestProperty("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7"); - if (headers != null) { - for (HashMap.Entry entry : headers.entrySet()) { - httpConnection.addRequestProperty(entry.getKey(), entry.getValue()); - } - } - } - } - httpConnection.connect(); - if (tryGzip) { - try { - httpConnectionStream = new GZIPInputStream(httpConnection.getInputStream()); - } catch (Exception e) { - try { - if (httpConnectionStream != null) { - httpConnectionStream.close(); - } - } catch (Exception ignore) { - } - httpConnection = downloadUrl.openConnection(); - httpConnection.connect(); - httpConnectionStream = httpConnection.getInputStream(); - } - } else { - httpConnectionStream = httpConnection.getInputStream(); - } - } catch (Throwable e) { - if (e instanceof SocketTimeoutException) { - if (ApplicationLoader.isNetworkOnline()) { - canRetry = false; - } - } else if (e instanceof UnknownHostException) { - canRetry = false; - } else if (e instanceof SocketException) { - if (e.getMessage() != null && e.getMessage().contains("ECONNRESET")) { - canRetry = false; - } - } else if (e instanceof FileNotFoundException) { - canRetry = false; - } - FileLog.e(e); + OkHttpClient client = HttpUtil.getOkhttpClientWithCurrProxy().newBuilder() + .followRedirects(true) + .followSslRedirects(true) + .build(); + + Request.Builder request = new Request.Builder() + .url(url) + .header("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20150101 Firefox/47.0 (Chrome)") + .header("Accept-Language", "en-us,en;q=0.5") + .header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8") + .header("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7"); + + for (Map.Entry header : headers.entrySet()) { + + request.addHeader(header.getKey(),header.getValue()); + } - if (canRetry) { + if (tryGzip) { + + request.addHeader("Accept-Encoding", "gzip, deflate"); + + } + + int count = 0; + + do { try { - if (httpConnection instanceof HttpURLConnection) { - int code = ((HttpURLConnection) httpConnection).getResponseCode(); - if (code != HttpURLConnection.HTTP_OK && code != HttpURLConnection.HTTP_ACCEPTED && code != HttpURLConnection.HTTP_NOT_MODIFIED) { - //canRetry = false; - } - } + return client.newCall(request.build()).execute().body().string(); } catch (Exception e) { FileLog.e(e); } + count ++; + ThreadUtil.sleep(1000); + } while (count < 3); - if (httpConnectionStream != null) { - try { - byte[] data = new byte[1024 * 32]; - while (true) { - if (parentTask.isCancelled()) { - break; - } - try { - int read = httpConnectionStream.read(data); - if (read > 0) { - if (result == null) { - result = new StringBuilder(); - } - result.append(new String(data, 0, read, StandardCharsets.UTF_8)); - } else if (read == -1) { - done = true; - break; - } else { - break; - } - } catch (Exception e) { - FileLog.e(e); - break; - } - } - } catch (Throwable e) { - FileLog.e(e); - } - } + return null; - try { - if (httpConnectionStream != null) { - httpConnectionStream.close(); - } - } catch (Throwable e) { - FileLog.e(e); - } - } - return done ? result.toString() : null; } private class YoutubeVideoTask extends AsyncTask { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java index 03b185d16..8266a65ce 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java @@ -142,9 +142,10 @@ import java.util.Date; import tw.nekomimi.nekogram.FilterPopup; import tw.nekomimi.nekogram.MessageHelper; import tw.nekomimi.nekogram.NekoConfig; +import tw.nekomimi.nekogram.utils.ProxyUtil; public class DialogsActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { - + private DialogsRecyclerView listView; private RecyclerListView searchListView; private LinearLayoutManager layoutManager; @@ -155,6 +156,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. private RadialProgressView progressView; private ActionBarMenuItem passcodeItem; private ActionBarMenuItem proxyItem; + private ActionBarMenuItem scanItem; private ProxyDrawable proxyDrawable; private ImageView floatingButton; private FrameLayout floatingButtonContainer; @@ -217,7 +219,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. private AlertDialog permissionDialog; private boolean askAboutContacts = true; - private boolean proxyItemVisisble; + private boolean proxyItemVisisble = true; private boolean closeSearchFieldOnHide; private long searchDialogId; private TLObject searchObject; @@ -257,7 +259,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. private int canUnmuteCount; private int canClearCacheCount; private int canReportSpamCount; - + private int folderId; private final static int pin = 100; @@ -947,7 +949,6 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. if (FilterPopup.DialogType.isDialogsType(dialogsType)) { askAboutContacts = MessagesController.getGlobalNotificationsSettings().getBoolean("askAboutContacts", true); - SharedConfig.loadProxyList(); } if (searchString == null) { @@ -1048,6 +1049,11 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. updatePasscodeButton(); updateProxyButton(false); } + + scanItem = menu.addItem(3, R.drawable.wallet_qr); + scanItem.setContentDescription(LocaleController.getString("ScanQRCode", R.string.ScanQRCode)); + scanItem.setVisibility(View.GONE); + final ActionBarMenuItem item = menu.addItem(0, R.drawable.ic_ab_search).setIsSearchField(true).setActionBarMenuItemSearchListener(new ActionBarMenuItem.ActionBarMenuItemSearchListener() { @Override public void onSearchExpand() { @@ -1057,6 +1063,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. } if (proxyItem != null && proxyItemVisisble) { proxyItem.setVisibility(View.GONE); + scanItem.setVisibility(View.VISIBLE); } if (listView != null) { if (searchString != null) { @@ -1079,6 +1086,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. } if (proxyItem != null && proxyItemVisisble) { proxyItem.setVisibility(View.VISIBLE); + scanItem.setVisibility(View.GONE); } if (searchString != null) { finishFragment(); @@ -1212,6 +1220,26 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. updatePasscodeButton(); } else if (id == 2) { presentFragment(new ProxyListActivity()); + } else if (id == 3) { + + if (Build.VERSION.SDK_INT >= 23) { + if (getParentActivity().checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { + getParentActivity().requestPermissions(new String[]{Manifest.permission.CAMERA}, 22); + return; + } + } + + CameraScanActivity.showAsSheet(DialogsActivity.this, new CameraScanActivity.CameraScanActivityDelegate() { + + @Override + public void didFindQr(String text) { + + ProxyUtil.showLinkAlert(getParentActivity(),text); + + } + }); + + } else if (id >= 10 && id < 10 + UserConfig.MAX_ACCOUNT_COUNT) { if (getParentActivity() == null) { return; @@ -3336,6 +3364,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. if (proxyDrawable == null) { return; } + /* SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); String proxyAddress = preferences.getString("proxy_ip", ""); boolean proxyEnabled; @@ -3343,12 +3372,16 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. if (!actionBar.isSearchFieldVisible()) { proxyItem.setVisibility(View.VISIBLE); } - proxyDrawable.setConnected(proxyEnabled, currentConnectionState == ConnectionsManager.ConnectionStateConnected || currentConnectionState == ConnectionsManager.ConnectionStateUpdating, animated); - proxyItemVisisble = true; + + */ + proxyDrawable.setConnected(true, currentConnectionState == ConnectionsManager.ConnectionStateConnected || currentConnectionState == ConnectionsManager.ConnectionStateUpdating, animated); + /* proxyItemVisisble = true; } else { - proxyItem.setVisibility(View.GONE); + // proxyItem.setVisibility(View.GONE); proxyItemVisisble = false; } + + */ } private void updateSelectedCount() { @@ -3493,6 +3526,9 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. case FilterPopup.DialogType.Users: title = LocaleController.getString("Users", R.string.Users); break; + case FilterPopup.DialogType.Contacts: + title = LocaleController.getString("Contacts", R.string.Contacts); + break; case FilterPopup.DialogType.Groups: title = LocaleController.getString("Groups", R.string.Groups); break; @@ -3508,6 +3544,12 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. case FilterPopup.DialogType.Unmuted: title = LocaleController.getString("NotificationsUnmuted", R.string.NotificationsUnmuted); break; + case FilterPopup.DialogType.Unread: + title = LocaleController.getString("NotificationsUnread", R.string.NotificationsUnread); + break; + case FilterPopup.DialogType.UnmutedAndUnread: + title = LocaleController.getString("NotificationsUnmutedAndUnread", R.string.NotificationsUnmutedAndUnread); + break; default: if (folderId != 0) { title = LocaleController.getString("ArchivedChats", R.string.ArchivedChats); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/GroupInviteActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/GroupInviteActivity.java index 9affe86ab..f921f514e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/GroupInviteActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/GroupInviteActivity.java @@ -16,13 +16,16 @@ import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.Toast; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.ChatObject; +import org.telegram.messenger.FileLog; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MessagesController; import org.telegram.messenger.NotificationCenter; -import org.telegram.messenger.ApplicationLoader; -import org.telegram.messenger.FileLog; import org.telegram.messenger.R; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLRPC; @@ -38,8 +41,7 @@ import org.telegram.ui.Components.EmptyTextProgressView; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.RecyclerListView; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; +import tw.nekomimi.nekogram.utils.ProxyUtil; public class GroupInviteActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { @@ -56,6 +58,7 @@ public class GroupInviteActivity extends BaseFragment implements NotificationCen private int copyLinkRow; private int revokeLinkRow; private int shareLinkRow; + private int shareQrCodeRow; private int shadowRow; private int rowCount; @@ -78,6 +81,7 @@ public class GroupInviteActivity extends BaseFragment implements NotificationCen copyLinkRow = rowCount++; revokeLinkRow = rowCount++; shareLinkRow = rowCount++; + shareQrCodeRow = rowCount++; shadowRow = rowCount++; return true; @@ -146,6 +150,11 @@ public class GroupInviteActivity extends BaseFragment implements NotificationCen } catch (Exception e) { FileLog.e(e); } + } else if (position == shareQrCodeRow) { + if (invite == null) { + return; + } + ProxyUtil.showQrDialog(getParentActivity(),invite.link); } else if (position == revokeLinkRow) { AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); builder.setMessage(LocaleController.getString("RevokeAlert", R.string.RevokeAlert)); @@ -214,7 +223,7 @@ public class GroupInviteActivity extends BaseFragment implements NotificationCen } private class ListAdapter extends RecyclerListView.SelectionAdapter { - + private Context mContext; public ListAdapter(Context context) { @@ -224,7 +233,7 @@ public class GroupInviteActivity extends BaseFragment implements NotificationCen @Override public boolean isEnabled(RecyclerView.ViewHolder holder) { int position = holder.getAdapterPosition(); - return position == revokeLinkRow || position == copyLinkRow || position == shareLinkRow || position == linkRow; + return position == revokeLinkRow || position == copyLinkRow || position == shareLinkRow || position == shareQrCodeRow || position == linkRow; } @Override @@ -261,6 +270,8 @@ public class GroupInviteActivity extends BaseFragment implements NotificationCen textCell.setText(LocaleController.getString("CopyLink", R.string.CopyLink), true); } else if (position == shareLinkRow) { textCell.setText(LocaleController.getString("ShareLink", R.string.ShareLink), false); + } else if (position == shareQrCodeRow) { + textCell.setText(LocaleController.getString("ShareQRCode", R.string.ShareQRCode), false); } else if (position == revokeLinkRow) { textCell.setText(LocaleController.getString("RevokeLink", R.string.RevokeLink), true); } @@ -289,7 +300,7 @@ public class GroupInviteActivity extends BaseFragment implements NotificationCen @Override public int getItemViewType(int position) { - if (position == copyLinkRow || position == shareLinkRow || position == revokeLinkRow) { + if (position == copyLinkRow || position == shareLinkRow || position == shareQrCodeRow || position == revokeLinkRow) { return 0; } else if (position == shadowRow || position == linkInfoRow) { return 1; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java index 9432a49bd..c554166cd 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java @@ -15,17 +15,15 @@ import android.app.Activity; import android.app.ActivityManager; import android.content.Intent; import android.content.SharedPreferences; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.database.Cursor; -import android.graphics.Canvas; import android.graphics.Bitmap; +import android.graphics.Canvas; import android.graphics.Point; import android.graphics.Shader; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; -import android.location.Location; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -50,43 +48,51 @@ import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.Toast; +import androidx.recyclerview.widget.ItemTouchHelper; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.v2ray.ang.V2RayConfig; +import com.v2ray.ang.dto.V2rayConfig; + import org.telegram.messenger.AccountInstance; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.BuildVars; import org.telegram.messenger.ChatObject; import org.telegram.messenger.ContactsController; -import org.telegram.messenger.LocationController; -import org.telegram.messenger.MediaDataController; import org.telegram.messenger.FileLoader; +import org.telegram.messenger.FileLog; import org.telegram.messenger.ImageLoader; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.LocationController; import org.telegram.messenger.MediaController; +import org.telegram.messenger.MediaDataController; import org.telegram.messenger.MessageObject; import org.telegram.messenger.MessagesController; import org.telegram.messenger.MessagesStorage; -import org.telegram.messenger.SendMessagesHelper; -import org.telegram.messenger.SharedConfig; -import org.telegram.messenger.UserObject; -import org.telegram.messenger.Utilities; -import org.telegram.messenger.ApplicationLoader; -import org.telegram.messenger.FileLog; -import org.telegram.messenger.LocaleController; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; +import org.telegram.messenger.SendMessagesHelper; +import org.telegram.messenger.SharedConfig; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.UserObject; +import org.telegram.messenger.Utilities; import org.telegram.messenger.browser.Browser; import org.telegram.messenger.camera.CameraController; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLRPC; -import org.telegram.messenger.UserConfig; -import org.telegram.ui.ActionBar.AlertDialog; -import org.telegram.ui.Adapters.DrawerLayoutAdapter; import org.telegram.ui.ActionBar.ActionBarLayout; +import org.telegram.ui.ActionBar.AlertDialog; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.DrawerLayoutContainer; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Adapters.DrawerLayoutAdapter; import org.telegram.ui.Cells.DrawerAddCell; import org.telegram.ui.Cells.DrawerUserCell; import org.telegram.ui.Cells.LanguageCell; -import org.telegram.ui.Components.AudioPlayerAlert; import org.telegram.ui.Components.AlertsCreator; +import org.telegram.ui.Components.AudioPlayerAlert; import org.telegram.ui.Components.BlockingUpdateView; import org.telegram.ui.Components.ChatActivityEnterView; import org.telegram.ui.Components.CubicBezierInterpolator; @@ -100,10 +106,8 @@ import org.telegram.ui.Components.RecyclerListView; import org.telegram.ui.Components.SharingLocationsAlert; import org.telegram.ui.Components.SideMenultItemAnimator; import org.telegram.ui.Components.StickersAlert; -import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Components.TermsOfServiceView; import org.telegram.ui.Components.ThemeEditorView; -import org.telegram.ui.Components.UpdateAppAlertDialog; import java.io.File; import java.util.ArrayList; @@ -111,11 +115,10 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import androidx.recyclerview.widget.ItemTouchHelper; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - import tw.nekomimi.nekogram.NekoSettingsActivity; +import tw.nekomimi.nekogram.NekoXConfig; +import tw.nekomimi.nekogram.NekoXSettingActivity; +import tw.nekomimi.nekogram.utils.ProxyUtil; public class LaunchActivity extends Activity implements ActionBarLayout.ActionBarLayoutDelegate, NotificationCenter.NotificationCenterDelegate, DialogsActivity.DialogsActivityDelegate { @@ -244,7 +247,12 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa Uri uri = intent.getData(); if (uri != null) { String url = uri.toString().toLowerCase(); - isProxy = url.startsWith("tg:proxy") || url.startsWith("tg://proxy") || url.startsWith("tg:socks") || url.startsWith("tg://socks"); + isProxy = url.startsWith("tg:proxy") || url.startsWith("tg://proxy") || url.startsWith("tg:socks") || url.startsWith("tg://socks") || + url.startsWith(V2RayConfig.VMESS_PROTOCOL) || + url.startsWith(V2RayConfig.VMESS1_PROTOCOL) || + url.startsWith(V2RayConfig.SS_PROTOCOL) || + url.startsWith(V2RayConfig.SSR_PROTOCOL) + ; } } } @@ -1594,6 +1602,8 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa open_settings = 2; } else if (url.contains("devices")) { open_settings = 3; + } else if (url.contains("nekox")) { + open_settings = 5; } else if (url.contains("neko")) { open_settings = 4; } @@ -1799,6 +1809,12 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa fragment = new SessionsActivity(0); } else if (open_settings == 4) { fragment = new NekoSettingsActivity(); + } else if (open_settings == 5) { + if (NekoXConfig.developerMode) { + fragment = new NekoXSettingActivity(); + } else { + fragment = new NekoSettingsActivity(); + } } else { fragment = null; } @@ -2468,49 +2484,6 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa } } - public void checkAppUpdate(boolean force) { - if (!force && BuildVars.DEBUG_VERSION || !force && !BuildVars.CHECK_UPDATES) { - return; - } - if (!force && Math.abs(System.currentTimeMillis() - UserConfig.getInstance(0).lastUpdateCheckTime) < 24 * 60 * 60 * 1000) { - return; - } - TLRPC.TL_help_getAppUpdate req = new TLRPC.TL_help_getAppUpdate(); - try { - req.source = ApplicationLoader.applicationContext.getPackageManager().getInstallerPackageName(ApplicationLoader.applicationContext.getPackageName()); - } catch (Exception ignore) { - - } - if (req.source == null) { - req.source = ""; - } - final int accountNum = currentAccount; - ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> { - UserConfig.getInstance(0).lastUpdateCheckTime = System.currentTimeMillis(); - UserConfig.getInstance(0).saveConfig(false); - if (response instanceof TLRPC.TL_help_appUpdate) { - final TLRPC.TL_help_appUpdate res = (TLRPC.TL_help_appUpdate) response; - AndroidUtilities.runOnUIThread(() -> { - if (res.can_not_skip) { - UserConfig.getInstance(0).pendingAppUpdate = res; - UserConfig.getInstance(0).pendingAppUpdateBuildVersion = BuildVars.BUILD_VERSION; - try { - PackageInfo packageInfo = ApplicationLoader.applicationContext.getPackageManager().getPackageInfo(ApplicationLoader.applicationContext.getPackageName(), 0); - UserConfig.getInstance(0).pendingAppUpdateInstallTime = Math.max(packageInfo.lastUpdateTime, packageInfo.firstInstallTime); - } catch (Exception e) { - FileLog.e(e); - UserConfig.getInstance(0).pendingAppUpdateInstallTime = 0; - } - UserConfig.getInstance(0).saveConfig(false); - showUpdateActivity(accountNum, res, false); - } else { - (new UpdateAppAlertDialog(LaunchActivity.this, res, accountNum)).show(); - } - }); - } - }); - } - public AlertDialog showAlertDialog(AlertDialog.Builder builder) { try { if (visibleDialog != null) { @@ -2534,12 +2507,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa } localeDialog = null; } else if (visibleDialog == proxyErrorDialog) { - SharedPreferences preferences = MessagesController.getGlobalMainSettings(); - SharedPreferences.Editor editor = MessagesController.getGlobalMainSettings().edit(); - editor.putBoolean("proxy_enabled", false); - editor.putBoolean("proxy_enabled_calls", false); - editor.commit(); - ConnectionsManager.setProxySettings(false, "", 1080, "", "", ""); + SharedConfig.setProxyEnable(false); NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.proxySettingsChanged); proxyErrorDialog = null; } @@ -2865,7 +2833,9 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa ApplicationLoader.mainInterfacePausedStageQueueTime = 0; }); onPasscodePause(); - actionBarLayout.onPause(); + if (actionBarLayout != null) { + actionBarLayout.onPause(); + } if (AndroidUtilities.isTablet()) { rightActionBarLayout.onPause(); layersActionBarLayout.onPause(); @@ -2976,6 +2946,9 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa AndroidUtilities.checkForUpdates(this); ConnectionsManager.getInstance(currentAccount).setAppPaused(false, false); updateCurrentConnectionState(currentAccount); + if (NekoXConfig.disableProxyWhenVpnEnabled && SharedConfig.proxyEnabled && ProxyUtil.isVPNEnabled()) { + SharedConfig.setProxyEnable(false); + } if (PhotoViewer.hasInstance() && PhotoViewer.getInstance().isVisible()) { PhotoViewer.getInstance().onResume(); } @@ -2991,7 +2964,6 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa } else if (UserConfig.getInstance(0).pendingAppUpdate != null) { showUpdateActivity(UserConfig.selectedAccount, UserConfig.getInstance(0).pendingAppUpdate, true); } - checkAppUpdate(false); } @Override @@ -3714,7 +3686,7 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa @Override public void onBackPressed() { - if (passcodeView.getVisibility() == View.VISIBLE) { + if (passcodeView != null && passcodeView.getVisibility() == View.VISIBLE) { finish(); return; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java index acf8369a1..3b5026676 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java @@ -18,7 +18,6 @@ import android.annotation.SuppressLint; import android.app.Activity; import android.app.Dialog; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageInfo; @@ -69,24 +68,25 @@ import android.widget.Toast; import org.telegram.PhoneFormat.PhoneFormat; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.BuildConfig; +import org.telegram.messenger.BuildVars; import org.telegram.messenger.ContactsController; +import org.telegram.messenger.FileLog; import org.telegram.messenger.ImageLocation; +import org.telegram.messenger.LocaleController; import org.telegram.messenger.MessageObject; import org.telegram.messenger.MessagesController; import org.telegram.messenger.MessagesStorage; import org.telegram.messenger.NotificationCenter; -import org.telegram.messenger.ApplicationLoader; -import org.telegram.messenger.BuildVars; -import org.telegram.messenger.FileLog; -import org.telegram.messenger.LocaleController; import org.telegram.messenger.R; import org.telegram.messenger.SRPHelper; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.Utilities; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.RequestDelegate; import org.telegram.tgnet.SerializedData; import org.telegram.tgnet.TLRPC; -import org.telegram.messenger.UserConfig; -import org.telegram.messenger.Utilities; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.ActionBarMenuItem; @@ -104,6 +104,7 @@ import org.telegram.ui.Components.EditTextBoldCursor; import org.telegram.ui.Components.HintEditText; import org.telegram.ui.Components.ImageUpdater; import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.ProxyDrawable; import org.telegram.ui.Components.RadialProgressView; import org.telegram.ui.Components.SlideView; @@ -118,7 +119,7 @@ import java.util.Set; import java.util.Timer; import java.util.TimerTask; -import tw.nekomimi.nekogram.NekoConfig; +import tw.nekomimi.nekogram.NekoXConfig; @SuppressLint("HardwareIds") public class LoginActivity extends BaseFragment { @@ -149,13 +150,16 @@ public class LoginActivity extends BaseFragment { private FrameLayout floatingButtonContainer; private RadialProgressView floatingProgressView; private int progressRequestId; - private boolean[] doneButtonVisible = new boolean[] {true, false}; + private boolean[] doneButtonVisible = new boolean[]{true, false}; private static final int DONE_TYPE_FLOATING = 0; private static final int DONE_TYPE_ACTION = 1; private final static int done_button = 1; + private ActionBarMenuItem proxyItem; + private ProxyDrawable proxyDrawable; + private class ProgressView extends View { private final Path path = new Path(); @@ -256,6 +260,10 @@ public class LoginActivity extends BaseFragment { finishFragment(); } } else if (id == 2) { + presentFragment(new ProxyListActivity()); + } else if (id == 3) { + presentFragment(new LanguageSelectActivity()); + } else if (id == 4) { AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle(LocaleController.getString("BotLogin", R.string.BotLogin)); @@ -291,10 +299,10 @@ public class LoginActivity extends BaseFragment { } ConnectionsManager.getInstance(currentAccount).cleanup(false); - final TLRPC.TL_auth_importBotAuthorization req = new TLRPC.TL_auth_importBotAuthorization (); + final TLRPC.TL_auth_importBotAuthorization req = new TLRPC.TL_auth_importBotAuthorization(); - req.api_hash = BuildVars.APP_HASH; - req.api_id = BuildVars.APP_ID; + req.api_hash = BuildConfig.APP_HASH; + req.api_id = BuildConfig.APP_ID; req.bot_auth_token = token; req.flags = 0; int reqId = ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { @@ -357,7 +365,19 @@ public class LoginActivity extends BaseFragment { doneButtonVisible[DONE_TYPE_ACTION] = false; ActionBarMenu menu = actionBar.createMenu(); - menu.addItem(2, R.drawable.list_bot); + + proxyItem = menu.addItem(2, R.drawable.proxy_on); + proxyItem.setContentDescription(LocaleController.getString("ProxySettings", R.string.ProxySettings)); + + + menu.addItem(3, R.drawable.ic_translate); + + if (NekoXConfig.showBotLogin) { + + menu.addItem(4, R.drawable.list_bot); + + } + actionBar.setAllowOverlayTitle(true); doneItem = menu.addItemWithWidth(done_button, R.drawable.ic_done, AndroidUtilities.dp(56)); doneProgressView = new ContextProgressView(context, 1); @@ -902,7 +922,7 @@ public class LoginActivity extends BaseFragment { views[currentViewNum].onNextPressed(); } } - + private void showEditDoneProgress(final boolean show, boolean animated) { if (doneItemAnimation != null) { doneItemAnimation.cancel(); @@ -1537,7 +1557,7 @@ public class LoginActivity extends BaseFragment { }); } - if (NekoConfig.showHiddenFeature) { + if (NekoXConfig.showTestBackend) { testBackendCell = new CheckBoxCell(context, 2); testBackendCell.setText(LocaleController.getString("TestBackend", R.string.TestBackend), "", ConnectionsManager.native_isTestBackend(currentAccount) != 0, false); addView(testBackendCell, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, 0, 0, 0)); @@ -1780,18 +1800,13 @@ public class LoginActivity extends BaseFragment { ConnectionsManager.getInstance(currentAccount).cleanup(false); final TLRPC.TL_auth_sendCode req = new TLRPC.TL_auth_sendCode(); - req.api_hash = BuildVars.APP_HASH; - req.api_id = BuildVars.APP_ID; + req.api_hash = BuildConfig.APP_HASH; + req.api_id = BuildConfig.APP_ID; req.phone_number = phone; req.settings = new TLRPC.TL_codeSettings(); req.settings.allow_flashcall = simcardAvailable && allowCall && allowCancelCall && allowReadCallLog; req.settings.allow_app_hash = ApplicationLoader.hasPlayServices; SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); - if (req.settings.allow_app_hash) { - preferences.edit().putString("sms_hash", BuildVars.SMS_HASH).commit(); - } else { - preferences.edit().remove("sms_hash").commit(); - } if (req.settings.allow_flashcall) { try { String number = tm.getLine1Number(); @@ -2420,23 +2435,7 @@ public class LoginActivity extends BaseFragment { problemText.setVisibility(time < 1000 ? VISIBLE : GONE); timeText.setVisibility(time < 1000 ? GONE : VISIBLE); - SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); - String hash = preferences.getString("sms_hash", null); - String savedCode = null; - if (!TextUtils.isEmpty(hash)) { - savedCode = preferences.getString("sms_hash_code", null); - if (savedCode != null && savedCode.contains(hash + "|")) { - savedCode = savedCode.substring(savedCode.indexOf('|') + 1); - } else { - savedCode = null; - } - } - if (savedCode != null) { - codeField[0].setText(savedCode); - onNextPressed(); - } else { - createTimer(); - } + createTimer(); } else if (currentType == 4 && nextType == 2) { timeText.setText(LocaleController.formatString("SmsText", R.string.SmsText, 2, 0)); problemText.setVisibility(time < 1000 ? VISIBLE : GONE); @@ -4109,7 +4108,7 @@ public class LoginActivity extends BaseFragment { @Override public ThemeDescription[] getThemeDescriptions() { - for (int a = 0;a < views.length; a++) { + for (int a = 0; a < views.length; a++) { if (views[a] == null) { return new ThemeDescription[0]; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/NotificationsSettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/NotificationsSettingsActivity.java index 1c7be25a4..c5d2efa14 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/NotificationsSettingsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/NotificationsSettingsActivity.java @@ -9,6 +9,7 @@ package org.telegram.ui; import android.app.Activity; +import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -36,6 +37,7 @@ import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.SharedConfig; import org.telegram.messenger.UserConfig; import org.telegram.messenger.Utilities; +import org.telegram.messenger.XiaomiUtilities; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLRPC; import org.telegram.messenger.FileLog; @@ -59,7 +61,9 @@ import org.telegram.ui.Components.RecyclerListView; import java.util.ArrayList; import java.util.Map; +import java.util.Set; +import androidx.core.app.NotificationManagerCompat; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -572,12 +576,35 @@ public class NotificationsSettingsActivity extends BaseFragment implements Notif } } } else if (position == notificationsServiceRow) { - SharedPreferences preferences = MessagesController.getNotificationsSettings(currentAccount); - enabled = preferences.getBoolean("pushService", getMessagesController().keepAliveService); - SharedPreferences.Editor editor = preferences.edit(); - editor.putBoolean("pushService", !enabled); - editor.commit(); - ApplicationLoader.startPushService(); + if (XiaomiUtilities.isMIUI() && !XiaomiUtilities.isCustomPermissionGranted(XiaomiUtilities.OP_AUTO_START)) { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle("Telegram"); + builder.setMessage(LocaleController.getString("MIUIPermissionNote",R.string.MIUIPermissionNote)); + builder.setPositiveButton(LocaleController.getString("OK",R.string.OK),(_x,_y) -> { + + getParentActivity().startActivity(XiaomiUtilities.getPermissionManagerIntent()); + + }); + builder.setNegativeButton(LocaleController.getString("Cancel",R.string.Cancel),null); + builder.show(); + return; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + if (openNotificationListenSettings()) { + if (isNotificationListenerEnabled()) { + AlertsCreator.showSimpleToast(null, LocaleController.getString("DisablePushAlert", R.string.DisablePushAlert)); + } else { + AlertsCreator.showSimpleToast(null, LocaleController.getString("EnablePushAlert", R.string.EnablePushAlert)); + } + } + } else { + SharedPreferences preferences = MessagesController.getNotificationsSettings(currentAccount); + enabled = preferences.getBoolean("pushService", getMessagesController().keepAliveService); + SharedPreferences.Editor editor = preferences.edit(); + editor.putBoolean("pushService", !enabled); + editor.commit(); + ApplicationLoader.startPushService(); + } } else if (position == callsVibrateRow) { if (getParentActivity() == null) { return; @@ -628,6 +655,40 @@ public class NotificationsSettingsActivity extends BaseFragment implements Notif return fragmentView; } + public boolean isNotificationListenerEnabled() { + Set packageNames = NotificationManagerCompat.getEnabledListenerPackages(getParentActivity()); + if (packageNames.contains(getParentActivity().getPackageName())) { + return true; + } + return false; + } + + public boolean openNotificationListenSettings() { + try { + Intent intent; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP_MR1) { + intent = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS); + } else { + intent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"); + } + getParentActivity().startActivity(intent); + return true; + } catch (Exception e) { + try { + Intent intent = new Intent(); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + ComponentName cn = new ComponentName("com.android.settings", "com.android.settings.Settings$NotificationAccessSettingsActivity"); + intent.setComponent(cn); + intent.putExtra(":settings:show_fragment", "NotificationAccessSettings"); + getParentActivity().startActivity(intent); + return true; + } catch (Exception ex) { + AlertsCreator.showSimpleToast(this,"Open NotificationAccessSettings Error"); + } + } + return false; + } + @Override public void onActivityResultFragment(int requestCode, int resultCode, Intent data) { if (resultCode == Activity.RESULT_OK) { @@ -821,7 +882,7 @@ public class NotificationsSettingsActivity extends BaseFragment implements Notif } else if (position == androidAutoAlertRow) { checkCell.setTextAndCheck("Android Auto", preferences.getBoolean("EnableAutoNotifications", false), true); } else if (position == notificationsServiceRow) { - checkCell.setTextAndValueAndCheck(LocaleController.getString("NotificationsService", R.string.NotificationsService), LocaleController.getString("NotificationsServiceInfo", R.string.NotificationsServiceInfo), preferences.getBoolean("pushService", getMessagesController().keepAliveService), true, true); + checkCell.setTextAndValueAndCheck(LocaleController.getString("NotificationsService", R.string.NotificationsService), LocaleController.getString("NotificationsServiceInfo", R.string.NotificationsServiceInfo), Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 ? isNotificationListenerEnabled() : preferences.getBoolean("pushService", getMessagesController().keepAliveService), true, true); } else if (position == notificationsServiceConnectionRow) { checkCell.setTextAndValueAndCheck(LocaleController.getString("NotificationsServiceConnection", R.string.NotificationsServiceConnection), LocaleController.getString("NotificationsServiceConnectionInfo", R.string.NotificationsServiceConnectionInfo), preferences.getBoolean("pushConnection", getMessagesController().backgroundConnection), true, true); } else if (position == badgeNumberShowRow) { @@ -1022,4 +1083,5 @@ public class NotificationsSettingsActivity extends BaseFragment implements Notif new ThemeDescription(listView, ThemeDescription.FLAG_LINKCOLOR, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteLinkText), }; } + } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PassportActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PassportActivity.java index 17d3ccb4e..bc10e00d2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PassportActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PassportActivity.java @@ -6289,11 +6289,6 @@ public class PassportActivity extends BaseFragment implements NotificationCenter req.settings.allow_flashcall = simcardAvailable && allowCall; req.settings.allow_app_hash = ApplicationLoader.hasPlayServices; SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); - if (req.settings.allow_app_hash) { - preferences.edit().putString("sms_hash", BuildVars.SMS_HASH).commit(); - } else { - preferences.edit().remove("sms_hash").commit(); - } if (req.settings.allow_flashcall) { try { @SuppressLint("HardwareIds") diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PaymentFormActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PaymentFormActivity.java deleted file mode 100644 index 0df66043b..000000000 --- a/TMessagesProj/src/main/java/org/telegram/ui/PaymentFormActivity.java +++ /dev/null @@ -1,3388 +0,0 @@ -/* - * This is the source code of Telegram for Android v. 5.x.x. - * It is licensed under GNU GPL v. 2 or later. - * You should have received a copy of the license in this archive (see LICENSE). - * - * Copyright Nikolai Kudashov, 2013-2018. - */ - -package org.telegram.ui; - -import android.Manifest; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.annotation.SuppressLint; -import android.app.Activity; -import android.app.Dialog; -import android.app.FragmentManager; -import android.app.FragmentTransaction; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.graphics.Canvas; -import android.graphics.Typeface; -import android.net.Uri; -import android.os.Build; -import android.os.Vibrator; -import android.telephony.TelephonyManager; -import android.text.Editable; -import android.text.InputFilter; -import android.text.InputType; -import android.text.SpannableStringBuilder; -import android.text.Spanned; -import android.text.TextPaint; -import android.text.TextUtils; -import android.text.TextWatcher; -import android.text.method.PasswordTransformationMethod; -import android.text.style.ClickableSpan; -import android.util.TypedValue; -import android.view.Gravity; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewParent; -import android.view.WindowManager; -import android.view.animation.DecelerateInterpolator; -import android.view.inputmethod.EditorInfo; -import android.webkit.CookieManager; -import android.webkit.JavascriptInterface; -import android.webkit.WebSettings; -import android.webkit.WebView; -import android.webkit.WebViewClient; -import android.widget.EditText; -import android.widget.FrameLayout; -import android.widget.LinearLayout; -import android.widget.ScrollView; -import android.widget.TextView; -import android.widget.Toast; - -import com.stripe.android.Stripe; -import com.stripe.android.TokenCallback; -import com.stripe.android.exception.APIConnectionException; -import com.stripe.android.exception.APIException; -import com.stripe.android.model.Card; -import com.stripe.android.model.Token; -import com.stripe.android.net.StripeApiHandler; -import com.stripe.android.net.TokenParser; - -import org.json.JSONException; -import org.json.JSONObject; -import org.telegram.PhoneFormat.PhoneFormat; -import org.telegram.messenger.AndroidUtilities; -import org.telegram.messenger.ApplicationLoader; -import org.telegram.messenger.ContactsController; -import org.telegram.messenger.FileLog; -import org.telegram.messenger.LocaleController; -import org.telegram.messenger.MessageObject; -import org.telegram.messenger.MessagesController; -import org.telegram.messenger.NotificationCenter; -import org.telegram.messenger.R; -import org.telegram.messenger.SRPHelper; -import org.telegram.messenger.SharedConfig; -import org.telegram.messenger.UserConfig; -import org.telegram.messenger.Utilities; -import org.telegram.tgnet.ConnectionsManager; -import org.telegram.tgnet.RequestDelegate; -import org.telegram.tgnet.TLObject; -import org.telegram.tgnet.TLRPC; -import org.telegram.ui.ActionBar.ActionBar; -import org.telegram.ui.ActionBar.ActionBarMenu; -import org.telegram.ui.ActionBar.ActionBarMenuItem; -import org.telegram.ui.ActionBar.AlertDialog; -import org.telegram.ui.ActionBar.BaseFragment; -import org.telegram.ui.ActionBar.Theme; -import org.telegram.ui.ActionBar.ThemeDescription; -import org.telegram.ui.Cells.EditTextSettingsCell; -import org.telegram.ui.Cells.HeaderCell; -import org.telegram.ui.Cells.PaymentInfoCell; -import org.telegram.ui.Cells.RadioCell; -import org.telegram.ui.Cells.ShadowSectionCell; -import org.telegram.ui.Cells.TextCheckCell; -import org.telegram.ui.Cells.TextDetailSettingsCell; -import org.telegram.ui.Cells.TextInfoPrivacyCell; -import org.telegram.ui.Cells.TextPriceCell; -import org.telegram.ui.Cells.TextSettingsCell; -import org.telegram.ui.Components.AlertsCreator; -import org.telegram.ui.Components.ContextProgressView; -import org.telegram.ui.Components.EditTextBoldCursor; -import org.telegram.ui.Components.HintEditText; -import org.telegram.ui.Components.LayoutHelper; - -import java.io.BufferedReader; -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collections; -import java.util.HashMap; -import java.util.Locale; - -public class PaymentFormActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { - - private final static int FIELD_CARD = 0; - private final static int FIELD_EXPIRE_DATE = 1; - private final static int FIELD_CARDNAME = 2; - private final static int FIELD_CVV = 3; - private final static int FIELD_CARD_COUNTRY = 4; - private final static int FIELD_CARD_POSTCODE = 5; - private final static int FIELDS_COUNT_CARD = 6; - - private final static int FIELD_STREET1 = 0; - private final static int FIELD_STREET2 = 1; - private final static int FIELD_CITY = 2; - private final static int FIELD_STATE = 3; - private final static int FIELD_COUNTRY = 4; - private final static int FIELD_POSTCODE = 5; - private final static int FIELD_NAME = 6; - private final static int FIELD_EMAIL = 7; - private final static int FIELD_PHONECODE = 8; - private final static int FIELD_PHONE = 9; - private final static int FIELDS_COUNT_ADDRESS = 10; - - private final static int FIELD_SAVEDCARD = 0; - private final static int FIELD_SAVEDPASSWORD = 1; - private final static int FIELDS_COUNT_SAVEDCARD = 2; - - private final static int FIELD_ENTERPASSWORD = 0; - private final static int FIELD_REENTERPASSWORD = 1; - private final static int FIELD_ENTERPASSWORDEMAIL = 2; - private final static int FIELDS_COUNT_PASSWORD = 3; - - private ArrayList countriesArray = new ArrayList<>(); - private HashMap countriesMap = new HashMap<>(); - private HashMap codesMap = new HashMap<>(); - private HashMap phoneFormatMap = new HashMap<>(); - - private EditTextBoldCursor[] inputFields; - private RadioCell[] radioCells; - private ActionBarMenuItem doneItem; - private ContextProgressView progressView; - private ContextProgressView progressViewButton; - private AnimatorSet doneItemAnimation; - private WebView webView; - private String webViewUrl; - private boolean shouldNavigateBack; - private ScrollView scrollView; - - private boolean swipeBackEnabled = true; - - private TextView textView; - private HeaderCell[] headerCell = new HeaderCell[3]; - private ArrayList dividers = new ArrayList<>(); - private ShadowSectionCell[] sectionCell = new ShadowSectionCell[3]; - private TextCheckCell checkCell1; - private TextInfoPrivacyCell[] bottomCell = new TextInfoPrivacyCell[3]; - private TextSettingsCell[] settingsCell = new TextSettingsCell[2]; - private FrameLayout androidPayContainer; - private LinearLayout linearLayout2; - - private EditTextSettingsCell codeFieldCell; - - private PaymentFormActivityDelegate delegate; - - private TextView payTextView; - private FrameLayout bottomLayout; - private PaymentInfoCell paymentInfoCell; - private TextDetailSettingsCell[] detailSettingsCell = new TextDetailSettingsCell[7]; - - private TLRPC.TL_account_password currentPassword; - private boolean waitingForEmail; - private int emailCodeLength = 6; - private Runnable shortPollRunnable; - private boolean loadingPasswordInfo; - private PaymentFormActivity passwordFragment; - - private boolean need_card_country; - private boolean need_card_postcode; - private boolean need_card_name; - private String stripeApiKey; - - private TLRPC.User botUser; - - private boolean ignoreOnTextChange; - private boolean ignoreOnPhoneChange; - private boolean ignoreOnCardChange; - - private String currentBotName; - private String currentItemName; - - private int currentStep; - private boolean passwordOk; - private String paymentJson; - private String cardName; - private boolean webviewLoading; - private String countryName; - private String totalPriceDecimal; - private TLRPC.TL_payments_paymentForm paymentForm; - private TLRPC.TL_payments_validatedRequestedInfo requestedInfo; - private TLRPC.TL_shippingOption shippingOption; - private TLRPC.TL_payments_validateRequestedInfo validateRequest; - private TLRPC.TL_inputPaymentCredentialsAndroidPay androidPayCredentials; - private String androidPayPublicKey; - private int androidPayBackgroundColor; - private boolean androidPayBlackTheme; - private MessageObject messageObject; - private boolean donePressed; - private boolean canceled; - - private boolean isWebView; - - private boolean saveShippingInfo; - private boolean saveCardInfo; - - private final static int done_button = 1; - - private static final int LOAD_MASKED_WALLET_REQUEST_CODE = 1000; - private static final int LOAD_FULL_WALLET_REQUEST_CODE = 1001; - private final static int fragment_container_id = 4000; - - private interface PaymentFormActivityDelegate { - boolean didSelectNewCard(String tokenJson, String card, boolean saveCard, TLRPC.TL_inputPaymentCredentialsAndroidPay androidPay); - void onFragmentDestroyed(); - void currentPasswordUpdated(TLRPC.TL_account_password password); - } - - private class TelegramWebviewProxy { - @JavascriptInterface - public void postEvent(final String eventName, final String eventData) { - AndroidUtilities.runOnUIThread(() -> { - if (getParentActivity() == null) { - return; - } - if (eventName.equals("payment_form_submit")) { - try { - JSONObject jsonObject = new JSONObject(eventData); - JSONObject response = jsonObject.getJSONObject("credentials"); - paymentJson = response.toString(); - cardName = jsonObject.getString("title"); - } catch (Throwable e) { - paymentJson = eventData; - FileLog.e(e); - } - goToNextStep(); - } - }); - } - } - - public class LinkSpan extends ClickableSpan { - @Override - public void updateDrawState(TextPaint ds) { - super.updateDrawState(ds); - ds.setUnderlineText(false); - } - - @Override - public void onClick(View widget) { - presentFragment(new TwoStepVerificationActivity(0)); - } - } - - public PaymentFormActivity(MessageObject message, TLRPC.TL_payments_paymentReceipt receipt) { - currentStep = 5; - paymentForm = new TLRPC.TL_payments_paymentForm(); - paymentForm.bot_id = receipt.bot_id; - paymentForm.invoice = receipt.invoice; - paymentForm.provider_id = receipt.provider_id; - paymentForm.users = receipt.users; - shippingOption = receipt.shipping; - messageObject = message; - botUser = MessagesController.getInstance(currentAccount).getUser(receipt.bot_id); - if (botUser != null) { - currentBotName = botUser.first_name; - } else { - currentBotName = ""; - } - currentItemName = message.messageOwner.media.title; - if (receipt.info != null) { - validateRequest = new TLRPC.TL_payments_validateRequestedInfo(); - validateRequest.info = receipt.info; - } - cardName = receipt.credentials_title; - } - - public PaymentFormActivity(TLRPC.TL_payments_paymentForm form, MessageObject message) { - int step; - if (form.invoice.shipping_address_requested || form.invoice.email_requested || form.invoice.name_requested || form.invoice.phone_requested) { - step = 0; - } else if (form.saved_credentials != null) { - if (UserConfig.getInstance(currentAccount).tmpPassword != null) { - if (UserConfig.getInstance(currentAccount).tmpPassword.valid_until < ConnectionsManager.getInstance(currentAccount).getCurrentTime() + 60) { - UserConfig.getInstance(currentAccount).tmpPassword = null; - UserConfig.getInstance(currentAccount).saveConfig(false); - } - } - if (UserConfig.getInstance(currentAccount).tmpPassword != null) { - step = 4; - } else { - step = 3; - } - } else { - step = 2; - } - init(form, message, step, null, null, null, null, null, false, null); - } - - private PaymentFormActivity(TLRPC.TL_payments_paymentForm form, MessageObject message, int step, TLRPC.TL_payments_validatedRequestedInfo validatedRequestedInfo, TLRPC.TL_shippingOption shipping, String tokenJson, String card, TLRPC.TL_payments_validateRequestedInfo request, boolean saveCard, TLRPC.TL_inputPaymentCredentialsAndroidPay androidPay) { - init(form, message, step, validatedRequestedInfo, shipping, tokenJson, card, request, saveCard, androidPay); - } - - private void setCurrentPassword(TLRPC.TL_account_password password) { - if (password.has_password) { - if (getParentActivity() == null) { - return; - } - goToNextStep(); - } else { - currentPassword = password; - if (currentPassword != null) { - waitingForEmail = !TextUtils.isEmpty(currentPassword.email_unconfirmed_pattern); - } - updatePasswordFields(); - } - } - - private void setDelegate(PaymentFormActivityDelegate paymentFormActivityDelegate) { - delegate = paymentFormActivityDelegate; - } - - private void init(TLRPC.TL_payments_paymentForm form, MessageObject message, int step, TLRPC.TL_payments_validatedRequestedInfo validatedRequestedInfo, TLRPC.TL_shippingOption shipping, String tokenJson, String card, TLRPC.TL_payments_validateRequestedInfo request, boolean saveCard, TLRPC.TL_inputPaymentCredentialsAndroidPay androidPay) { - currentStep = step; - paymentJson = tokenJson; - androidPayCredentials = androidPay; - requestedInfo = validatedRequestedInfo; - paymentForm = form; - shippingOption = shipping; - messageObject = message; - saveCardInfo = saveCard; - isWebView = !"stripe".equals(paymentForm.native_provider); - botUser = MessagesController.getInstance(currentAccount).getUser(form.bot_id); - if (botUser != null) { - currentBotName = botUser.first_name; - } else { - currentBotName = ""; - } - currentItemName = message.messageOwner.media.title; - validateRequest = request; - saveShippingInfo = true; - if (saveCard) { - saveCardInfo = saveCard; - } else { - saveCardInfo = paymentForm.saved_credentials != null; - } - if (card == null) { - if (form.saved_credentials != null) { - cardName = form.saved_credentials.title; - } - } else { - cardName = card; - } - } - - @Override - public void onResume() { - super.onResume(); - AndroidUtilities.requestAdjustResize(getParentActivity(), classGuid); - if (Build.VERSION.SDK_INT >= 23) { - try { - if ((currentStep == 2 || currentStep == 6) && !paymentForm.invoice.test) { - getParentActivity().getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE); - } else if (SharedConfig.passcodeHash.length() == 0 || SharedConfig.allowScreenCapture) { - getParentActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE); - } - } catch (Throwable e) { - FileLog.e(e); - } - } - } - - @Override - public void onPause() { - } - - @SuppressLint({"SetJavaScriptEnabled", "AddJavascriptInterface"}) - @Override - public View createView(Context context) { - if (currentStep == 0) { - actionBar.setTitle(LocaleController.getString("PaymentShippingInfo", R.string.PaymentShippingInfo)); - } else if (currentStep == 1) { - actionBar.setTitle(LocaleController.getString("PaymentShippingMethod", R.string.PaymentShippingMethod)); - } else if (currentStep == 2) { - actionBar.setTitle(LocaleController.getString("PaymentCardInfo", R.string.PaymentCardInfo)); - } else if (currentStep == 3) { - actionBar.setTitle(LocaleController.getString("PaymentCardInfo", R.string.PaymentCardInfo)); - } else if (currentStep == 4) { - if (paymentForm.invoice.test) { - actionBar.setTitle("Test " + LocaleController.getString("PaymentCheckout", R.string.PaymentCheckout)); - } else { - actionBar.setTitle(LocaleController.getString("PaymentCheckout", R.string.PaymentCheckout)); - } - } else if (currentStep == 5) { - if (paymentForm.invoice.test) { - actionBar.setTitle("Test " + LocaleController.getString("PaymentReceipt", R.string.PaymentReceipt)); - } else { - actionBar.setTitle(LocaleController.getString("PaymentReceipt", R.string.PaymentReceipt)); - } - } else if (currentStep == 6) { - actionBar.setTitle(LocaleController.getString("PaymentPassword", R.string.PaymentPassword)); - } - - actionBar.setBackButtonImage(R.drawable.ic_ab_back); - actionBar.setAllowOverlayTitle(true); - - actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { - @Override - public void onItemClick(int id) { - if (id == -1) { - if (donePressed) { - return; - } - finishFragment(); - } else if (id == done_button) { - if (donePressed) { - return; - } - if (currentStep != 3) { - AndroidUtilities.hideKeyboard(getParentActivity().getCurrentFocus()); - } - if (currentStep == 0) { - setDonePressed(true); - sendForm(); - } else if (currentStep == 1) { - for (int a = 0; a < radioCells.length; a++) { - if (radioCells[a].isChecked()) { - shippingOption = requestedInfo.shipping_options.get(a); - break; - } - } - goToNextStep(); - } else if (currentStep == 2) { - sendCardData(); - } else if (currentStep == 3) { - checkPassword(); - } else if (currentStep == 6) { - sendSavePassword(false); - } - } - } - }); - - ActionBarMenu menu = actionBar.createMenu(); - - if (currentStep == 0 || currentStep == 1 || currentStep == 2 || currentStep == 3 || currentStep == 4 || currentStep == 6) { - doneItem = menu.addItemWithWidth(done_button, R.drawable.ic_done, AndroidUtilities.dp(56)); - progressView = new ContextProgressView(context, 1); - progressView.setAlpha(0.0f); - progressView.setScaleX(0.1f); - progressView.setScaleY(0.1f); - progressView.setVisibility(View.INVISIBLE); - doneItem.addView(progressView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); - } - - fragmentView = new FrameLayout(context); - FrameLayout frameLayout = (FrameLayout) fragmentView; - fragmentView.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundGray)); - - scrollView = new ScrollView(context); - scrollView.setFillViewport(true); - AndroidUtilities.setScrollViewEdgeEffectColor(scrollView, Theme.getColor(Theme.key_actionBarDefault)); - frameLayout.addView(scrollView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, 0, 0, currentStep == 4 ? 48 : 0)); - - linearLayout2 = new LinearLayout(context); - linearLayout2.setOrientation(LinearLayout.VERTICAL); - scrollView.addView(linearLayout2, new ScrollView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); - - if (currentStep == 0) { - HashMap languageMap = new HashMap<>(); - HashMap countryMap = new HashMap<>(); - try { - BufferedReader reader = new BufferedReader(new InputStreamReader(context.getResources().getAssets().open("countries.txt"))); - String line; - while ((line = reader.readLine()) != null) { - String[] args = line.split(";"); - countriesArray.add(0, args[2]); - countriesMap.put(args[2], args[0]); - codesMap.put(args[0], args[2]); - countryMap.put(args[1], args[2]); - if (args.length > 3) { - phoneFormatMap.put(args[0], args[3]); - } - languageMap.put(args[1], args[2]); - } - reader.close(); - } catch (Exception e) { - FileLog.e(e); - } - - Collections.sort(countriesArray, String::compareTo); - - inputFields = new EditTextBoldCursor[FIELDS_COUNT_ADDRESS]; - for (int a = 0; a < FIELDS_COUNT_ADDRESS; a++) { - if (a == FIELD_STREET1) { - headerCell[0] = new HeaderCell(context); - headerCell[0].setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - headerCell[0].setText(LocaleController.getString("PaymentShippingAddress", R.string.PaymentShippingAddress)); - linearLayout2.addView(headerCell[0], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - } else if (a == FIELD_NAME) { - sectionCell[0] = new ShadowSectionCell(context); - linearLayout2.addView(sectionCell[0], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - - headerCell[1] = new HeaderCell(context); - headerCell[1].setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - headerCell[1].setText(LocaleController.getString("PaymentShippingReceiver", R.string.PaymentShippingReceiver)); - linearLayout2.addView(headerCell[1], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - } - ViewGroup container; - if (a == FIELD_PHONECODE) { - container = new LinearLayout(context); - ((LinearLayout) container).setOrientation(LinearLayout.HORIZONTAL); - linearLayout2.addView(container, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 50)); - container.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - } else if (a == FIELD_PHONE) { - container = (ViewGroup) inputFields[FIELD_PHONECODE].getParent(); - } else { - container = new FrameLayout(context); - linearLayout2.addView(container, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 50)); - container.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - - boolean allowDivider = a != FIELD_POSTCODE && a != FIELD_PHONE; - if (allowDivider) { - if (a == FIELD_EMAIL && !paymentForm.invoice.phone_requested) { - allowDivider = false; - } else if (a == FIELD_NAME && !paymentForm.invoice.phone_requested && !paymentForm.invoice.email_requested) { - allowDivider = false; - } - } - if (allowDivider) { - View divider = new View(context) { - @Override - protected void onDraw(Canvas canvas) { - canvas.drawLine(LocaleController.isRTL ? 0 : AndroidUtilities.dp(20), getMeasuredHeight() - 1, getMeasuredWidth() - (LocaleController.isRTL ? AndroidUtilities.dp(20) : 0), getMeasuredHeight() - 1, Theme.dividerPaint); - } - }; - divider.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - dividers.add(divider); - container.addView(divider, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1, Gravity.LEFT | Gravity.BOTTOM)); - } - } - - if (a == FIELD_PHONE) { - inputFields[a] = new HintEditText(context); - } else { - inputFields[a] = new EditTextBoldCursor(context); - } - inputFields[a].setTag(a); - inputFields[a].setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); - inputFields[a].setHintTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteHintText)); - inputFields[a].setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); - inputFields[a].setBackgroundDrawable(null); - inputFields[a].setCursorColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); - inputFields[a].setCursorSize(AndroidUtilities.dp(20)); - inputFields[a].setCursorWidth(1.5f); - if (a == FIELD_COUNTRY) { - inputFields[a].setOnTouchListener((v, event) -> { - if (getParentActivity() == null) { - return false; - } - if (event.getAction() == MotionEvent.ACTION_UP) { - CountrySelectActivity fragment = new CountrySelectActivity(false); - fragment.setCountrySelectActivityDelegate((name, shortName) -> { - inputFields[FIELD_COUNTRY].setText(name); - countryName = shortName; - }); - presentFragment(fragment); - } - return true; - }); - inputFields[a].setInputType(0); - } - if (a == FIELD_PHONE || a == FIELD_PHONECODE) { - inputFields[a].setInputType(InputType.TYPE_CLASS_PHONE); - } else if (a == FIELD_EMAIL) { - inputFields[a].setInputType(InputType.TYPE_CLASS_TEXT); - } else { - inputFields[a].setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES); - } - inputFields[a].setImeOptions(EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NO_EXTRACT_UI); - switch (a) { - case FIELD_NAME: - inputFields[a].setHint(LocaleController.getString("PaymentShippingName", R.string.PaymentShippingName)); - if (paymentForm.saved_info != null && paymentForm.saved_info.name != null) { - inputFields[a].setText(paymentForm.saved_info.name); - } - break; - case FIELD_EMAIL: - inputFields[a].setHint(LocaleController.getString("PaymentShippingEmailPlaceholder", R.string.PaymentShippingEmailPlaceholder)); - if (paymentForm.saved_info != null && paymentForm.saved_info.email != null) { - inputFields[a].setText(paymentForm.saved_info.email); - } - break; - case FIELD_STREET1: - inputFields[a].setHint(LocaleController.getString("PaymentShippingAddress1Placeholder", R.string.PaymentShippingAddress1Placeholder)); - if (paymentForm.saved_info != null && paymentForm.saved_info.shipping_address != null) { - inputFields[a].setText(paymentForm.saved_info.shipping_address.street_line1); - } - break; - case FIELD_STREET2: - inputFields[a].setHint(LocaleController.getString("PaymentShippingAddress2Placeholder", R.string.PaymentShippingAddress2Placeholder)); - if (paymentForm.saved_info != null && paymentForm.saved_info.shipping_address != null) { - inputFields[a].setText(paymentForm.saved_info.shipping_address.street_line2); - } - break; - case FIELD_CITY: - inputFields[a].setHint(LocaleController.getString("PaymentShippingCityPlaceholder", R.string.PaymentShippingCityPlaceholder)); - if (paymentForm.saved_info != null && paymentForm.saved_info.shipping_address != null) { - inputFields[a].setText(paymentForm.saved_info.shipping_address.city); - } - break; - case FIELD_STATE: - inputFields[a].setHint(LocaleController.getString("PaymentShippingStatePlaceholder", R.string.PaymentShippingStatePlaceholder)); - if (paymentForm.saved_info != null && paymentForm.saved_info.shipping_address != null) { - inputFields[a].setText(paymentForm.saved_info.shipping_address.state); - } - break; - case FIELD_COUNTRY: - inputFields[a].setHint(LocaleController.getString("PaymentShippingCountry", R.string.PaymentShippingCountry)); - if (paymentForm.saved_info != null && paymentForm.saved_info.shipping_address != null) { - String value = countryMap.get(paymentForm.saved_info.shipping_address.country_iso2); - countryName = paymentForm.saved_info.shipping_address.country_iso2; - inputFields[a].setText(value != null ? value : countryName); - } - break; - case FIELD_POSTCODE: - inputFields[a].setHint(LocaleController.getString("PaymentShippingZipPlaceholder", R.string.PaymentShippingZipPlaceholder)); - if (paymentForm.saved_info != null && paymentForm.saved_info.shipping_address != null) { - inputFields[a].setText(paymentForm.saved_info.shipping_address.post_code); - } - break; - } - inputFields[a].setSelection(inputFields[a].length()); - - if (a == FIELD_PHONECODE) { - textView = new TextView(context); - textView.setText("+"); - textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); - textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); - container.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, 21, 12, 0, 6)); - - inputFields[a].setPadding(AndroidUtilities.dp(10), 0, 0, 0); - inputFields[a].setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL); - InputFilter[] inputFilters = new InputFilter[1]; - inputFilters[0] = new InputFilter.LengthFilter(5); - inputFields[a].setFilters(inputFilters); - container.addView(inputFields[a], LayoutHelper.createLinear(55, LayoutHelper.WRAP_CONTENT, 0, 12, 21, 6)); - inputFields[a].addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { - - } - - @Override - public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) { - - } - - @Override - public void afterTextChanged(Editable editable) { - if (ignoreOnTextChange) { - return; - } - ignoreOnTextChange = true; - String text = PhoneFormat.stripExceptNumbers(inputFields[FIELD_PHONECODE].getText().toString()); - inputFields[FIELD_PHONECODE].setText(text); - HintEditText phoneField = (HintEditText) inputFields[FIELD_PHONE]; - if (text.length() == 0) { - phoneField.setHintText(null); - phoneField.setHint(LocaleController.getString("PaymentShippingPhoneNumber", R.string.PaymentShippingPhoneNumber)); - } else { - String country; - boolean ok = false; - String textToSet = null; - if (text.length() > 4) { - for (int a = 4; a >= 1; a--) { - String sub = text.substring(0, a); - country = codesMap.get(sub); - if (country != null) { - ok = true; - textToSet = text.substring(a) + inputFields[FIELD_PHONE].getText().toString(); - inputFields[FIELD_PHONECODE].setText(text = sub); - break; - } - } - if (!ok) { - textToSet = text.substring(1) + inputFields[FIELD_PHONE].getText().toString(); - inputFields[FIELD_PHONECODE].setText(text = text.substring(0, 1)); - } - } - country = codesMap.get(text); - boolean set = false; - if (country != null) { - int index = countriesArray.indexOf(country); - if (index != -1) { - String hint = phoneFormatMap.get(text); - if (hint != null) { - set = true; - phoneField.setHintText(hint.replace('X', '–')); - phoneField.setHint(null); - } - } - } - if (!set) { - phoneField.setHintText(null); - phoneField.setHint(LocaleController.getString("PaymentShippingPhoneNumber", R.string.PaymentShippingPhoneNumber)); - } - if (!ok) { - inputFields[FIELD_PHONECODE].setSelection(inputFields[FIELD_PHONECODE].getText().length()); - } - if (textToSet != null) { - phoneField.requestFocus(); - phoneField.setText(textToSet); - phoneField.setSelection(phoneField.length()); - } - } - ignoreOnTextChange = false; - } - }); - } else if (a == FIELD_PHONE) { - inputFields[a].setPadding(0, 0, 0, 0); - inputFields[a].setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL); - container.addView(inputFields[a], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 12, 21, 6)); - inputFields[a].addTextChangedListener(new TextWatcher() { - private int characterAction = -1; - private int actionPosition; - - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - if (count == 0 && after == 1) { - characterAction = 1; - } else if (count == 1 && after == 0) { - if (s.charAt(start) == ' ' && start > 0) { - characterAction = 3; - actionPosition = start - 1; - } else { - characterAction = 2; - } - } else { - characterAction = -1; - } - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - - } - - @Override - public void afterTextChanged(Editable s) { - if (ignoreOnPhoneChange) { - return; - } - HintEditText phoneField = (HintEditText) inputFields[FIELD_PHONE]; - int start = phoneField.getSelectionStart(); - String phoneChars = "0123456789"; - String str = phoneField.getText().toString(); - if (characterAction == 3) { - str = str.substring(0, actionPosition) + str.substring(actionPosition + 1); - start--; - } - StringBuilder builder = new StringBuilder(str.length()); - for (int a = 0; a < str.length(); a++) { - String ch = str.substring(a, a + 1); - if (phoneChars.contains(ch)) { - builder.append(ch); - } - } - ignoreOnPhoneChange = true; - String hint = phoneField.getHintText(); - if (hint != null) { - for (int a = 0; a < builder.length(); a++) { - if (a < hint.length()) { - if (hint.charAt(a) == ' ') { - builder.insert(a, ' '); - a++; - if (start == a && characterAction != 2 && characterAction != 3) { - start++; - } - } - } else { - builder.insert(a, ' '); - if (start == a + 1 && characterAction != 2 && characterAction != 3) { - start++; - } - break; - } - } - } - phoneField.setText(builder); - if (start >= 0) { - phoneField.setSelection(start <= phoneField.length() ? start : phoneField.length()); - } - phoneField.onTextChange(); - ignoreOnPhoneChange = false; - } - }); - } else { - inputFields[a].setPadding(0, 0, 0, AndroidUtilities.dp(6)); - inputFields[a].setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); - container.addView(inputFields[a], LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 21, 12, 21, 6)); - } - - inputFields[a].setOnEditorActionListener((textView, i, keyEvent) -> { - if (i == EditorInfo.IME_ACTION_NEXT) { - int num = (Integer) textView.getTag(); - while (num + 1 < inputFields.length) { - num++; - if (num != FIELD_COUNTRY && ((View) inputFields[num].getParent()).getVisibility() == View.VISIBLE) { - inputFields[num].requestFocus(); - break; - } - } - return true; - } else if (i == EditorInfo.IME_ACTION_DONE) { - doneItem.performClick(); - return true; - } - return false; - }); - if (a == FIELD_PHONE) { - if (paymentForm.invoice.email_to_provider || paymentForm.invoice.phone_to_provider) { - TLRPC.User providerUser = null; - for (int b = 0; b < paymentForm.users.size(); b++) { - TLRPC.User user = paymentForm.users.get(b); - if (user.id == paymentForm.provider_id) { - providerUser = user; - } - } - final String providerName; - if (providerUser != null) { - providerName = ContactsController.formatName(providerUser.first_name, providerUser.last_name); - } else { - providerName = ""; - } - - bottomCell[1] = new TextInfoPrivacyCell(context); - bottomCell[1].setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); - linearLayout2.addView(bottomCell[1], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - if (paymentForm.invoice.email_to_provider && paymentForm.invoice.phone_to_provider) { - bottomCell[1].setText(LocaleController.formatString("PaymentPhoneEmailToProvider", R.string.PaymentPhoneEmailToProvider, providerName)); - } else if (paymentForm.invoice.email_to_provider) { - bottomCell[1].setText(LocaleController.formatString("PaymentEmailToProvider", R.string.PaymentEmailToProvider, providerName)); - } else { - bottomCell[1].setText(LocaleController.formatString("PaymentPhoneToProvider", R.string.PaymentPhoneToProvider, providerName)); - } - } else { - sectionCell[1] = new ShadowSectionCell(context); - linearLayout2.addView(sectionCell[1], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - } - - checkCell1 = new TextCheckCell(context); - checkCell1.setBackgroundDrawable(Theme.getSelectorDrawable(true)); - checkCell1.setTextAndCheck(LocaleController.getString("PaymentShippingSave", R.string.PaymentShippingSave), saveShippingInfo, false); - linearLayout2.addView(checkCell1, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - checkCell1.setOnClickListener(v -> { - saveShippingInfo = !saveShippingInfo; - checkCell1.setChecked(saveShippingInfo); - }); - - bottomCell[0] = new TextInfoPrivacyCell(context); - bottomCell[0].setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); - bottomCell[0].setText(LocaleController.getString("PaymentShippingSaveInfo", R.string.PaymentShippingSaveInfo)); - linearLayout2.addView(bottomCell[0], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - } - } - - if (!paymentForm.invoice.name_requested) { - ((ViewGroup) inputFields[FIELD_NAME].getParent()).setVisibility(View.GONE); - } - if (!paymentForm.invoice.phone_requested) { - ((ViewGroup) inputFields[FIELD_PHONECODE].getParent()).setVisibility(View.GONE); - } - if (!paymentForm.invoice.email_requested) { - ((ViewGroup) inputFields[FIELD_EMAIL].getParent()).setVisibility(View.GONE); - } - - if (paymentForm.invoice.phone_requested) { - inputFields[FIELD_PHONE].setImeOptions(EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI); - } else if (paymentForm.invoice.email_requested) { - inputFields[FIELD_EMAIL].setImeOptions(EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI); - } else if (paymentForm.invoice.name_requested) { - inputFields[FIELD_NAME].setImeOptions(EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI); - } else { - inputFields[FIELD_POSTCODE].setImeOptions(EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI); - } - - if (sectionCell[1] != null) { - sectionCell[1].setVisibility(paymentForm.invoice.name_requested || paymentForm.invoice.phone_requested || paymentForm.invoice.email_requested ? View.VISIBLE : View.GONE); - } else if (bottomCell[1] != null) { - bottomCell[1].setVisibility(paymentForm.invoice.name_requested || paymentForm.invoice.phone_requested || paymentForm.invoice.email_requested ? View.VISIBLE : View.GONE); - } - headerCell[1].setVisibility(paymentForm.invoice.name_requested || paymentForm.invoice.phone_requested || paymentForm.invoice.email_requested ? View.VISIBLE : View.GONE); - if (!paymentForm.invoice.shipping_address_requested) { - headerCell[0].setVisibility(View.GONE); - sectionCell[0].setVisibility(View.GONE); - ((ViewGroup) inputFields[FIELD_STREET1].getParent()).setVisibility(View.GONE); - ((ViewGroup) inputFields[FIELD_STREET2].getParent()).setVisibility(View.GONE); - ((ViewGroup) inputFields[FIELD_CITY].getParent()).setVisibility(View.GONE); - ((ViewGroup) inputFields[FIELD_STATE].getParent()).setVisibility(View.GONE); - ((ViewGroup) inputFields[FIELD_COUNTRY].getParent()).setVisibility(View.GONE); - ((ViewGroup) inputFields[FIELD_POSTCODE].getParent()).setVisibility(View.GONE); - } - - if (paymentForm.saved_info != null && !TextUtils.isEmpty(paymentForm.saved_info.phone)) { - fillNumber(paymentForm.saved_info.phone); - } else { - fillNumber(null); - } - - if (inputFields[FIELD_PHONECODE].length() == 0 && (paymentForm.invoice.phone_requested && (paymentForm.saved_info == null || TextUtils.isEmpty(paymentForm.saved_info.phone)))) { - String country = null; - - try { - TelephonyManager telephonyManager = (TelephonyManager) ApplicationLoader.applicationContext.getSystemService(Context.TELEPHONY_SERVICE); - if (telephonyManager != null) { - country = telephonyManager.getSimCountryIso().toUpperCase(); - } - } catch (Exception e) { - FileLog.e(e); - } - - if (country != null) { - String countryName = languageMap.get(country); - if (countryName != null) { - int index = countriesArray.indexOf(countryName); - if (index != -1) { - inputFields[FIELD_PHONECODE].setText(countriesMap.get(countryName)); - } - } - } - } - } else if (currentStep == 2) { - if (paymentForm.native_params != null) { - try { - JSONObject jsonObject = new JSONObject(paymentForm.native_params.data); - try { - String androidPayKey = jsonObject.getString("android_pay_public_key"); - if (!TextUtils.isEmpty(androidPayKey)) { - androidPayPublicKey = androidPayKey; - } - } catch (Exception e) { - androidPayPublicKey = null; - } - try { - androidPayBackgroundColor = jsonObject.getInt("android_pay_bgcolor") | 0xff000000; - } catch (Exception e) { - androidPayBackgroundColor = 0xffffffff; - } - try { - androidPayBlackTheme = jsonObject.getBoolean("android_pay_inverse"); - } catch (Exception e) { - androidPayBlackTheme = false; - } - } catch (Exception e) { - FileLog.e(e); - } - } - if (isWebView) { - if (androidPayPublicKey != null) { - initAndroidPay(context); - } - androidPayContainer = new FrameLayout(context); - androidPayContainer.setId(fragment_container_id); - androidPayContainer.setBackgroundDrawable(Theme.getSelectorDrawable(true)); - androidPayContainer.setVisibility(View.GONE); - linearLayout2.addView(androidPayContainer, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 50)); - - webviewLoading = true; - showEditDoneProgress(true, true); - progressView.setVisibility(View.VISIBLE); - doneItem.setEnabled(false); - doneItem.getContentView().setVisibility(View.INVISIBLE); - webView = new WebView(context) { - @Override - public boolean onTouchEvent(MotionEvent event) { - ((ViewGroup) fragmentView).requestDisallowInterceptTouchEvent(true); - return super.onTouchEvent(event); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } - }; - webView.getSettings().setJavaScriptEnabled(true); - webView.getSettings().setDomStorageEnabled(true); - - if (Build.VERSION.SDK_INT >= 21) { - webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); - CookieManager cookieManager = CookieManager.getInstance(); - cookieManager.setAcceptThirdPartyCookies(webView, true); - } - if (Build.VERSION.SDK_INT >= 17) { - webView.addJavascriptInterface(new TelegramWebviewProxy(), "TelegramWebviewProxy"); - } - webView.setWebViewClient(new WebViewClient() { - @Override - public void onLoadResource(WebView view, String url) { - super.onLoadResource(view, url); - } - - @Override - public boolean shouldOverrideUrlLoading(WebView view, String url) { - shouldNavigateBack = !url.equals(webViewUrl); - return super.shouldOverrideUrlLoading(view, url); - } - - @Override - public void onPageFinished(WebView view, String url) { - super.onPageFinished(view, url); - webviewLoading = false; - showEditDoneProgress(true, false); - updateSavePaymentField(); - } - }); - - linearLayout2.addView(webView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - - sectionCell[2] = new ShadowSectionCell(context); - linearLayout2.addView(sectionCell[2], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - - checkCell1 = new TextCheckCell(context); - checkCell1.setBackgroundDrawable(Theme.getSelectorDrawable(true)); - checkCell1.setTextAndCheck(LocaleController.getString("PaymentCardSavePaymentInformation", R.string.PaymentCardSavePaymentInformation), saveCardInfo, false); - linearLayout2.addView(checkCell1, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - checkCell1.setOnClickListener(v -> { - saveCardInfo = !saveCardInfo; - checkCell1.setChecked(saveCardInfo); - }); - - bottomCell[0] = new TextInfoPrivacyCell(context); - bottomCell[0].setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); - updateSavePaymentField(); - linearLayout2.addView(bottomCell[0], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - } else { - if (paymentForm.native_params != null) { - try { - JSONObject jsonObject = new JSONObject(paymentForm.native_params.data); - try { - need_card_country = jsonObject.getBoolean("need_country"); - } catch (Exception e) { - need_card_country = false; - } - try { - need_card_postcode = jsonObject.getBoolean("need_zip"); - } catch (Exception e) { - need_card_postcode = false; - } - try { - need_card_name = jsonObject.getBoolean("need_cardholder_name"); - } catch (Exception e) { - need_card_name = false; - } - try { - stripeApiKey = jsonObject.getString("publishable_key"); - } catch (Exception e) { - stripeApiKey = ""; - } - } catch (Exception e) { - FileLog.e(e); - } - } - - initAndroidPay(context); - - inputFields = new EditTextBoldCursor[FIELDS_COUNT_CARD]; - for (int a = 0; a < FIELDS_COUNT_CARD; a++) { - if (a == FIELD_CARD) { - headerCell[0] = new HeaderCell(context); - headerCell[0].setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - headerCell[0].setText(LocaleController.getString("PaymentCardTitle", R.string.PaymentCardTitle)); - linearLayout2.addView(headerCell[0], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - } else if (a == FIELD_CARD_COUNTRY) { - headerCell[1] = new HeaderCell(context); - headerCell[1].setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - headerCell[1].setText(LocaleController.getString("PaymentBillingAddress", R.string.PaymentBillingAddress)); - linearLayout2.addView(headerCell[1], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - } - - boolean allowDivider = a != FIELD_CVV && a != FIELD_CARD_POSTCODE && !(a == FIELD_CARD_COUNTRY && !need_card_postcode); - ViewGroup container = new FrameLayout(context); - linearLayout2.addView(container, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 50)); - container.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - - View.OnTouchListener onTouchListener = null; - inputFields[a] = new EditTextBoldCursor(context); - inputFields[a].setTag(a); - inputFields[a].setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); - inputFields[a].setHintTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteHintText)); - inputFields[a].setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); - inputFields[a].setBackgroundDrawable(null); - inputFields[a].setCursorColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); - inputFields[a].setCursorSize(AndroidUtilities.dp(20)); - inputFields[a].setCursorWidth(1.5f); - if (a == FIELD_CVV) { - InputFilter[] inputFilters = new InputFilter[1]; - inputFilters[0] = new InputFilter.LengthFilter(3); - inputFields[a].setFilters(inputFilters); - inputFields[a].setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_TEXT_VARIATION_PASSWORD); - inputFields[a].setTypeface(Typeface.DEFAULT); - inputFields[a].setTransformationMethod(PasswordTransformationMethod.getInstance()); - } else if (a == FIELD_CARD) { - inputFields[a].setInputType(InputType.TYPE_CLASS_PHONE); - } else if (a == FIELD_CARD_COUNTRY) { - inputFields[a].setOnTouchListener((v, event) -> { - if (getParentActivity() == null) { - return false; - } - if (event.getAction() == MotionEvent.ACTION_UP) { - CountrySelectActivity fragment = new CountrySelectActivity(false); - fragment.setCountrySelectActivityDelegate((name, shortName) -> inputFields[FIELD_CARD_COUNTRY].setText(name)); - presentFragment(fragment); - } - return true; - }); - inputFields[a].setInputType(0); - } else if (a == FIELD_EXPIRE_DATE) { - inputFields[a].setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES); - } else if (a == FIELD_CARDNAME) { - inputFields[a].setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS); - } else { - inputFields[a].setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES); - } - inputFields[a].setImeOptions(EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NO_EXTRACT_UI); - switch (a) { - case FIELD_CARD: - inputFields[a].setHint(LocaleController.getString("PaymentCardNumber", R.string.PaymentCardNumber)); - break; - case FIELD_CVV: - inputFields[a].setHint(LocaleController.getString("PaymentCardCvv", R.string.PaymentCardCvv)); - break; - case FIELD_EXPIRE_DATE: - inputFields[a].setHint(LocaleController.getString("PaymentCardExpireDate", R.string.PaymentCardExpireDate)); - break; - case FIELD_CARDNAME: - inputFields[a].setHint(LocaleController.getString("PaymentCardName", R.string.PaymentCardName)); - break; - case FIELD_CARD_POSTCODE: - inputFields[a].setHint(LocaleController.getString("PaymentShippingZipPlaceholder", R.string.PaymentShippingZipPlaceholder)); - break; - case FIELD_CARD_COUNTRY: - inputFields[a].setHint(LocaleController.getString("PaymentShippingCountry", R.string.PaymentShippingCountry)); - break; - } - - if (a == FIELD_CARD) { - inputFields[a].addTextChangedListener(new TextWatcher() { - - public final String[] PREFIXES_15 = {"34", "37"}; - public final String[] PREFIXES_14 = {"300", "301", "302", "303", "304", "305", "309", "36", "38", "39"}; - public final String[] PREFIXES_16 = { - "2221", "2222", "2223", "2224", "2225", "2226", "2227", "2228", "2229", - "223", "224", "225", "226", "227", "228", "229", - "23", "24", "25", "26", - "270", "271", "2720", - "50", "51", "52", "53", "54", "55", - - "4", - - "60", "62", "64", "65", - - "35" - }; - - public static final int MAX_LENGTH_STANDARD = 16; - public static final int MAX_LENGTH_AMERICAN_EXPRESS = 15; - public static final int MAX_LENGTH_DINERS_CLUB = 14; - - private int characterAction = -1; - private int actionPosition; - - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - if (count == 0 && after == 1) { - characterAction = 1; - } else if (count == 1 && after == 0) { - if (s.charAt(start) == ' ' && start > 0) { - characterAction = 3; - actionPosition = start - 1; - } else { - characterAction = 2; - } - } else { - characterAction = -1; - } - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - - } - - @Override - public void afterTextChanged(Editable editable) { - if (ignoreOnCardChange) { - return; - } - EditText phoneField = inputFields[FIELD_CARD]; - int start = phoneField.getSelectionStart(); - String phoneChars = "0123456789"; - String str = phoneField.getText().toString(); - if (characterAction == 3) { - str = str.substring(0, actionPosition) + str.substring(actionPosition + 1); - start--; - } - StringBuilder builder = new StringBuilder(str.length()); - for (int a = 0; a < str.length(); a++) { - String ch = str.substring(a, a + 1); - if (phoneChars.contains(ch)) { - builder.append(ch); - } - } - ignoreOnCardChange = true; - String hint = null; - int maxLength = 100; - if (builder.length() > 0) { - String currentString = builder.toString(); - for (int a = 0; a < 3; a++) { - String[] checkArr; - String resultHint; - int resultMaxLength; - switch (a) { - case 0: - checkArr = PREFIXES_16; - resultMaxLength = 16; - resultHint = "xxxx xxxx xxxx xxxx"; - break; - case 1: - checkArr = PREFIXES_15; - resultMaxLength = 15; - resultHint = "xxxx xxxx xxxx xxx"; - break; - case 2: - default: - checkArr = PREFIXES_14; - resultMaxLength = 14; - resultHint = "xxxx xxxx xxxx xx"; - break; - } - for (int b = 0; b < checkArr.length; b++) { - String prefix = checkArr[b]; - if (currentString.length() <= prefix.length()) { - if (prefix.startsWith(currentString)) { - hint = resultHint; - maxLength = resultMaxLength; - break; - } - } else { - if (currentString.startsWith(prefix)) { - hint = resultHint; - maxLength = resultMaxLength; - break; - } - } - } - if (hint != null) { - break; - } - } - if (maxLength != 0) { - if (builder.length() > maxLength) { - builder.setLength(maxLength); - } - } - } - if (hint != null) { - if (maxLength != 0) { - if (builder.length() == maxLength) { - inputFields[FIELD_EXPIRE_DATE].requestFocus(); - } - } - phoneField.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); - for (int a = 0; a < builder.length(); a++) { - if (a < hint.length()) { - if (hint.charAt(a) == ' ') { - builder.insert(a, ' '); - a++; - if (start == a && characterAction != 2 && characterAction != 3) { - start++; - } - } - } else { - builder.insert(a, ' '); - if (start == a + 1 && characterAction != 2 && characterAction != 3) { - start++; - } - break; - } - } - } else { - phoneField.setTextColor(builder.length() > 0 ? Theme.getColor(Theme.key_windowBackgroundWhiteRedText4) : Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); - } - if (!builder.toString().equals(editable.toString())) { - editable.replace(0, editable.length(), builder); - } - if (start >= 0) { - phoneField.setSelection(start <= phoneField.length() ? start : phoneField.length()); - } - ignoreOnCardChange = false; - } - }); - } else if (a == FIELD_EXPIRE_DATE) { - inputFields[a].addTextChangedListener(new TextWatcher() { - - private int characterAction = -1; - private boolean isYear; - private int actionPosition; - - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - if (count == 0 && after == 1) { - isYear = TextUtils.indexOf(inputFields[FIELD_EXPIRE_DATE].getText(), '/') != -1; - characterAction = 1; - } else if (count == 1 && after == 0) { - if (s.charAt(start) == '/' && start > 0) { - isYear = false; - characterAction = 3; - actionPosition = start - 1; - } else { - characterAction = 2; - } - } else { - characterAction = -1; - } - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - - } - - @Override - public void afterTextChanged(Editable s) { - if (ignoreOnCardChange) { - return; - } - EditText phoneField = inputFields[FIELD_EXPIRE_DATE]; - int start = phoneField.getSelectionStart(); - String phoneChars = "0123456789"; - String str = phoneField.getText().toString(); - if (characterAction == 3) { - str = str.substring(0, actionPosition) + str.substring(actionPosition + 1); - start--; - } - StringBuilder builder = new StringBuilder(str.length()); - for (int a = 0; a < str.length(); a++) { - String ch = str.substring(a, a + 1); - if (phoneChars.contains(ch)) { - builder.append(ch); - } - } - ignoreOnCardChange = true; - inputFields[FIELD_EXPIRE_DATE].setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); - if (builder.length() > 4) { - builder.setLength(4); - } - if (builder.length() < 2) { - isYear = false; - } - boolean isError = false; - if (isYear) { - String[] args = new String[builder.length() > 2 ? 2 : 1]; - args[0] = builder.substring(0, 2); - if (args.length == 2) { - args[1] = builder.substring(2); - } - if (builder.length() == 4 && args.length == 2) { - int month = Utilities.parseInt(args[0]); - int year = Utilities.parseInt(args[1]) + 2000; - Calendar rightNow = Calendar.getInstance(); - int currentYear = rightNow.get(Calendar.YEAR); - int currentMonth = rightNow.get(Calendar.MONTH) + 1; - if (year < currentYear || year == currentYear && month < currentMonth) { - inputFields[FIELD_EXPIRE_DATE].setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText4)); - isError = true; - } - } else { - int value = Utilities.parseInt(args[0]); - if (value > 12 || value == 0) { - inputFields[FIELD_EXPIRE_DATE].setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText4)); - isError = true; - } - } - } else { - if (builder.length() == 1) { - int value = Utilities.parseInt(builder.toString()); - if (value != 1 && value != 0) { - builder.insert(0, "0"); - start++; - } - } else if (builder.length() == 2) { - int value = Utilities.parseInt(builder.toString()); - if (value > 12 || value == 0) { - inputFields[FIELD_EXPIRE_DATE].setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText4)); - isError = true; - } - start++; - } - } - if (!isError && builder.length() == 4) { - inputFields[need_card_name ? FIELD_CARDNAME : FIELD_CVV].requestFocus(); - } - if (builder.length() == 2) { - builder.append('/'); - start++; - } else if (builder.length() > 2 && builder.charAt(2) != '/') { - builder.insert(2, '/'); - start++; - } - - phoneField.setText(builder); - if (start >= 0) { - phoneField.setSelection(start <= phoneField.length() ? start : phoneField.length()); - } - ignoreOnCardChange = false; - } - }); - } - inputFields[a].setPadding(0, 0, 0, AndroidUtilities.dp(6)); - inputFields[a].setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); - container.addView(inputFields[a], LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 21, 12, 21, 6)); - - inputFields[a].setOnEditorActionListener((textView, i, keyEvent) -> { - if (i == EditorInfo.IME_ACTION_NEXT) { - int num = (Integer) textView.getTag(); - while (num + 1 < inputFields.length) { - num++; - if (num == FIELD_CARD_COUNTRY) { - num++; - } - if (((View) inputFields[num].getParent()).getVisibility() == View.VISIBLE) { - inputFields[num].requestFocus(); - break; - } - } - return true; - } else if (i == EditorInfo.IME_ACTION_DONE) { - doneItem.performClick(); - return true; - } - return false; - }); - if (a == FIELD_CVV) { - sectionCell[0] = new ShadowSectionCell(context); - linearLayout2.addView(sectionCell[0], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - } else if (a == FIELD_CARD_POSTCODE) { - sectionCell[2] = new ShadowSectionCell(context); - linearLayout2.addView(sectionCell[2], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - - checkCell1 = new TextCheckCell(context); - checkCell1.setBackgroundDrawable(Theme.getSelectorDrawable(true)); - checkCell1.setTextAndCheck(LocaleController.getString("PaymentCardSavePaymentInformation", R.string.PaymentCardSavePaymentInformation), saveCardInfo, false); - linearLayout2.addView(checkCell1, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - checkCell1.setOnClickListener(v -> { - saveCardInfo = !saveCardInfo; - checkCell1.setChecked(saveCardInfo); - }); - - bottomCell[0] = new TextInfoPrivacyCell(context); - bottomCell[0].setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); - updateSavePaymentField(); - linearLayout2.addView(bottomCell[0], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - } else if (a == FIELD_CARD) { - androidPayContainer = new FrameLayout(context); - androidPayContainer.setId(fragment_container_id); - androidPayContainer.setBackgroundDrawable(Theme.getSelectorDrawable(true)); - androidPayContainer.setVisibility(View.GONE); - container.addView(androidPayContainer, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL | Gravity.RIGHT, 0, 0, 4, 0)); - } - - if (allowDivider) { - View divider = new View(context) { - @Override - protected void onDraw(Canvas canvas) { - canvas.drawLine(LocaleController.isRTL ? 0 : AndroidUtilities.dp(20), getMeasuredHeight() - 1, getMeasuredWidth() - (LocaleController.isRTL ? AndroidUtilities.dp(20) : 0), getMeasuredHeight() - 1, Theme.dividerPaint); - } - }; - divider.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - dividers.add(divider); - container.addView(divider, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1, Gravity.LEFT | Gravity.BOTTOM)); - } - - if (a == FIELD_CARD_COUNTRY && !need_card_country || a == FIELD_CARD_POSTCODE && !need_card_postcode || a == FIELD_CARDNAME && !need_card_name) { - container.setVisibility(View.GONE); - } - } - if (!need_card_country && !need_card_postcode) { - headerCell[1].setVisibility(View.GONE); - sectionCell[0].setVisibility(View.GONE); - } - if (need_card_postcode) { - inputFields[FIELD_CARD_POSTCODE].setImeOptions(EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI); - } else { - inputFields[FIELD_CVV].setImeOptions(EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI); - } - } - } else if (currentStep == 1) { - int count = requestedInfo.shipping_options.size(); - radioCells = new RadioCell[count]; - for (int a = 0; a < count; a++) { - TLRPC.TL_shippingOption shippingOption = requestedInfo.shipping_options.get(a); - radioCells[a] = new RadioCell(context); - radioCells[a].setTag(a); - radioCells[a].setBackgroundDrawable(Theme.getSelectorDrawable(true)); - radioCells[a].setText(String.format("%s - %s", getTotalPriceString(shippingOption.prices), shippingOption.title), a == 0, a != count - 1); - radioCells[a].setOnClickListener(v -> { - int num = (Integer) v.getTag(); - for (int a1 = 0; a1 < radioCells.length; a1++) { - radioCells[a1].setChecked(num == a1, true); - } - }); - linearLayout2.addView(radioCells[a]); - } - bottomCell[0] = new TextInfoPrivacyCell(context); - bottomCell[0].setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); - linearLayout2.addView(bottomCell[0], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - } else if (currentStep == 3) { - inputFields = new EditTextBoldCursor[FIELDS_COUNT_SAVEDCARD]; - for (int a = 0; a < FIELDS_COUNT_SAVEDCARD; a++) { - if (a == FIELD_SAVEDCARD) { - headerCell[0] = new HeaderCell(context); - headerCell[0].setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - headerCell[0].setText(LocaleController.getString("PaymentCardTitle", R.string.PaymentCardTitle)); - linearLayout2.addView(headerCell[0], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - } - - ViewGroup container = new FrameLayout(context); - linearLayout2.addView(container, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 50)); - container.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - - boolean allowDivider = a != FIELD_SAVEDPASSWORD; - if (allowDivider) { - if (a == FIELD_EMAIL && !paymentForm.invoice.phone_requested) { - allowDivider = false; - } else if (a == FIELD_NAME && !paymentForm.invoice.phone_requested && !paymentForm.invoice.email_requested) { - allowDivider = false; - } - } - if (allowDivider) { - View divider = new View(context) { - @Override - protected void onDraw(Canvas canvas) { - canvas.drawLine(LocaleController.isRTL ? 0 : AndroidUtilities.dp(20), getMeasuredHeight() - 1, getMeasuredWidth() - (LocaleController.isRTL ? AndroidUtilities.dp(20) : 0), getMeasuredHeight() - 1, Theme.dividerPaint); - } - }; - divider.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - dividers.add(divider); - container.addView(divider, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1, Gravity.LEFT | Gravity.BOTTOM)); - } - - inputFields[a] = new EditTextBoldCursor(context); - inputFields[a].setTag(a); - inputFields[a].setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); - inputFields[a].setHintTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteHintText)); - inputFields[a].setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); - inputFields[a].setBackgroundDrawable(null); - inputFields[a].setCursorColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); - inputFields[a].setCursorSize(AndroidUtilities.dp(20)); - inputFields[a].setCursorWidth(1.5f); - if (a == FIELD_SAVEDCARD) { - inputFields[a].setOnTouchListener((v, event) -> true); - inputFields[a].setInputType(0); - } else { - inputFields[a].setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); - inputFields[a].setTypeface(Typeface.DEFAULT); - } - inputFields[a].setImeOptions(EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI); - switch (a) { - case FIELD_SAVEDCARD: - inputFields[a].setText(paymentForm.saved_credentials.title); - break; - case FIELD_SAVEDPASSWORD: - inputFields[a].setHint(LocaleController.getString("LoginPassword", R.string.LoginPassword)); - inputFields[a].requestFocus(); - break; - } - - inputFields[a].setPadding(0, 0, 0, AndroidUtilities.dp(6)); - inputFields[a].setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); - container.addView(inputFields[a], LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 21, 12, 21, 6)); - - inputFields[a].setOnEditorActionListener((textView, i, keyEvent) -> { - if (i == EditorInfo.IME_ACTION_DONE) { - doneItem.performClick(); - return true; - } - return false; - }); - if (a == FIELD_SAVEDPASSWORD) { - bottomCell[0] = new TextInfoPrivacyCell(context); - bottomCell[0].setText(LocaleController.formatString("PaymentConfirmationMessage", R.string.PaymentConfirmationMessage, paymentForm.saved_credentials.title)); - bottomCell[0].setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); - linearLayout2.addView(bottomCell[0], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - - settingsCell[0] = new TextSettingsCell(context); - settingsCell[0].setBackgroundDrawable(Theme.getSelectorDrawable(true)); - settingsCell[0].setText(LocaleController.getString("PaymentConfirmationNewCard", R.string.PaymentConfirmationNewCard), false); - linearLayout2.addView(settingsCell[0], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - settingsCell[0].setOnClickListener(v -> { - passwordOk = false; - goToNextStep(); - }); - - bottomCell[1] = new TextInfoPrivacyCell(context); - bottomCell[1].setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); - linearLayout2.addView(bottomCell[1], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - } - } - } else if (currentStep == 4 || currentStep == 5) { - paymentInfoCell = new PaymentInfoCell(context); - paymentInfoCell.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - paymentInfoCell.setInvoice((TLRPC.TL_messageMediaInvoice) messageObject.messageOwner.media, currentBotName); - linearLayout2.addView(paymentInfoCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - - sectionCell[0] = new ShadowSectionCell(context); - linearLayout2.addView(sectionCell[0], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - - ArrayList arrayList = new ArrayList<>(paymentForm.invoice.prices); - if (shippingOption != null) { - arrayList.addAll(shippingOption.prices); - } - final String totalPrice = getTotalPriceString(arrayList); - - for (int a = 0; a < arrayList.size(); a++) { - TLRPC.TL_labeledPrice price = arrayList.get(a); - - TextPriceCell priceCell = new TextPriceCell(context); - priceCell.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - priceCell.setTextAndValue(price.label, LocaleController.getInstance().formatCurrencyString(price.amount, paymentForm.invoice.currency), false); - linearLayout2.addView(priceCell); - } - - TextPriceCell priceCell = new TextPriceCell(context); - priceCell.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - priceCell.setTextAndValue(LocaleController.getString("PaymentTransactionTotal", R.string.PaymentTransactionTotal), totalPrice, true); - linearLayout2.addView(priceCell); - - View divider = new View(context) { - @Override - protected void onDraw(Canvas canvas) { - canvas.drawLine(LocaleController.isRTL ? 0 : AndroidUtilities.dp(20), getMeasuredHeight() - 1, getMeasuredWidth() - (LocaleController.isRTL ? AndroidUtilities.dp(20) : 0), getMeasuredHeight() - 1, Theme.dividerPaint); - } - }; - divider.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - dividers.add(divider); - linearLayout2.addView(divider, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1, Gravity.LEFT | Gravity.BOTTOM)); - - detailSettingsCell[0] = new TextDetailSettingsCell(context); - detailSettingsCell[0].setBackgroundDrawable(Theme.getSelectorDrawable(true)); - detailSettingsCell[0].setTextAndValue(cardName, LocaleController.getString("PaymentCheckoutMethod", R.string.PaymentCheckoutMethod), true); - linearLayout2.addView(detailSettingsCell[0]); - if (currentStep == 4) { - detailSettingsCell[0].setOnClickListener(v -> { - PaymentFormActivity activity = new PaymentFormActivity(paymentForm, messageObject, 2, requestedInfo, shippingOption, null, cardName, validateRequest, saveCardInfo, null); - activity.setDelegate(new PaymentFormActivityDelegate() { - @Override - public boolean didSelectNewCard(String tokenJson, String card, boolean saveCard, TLRPC.TL_inputPaymentCredentialsAndroidPay androidPay) { - paymentForm.saved_credentials = null; - paymentJson = tokenJson; - saveCardInfo = saveCard; - cardName = card; - androidPayCredentials = androidPay; - detailSettingsCell[0].setTextAndValue(cardName, LocaleController.getString("PaymentCheckoutMethod", R.string.PaymentCheckoutMethod), true); - return false; - } - - @Override - public void onFragmentDestroyed() { - - } - - @Override - public void currentPasswordUpdated(TLRPC.TL_account_password password) { - - } - }); - presentFragment(activity); - }); - } - - TLRPC.User providerUser = null; - for (int a = 0; a < paymentForm.users.size(); a++) { - TLRPC.User user = paymentForm.users.get(a); - if (user.id == paymentForm.provider_id) { - providerUser = user; - } - } - final String providerName; - if (providerUser != null) { - detailSettingsCell[1] = new TextDetailSettingsCell(context); - detailSettingsCell[1].setBackgroundDrawable(Theme.getSelectorDrawable(true)); - detailSettingsCell[1].setTextAndValue(providerName = ContactsController.formatName(providerUser.first_name, providerUser.last_name), LocaleController.getString("PaymentCheckoutProvider", R.string.PaymentCheckoutProvider), true); - linearLayout2.addView(detailSettingsCell[1]); - } else { - providerName = ""; - } - - if (validateRequest != null) { - if (validateRequest.info.shipping_address != null) { - String address = String.format("%s %s, %s, %s, %s, %s", validateRequest.info.shipping_address.street_line1, validateRequest.info.shipping_address.street_line2, validateRequest.info.shipping_address.city, validateRequest.info.shipping_address.state, validateRequest.info.shipping_address.country_iso2, validateRequest.info.shipping_address.post_code); - detailSettingsCell[2] = new TextDetailSettingsCell(context); - detailSettingsCell[2].setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - detailSettingsCell[2].setTextAndValue(address, LocaleController.getString("PaymentShippingAddress", R.string.PaymentShippingAddress), true); - linearLayout2.addView(detailSettingsCell[2]); - } - - if (validateRequest.info.name != null) { - detailSettingsCell[3] = new TextDetailSettingsCell(context); - detailSettingsCell[3].setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - detailSettingsCell[3].setTextAndValue(validateRequest.info.name, LocaleController.getString("PaymentCheckoutName", R.string.PaymentCheckoutName), true); - linearLayout2.addView(detailSettingsCell[3]); - } - - if (validateRequest.info.phone != null) { - detailSettingsCell[4] = new TextDetailSettingsCell(context); - detailSettingsCell[4].setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - detailSettingsCell[4].setTextAndValue(PhoneFormat.getInstance().format(validateRequest.info.phone), LocaleController.getString("PaymentCheckoutPhoneNumber", R.string.PaymentCheckoutPhoneNumber), true); - linearLayout2.addView(detailSettingsCell[4]); - } - - if (validateRequest.info.email != null) { - detailSettingsCell[5] = new TextDetailSettingsCell(context); - detailSettingsCell[5].setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - detailSettingsCell[5].setTextAndValue(validateRequest.info.email, LocaleController.getString("PaymentCheckoutEmail", R.string.PaymentCheckoutEmail), true); - linearLayout2.addView(detailSettingsCell[5]); - } - - if (shippingOption != null) { - detailSettingsCell[6] = new TextDetailSettingsCell(context); - detailSettingsCell[6].setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - detailSettingsCell[6].setTextAndValue(shippingOption.title, LocaleController.getString("PaymentCheckoutShippingMethod", R.string.PaymentCheckoutShippingMethod), false); - linearLayout2.addView(detailSettingsCell[6]); - } - } - - if (currentStep == 4) { - bottomLayout = new FrameLayout(context); - bottomLayout.setBackgroundDrawable(Theme.getSelectorDrawable(true)); - frameLayout.addView(bottomLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.BOTTOM)); - bottomLayout.setOnClickListener(v -> { - if (botUser != null && !botUser.verified) { - String botKey = "payment_warning_" + botUser.id; - SharedPreferences preferences = MessagesController.getNotificationsSettings(currentAccount); - if (!preferences.getBoolean(botKey, false)) { - preferences.edit().putBoolean(botKey, true).commit(); - AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); - builder.setTitle(LocaleController.getString("PaymentWarning", R.string.PaymentWarning)); - builder.setMessage(LocaleController.formatString("PaymentWarningText", R.string.PaymentWarningText, currentBotName, providerName)); - builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), (dialogInterface, i) -> showPayAlert(totalPrice)); - showDialog(builder.create()); - } else { - showPayAlert(totalPrice); - } - } else { - showPayAlert(totalPrice); - } - }); - payTextView = new TextView(context); - payTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlueText6)); - payTextView.setText(LocaleController.formatString("PaymentCheckoutPay", R.string.PaymentCheckoutPay, totalPrice)); - payTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - payTextView.setGravity(Gravity.CENTER); - payTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - bottomLayout.addView(payTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); - - progressViewButton = new ContextProgressView(context, 0); - progressViewButton.setVisibility(View.INVISIBLE); - bottomLayout.addView(progressViewButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); - - View shadow = new View(context); - shadow.setBackgroundResource(R.drawable.header_shadow_reverse); - frameLayout.addView(shadow, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 3, Gravity.LEFT | Gravity.BOTTOM, 0, 0, 0, 48)); - - doneItem.setEnabled(false); - doneItem.getContentView().setVisibility(View.INVISIBLE); - - webView = new WebView(context) { - @Override - public boolean onTouchEvent(MotionEvent event) { - getParent().requestDisallowInterceptTouchEvent(true); - return super.onTouchEvent(event); - } - }; - webView.setBackgroundColor(0xffffffff); - webView.getSettings().setJavaScriptEnabled(true); - webView.getSettings().setDomStorageEnabled(true); - - if (Build.VERSION.SDK_INT >= 21) { - webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); - CookieManager cookieManager = CookieManager.getInstance(); - cookieManager.setAcceptThirdPartyCookies(webView, true); - } - - webView.setWebViewClient(new WebViewClient() { - - @Override - public void onLoadResource(WebView view, String url) { - try { - Uri uri = Uri.parse(url); - if ("t.me".equals(uri.getHost())) { - goToNextStep(); - return; - } - } catch (Exception ignore) { - - } - super.onLoadResource(view, url); - } - - @Override - public void onPageFinished(WebView view, String url) { - super.onPageFinished(view, url); - webviewLoading = false; - showEditDoneProgress(true, false); - updateSavePaymentField(); - } - - @Override - public boolean shouldOverrideUrlLoading(WebView view, String url) { - try { - Uri uri = Uri.parse(url); - if ("t.me".equals(uri.getHost())) { - goToNextStep(); - return true; - } - } catch (Exception ignore) { - - } - return false; - } - }); - - frameLayout.addView(webView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); - webView.setVisibility(View.GONE); - } - - sectionCell[1] = new ShadowSectionCell(context); - sectionCell[1].setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); - linearLayout2.addView(sectionCell[1], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - } else if (currentStep == 6) { - codeFieldCell = new EditTextSettingsCell(context); - codeFieldCell.setTextAndHint("", LocaleController.getString("PasswordCode", R.string.PasswordCode), false); - codeFieldCell.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - EditTextBoldCursor editText = codeFieldCell.getTextView(); - editText.setInputType(InputType.TYPE_CLASS_PHONE); - editText.setImeOptions(EditorInfo.IME_ACTION_DONE); - editText.setOnEditorActionListener((textView, i, keyEvent) -> { - if (i == EditorInfo.IME_ACTION_DONE) { - sendSavePassword(false); - return true; - } - return false; - }); - editText.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - - } - - @Override - public void afterTextChanged(Editable s) { - if (emailCodeLength != 0 && s.length() == emailCodeLength) { - sendSavePassword(false); - } - } - }); - linearLayout2.addView(codeFieldCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - - bottomCell[2] = new TextInfoPrivacyCell(context); - bottomCell[2].setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); - linearLayout2.addView(bottomCell[2], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - - settingsCell[1] = new TextSettingsCell(context); - settingsCell[1].setBackgroundDrawable(Theme.getSelectorDrawable(true)); - settingsCell[1].setTag(Theme.key_windowBackgroundWhiteBlackText); - settingsCell[1].setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); - settingsCell[1].setText(LocaleController.getString("ResendCode", R.string.ResendCode), true); - linearLayout2.addView(settingsCell[1], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - settingsCell[1].setOnClickListener(v -> { - TLRPC.TL_account_resendPasswordEmail req = new TLRPC.TL_account_resendPasswordEmail(); - ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> { - - }); - AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); - builder.setMessage(LocaleController.getString("ResendCodeInfo", R.string.ResendCodeInfo)); - builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); - builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null); - showDialog(builder.create()); - }); - - settingsCell[0] = new TextSettingsCell(context); - settingsCell[0].setBackgroundDrawable(Theme.getSelectorDrawable(true)); - settingsCell[0].setTag(Theme.key_windowBackgroundWhiteRedText3); - settingsCell[0].setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText3)); - settingsCell[0].setText(LocaleController.getString("AbortPassword", R.string.AbortPassword), false); - linearLayout2.addView(settingsCell[0], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - settingsCell[0].setOnClickListener(v -> { - AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); - String text = LocaleController.getString("TurnPasswordOffQuestion", R.string.TurnPasswordOffQuestion); - if (currentPassword.has_secure_values) { - text += "\n\n" + LocaleController.getString("TurnPasswordOffPassport", R.string.TurnPasswordOffPassport); - } - builder.setMessage(text); - builder.setTitle(LocaleController.getString("TurnPasswordOffQuestionTitle", R.string.TurnPasswordOffQuestionTitle)); - builder.setPositiveButton(LocaleController.getString("Disable", R.string.Disable), (dialogInterface, i) -> sendSavePassword(true)); - builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - AlertDialog alertDialog = builder.create(); - showDialog(alertDialog); - TextView button = (TextView) alertDialog.getButton(DialogInterface.BUTTON_POSITIVE); - if (button != null) { - button.setTextColor(Theme.getColor(Theme.key_dialogTextRed2)); - } - }); - - inputFields = new EditTextBoldCursor[FIELDS_COUNT_PASSWORD]; - for (int a = 0; a < FIELDS_COUNT_PASSWORD; a++) { - if (a == FIELD_ENTERPASSWORD) { - headerCell[0] = new HeaderCell(context); - headerCell[0].setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - headerCell[0].setText(LocaleController.getString("PaymentPasswordTitle", R.string.PaymentPasswordTitle)); - linearLayout2.addView(headerCell[0], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - } else if (a == FIELD_ENTERPASSWORDEMAIL) { - headerCell[1] = new HeaderCell(context); - headerCell[1].setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - headerCell[1].setText(LocaleController.getString("PaymentPasswordEmailTitle", R.string.PaymentPasswordEmailTitle)); - linearLayout2.addView(headerCell[1], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - } - - ViewGroup container = new FrameLayout(context); - linearLayout2.addView(container, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 50)); - container.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - - if (a == FIELD_ENTERPASSWORD) { - View divider = new View(context) { - @Override - protected void onDraw(Canvas canvas) { - canvas.drawLine(LocaleController.isRTL ? 0 : AndroidUtilities.dp(20), getMeasuredHeight() - 1, getMeasuredWidth() - (LocaleController.isRTL ? AndroidUtilities.dp(20) : 0), getMeasuredHeight() - 1, Theme.dividerPaint); - } - }; - divider.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); - dividers.add(divider); - container.addView(divider, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1, Gravity.LEFT | Gravity.BOTTOM)); - } - - inputFields[a] = new EditTextBoldCursor(context); - inputFields[a].setTag(a); - inputFields[a].setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); - inputFields[a].setHintTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteHintText)); - inputFields[a].setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); - inputFields[a].setBackgroundDrawable(null); - inputFields[a].setCursorColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); - inputFields[a].setCursorSize(AndroidUtilities.dp(20)); - inputFields[a].setCursorWidth(1.5f); - - if (a == FIELD_ENTERPASSWORD || a == FIELD_REENTERPASSWORD) { - inputFields[a].setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); - inputFields[a].setTypeface(Typeface.DEFAULT); - inputFields[a].setImeOptions(EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NO_EXTRACT_UI); - } else { - inputFields[a].setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS); - inputFields[a].setImeOptions(EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI); - } - - switch (a) { - case FIELD_ENTERPASSWORD: - inputFields[a].setHint(LocaleController.getString("PaymentPasswordEnter", R.string.PaymentPasswordEnter)); - inputFields[a].requestFocus(); - break; - case FIELD_REENTERPASSWORD: - inputFields[a].setHint(LocaleController.getString("PaymentPasswordReEnter", R.string.PaymentPasswordReEnter)); - break; - case FIELD_ENTERPASSWORDEMAIL: - inputFields[a].setHint(LocaleController.getString("PaymentPasswordEmail", R.string.PaymentPasswordEmail)); - break; - } - - inputFields[a].setPadding(0, 0, 0, AndroidUtilities.dp(6)); - inputFields[a].setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); - container.addView(inputFields[a], LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 21, 12, 21, 6)); - - inputFields[a].setOnEditorActionListener((textView, i, keyEvent) -> { - if (i == EditorInfo.IME_ACTION_DONE) { - doneItem.performClick(); - return true; - } else if (i == EditorInfo.IME_ACTION_NEXT) { - int num = (Integer) textView.getTag(); - if (num == FIELD_ENTERPASSWORD) { - inputFields[FIELD_REENTERPASSWORD].requestFocus(); - } else if (num == FIELD_REENTERPASSWORD) { - inputFields[FIELD_ENTERPASSWORDEMAIL].requestFocus(); - } - } - return false; - }); - if (a == FIELD_REENTERPASSWORD) { - bottomCell[0] = new TextInfoPrivacyCell(context); - bottomCell[0].setText(LocaleController.getString("PaymentPasswordInfo", R.string.PaymentPasswordInfo)); - bottomCell[0].setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); - linearLayout2.addView(bottomCell[0], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - } else if (a == FIELD_ENTERPASSWORDEMAIL) { - bottomCell[1] = new TextInfoPrivacyCell(context); - bottomCell[1].setText(LocaleController.getString("PaymentPasswordEmailInfo", R.string.PaymentPasswordEmailInfo)); - bottomCell[1].setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); - linearLayout2.addView(bottomCell[1], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - } - } - updatePasswordFields(); - } - return fragmentView; - } - - private void updatePasswordFields() { - if (currentStep != 6 || bottomCell[2] == null) { - return; - } - doneItem.setVisibility(View.VISIBLE); - if (currentPassword == null) { - showEditDoneProgress(true, true); - bottomCell[2].setVisibility(View.GONE); - settingsCell[0].setVisibility(View.GONE); - settingsCell[1].setVisibility(View.GONE); - codeFieldCell.setVisibility(View.GONE); - headerCell[0].setVisibility(View.GONE); - headerCell[1].setVisibility(View.GONE); - bottomCell[0].setVisibility(View.GONE); - for (int a = 0; a < FIELDS_COUNT_PASSWORD; a++) { - ((View) inputFields[a].getParent()).setVisibility(View.GONE); - } - for (int a = 0; a < dividers.size(); a++) { - dividers.get(a).setVisibility(View.GONE); - } - } else { - showEditDoneProgress(true, false); - if (waitingForEmail) { - bottomCell[2].setText(LocaleController.formatString("EmailPasswordConfirmText2", R.string.EmailPasswordConfirmText2, currentPassword.email_unconfirmed_pattern != null ? currentPassword.email_unconfirmed_pattern : "")); - bottomCell[2].setVisibility(View.VISIBLE); - settingsCell[0].setVisibility(View.VISIBLE); - settingsCell[1].setVisibility(View.VISIBLE); - codeFieldCell.setVisibility(View.VISIBLE); - bottomCell[1].setText(""); - - headerCell[0].setVisibility(View.GONE); - headerCell[1].setVisibility(View.GONE); - bottomCell[0].setVisibility(View.GONE); - for (int a = 0; a < FIELDS_COUNT_PASSWORD; a++) { - ((View) inputFields[a].getParent()).setVisibility(View.GONE); - } - for (int a = 0; a < dividers.size(); a++) { - dividers.get(a).setVisibility(View.GONE); - } - } else { - bottomCell[2].setVisibility(View.GONE); - settingsCell[0].setVisibility(View.GONE); - settingsCell[1].setVisibility(View.GONE); - bottomCell[1].setText(LocaleController.getString("PaymentPasswordEmailInfo", R.string.PaymentPasswordEmailInfo)); - codeFieldCell.setVisibility(View.GONE); - - headerCell[0].setVisibility(View.VISIBLE); - headerCell[1].setVisibility(View.VISIBLE); - bottomCell[0].setVisibility(View.VISIBLE); - for (int a = 0; a < FIELDS_COUNT_PASSWORD; a++) { - ((View) inputFields[a].getParent()).setVisibility(View.VISIBLE); - } - for (int a = 0; a < dividers.size(); a++) { - dividers.get(a).setVisibility(View.VISIBLE); - } - } - } - } - - private void loadPasswordInfo() { - if (loadingPasswordInfo) { - return; - } - loadingPasswordInfo = true; - TLRPC.TL_account_getPassword req = new TLRPC.TL_account_getPassword(); - ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { - loadingPasswordInfo = false; - if (error == null) { - currentPassword = (TLRPC.TL_account_password) response; - if (!TwoStepVerificationActivity.canHandleCurrentPassword(currentPassword, false)) { - AlertsCreator.showUpdateAppAlert(getParentActivity(), LocaleController.getString("UpdateAppAlert", R.string.UpdateAppAlert), true); - return; - } - if (paymentForm != null && currentPassword.has_password) { - paymentForm.password_missing = false; - paymentForm.can_save_credentials = true; - updateSavePaymentField(); - } - TwoStepVerificationActivity.initPasswordNewAlgo(currentPassword); - if (passwordFragment != null) { - passwordFragment.setCurrentPassword(currentPassword); - } - if (!currentPassword.has_password && shortPollRunnable == null) { - shortPollRunnable = () -> { - if (shortPollRunnable == null) { - return; - } - loadPasswordInfo(); - shortPollRunnable = null; - }; - AndroidUtilities.runOnUIThread(shortPollRunnable, 5000); - } - } - }), ConnectionsManager.RequestFlagFailOnServerErrors | ConnectionsManager.RequestFlagWithoutLogin); - } - - private void showAlertWithText(String title, String text) { - AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); - builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null); - builder.setTitle(title); - builder.setMessage(text); - showDialog(builder.create()); - } - - private void showPayAlert(final String totalPrice) { - AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); - builder.setTitle(LocaleController.getString("PaymentTransactionReview", R.string.PaymentTransactionReview)); - builder.setMessage(LocaleController.formatString("PaymentTransactionMessage", R.string.PaymentTransactionMessage, totalPrice, currentBotName, currentItemName)); - builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), (dialogInterface, i) -> { - setDonePressed(true); - sendData(); - }); - builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - showDialog(builder.create()); - } - - private void initAndroidPay(Context context) { - /*if (Build.VERSION.SDK_INT < 19) { - return; - } - googleApiClient = new GoogleApiClient.Builder(context) - .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() { - @Override - public void onConnected(Bundle bundle) { - - } - - @Override - public void onConnectionSuspended(int i) { - - } - }) - .addOnConnectionFailedListener(connectionResult -> { - - }) - .addApi(Wallet.API, new Wallet.WalletOptions.Builder() - .setEnvironment(paymentForm.invoice.test ? WalletConstants.ENVIRONMENT_TEST : WalletConstants.ENVIRONMENT_PRODUCTION) - .setTheme(WalletConstants.THEME_LIGHT) - .build()) - .build(); - - Wallet.Payments.isReadyToPay(googleApiClient).setResultCallback( - booleanResult -> { - if (booleanResult.getStatus().isSuccess()) { - if (booleanResult.getValue()) { - showAndroidPay(); - } - } else { - - } - } - ); - googleApiClient.connect();*/ - } - - private String getTotalPriceString(ArrayList prices) { - long amount = 0; - for (int a = 0; a < prices.size(); a++) { - amount += prices.get(a).amount; - } - return LocaleController.getInstance().formatCurrencyString(amount, paymentForm.invoice.currency); - } - - private String getTotalPriceDecimalString(ArrayList prices) { - long amount = 0; - for (int a = 0; a < prices.size(); a++) { - amount += prices.get(a).amount; - } - return LocaleController.getInstance().formatCurrencyDecimalString(amount, paymentForm.invoice.currency, false); - } - - @Override - public boolean onFragmentCreate() { - NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.didSetTwoStepPassword); - NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.didRemoveTwoStepPassword); - if (currentStep != 4) { - NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.paymentFinished); - } - return super.onFragmentCreate(); - } - - @Override - public void onFragmentDestroy() { - if (delegate != null) { - delegate.onFragmentDestroyed(); - } - NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.didSetTwoStepPassword); - NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.didRemoveTwoStepPassword); - if (currentStep != 4) { - NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.paymentFinished); - } - if (webView != null) { - try { - ViewParent parent = webView.getParent(); - if (parent != null) { - ((ViewGroup) parent).removeView(webView); - } - webView.stopLoading(); - webView.loadUrl("about:blank"); - webViewUrl = null; - webView.destroy(); - webView = null; - } catch (Exception e) { - FileLog.e(e); - } - } - try { - if ((currentStep == 2 || currentStep == 6) && Build.VERSION.SDK_INT >= 23 && (SharedConfig.passcodeHash.length() == 0 || SharedConfig.allowScreenCapture)) { - getParentActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE); - } - } catch (Throwable e) { - FileLog.e(e); - } - super.onFragmentDestroy(); - canceled = true; - } - - @Override - protected void onTransitionAnimationEnd(boolean isOpen, boolean backward) { - if (isOpen && !backward) { - if (webView != null) { - if (currentStep != 4) { - webView.loadUrl(webViewUrl = paymentForm.url); - } - } else if (currentStep == 2) { - inputFields[FIELD_CARD].requestFocus(); - AndroidUtilities.showKeyboard(inputFields[FIELD_CARD]); - } else if (currentStep == 3) { - inputFields[FIELD_SAVEDPASSWORD].requestFocus(); - AndroidUtilities.showKeyboard(inputFields[FIELD_SAVEDPASSWORD]); - } else if (currentStep == 6) { - if (!waitingForEmail) { - inputFields[FIELD_ENTERPASSWORD].requestFocus(); - AndroidUtilities.showKeyboard(inputFields[FIELD_ENTERPASSWORD]); - } - } - } - } - - @Override - public void didReceivedNotification(int id, int account, Object... args) { - if (id == NotificationCenter.didSetTwoStepPassword) { - paymentForm.password_missing = false; - paymentForm.can_save_credentials = true; - updateSavePaymentField(); - } else if (id == NotificationCenter.didRemoveTwoStepPassword) { - paymentForm.password_missing = true; - paymentForm.can_save_credentials = false; - updateSavePaymentField(); - } else if (id == NotificationCenter.paymentFinished) { - removeSelfFromStack(); - } - } - /* - private void showAndroidPay() { - if (getParentActivity() == null || androidPayContainer == null) { - return; - } - - WalletFragmentOptions.Builder optionsBuilder = WalletFragmentOptions.newBuilder(); - optionsBuilder.setEnvironment(paymentForm.invoice.test ? WalletConstants.ENVIRONMENT_TEST : WalletConstants.ENVIRONMENT_PRODUCTION); - optionsBuilder.setMode(WalletFragmentMode.BUY_BUTTON); - - WalletFragmentStyle walletFragmentStyle; - if (androidPayPublicKey != null) { - androidPayContainer.setBackgroundColor(androidPayBackgroundColor); - walletFragmentStyle = new WalletFragmentStyle() - .setBuyButtonText(WalletFragmentStyle.BuyButtonText.BUY_WITH) - .setBuyButtonAppearance(androidPayBlackTheme ? WalletFragmentStyle.BuyButtonAppearance.ANDROID_PAY_LIGHT_WITH_BORDER : WalletFragmentStyle.BuyButtonAppearance.ANDROID_PAY_DARK) - .setBuyButtonWidth(WalletFragmentStyle.Dimension.MATCH_PARENT); - } else { - walletFragmentStyle = new WalletFragmentStyle() - .setBuyButtonText(WalletFragmentStyle.BuyButtonText.LOGO_ONLY) - .setBuyButtonAppearance(WalletFragmentStyle.BuyButtonAppearance.ANDROID_PAY_LIGHT_WITH_BORDER) - .setBuyButtonWidth(WalletFragmentStyle.Dimension.WRAP_CONTENT); - } - - optionsBuilder.setFragmentStyle(walletFragmentStyle); - WalletFragment walletFragment = WalletFragment.newInstance(optionsBuilder.build()); - FragmentManager fragmentManager = getParentActivity().getFragmentManager(); - FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); - fragmentTransaction.replace(fragment_container_id, walletFragment); - fragmentTransaction.commit(); - - ArrayList arrayList = new ArrayList<>(paymentForm.invoice.prices); - if (shippingOption != null) { - arrayList.addAll(shippingOption.prices); - } - totalPriceDecimal = getTotalPriceDecimalString(arrayList); - - PaymentMethodTokenizationParameters parameters; - if (androidPayPublicKey != null) { - parameters = PaymentMethodTokenizationParameters.newBuilder() - .setPaymentMethodTokenizationType(PaymentMethodTokenizationType.NETWORK_TOKEN) - .addParameter("publicKey", androidPayPublicKey) - .build(); - } else { - parameters = PaymentMethodTokenizationParameters.newBuilder() - .setPaymentMethodTokenizationType(PaymentMethodTokenizationType.PAYMENT_GATEWAY) - .addParameter("gateway", "stripe") - .addParameter("stripe:publishableKey", stripeApiKey) - .addParameter("stripe:version", StripeApiHandler.VERSION) - .build(); - } - - MaskedWalletRequest maskedWalletRequest = MaskedWalletRequest.newBuilder() - .setPaymentMethodTokenizationParameters(parameters) - .setEstimatedTotalPrice(totalPriceDecimal) - .setCurrencyCode(paymentForm.invoice.currency) - .build(); - - WalletFragmentInitParams initParams = WalletFragmentInitParams.newBuilder() - .setMaskedWalletRequest(maskedWalletRequest) - .setMaskedWalletRequestCode(LOAD_MASKED_WALLET_REQUEST_CODE) - .build(); - - walletFragment.initialize(initParams); - androidPayContainer.setVisibility(View.VISIBLE); - AnimatorSet animatorSet = new AnimatorSet(); - animatorSet.playTogether(ObjectAnimator.ofFloat(androidPayContainer, "alpha", 0.0f, 1.0f)); - animatorSet.setInterpolator(new DecelerateInterpolator()); - animatorSet.setDuration(180); - animatorSet.start(); - } - - @Override - public void onActivityResultFragment(int requestCode, int resultCode, Intent data) { - if (requestCode == LOAD_MASKED_WALLET_REQUEST_CODE) { - if (resultCode == Activity.RESULT_OK) { - showEditDoneProgress(true, true); - setDonePressed(true); - - MaskedWallet maskedWallet = data.getParcelableExtra(WalletConstants.EXTRA_MASKED_WALLET); - - Cart.Builder cardBuilder = Cart.newBuilder() - .setCurrencyCode(paymentForm.invoice.currency) - .setTotalPrice(totalPriceDecimal); - - ArrayList arrayList = new ArrayList<>(paymentForm.invoice.prices); - if (shippingOption != null) { - arrayList.addAll(shippingOption.prices); - } - for (int a = 0; a < arrayList.size(); a++) { - TLRPC.TL_labeledPrice price = arrayList.get(a); - String amount = LocaleController.getInstance().formatCurrencyDecimalString(price.amount, paymentForm.invoice.currency, false); - cardBuilder.addLineItem(LineItem.newBuilder() - .setCurrencyCode(paymentForm.invoice.currency) - .setQuantity("1") - .setDescription(price.label) - .setTotalPrice(amount) - .setUnitPrice(amount).build()); - } - FullWalletRequest fullWalletRequest = FullWalletRequest.newBuilder() - .setCart(cardBuilder.build()) - .setGoogleTransactionId(maskedWallet.getGoogleTransactionId()) - .build(); - Wallet.Payments.loadFullWallet(googleApiClient, fullWalletRequest, LOAD_FULL_WALLET_REQUEST_CODE); - } else { - showEditDoneProgress(true, false); - setDonePressed(false); - } - } else if (requestCode == LOAD_FULL_WALLET_REQUEST_CODE) { - if (resultCode == Activity.RESULT_OK) { - FullWallet fullWallet = data.getParcelableExtra(WalletConstants.EXTRA_FULL_WALLET); - String tokenJSON = fullWallet.getPaymentMethodToken().getToken(); - try { - if (androidPayPublicKey != null) { - androidPayCredentials = new TLRPC.TL_inputPaymentCredentialsAndroidPay(); - androidPayCredentials.payment_token = new TLRPC.TL_dataJSON(); - androidPayCredentials.payment_token.data = tokenJSON; - androidPayCredentials.google_transaction_id = fullWallet.getGoogleTransactionId(); - String[] descriptions = fullWallet.getPaymentDescriptions(); - if (descriptions.length > 0) { - cardName = descriptions[0]; - } else { - cardName = "Android Pay"; - } - } else { - Token token = TokenParser.parseToken(tokenJSON); - paymentJson = String.format(Locale.US, "{\"type\":\"%1$s\", \"id\":\"%2$s\"}", token.getType(), token.getId()); - Card card = token.getCard(); - cardName = card.getType() + " *" + card.getLast4(); - } - goToNextStep(); - showEditDoneProgress(true, false); - setDonePressed(false); - } catch (JSONException ignore) { - showEditDoneProgress(true, false); - setDonePressed(false); - } - } else { - showEditDoneProgress(true, false); - setDonePressed(false); - } - } - } - */ - - private void goToNextStep() { - if (currentStep == 0) { - int nextStep; - if (paymentForm.invoice.flexible) { - nextStep = 1; - } else if (paymentForm.saved_credentials != null) { - if (UserConfig.getInstance(currentAccount).tmpPassword != null) { - if (UserConfig.getInstance(currentAccount).tmpPassword.valid_until < ConnectionsManager.getInstance(currentAccount).getCurrentTime() + 60) { - UserConfig.getInstance(currentAccount).tmpPassword = null; - UserConfig.getInstance(currentAccount).saveConfig(false); - } - } - if (UserConfig.getInstance(currentAccount).tmpPassword != null) { - nextStep = 4; - } else { - nextStep = 3; - } - } else { - nextStep = 2; - } - presentFragment(new PaymentFormActivity(paymentForm, messageObject, nextStep, requestedInfo, null, null, cardName, validateRequest, saveCardInfo, androidPayCredentials), isWebView); - } else if (currentStep == 1) { - int nextStep; - if (paymentForm.saved_credentials != null) { - if (UserConfig.getInstance(currentAccount).tmpPassword != null) { - if (UserConfig.getInstance(currentAccount).tmpPassword.valid_until < ConnectionsManager.getInstance(currentAccount).getCurrentTime() + 60) { - UserConfig.getInstance(currentAccount).tmpPassword = null; - UserConfig.getInstance(currentAccount).saveConfig(false); - } - } - if (UserConfig.getInstance(currentAccount).tmpPassword != null) { - nextStep = 4; - } else { - nextStep = 3; - } - } else { - nextStep = 2; - } - presentFragment(new PaymentFormActivity(paymentForm, messageObject, nextStep, requestedInfo, shippingOption, null, cardName, validateRequest, saveCardInfo, androidPayCredentials), isWebView); - } else if (currentStep == 2) { - if (paymentForm.password_missing && saveCardInfo) { - passwordFragment = new PaymentFormActivity(paymentForm, messageObject, 6, requestedInfo, shippingOption, paymentJson, cardName, validateRequest, saveCardInfo, androidPayCredentials); - passwordFragment.setCurrentPassword(currentPassword); - passwordFragment.setDelegate(new PaymentFormActivityDelegate() { - @Override - public boolean didSelectNewCard(String tokenJson, String card, boolean saveCard, TLRPC.TL_inputPaymentCredentialsAndroidPay androidPay) { - if (delegate != null) { - delegate.didSelectNewCard(tokenJson, card, saveCard, androidPay); - } - if (isWebView) { - removeSelfFromStack(); - } - return delegate != null; - } - - @Override - public void onFragmentDestroyed() { - passwordFragment = null; - } - - @Override - public void currentPasswordUpdated(TLRPC.TL_account_password password) { - currentPassword = password; - } - }); - presentFragment(passwordFragment, isWebView); - } else { - if (delegate != null) { - delegate.didSelectNewCard(paymentJson, cardName, saveCardInfo, androidPayCredentials); - finishFragment(); - } else { - presentFragment(new PaymentFormActivity(paymentForm, messageObject, 4, requestedInfo, shippingOption, paymentJson, cardName, validateRequest, saveCardInfo, androidPayCredentials), isWebView); - } - } - } else if (currentStep == 3) { - int nextStep; - if (passwordOk) { - nextStep = 4; - } else { - nextStep = 2; - } - presentFragment(new PaymentFormActivity(paymentForm, messageObject, nextStep, requestedInfo, shippingOption, paymentJson, cardName, validateRequest, saveCardInfo, androidPayCredentials), !passwordOk); - } else if (currentStep == 4) { - NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.paymentFinished); - finishFragment(); - } else if (currentStep == 6) { - if (!delegate.didSelectNewCard(paymentJson, cardName, saveCardInfo, androidPayCredentials)) { - presentFragment(new PaymentFormActivity(paymentForm, messageObject, 4, requestedInfo, shippingOption, paymentJson, cardName, validateRequest, saveCardInfo, androidPayCredentials), true); - } else { - finishFragment(); - } - } - } - - private void updateSavePaymentField() { - if (bottomCell[0] == null || sectionCell[2] == null) { - return; - } - if ((paymentForm.password_missing || paymentForm.can_save_credentials) && (webView == null || webView != null && !webviewLoading)) { - SpannableStringBuilder text = new SpannableStringBuilder(LocaleController.getString("PaymentCardSavePaymentInformationInfoLine1", R.string.PaymentCardSavePaymentInformationInfoLine1)); - if (paymentForm.password_missing) { - loadPasswordInfo(); - text.append("\n"); - int len = text.length(); - String str2 = LocaleController.getString("PaymentCardSavePaymentInformationInfoLine2", R.string.PaymentCardSavePaymentInformationInfoLine2); - int index1 = str2.indexOf('*'); - int index2 = str2.lastIndexOf('*'); - text.append(str2); - if (index1 != -1 && index2 != -1) { - index1 += len; - index2 += len; - bottomCell[0].getTextView().setMovementMethod(new AndroidUtilities.LinkMovementMethodMy()); - text.replace(index2, index2 + 1, ""); - text.replace(index1, index1 + 1, ""); - text.setSpan(new LinkSpan(), index1, index2 - 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - } - } - checkCell1.setEnabled(true); - bottomCell[0].setText(text); - checkCell1.setVisibility(View.VISIBLE); - bottomCell[0].setVisibility(View.VISIBLE); - sectionCell[2].setBackgroundDrawable(Theme.getThemedDrawable(sectionCell[2].getContext(), R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); - } else { - checkCell1.setVisibility(View.GONE); - bottomCell[0].setVisibility(View.GONE); - sectionCell[2].setBackgroundDrawable(Theme.getThemedDrawable(sectionCell[2].getContext(), R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); - } - } - - @SuppressLint("HardwareIds") - public void fillNumber(String number) { - try { - TelephonyManager tm = (TelephonyManager) ApplicationLoader.applicationContext.getSystemService(Context.TELEPHONY_SERVICE); - boolean allowCall = true; - boolean allowSms = true; - if (number != null || tm.getSimState() != TelephonyManager.SIM_STATE_ABSENT && tm.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE) { - if (Build.VERSION.SDK_INT >= 23) { - allowCall = getParentActivity().checkSelfPermission(Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED; - } - if (number != null || allowCall) { - if (number == null) { - number = PhoneFormat.stripExceptNumbers(tm.getLine1Number()); - } - String textToSet = null; - boolean ok = false; - if (!TextUtils.isEmpty(number)) { - if (number.length() > 4) { - for (int a = 4; a >= 1; a--) { - String sub = number.substring(0, a); - String country = codesMap.get(sub); - if (country != null) { - ok = true; - textToSet = number.substring(a); - inputFields[FIELD_PHONECODE].setText(sub); - break; - } - } - if (!ok) { - textToSet = number.substring(1); - inputFields[FIELD_PHONECODE].setText(number.substring(0, 1)); - } - } - if (textToSet != null) { - inputFields[FIELD_PHONE].setText(textToSet); - inputFields[FIELD_PHONE].setSelection(inputFields[FIELD_PHONE].length()); - } - } - } - } - } catch (Exception e) { - FileLog.e(e); - } - } - - private void sendSavePassword(final boolean clear) { - if (!clear && codeFieldCell.getVisibility() == View.VISIBLE) { - String code = codeFieldCell.getText(); - if (code.length() == 0) { - shakeView(codeFieldCell); - return; - } - showEditDoneProgress(true, true); - TLRPC.TL_account_confirmPasswordEmail req = new TLRPC.TL_account_confirmPasswordEmail(); - req.code = code; - ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { - showEditDoneProgress(true, false); - if (error == null) { - if (getParentActivity() == null) { - return; - } - if (shortPollRunnable != null) { - AndroidUtilities.cancelRunOnUIThread(shortPollRunnable); - shortPollRunnable = null; - } - goToNextStep(); - } else { - if (error.text.startsWith("CODE_INVALID")) { - shakeView(codeFieldCell); - codeFieldCell.setText("", false); - } else if (error.text.startsWith("FLOOD_WAIT")) { - int time = Utilities.parseInt(error.text); - String timeString; - if (time < 60) { - timeString = LocaleController.formatPluralString("Seconds", time); - } else { - timeString = LocaleController.formatPluralString("Minutes", time / 60); - } - showAlertWithText(LocaleController.getString("AppName", R.string.AppName), LocaleController.formatString("FloodWaitTime", R.string.FloodWaitTime, timeString)); - } else { - showAlertWithText(LocaleController.getString("AppName", R.string.AppName), error.text); - } - } - }), ConnectionsManager.RequestFlagFailOnServerErrors | ConnectionsManager.RequestFlagWithoutLogin); - } else { - final TLRPC.TL_account_updatePasswordSettings req = new TLRPC.TL_account_updatePasswordSettings(); - final String email; - final String firstPassword; - if (clear) { - doneItem.setVisibility(View.VISIBLE); - email = null; - firstPassword = null; - req.new_settings = new TLRPC.TL_account_passwordInputSettings(); - req.new_settings.flags = 2; - req.new_settings.email = ""; - req.password = new TLRPC.TL_inputCheckPasswordEmpty(); - } else { - firstPassword = inputFields[FIELD_ENTERPASSWORD].getText().toString(); - if (TextUtils.isEmpty(firstPassword)) { - shakeField(FIELD_ENTERPASSWORD); - return; - } - String secondPassword = inputFields[FIELD_REENTERPASSWORD].getText().toString(); - if (!firstPassword.equals(secondPassword)) { - try { - Toast.makeText(getParentActivity(), LocaleController.getString("PasswordDoNotMatch", R.string.PasswordDoNotMatch), Toast.LENGTH_SHORT).show(); - } catch (Exception e) { - FileLog.e(e); - } - shakeField(FIELD_REENTERPASSWORD); - return; - } - email = inputFields[FIELD_ENTERPASSWORDEMAIL].getText().toString(); - if (email.length() < 3) { - shakeField(FIELD_ENTERPASSWORDEMAIL); - return; - } - int dot = email.lastIndexOf('.'); - int dog = email.lastIndexOf('@'); - if (dog < 0 || dot < dog) { - shakeField(FIELD_ENTERPASSWORDEMAIL); - return; - } - - req.password = new TLRPC.TL_inputCheckPasswordEmpty(); - req.new_settings = new TLRPC.TL_account_passwordInputSettings(); - req.new_settings.flags |= 1; - req.new_settings.hint = ""; - req.new_settings.new_algo = currentPassword.new_algo; - - if (email.length() > 0) { - req.new_settings.flags |= 2; - req.new_settings.email = email.trim(); - } - } - showEditDoneProgress(true, true); - Utilities.globalQueue.postRunnable(() -> { - RequestDelegate requestDelegate = (response, error) -> AndroidUtilities.runOnUIThread(() -> { - if (error != null && "SRP_ID_INVALID".equals(error.text)) { - TLRPC.TL_account_getPassword getPasswordReq = new TLRPC.TL_account_getPassword(); - ConnectionsManager.getInstance(currentAccount).sendRequest(getPasswordReq, (response2, error2) -> AndroidUtilities.runOnUIThread(() -> { - if (error2 == null) { - currentPassword = (TLRPC.TL_account_password) response2; - TwoStepVerificationActivity.initPasswordNewAlgo(currentPassword); - sendSavePassword(clear); - } - }), ConnectionsManager.RequestFlagWithoutLogin); - return; - } - showEditDoneProgress(true, false); - if (clear) { - currentPassword.has_password = false; - currentPassword.current_algo = null; - delegate.currentPasswordUpdated(currentPassword); - finishFragment(); - } else { - if (error == null && response instanceof TLRPC.TL_boolTrue) { - if (getParentActivity() == null) { - return; - } - goToNextStep(); - } else if (error != null) { - if (error.text.equals("EMAIL_UNCONFIRMED") || error.text.startsWith("EMAIL_UNCONFIRMED_")) { - emailCodeLength = Utilities.parseInt(error.text); - AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); - builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), (dialogInterface, i) -> { - waitingForEmail = true; - currentPassword.email_unconfirmed_pattern = email; - updatePasswordFields(); - }); - builder.setMessage(LocaleController.getString("YourEmailAlmostThereText", R.string.YourEmailAlmostThereText)); - builder.setTitle(LocaleController.getString("YourEmailAlmostThere", R.string.YourEmailAlmostThere)); - Dialog dialog = showDialog(builder.create()); - if (dialog != null) { - dialog.setCanceledOnTouchOutside(false); - dialog.setCancelable(false); - } - } else { - if (error.text.equals("EMAIL_INVALID")) { - showAlertWithText(LocaleController.getString("AppName", R.string.AppName), LocaleController.getString("PasswordEmailInvalid", R.string.PasswordEmailInvalid)); - } else if (error.text.startsWith("FLOOD_WAIT")) { - int time = Utilities.parseInt(error.text); - String timeString; - if (time < 60) { - timeString = LocaleController.formatPluralString("Seconds", time); - } else { - timeString = LocaleController.formatPluralString("Minutes", time / 60); - } - showAlertWithText(LocaleController.getString("AppName", R.string.AppName), LocaleController.formatString("FloodWaitTime", R.string.FloodWaitTime, timeString)); - } else { - showAlertWithText(LocaleController.getString("AppName", R.string.AppName), error.text); - } - } - } - } - }); - - if (!clear) { - byte[] newPasswordBytes = AndroidUtilities.getStringBytes(firstPassword); - if (currentPassword.new_algo instanceof TLRPC.TL_passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow) { - TLRPC.TL_passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow algo = (TLRPC.TL_passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow) currentPassword.new_algo; - req.new_settings.new_password_hash = SRPHelper.getVBytes(newPasswordBytes, algo); - if (req.new_settings.new_password_hash == null) { - TLRPC.TL_error error = new TLRPC.TL_error(); - error.text = "ALGO_INVALID"; - requestDelegate.run(null, error); - } - ConnectionsManager.getInstance(currentAccount).sendRequest(req, requestDelegate, ConnectionsManager.RequestFlagFailOnServerErrors | ConnectionsManager.RequestFlagWithoutLogin); - } else { - TLRPC.TL_error error = new TLRPC.TL_error(); - error.text = "PASSWORD_HASH_INVALID"; - requestDelegate.run(null, error); - } - } else { - ConnectionsManager.getInstance(currentAccount).sendRequest(req, requestDelegate, ConnectionsManager.RequestFlagFailOnServerErrors | ConnectionsManager.RequestFlagWithoutLogin); - } - }); - } - } - - private boolean sendCardData() { - Integer month; - Integer year; - String date = inputFields[FIELD_EXPIRE_DATE].getText().toString(); - String[] args = date.split("/"); - if (args.length == 2) { - month = Utilities.parseInt(args[0]); - year = Utilities.parseInt(args[1]); - } else { - month = null; - year = null; - } - Card card = new Card( - inputFields[FIELD_CARD].getText().toString(), - month, - year, - inputFields[FIELD_CVV].getText().toString(), - inputFields[FIELD_CARDNAME].getText().toString(), - null, null, null, null, - inputFields[FIELD_CARD_POSTCODE].getText().toString(), - inputFields[FIELD_CARD_COUNTRY].getText().toString(), - null); - cardName = card.getType() + " *" + card.getLast4(); - if (!card.validateNumber()) { - shakeField(FIELD_CARD); - return false; - } else if (!card.validateExpMonth() || !card.validateExpYear() || !card.validateExpiryDate()) { - shakeField(FIELD_EXPIRE_DATE); - return false; - } else if (need_card_name && inputFields[FIELD_CARDNAME].length() == 0) { - shakeField(FIELD_CARDNAME); - return false; - } else if (!card.validateCVC()) { - shakeField(FIELD_CVV); - return false; - } else if (need_card_country && inputFields[FIELD_CARD_COUNTRY].length() == 0) { - shakeField(FIELD_CARD_COUNTRY); - return false; - } else if (need_card_postcode && inputFields[FIELD_CARD_POSTCODE].length() == 0) { - shakeField(FIELD_CARD_POSTCODE); - return false; - } - showEditDoneProgress(true, true); - try { - Stripe stripe = new Stripe(stripeApiKey); - stripe.createToken(card, new TokenCallback() { - public void onSuccess(Token token) { - if (canceled) { - return; - } - paymentJson = String.format(Locale.US, "{\"type\":\"%1$s\", \"id\":\"%2$s\"}", token.getType(), token.getId()); - AndroidUtilities.runOnUIThread(() -> { - goToNextStep(); - showEditDoneProgress(true, false); - setDonePressed(false); - }); - } - - public void onError(Exception error) { - if (canceled) { - return; - } - showEditDoneProgress(true, false); - setDonePressed(false); - if (error instanceof APIConnectionException || error instanceof APIException) { - AlertsCreator.showSimpleToast(PaymentFormActivity.this, LocaleController.getString("PaymentConnectionFailed", R.string.PaymentConnectionFailed)); - } else { - AlertsCreator.showSimpleToast(PaymentFormActivity.this, error.getMessage()); - } - } - } - ); - } catch (Exception e) { - FileLog.e(e); - } - return true; - } - - private void sendForm() { - if (canceled) { - return; - } - showEditDoneProgress(true, true); - validateRequest = new TLRPC.TL_payments_validateRequestedInfo(); - validateRequest.save = saveShippingInfo; - validateRequest.msg_id = messageObject.getId(); - validateRequest.info = new TLRPC.TL_paymentRequestedInfo(); - if (paymentForm.invoice.name_requested) { - validateRequest.info.name = inputFields[FIELD_NAME].getText().toString(); - validateRequest.info.flags |= 1; - } - if (paymentForm.invoice.phone_requested) { - validateRequest.info.phone = "+" + inputFields[FIELD_PHONECODE].getText().toString() + inputFields[FIELD_PHONE].getText().toString(); - validateRequest.info.flags |= 2; - } - if (paymentForm.invoice.email_requested) { - validateRequest.info.email = inputFields[FIELD_EMAIL].getText().toString().trim(); - validateRequest.info.flags |= 4; - } - if (paymentForm.invoice.shipping_address_requested) { - validateRequest.info.shipping_address = new TLRPC.TL_postAddress(); - validateRequest.info.shipping_address.street_line1 = inputFields[FIELD_STREET1].getText().toString(); - validateRequest.info.shipping_address.street_line2 = inputFields[FIELD_STREET2].getText().toString(); - validateRequest.info.shipping_address.city = inputFields[FIELD_CITY].getText().toString(); - validateRequest.info.shipping_address.state = inputFields[FIELD_STATE].getText().toString(); - validateRequest.info.shipping_address.country_iso2 = countryName != null ? countryName : ""; - validateRequest.info.shipping_address.post_code = inputFields[FIELD_POSTCODE].getText().toString(); - validateRequest.info.flags |= 8; - } - final TLObject req = validateRequest; - ConnectionsManager.getInstance(currentAccount).sendRequest(validateRequest, (response, error) -> { - if (response instanceof TLRPC.TL_payments_validatedRequestedInfo) { - AndroidUtilities.runOnUIThread(() -> { - requestedInfo = (TLRPC.TL_payments_validatedRequestedInfo) response; - if (paymentForm.saved_info != null && !saveShippingInfo) { - TLRPC.TL_payments_clearSavedInfo req1 = new TLRPC.TL_payments_clearSavedInfo(); - req1.info = true; - ConnectionsManager.getInstance(currentAccount).sendRequest(req1, (response1, error1) -> { - - }); - } - goToNextStep(); - setDonePressed(false); - showEditDoneProgress(true, false); - }); - } else { - AndroidUtilities.runOnUIThread(() -> { - setDonePressed(false); - showEditDoneProgress(true, false); - if (error != null) { - switch (error.text) { - case "REQ_INFO_NAME_INVALID": - shakeField(FIELD_NAME); - break; - case "REQ_INFO_PHONE_INVALID": - shakeField(FIELD_PHONE); - break; - case "REQ_INFO_EMAIL_INVALID": - shakeField(FIELD_EMAIL); - break; - case "ADDRESS_COUNTRY_INVALID": - shakeField(FIELD_COUNTRY); - break; - case "ADDRESS_CITY_INVALID": - shakeField(FIELD_CITY); - break; - case "ADDRESS_POSTCODE_INVALID": - shakeField(FIELD_POSTCODE); - break; - case "ADDRESS_STATE_INVALID": - shakeField(FIELD_STATE); - break; - case "ADDRESS_STREET_LINE1_INVALID": - shakeField(FIELD_STREET1); - break; - case "ADDRESS_STREET_LINE2_INVALID": - shakeField(FIELD_STREET2); - break; - default: - AlertsCreator.processError(currentAccount, error, PaymentFormActivity.this, req); - break; - } - } - }); - } - }, ConnectionsManager.RequestFlagFailOnServerErrors); - } - - private TLRPC.TL_paymentRequestedInfo getRequestInfo() { - TLRPC.TL_paymentRequestedInfo info = new TLRPC.TL_paymentRequestedInfo(); - if (paymentForm.invoice.name_requested) { - info.name = inputFields[FIELD_NAME].getText().toString(); - info.flags |= 1; - } - if (paymentForm.invoice.phone_requested) { - info.phone = "+" + inputFields[FIELD_PHONECODE].getText().toString() + inputFields[FIELD_PHONE].getText().toString(); - info.flags |= 2; - } - if (paymentForm.invoice.email_requested) { - info.email = inputFields[FIELD_EMAIL].getText().toString().trim(); - info.flags |= 4; - } - if (paymentForm.invoice.shipping_address_requested) { - info.shipping_address = new TLRPC.TL_postAddress(); - info.shipping_address.street_line1 = inputFields[FIELD_STREET1].getText().toString(); - info.shipping_address.street_line2 = inputFields[FIELD_STREET2].getText().toString(); - info.shipping_address.city = inputFields[FIELD_CITY].getText().toString(); - info.shipping_address.state = inputFields[FIELD_STATE].getText().toString(); - info.shipping_address.country_iso2 = countryName != null ? countryName : ""; - info.shipping_address.post_code = inputFields[FIELD_POSTCODE].getText().toString(); - info.flags |= 8; - } - return info; - } - - private void sendData() { - if (canceled) { - return; - } - showEditDoneProgress(false, true); - final TLRPC.TL_payments_sendPaymentForm req = new TLRPC.TL_payments_sendPaymentForm(); - req.msg_id = messageObject.getId(); - if (UserConfig.getInstance(currentAccount).tmpPassword != null && paymentForm.saved_credentials != null) { - req.credentials = new TLRPC.TL_inputPaymentCredentialsSaved(); - req.credentials.id = paymentForm.saved_credentials.id; - req.credentials.tmp_password = UserConfig.getInstance(currentAccount).tmpPassword.tmp_password; - } else if (androidPayCredentials != null) { - req.credentials = androidPayCredentials; - } else { - req.credentials = new TLRPC.TL_inputPaymentCredentials(); - req.credentials.save = saveCardInfo; - req.credentials.data = new TLRPC.TL_dataJSON(); - req.credentials.data.data = paymentJson; - } - if (requestedInfo != null && requestedInfo.id != null) { - req.requested_info_id = requestedInfo.id; - req.flags |= 1; - } - if (shippingOption != null) { - req.shipping_option_id = shippingOption.id; - req.flags |= 2; - } - ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> { - if (response != null) { - if (response instanceof TLRPC.TL_payments_paymentResult) { - MessagesController.getInstance(currentAccount).processUpdates(((TLRPC.TL_payments_paymentResult) response).updates, false); - AndroidUtilities.runOnUIThread(this::goToNextStep); - } else if (response instanceof TLRPC.TL_payments_paymentVerificationNeeded) { - AndroidUtilities.runOnUIThread(() -> { - NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.paymentFinished); - setDonePressed(false); - webviewLoading = true; - showEditDoneProgress(true, true); - if (progressView != null) { - progressView.setVisibility(View.VISIBLE); - } - if (doneItem != null) { - doneItem.setEnabled(false); - doneItem.getContentView().setVisibility(View.INVISIBLE); - } - if (webView != null) { - webView.setVisibility(View.VISIBLE); - webView.loadUrl(webViewUrl = ((TLRPC.TL_payments_paymentVerificationNeeded) response).url); - } - }); - } - } else { - AndroidUtilities.runOnUIThread(() -> { - AlertsCreator.processError(currentAccount, error, PaymentFormActivity.this, req); - setDonePressed(false); - showEditDoneProgress(false, false); - }); - } - }, ConnectionsManager.RequestFlagFailOnServerErrors); - } - - private void shakeField(int field) { - shakeView(inputFields[field]); - } - - private void shakeView(View view) { - Vibrator v = (Vibrator) getParentActivity().getSystemService(Context.VIBRATOR_SERVICE); - if (v != null) { - v.vibrate(200); - } - AndroidUtilities.shakeView(view, 2, 0); - } - - private void setDonePressed(boolean value) { - donePressed = value; - swipeBackEnabled = !value; - if (actionBar != null) { - actionBar.getBackButton().setEnabled(!donePressed); - } - if (detailSettingsCell[0] != null) { - detailSettingsCell[0].setEnabled(!donePressed); - } - } - - @Override - public boolean isSwipeBackEnabled(MotionEvent event) { - return swipeBackEnabled; - } - - private void checkPassword() { - if (UserConfig.getInstance(currentAccount).tmpPassword != null) { - if (UserConfig.getInstance(currentAccount).tmpPassword.valid_until < ConnectionsManager.getInstance(currentAccount).getCurrentTime() + 60) { - UserConfig.getInstance(currentAccount).tmpPassword = null; - UserConfig.getInstance(currentAccount).saveConfig(false); - } - } - if (UserConfig.getInstance(currentAccount).tmpPassword != null) { - sendData(); - return; - } - if (inputFields[FIELD_SAVEDPASSWORD].length() == 0) { - Vibrator v = (Vibrator) ApplicationLoader.applicationContext.getSystemService(Context.VIBRATOR_SERVICE); - if (v != null) { - v.vibrate(200); - } - AndroidUtilities.shakeView(inputFields[FIELD_SAVEDPASSWORD], 2, 0); - return; - } - final String password = inputFields[FIELD_SAVEDPASSWORD].getText().toString(); - showEditDoneProgress(true, true); - setDonePressed(true); - final TLRPC.TL_account_getPassword req = new TLRPC.TL_account_getPassword(); - ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { - if (error == null) { - TLRPC.TL_account_password currentPassword = (TLRPC.TL_account_password) response; - if (!TwoStepVerificationActivity.canHandleCurrentPassword(currentPassword, false)) { - AlertsCreator.showUpdateAppAlert(getParentActivity(), LocaleController.getString("UpdateAppAlert", R.string.UpdateAppAlert), true); - return; - } - if (!currentPassword.has_password) { - passwordOk = false; - goToNextStep(); - } else { - byte[] passwordBytes = AndroidUtilities.getStringBytes(password); - - Utilities.globalQueue.postRunnable(() -> { - final byte[] x_bytes; - if (currentPassword.current_algo instanceof TLRPC.TL_passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow) { - TLRPC.TL_passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow algo = (TLRPC.TL_passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow) currentPassword.current_algo; - x_bytes = SRPHelper.getX(passwordBytes, algo); - } else { - x_bytes = null; - } - - final TLRPC.TL_account_getTmpPassword req1 = new TLRPC.TL_account_getTmpPassword(); - req1.period = 60 * 30; - - RequestDelegate requestDelegate = (response1, error1) -> AndroidUtilities.runOnUIThread(() -> { - showEditDoneProgress(true, false); - setDonePressed(false); - if (response1 != null) { - passwordOk = true; - UserConfig.getInstance(currentAccount).tmpPassword = (TLRPC.TL_account_tmpPassword) response1; - UserConfig.getInstance(currentAccount).saveConfig(false); - goToNextStep(); - } else { - if (error1.text.equals("PASSWORD_HASH_INVALID")) { - Vibrator v = (Vibrator) ApplicationLoader.applicationContext.getSystemService(Context.VIBRATOR_SERVICE); - if (v != null) { - v.vibrate(200); - } - AndroidUtilities.shakeView(inputFields[FIELD_SAVEDPASSWORD], 2, 0); - inputFields[FIELD_SAVEDPASSWORD].setText(""); - } else { - AlertsCreator.processError(currentAccount, error1, PaymentFormActivity.this, req1); - } - } - }); - - if (currentPassword.current_algo instanceof TLRPC.TL_passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow) { - TLRPC.TL_passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow algo = (TLRPC.TL_passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow) currentPassword.current_algo; - req1.password = SRPHelper.startCheck(x_bytes, currentPassword.srp_id, currentPassword.srp_B, algo); - if (req1.password == null) { - TLRPC.TL_error error2 = new TLRPC.TL_error(); - error2.text = "ALGO_INVALID"; - requestDelegate.run(null, error2); - return; - } - ConnectionsManager.getInstance(currentAccount).sendRequest(req1, requestDelegate, ConnectionsManager.RequestFlagFailOnServerErrors | ConnectionsManager.RequestFlagWithoutLogin); - } else { - TLRPC.TL_error error2 = new TLRPC.TL_error(); - error2.text = "PASSWORD_HASH_INVALID"; - requestDelegate.run(null, error2); - } - }); - } - } else { - AlertsCreator.processError(currentAccount, error, PaymentFormActivity.this, req); - showEditDoneProgress(true, false); - setDonePressed(false); - } - }), ConnectionsManager.RequestFlagFailOnServerErrors); - } - - private void showEditDoneProgress(final boolean animateDoneItem, final boolean show) { - if (doneItemAnimation != null) { - doneItemAnimation.cancel(); - } - if (animateDoneItem && doneItem != null) { - doneItemAnimation = new AnimatorSet(); - if (show) { - progressView.setVisibility(View.VISIBLE); - doneItem.setEnabled(false); - doneItemAnimation.playTogether( - ObjectAnimator.ofFloat(doneItem.getContentView(), "scaleX", 0.1f), - ObjectAnimator.ofFloat(doneItem.getContentView(), "scaleY", 0.1f), - ObjectAnimator.ofFloat(doneItem.getContentView(), "alpha", 0.0f), - ObjectAnimator.ofFloat(progressView, "scaleX", 1.0f), - ObjectAnimator.ofFloat(progressView, "scaleY", 1.0f), - ObjectAnimator.ofFloat(progressView, "alpha", 1.0f)); - } else { - if (webView != null) { - doneItemAnimation.playTogether( - ObjectAnimator.ofFloat(progressView, "scaleX", 0.1f), - ObjectAnimator.ofFloat(progressView, "scaleY", 0.1f), - ObjectAnimator.ofFloat(progressView, "alpha", 0.0f)); - } else { - doneItem.getContentView().setVisibility(View.VISIBLE); - doneItem.setEnabled(true); - doneItemAnimation.playTogether( - ObjectAnimator.ofFloat(progressView, "scaleX", 0.1f), - ObjectAnimator.ofFloat(progressView, "scaleY", 0.1f), - ObjectAnimator.ofFloat(progressView, "alpha", 0.0f), - ObjectAnimator.ofFloat(doneItem.getContentView(), "scaleX", 1.0f), - ObjectAnimator.ofFloat(doneItem.getContentView(), "scaleY", 1.0f), - ObjectAnimator.ofFloat(doneItem.getContentView(), "alpha", 1.0f)); - } - - } - doneItemAnimation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (doneItemAnimation != null && doneItemAnimation.equals(animation)) { - if (!show) { - progressView.setVisibility(View.INVISIBLE); - } else { - doneItem.getContentView().setVisibility(View.INVISIBLE); - } - } - } - - @Override - public void onAnimationCancel(Animator animation) { - if (doneItemAnimation != null && doneItemAnimation.equals(animation)) { - doneItemAnimation = null; - } - } - }); - doneItemAnimation.setDuration(150); - doneItemAnimation.start(); - } else if (payTextView != null) { - doneItemAnimation = new AnimatorSet(); - if (show) { - progressViewButton.setVisibility(View.VISIBLE); - bottomLayout.setEnabled(false); - doneItemAnimation.playTogether( - ObjectAnimator.ofFloat(payTextView, "scaleX", 0.1f), - ObjectAnimator.ofFloat(payTextView, "scaleY", 0.1f), - ObjectAnimator.ofFloat(payTextView, "alpha", 0.0f), - ObjectAnimator.ofFloat(progressViewButton, "scaleX", 1.0f), - ObjectAnimator.ofFloat(progressViewButton, "scaleY", 1.0f), - ObjectAnimator.ofFloat(progressViewButton, "alpha", 1.0f)); - } else { - payTextView.setVisibility(View.VISIBLE); - bottomLayout.setEnabled(true); - doneItemAnimation.playTogether( - ObjectAnimator.ofFloat(progressViewButton, "scaleX", 0.1f), - ObjectAnimator.ofFloat(progressViewButton, "scaleY", 0.1f), - ObjectAnimator.ofFloat(progressViewButton, "alpha", 0.0f), - ObjectAnimator.ofFloat(payTextView, "scaleX", 1.0f), - ObjectAnimator.ofFloat(payTextView, "scaleY", 1.0f), - ObjectAnimator.ofFloat(payTextView, "alpha", 1.0f)); - - } - doneItemAnimation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (doneItemAnimation != null && doneItemAnimation.equals(animation)) { - if (!show) { - progressViewButton.setVisibility(View.INVISIBLE); - } else { - payTextView.setVisibility(View.INVISIBLE); - } - } - } - - @Override - public void onAnimationCancel(Animator animation) { - if (doneItemAnimation != null && doneItemAnimation.equals(animation)) { - doneItemAnimation = null; - } - } - }); - doneItemAnimation.setDuration(150); - doneItemAnimation.start(); - } - } - - @Override - public boolean onBackPressed() { - if (shouldNavigateBack) { - webView.loadUrl(webViewUrl); - shouldNavigateBack = false; - return false; - } - return !donePressed; - } - - @Override - public ThemeDescription[] getThemeDescriptions() { - ArrayList arrayList = new ArrayList<>(); - arrayList.add(new ThemeDescription(fragmentView, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundGray)); - arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_actionBarDefault)); - arrayList.add(new ThemeDescription(scrollView, ThemeDescription.FLAG_LISTGLOWCOLOR, null, null, null, null, Theme.key_actionBarDefault)); - arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_ITEMSCOLOR, null, null, null, null, Theme.key_actionBarDefaultIcon)); - arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_TITLECOLOR, null, null, null, null, Theme.key_actionBarDefaultTitle)); - arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SELECTORCOLOR, null, null, null, null, Theme.key_actionBarDefaultSelector)); - arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SEARCH, null, null, null, null, Theme.key_actionBarDefaultSearch)); - arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SEARCHPLACEHOLDER, null, null, null, null, Theme.key_actionBarDefaultSearchPlaceholder)); - arrayList.add(new ThemeDescription(linearLayout2, 0, new Class[]{View.class}, Theme.dividerPaint, null, null, Theme.key_divider)); - arrayList.add(new ThemeDescription(progressView, 0, null, null, null, null, Theme.key_contextProgressInner2)); - arrayList.add(new ThemeDescription(progressView, 0, null, null, null, null, Theme.key_contextProgressOuter2)); - arrayList.add(new ThemeDescription(progressViewButton, 0, null, null, null, null, Theme.key_contextProgressInner2)); - arrayList.add(new ThemeDescription(progressViewButton, 0, null, null, null, null, Theme.key_contextProgressOuter2)); - - if (inputFields != null) { - for (int a = 0; a < inputFields.length; a++) { - arrayList.add(new ThemeDescription((View) inputFields[a].getParent(), ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundWhite)); - arrayList.add(new ThemeDescription(inputFields[a], ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); - arrayList.add(new ThemeDescription(inputFields[a], ThemeDescription.FLAG_HINTTEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteHintText)); - } - } else { - arrayList.add(new ThemeDescription(null, ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); - arrayList.add(new ThemeDescription(null, ThemeDescription.FLAG_HINTTEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteHintText)); - } - if (radioCells != null) { - for (int a = 0; a < radioCells.length; a++) { - arrayList.add(new ThemeDescription(radioCells[a], ThemeDescription.FLAG_SELECTORWHITE, null, null, null, null, Theme.key_windowBackgroundWhite)); - arrayList.add(new ThemeDescription(radioCells[a], ThemeDescription.FLAG_SELECTORWHITE, null, null, null, null, Theme.key_listSelector)); - arrayList.add(new ThemeDescription(radioCells[a], 0, new Class[]{RadioCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); - arrayList.add(new ThemeDescription(radioCells[a], ThemeDescription.FLAG_CHECKBOX, new Class[]{RadioCell.class}, new String[]{"radioButton"}, null, null, null, Theme.key_radioBackground)); - arrayList.add(new ThemeDescription(radioCells[a], ThemeDescription.FLAG_CHECKBOXCHECK, new Class[]{RadioCell.class}, new String[]{"radioButton"}, null, null, null, Theme.key_radioBackgroundChecked)); - } - } else { - arrayList.add(new ThemeDescription(null, 0, new Class[]{RadioCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); - arrayList.add(new ThemeDescription(null, ThemeDescription.FLAG_CHECKBOX, new Class[]{RadioCell.class}, new String[]{"radioButton"}, null, null, null, Theme.key_radioBackground)); - arrayList.add(new ThemeDescription(null, ThemeDescription.FLAG_CHECKBOXCHECK, new Class[]{RadioCell.class}, new String[]{"radioButton"}, null, null, null, Theme.key_radioBackgroundChecked)); - } - for (int a = 0; a < headerCell.length; a++) { - arrayList.add(new ThemeDescription(headerCell[a], ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundWhite)); - arrayList.add(new ThemeDescription(headerCell[a], 0, new Class[]{HeaderCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueHeader)); - } - for (int a = 0; a < sectionCell.length; a++) { - arrayList.add(new ThemeDescription(sectionCell[a], ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{ShadowSectionCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow)); - } - for (int a = 0; a < bottomCell.length; a++) { - arrayList.add(new ThemeDescription(bottomCell[a], ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{TextInfoPrivacyCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow)); - arrayList.add(new ThemeDescription(bottomCell[a], 0, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText4)); - arrayList.add(new ThemeDescription(bottomCell[a], ThemeDescription.FLAG_LINKCOLOR, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteLinkText)); - } - for (int a = 0; a < dividers.size(); a++) { - arrayList.add(new ThemeDescription(dividers.get(a), ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundWhite)); - } - - arrayList.add(new ThemeDescription(codeFieldCell, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundWhite)); - arrayList.add(new ThemeDescription(codeFieldCell, ThemeDescription.FLAG_TEXTCOLOR, new Class[]{EditTextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); - arrayList.add(new ThemeDescription(codeFieldCell, ThemeDescription.FLAG_HINTTEXTCOLOR, new Class[]{EditTextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteHintText)); - - arrayList.add(new ThemeDescription(textView, ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); - - arrayList.add(new ThemeDescription(checkCell1, 0, new Class[]{TextCheckCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); - arrayList.add(new ThemeDescription(checkCell1, 0, new Class[]{TextCheckCell.class}, new String[]{"checkBox"}, null, null, null, Theme.key_switchTrack)); - arrayList.add(new ThemeDescription(checkCell1, 0, new Class[]{TextCheckCell.class}, new String[]{"checkBox"}, null, null, null, Theme.key_switchTrackChecked)); - arrayList.add(new ThemeDescription(checkCell1, ThemeDescription.FLAG_SELECTORWHITE, null, null, null, null, Theme.key_windowBackgroundWhite)); - arrayList.add(new ThemeDescription(checkCell1, ThemeDescription.FLAG_SELECTORWHITE, null, null, null, null, Theme.key_listSelector)); - - for (int a = 0; a < settingsCell.length; a++) { - arrayList.add(new ThemeDescription(settingsCell[a], ThemeDescription.FLAG_SELECTORWHITE, null, null, null, null, Theme.key_windowBackgroundWhite)); - arrayList.add(new ThemeDescription(settingsCell[a], ThemeDescription.FLAG_SELECTORWHITE, null, null, null, null, Theme.key_listSelector)); - arrayList.add(new ThemeDescription(settingsCell[a], 0, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); - } - - arrayList.add(new ThemeDescription(payTextView, ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteBlueText6)); - - arrayList.add(new ThemeDescription(linearLayout2, ThemeDescription.FLAG_CELLBACKGROUNDCOLOR, new Class[]{TextPriceCell.class}, null, null, null, Theme.key_windowBackgroundWhite)); - arrayList.add(new ThemeDescription(linearLayout2, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextPriceCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); - arrayList.add(new ThemeDescription(linearLayout2, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextPriceCell.class}, new String[]{"valueTextView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); - arrayList.add(new ThemeDescription(linearLayout2, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextPriceCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText2)); - arrayList.add(new ThemeDescription(linearLayout2, ThemeDescription.FLAG_CHECKTAG, new Class[]{TextPriceCell.class}, new String[]{"valueTextView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText2)); - - arrayList.add(new ThemeDescription(detailSettingsCell[0], ThemeDescription.FLAG_SELECTORWHITE, null, null, null, null, Theme.key_windowBackgroundWhite)); - arrayList.add(new ThemeDescription(detailSettingsCell[0], ThemeDescription.FLAG_SELECTORWHITE, null, null, null, null, Theme.key_listSelector)); - - for (int a = 1; a < detailSettingsCell.length; a++) { - arrayList.add(new ThemeDescription(detailSettingsCell[a], ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundWhite)); - arrayList.add(new ThemeDescription(detailSettingsCell[a], 0, new Class[]{TextDetailSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); - arrayList.add(new ThemeDescription(detailSettingsCell[a], 0, new Class[]{TextDetailSettingsCell.class}, new String[]{"valueTextView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText2)); - } - - arrayList.add(new ThemeDescription(paymentInfoCell, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundWhite)); - arrayList.add(new ThemeDescription(paymentInfoCell, 0, new Class[]{PaymentInfoCell.class}, new String[]{"nameTextView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); - arrayList.add(new ThemeDescription(paymentInfoCell, 0, new Class[]{PaymentInfoCell.class}, new String[]{"detailTextView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); - arrayList.add(new ThemeDescription(paymentInfoCell, 0, new Class[]{PaymentInfoCell.class}, new String[]{"detailExTextView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText2)); - - arrayList.add(new ThemeDescription(bottomLayout, ThemeDescription.FLAG_SELECTORWHITE, null, null, null, null, Theme.key_windowBackgroundWhite)); - arrayList.add(new ThemeDescription(bottomLayout, ThemeDescription.FLAG_SELECTORWHITE, null, null, null, null, Theme.key_listSelector)); - - return arrayList.toArray(new ThemeDescription[0]); - } -} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java index 58f91d5d1..855bb90e8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java @@ -37,12 +37,14 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; +import android.media.Image; import android.media.MediaCodecInfo; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.SystemClock; import android.provider.Settings; + import androidx.annotation.Keep; import androidx.annotation.NonNull; import androidx.core.content.FileProvider; @@ -181,6 +183,9 @@ import java.util.HashMap; import java.util.Locale; import tw.nekomimi.nekogram.NekoConfig; +import tw.nekomimi.nekogram.NekoXConfig; +import tw.nekomimi.nekogram.utils.AlertUtil; +import tw.nekomimi.nekogram.utils.ProxyUtil; @SuppressWarnings("unchecked") public class PhotoViewer implements NotificationCenter.NotificationCenterDelegate, GestureDetector2.OnGestureListener, GestureDetector2.OnDoubleTapListener { @@ -201,7 +206,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private Runnable miniProgressShowRunnable = () -> toggleMiniProgressInternal(true); private Activity parentActivity; - private Context actvityContext; + private Context activityContext; private ActionBar actionBar; private boolean isActionBarVisible = true; @@ -345,7 +350,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private View playButtonAccessibilityOverlay; private int currentPanTranslationY; - + public final static int SELECT_TYPE_AVATAR = 1; public final static int SELECT_TYPE_WALLPAPER = 3; public final static int SELECT_TYPE_QR = 10; @@ -678,9 +683,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private boolean bottomTouchEnabled = true; private ArrayList imagesArrTemp = new ArrayList<>(); - private SparseArray[] imagesByIdsTemp = new SparseArray[] {new SparseArray<>(), new SparseArray<>()}; + private SparseArray[] imagesByIdsTemp = new SparseArray[]{new SparseArray<>(), new SparseArray<>()}; private ArrayList imagesArr = new ArrayList<>(); - private SparseArray[] imagesByIds = new SparseArray[] {new SparseArray<>(), new SparseArray<>()}; + private SparseArray[] imagesByIds = new SparseArray[]{new SparseArray<>(), new SparseArray<>()}; private ArrayList imagesArrLocations = new ArrayList<>(); private ArrayList secureDocuments = new ArrayList<>(); private ArrayList avatarsArr = new ArrayList<>(); @@ -689,6 +694,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat private ImageLocation currentUserAvatarLocation = null; private final static int gallery_menu_save = 1; + private final static int gallery_menu_scan = 8; private final static int gallery_menu_showall = 2; private final static int gallery_menu_send = 3; private final static int gallery_menu_showinchat = 4; @@ -1793,7 +1799,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat String finalPath = (String) args[1]; long finalSize = (Long) args[3]; float progress = (float) args[4]; - photoProgressViews[0].setProgress(progress,true); + photoProgressViews[0].setProgress(progress, true); if (finalSize != 0) { requestingPreview = false; photoProgressViews[0].setProgress(1f, true); @@ -1817,6 +1823,57 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat showAlertDialog(builder); } + private void onScanPressed() { + if (parentActivity == null) { + return; + } + try { + File f = null; + boolean isVideo = false; + + if (currentMessageObject != null) { + isVideo = currentMessageObject.isVideo(); + /*if (currentMessageObject.messageOwner.media instanceof TLRPC.TL_messageMediaWebPage) { + AndroidUtilities.openUrl(parentActivity, currentMessageObject.messageOwner.media.webpage.url); + return; + }*/ + if (!TextUtils.isEmpty(currentMessageObject.messageOwner.attachPath)) { + f = new File(currentMessageObject.messageOwner.attachPath); + if (!f.exists()) { + f = null; + } + } + if (f == null) { + f = FileLoader.getPathToMessage(currentMessageObject.messageOwner); + } + } else if (currentFileLocation != null) { + f = FileLoader.getPathToAttach(currentFileLocation.location, avatarsDialogId != 0 || isEvent); + } + + if (f.exists()) { + + Bitmap bitmap; + + if (isVideo) { + + bitmap = videoTextureView.getBitmap(); + + } else { + + bitmap = ImageLoader.loadBitmap(f.getPath(), null, -1f, -1f, false); + + } + + ProxyUtil.tryReadQR(parentActivity, bitmap); + + } else { + showDownloadAlert(); + } + } catch (Exception ignored) { + AlertUtil.showToast(LocaleController.getString("NoQrFound", R.string.NoQrFound)); + } + } + private void onSharePressed() { if (parentActivity == null || !allowShare) { return; @@ -1903,7 +1960,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat return; } parentActivity = activity; - actvityContext = new ContextThemeWrapper(parentActivity, R.style.Theme_TMessages); + activityContext = new ContextThemeWrapper(parentActivity, R.style.Theme_TMessages); touchSlop = ViewConfiguration.get(parentActivity).getScaledTouchSlop(); if (progressDrawables == null) { @@ -2138,6 +2195,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } else { showDownloadAlert(); } + } else if (id == gallery_menu_scan) { + onScanPressed(); } else if (id == gallery_menu_showall) { if (currentDialogId != 0) { disableShowCheck = true; @@ -2456,21 +2515,24 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat menuItem.addSubItem(gallery_menu_showinchat, R.drawable.msg_message, LocaleController.getString("ShowInChat", R.string.ShowInChat)).setColors(0xfffafafa, 0xfffafafa); menuItem.addSubItem(gallery_menu_share, R.drawable.msg_shareout, LocaleController.getString("ShareFile", R.string.ShareFile)).setColors(0xfffafafa, 0xfffafafa); menuItem.addSubItem(gallery_menu_save, R.drawable.msg_gallery, LocaleController.getString("SaveToGallery", R.string.SaveToGallery)).setColors(0xfffafafa, 0xfffafafa); + + menuItem.addSubItem(gallery_menu_scan, R.drawable.wallet_qr, LocaleController.getString("ScanQRCode", R.string.ScanQRCode)).setColors(0xfffafafa, 0xfffafafa); + menuItem.addSubItem(gallery_menu_delete, R.drawable.msg_delete, LocaleController.getString("Delete", R.string.Delete)).setColors(0xfffafafa, 0xfffafafa); menuItem.addSubItem(gallery_menu_cancel_loading, R.drawable.msg_cancel, LocaleController.getString("StopDownload", R.string.StopDownload)).setColors(0xfffafafa, 0xfffafafa); menuItem.redrawPopup(0xf9222222); sendItem.setContentDescription(LocaleController.getString("Forward", R.string.Forward)); - bottomLayout = new FrameLayout(actvityContext); + bottomLayout = new FrameLayout(activityContext); bottomLayout.setBackgroundColor(0x7f000000); containerView.addView(bottomLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.BOTTOM | Gravity.LEFT)); - pressedDrawable[0] = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, new int[] {0x32000000, 0}); + pressedDrawable[0] = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, new int[]{0x32000000, 0}); pressedDrawable[0].setShape(GradientDrawable.RECTANGLE); - pressedDrawable[1] = new GradientDrawable(GradientDrawable.Orientation.RIGHT_LEFT, new int[] {0x32000000, 0}); + pressedDrawable[1] = new GradientDrawable(GradientDrawable.Orientation.RIGHT_LEFT, new int[]{0x32000000, 0}); pressedDrawable[1].setShape(GradientDrawable.RECTANGLE); - groupedPhotosListView = new GroupedPhotosListView(actvityContext); + groupedPhotosListView = new GroupedPhotosListView(activityContext); containerView.addView(groupedPhotosListView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 62, Gravity.BOTTOM | Gravity.LEFT, 0, 0, 0, 48)); groupedPhotosListView.setDelegate(new GroupedPhotosListView.GroupedPhotosListViewDelegate() { @Override @@ -2532,7 +2594,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat photoProgressViews[a].setBackgroundState(0, false); } - miniProgressView = new RadialProgressView(actvityContext) { + miniProgressView = new RadialProgressView(activityContext) { @Override public void setAlpha(float alpha) { super.setAlpha(alpha); @@ -2630,7 +2692,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat qualityChooseView.setBackgroundColor(0x7f000000); containerView.addView(qualityChooseView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 70, Gravity.LEFT | Gravity.BOTTOM, 0, 0, 0, 48)); - pickerView = new FrameLayout(actvityContext) { + pickerView = new FrameLayout(activityContext) { @Override public boolean dispatchTouchEvent(MotionEvent ev) { return bottomTouchEnabled && super.dispatchTouchEvent(ev); @@ -3142,7 +3204,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat bottomSheet.setBackgroundColor(0xff000000); }); - editorDoneLayout = new PickerBottomLayoutViewer(actvityContext); + editorDoneLayout = new PickerBottomLayoutViewer(activityContext); editorDoneLayout.setBackgroundColor(0x7f000000); editorDoneLayout.updateSelectedCount(0, false); editorDoneLayout.setVisibility(View.GONE); @@ -3156,7 +3218,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat switchToEditMode(0); }); - resetButton = new TextView(actvityContext); + resetButton = new TextView(activityContext); resetButton.setVisibility(View.GONE); resetButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); resetButton.setTextColor(0xffffffff); @@ -3278,7 +3340,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat ignoreDidSetImage = false; }); - captionEditText = new PhotoViewerCaptionEnterView(actvityContext, containerView, windowView) { + captionEditText = new PhotoViewerCaptionEnterView(activityContext, containerView, windowView) { @Override public boolean dispatchTouchEvent(MotionEvent ev) { try { @@ -3345,7 +3407,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } containerView.addView(captionEditText, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM | Gravity.LEFT)); - mentionListView = new RecyclerListView(actvityContext) { + mentionListView = new RecyclerListView(activityContext) { @Override public boolean dispatchTouchEvent(MotionEvent ev) { return !bottomTouchEnabled && super.dispatchTouchEvent(ev); @@ -3362,7 +3424,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } }; mentionListView.setTag(5); - mentionLayoutManager = new LinearLayoutManager(actvityContext) { + mentionLayoutManager = new LinearLayoutManager(activityContext) { @Override public boolean supportsPredictiveItemAnimations() { return false; @@ -3376,7 +3438,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat mentionListView.setOverScrollMode(RecyclerListView.OVER_SCROLL_NEVER); containerView.addView(mentionListView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 110, Gravity.LEFT | Gravity.BOTTOM)); - mentionListView.setAdapter(mentionsAdapter = new MentionsAdapter(actvityContext, true, 0, new MentionsAdapter.MentionsAdapterDelegate() { + mentionListView.setAdapter(mentionsAdapter = new MentionsAdapter(activityContext, true, 0, new MentionsAdapter.MentionsAdapterDelegate() { @Override public void needChangePanelVisibility(boolean show) { if (show) { @@ -3496,9 +3558,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat return false; }); - AccessibilityManager am = (AccessibilityManager) actvityContext.getSystemService(Context.ACCESSIBILITY_SERVICE); + AccessibilityManager am = (AccessibilityManager) activityContext.getSystemService(Context.ACCESSIBILITY_SERVICE); if (am.isEnabled()) { - playButtonAccessibilityOverlay = new View(actvityContext); + playButtonAccessibilityOverlay = new View(activityContext); playButtonAccessibilityOverlay.setContentDescription(LocaleController.getString("AccActionPlay", R.string.AccActionPlay)); playButtonAccessibilityOverlay.setFocusable(true); containerView.addView(playButtonAccessibilityOverlay, LayoutHelper.createFrame(64, 64, Gravity.CENTER)); @@ -3550,7 +3612,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } private TextView createCaptionTextView() { - TextView textView = new TextView(actvityContext) { + TextView textView = new TextView(activityContext) { @Override public boolean onTouchEvent(MotionEvent event) { return bottomTouchEnabled && super.onTouchEvent(event); @@ -3581,12 +3643,12 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat return 0; } - private int getRightInset() { - if (lastInsets != null && Build.VERSION.SDK_INT >= 21) { - return ((WindowInsets) lastInsets).getSystemWindowInsetRight(); - } - return 0; - } + private int getRightInset() { + if (lastInsets != null && Build.VERSION.SDK_INT >= 21) { + return ((WindowInsets) lastInsets).getSystemWindowInsetRight(); + } + return 0; + } private void dismissInternal() { try { @@ -3749,7 +3811,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat x = max; } videoPreviewFrame.setTranslationX(x); - } + } private void showVideoSeekPreviewPosition(boolean show) { if (show && videoPreviewFrame.getTag() != null || !show && videoPreviewFrame.getTag() == null) { @@ -3995,7 +4057,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat Object object = imagesArrLocals.get(currentIndex); if (apply) { CharSequence caption = captionEditText.getFieldCharSequence(); - CharSequence[] result = new CharSequence[] {caption}; + CharSequence[] result = new CharSequence[]{caption}; ArrayList entities = MediaDataController.getInstance(currentAccount).getEntities(result, supportsSendingNewEntities()); if (object instanceof MediaController.PhotoEntry) { @@ -4409,8 +4471,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat aspectRatioFrameLayout.getLocationInWindow(pipPosition); //pipPosition[0] -= getLeftInset(); pipPosition[1] -= containerView.getTranslationY(); - textureImageView.setTranslationX(textureImageView.getTranslationX()+getLeftInset()); - videoTextureView.setTranslationX(videoTextureView.getTranslationX()+getLeftInset()-aspectRatioFrameLayout.getX()); + textureImageView.setTranslationX(textureImageView.getTranslationX() + getLeftInset()); + videoTextureView.setTranslationX(videoTextureView.getTranslationX() + getLeftInset() - aspectRatioFrameLayout.getX()); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playTogether( @@ -4717,7 +4779,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat if (photoCropView != null) { return; } - photoCropView = new PhotoCropView(actvityContext); + photoCropView = new PhotoCropView(activityContext); photoCropView.setVisibility(View.GONE); int index = containerView.indexOfChild(pickerViewSendButton); containerView.addView(photoCropView, index - 1, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT, 0, 0, 0, 48)); @@ -5442,7 +5504,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat TLRPC.BotInlineResult botInlineResult = (TLRPC.BotInlineResult) object; if (botInlineResult.document != null) { return FileLoader.getAttachFileName(botInlineResult.document); - } else if (botInlineResult.photo != null) { + } else if (botInlineResult.photo != null) { TLRPC.PhotoSize sizeFull = FileLoader.getClosestPhotoSizeWithSize(botInlineResult.photo.sizes, AndroidUtilities.getPhotoSize()); return FileLoader.getAttachFileName(sizeFull); } else if (botInlineResult.content instanceof TLRPC.TL_webDocument) { @@ -6056,6 +6118,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat if (currentAnimation != null) { menuItem.hideSubItem(gallery_menu_save); menuItem.hideSubItem(gallery_menu_share); + menuItem.hideSubItem(gallery_menu_scan); if (!newMessageObject.canDeleteMessage(parentChatActivity != null && parentChatActivity.isInScheduleMode(), null)) { menuItem.hideSubItem(gallery_menu_delete); } @@ -6117,10 +6180,12 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat allowShare = false; menuItem.hideSubItem(gallery_menu_save); shareButton.setVisibility(View.GONE); + menuItem.hideSubItem(gallery_menu_scan); menuItem.hideSubItem(gallery_menu_share); } else { allowShare = true; menuItem.showSubItem(gallery_menu_save); + menuItem.showSubItem(gallery_menu_scan); shareButton.setVisibility(videoPlayerControlFrameLayout.getVisibility() != View.VISIBLE ? View.VISIBLE : View.GONE); if (shareButton.getVisibility() == View.VISIBLE) { menuItem.hideSubItem(gallery_menu_share); @@ -6133,6 +6198,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } else if (!secureDocuments.isEmpty()) { allowShare = false; menuItem.hideSubItem(gallery_menu_save); + menuItem.hideSubItem(gallery_menu_scan); nameTextView.setText(""); dateTextView.setText(""); actionBar.setTitle(LocaleController.formatString("Of", R.string.Of, switchingToIndex + 1, secureDocuments.size())); @@ -6155,7 +6221,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat nameTextView.setText(""); } } - long date = (long) avatarsArr.get(switchingToIndex).date * 1000; + long date = (long) avatarsArr.get(switchingToIndex).date * 1000; if (date != 0) { String dateString = LocaleController.formatString("formatDateAtTime", R.string.formatDateAtTime, LocaleController.getInstance().formatterYear.format(new Date(date)), LocaleController.getInstance().formatterDay.format(new Date(date))); dateTextView.setText(dateString); @@ -6171,6 +6237,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat actionBar.setTitle(LocaleController.formatString("Of", R.string.Of, switchingToIndex + 1, imagesArrLocations.size())); } menuItem.showSubItem(gallery_menu_save); + menuItem.showSubItem(gallery_menu_scan); allowShare = true; shareButton.setVisibility(videoPlayerControlFrameLayout.getVisibility() != View.VISIBLE ? View.VISIBLE : View.GONE); if (shareButton.getVisibility() == View.VISIBLE) { @@ -6312,7 +6379,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat updateCaptionTextForCurrentPhoto(object); PorterDuffColorFilter filter = new PorterDuffColorFilter(Theme.getColor(Theme.key_dialogFloatingButton), PorterDuff.Mode.MULTIPLY); - timeItem.setColorFilter(ttl != 0 ? filter : null); + timeItem.setColorFilter(ttl != 0 ? filter : null); paintItem.setColorFilter(isPainted ? filter : null); cropItem.setColorFilter(isCropped ? filter : null); tuneItem.setColorFilter(isFiltered ? filter : null); @@ -6363,9 +6430,11 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat if (sharedMediaType == MediaDataController.MEDIA_FILE) { if (canZoom = newMessageObject.canPreviewDocument()) { menuItem.showSubItem(gallery_menu_save); + menuItem.showSubItem(gallery_menu_scan); setDoubleTapEnabled(true); } else { menuItem.hideSubItem(gallery_menu_save); + menuItem.hideSubItem(gallery_menu_scan); setDoubleTapEnabled(false); } } @@ -7158,9 +7227,9 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat animationStartTime = System.currentTimeMillis(); animateToX = getLeftInset() / 2 - getRightInset() / 2; if (currentEditMode == 2) { - animateToY = AndroidUtilities.dp(92)-AndroidUtilities.dp(24 + 32); + animateToY = AndroidUtilities.dp(92) - AndroidUtilities.dp(24 + 32); } else if (currentEditMode == 3) { - animateToY = AndroidUtilities.dp(44)-AndroidUtilities.dp(24 + 32); + animateToY = AndroidUtilities.dp(44) - AndroidUtilities.dp(24 + 32); } //animateToY = -AndroidUtilities.dp(24 + 32) + (Build.VERSION.SDK_INT >= 21 ? AndroidUtilities.statusBarHeight / 2 : 0); animateToScale = newScale / scaleFinal; @@ -7230,10 +7299,10 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } else { windowLayoutParams.flags = WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; } - if (chatActivity != null && chatActivity.getCurrentEncryptedChat() != null) { + if (chatActivity != null && chatActivity.getCurrentEncryptedChat() != null && !NekoXConfig.disableFlagSecure) { windowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_SECURE; } else { - windowLayoutParams.flags &=~ WindowManager.LayoutParams.FLAG_SECURE; + windowLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_SECURE; } windowLayoutParams.softInputMode = (useSmoothKeyboard ? WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN : WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) | WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; windowView.setFocusable(false); @@ -7362,7 +7431,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat animatingImageViews[i].setLayoutParams(layoutParams); } - windowView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){ + windowView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { if (animatingImageViews.length > 1) { @@ -8793,7 +8862,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } if (uri == null) { if (download) { - if (currentMessageObject != null) { + if (currentMessageObject != null) { if (!FileLoader.getInstance(currentAccount).isLoadingFile(currentFileNames[0])) { FileLoader.getInstance(currentAccount).loadFile(currentMessageObject.getDocument(), currentMessageObject, 1, 0); } else { @@ -9293,8 +9362,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat if (request == 1) { if (resultHeight == originalHeight && resultWidth == originalWidth) { tryStartRequestPreviewOnFinish = false; - photoProgressViews[0].setProgress(0,photoProgressViews[0].backgroundState == 0 || photoProgressViews[0].previousBackgroundState == 0); - photoProgressViews[0].setBackgroundState(3,false); + photoProgressViews[0].setProgress(0, photoProgressViews[0].backgroundState == 0 || photoProgressViews[0].previousBackgroundState == 0); + photoProgressViews[0].setBackgroundState(3, false); if (!wasRequestingPreview) { preparePlayer(currentPlayingVideoFile, false, false); videoPlayer.seekTo((long) (videoTimelineView.getLeftProgress() * videoDuration)); @@ -9341,12 +9410,12 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } requestingPreview = true; - photoProgressViews[0].setProgress(0,photoProgressViews[0].backgroundState == 0 || photoProgressViews[0].previousBackgroundState == 0); - photoProgressViews[0].setBackgroundState(0,false); + photoProgressViews[0].setProgress(0, photoProgressViews[0].backgroundState == 0 || photoProgressViews[0].previousBackgroundState == 0); + photoProgressViews[0].setBackgroundState(0, false); } } else { tryStartRequestPreviewOnFinish = false; - photoProgressViews[0].setBackgroundState(3,false); + photoProgressViews[0].setBackgroundState(3, false); if (request == 2) { preparePlayer(currentPlayingVideoFile, false, false); videoPlayer.seekTo((long) (videoTimelineView.getLeftProgress() * videoDuration)); @@ -9549,7 +9618,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat selectedCompression = preferences.getInt("compress_video2", selectCompression()); updateWidthHeightBitrateForCompression(); - if (selectedCompression > compressionsCount - 1) selectedCompression = compressionsCount - 1; + if (selectedCompression > compressionsCount - 1) + selectedCompression = compressionsCount - 1; setCompressItemEnabled(compressionsCount > 1, true); if (BuildVars.LOGS_ENABLED) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PrivacyUsersActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PrivacyUsersActivity.java index 7969aaf8c..33f7ee3fa 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PrivacyUsersActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PrivacyUsersActivity.java @@ -12,6 +12,7 @@ import android.content.Context; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.text.TextUtils; +import android.util.SparseIntArray; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; @@ -22,6 +23,8 @@ import org.telegram.tgnet.TLRPC; import org.telegram.messenger.MessagesController; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; +import org.telegram.ui.ActionBar.ActionBarMenu; +import org.telegram.ui.ActionBar.ActionBarMenuItem; import org.telegram.ui.ActionBar.AlertDialog; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.ActionBar.ThemeDescription; @@ -64,6 +67,8 @@ public class PrivacyUsersActivity extends BaseFragment implements NotificationCe private PrivacyActivityDelegate delegate; + private int unblock_all = 1; + public interface PrivacyActivityDelegate { void didUpdateUserList(ArrayList ids, boolean added); } @@ -126,10 +131,34 @@ public class PrivacyUsersActivity extends BaseFragment implements NotificationCe public void onItemClick(int id) { if (id == -1) { finishFragment(); + } else if (id == unblock_all) { + AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); + builder.setTitle(LocaleController.getString("UnblockAll", R.string.UnblockAll)); + if (getMessagesController().totalBlockedCount != 0) { + builder.setMessage(LocaleController.getString("UnblockAllWarn", R.string.UnblockAllWarn)); + builder.setPositiveButton(LocaleController.getString("UnblockAll", R.string.UnblockAll), (dialog, which) -> { + new Thread(() -> getMessagesController().unblockAllUsers()).start(); + }); + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + } else { + builder.setMessage(LocaleController.getString("BlockedListEmpty",R.string.BlockedListEmpty)); + builder.setPositiveButton(LocaleController.getString("OK",R.string.OK),null); + } + showDialog(builder.create()); } } }); + if (blockedUsersActivity) { + + ActionBarMenu menu = actionBar.createMenu(); + + ActionBarMenuItem otherItem = menu.addItem(0, R.drawable.ic_ab_other); + otherItem.setContentDescription(LocaleController.getString("AccDescrMoreOptions", R.string.AccDescrMoreOptions)); + otherItem.addSubItem(unblock_all, LocaleController.getString("UnblockAll", R.string.UnblockAll)); + + } + fragmentView = new FrameLayout(context); FrameLayout frameLayout = (FrameLayout) fragmentView; frameLayout.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundGray)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java index 50b6c9080..39a46040a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java @@ -137,7 +137,10 @@ import java.util.Collections; import java.util.HashMap; import java.util.concurrent.CountDownLatch; +import kotlin.collections.ArraysKt; import tw.nekomimi.nekogram.NekoConfig; +import tw.nekomimi.nekogram.NekoXConfig; +import tw.nekomimi.nekogram.utils.ProxyUtil; public class ProfileActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate, DialogsActivity.DialogsActivityDelegate, SharedMediaLayout.SharedMediaPreloaderDelegate { @@ -246,6 +249,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. private final static int leave_group = 7; private final static int invite_to_group = 9; private final static int share = 10; + private final static int qr_code = 11; private final static int edit_channel = 12; private final static int add_shortcut = 14; private final static int call_item = 15; @@ -292,6 +296,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. private int joinRow; private int lastSectionRow; + private final Property HEADER_SHADOW = new AnimationProperties.FloatProperty("headerShadow") { @Override public void setValue(ProfileActivity object, float value) { @@ -506,10 +511,10 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. selectedBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG); selectedBarPaint.setColor(0xffffffff); - topOverlayGradient = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, new int[] {0x42000000, 0}); + topOverlayGradient = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, new int[]{0x42000000, 0}); topOverlayGradient.setShape(GradientDrawable.RECTANGLE); - bottomOverlayGradient = new GradientDrawable(GradientDrawable.Orientation.BOTTOM_TOP, new int[] {0x42000000, 0}); + bottomOverlayGradient = new GradientDrawable(GradientDrawable.Orientation.BOTTOM_TOP, new int[]{0x42000000, 0}); bottomOverlayGradient.setShape(GradientDrawable.RECTANGLE); backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); @@ -1183,7 +1188,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. removeSelfFromStack(); }); presentFragment(fragment); - } else if (id == share) { + } else if (id == share || id == qr_code) { try { String text = null; if (user_id != 0) { @@ -1191,7 +1196,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. if (user == null) { return; } - if (botInfo != null && userInfo != null && !TextUtils.isEmpty(userInfo.about)) { + if (botInfo != null && userInfo != null && !TextUtils.isEmpty(userInfo.about) && id == share) { text = String.format("%s https://" + MessagesController.getInstance(currentAccount).linkPrefix + "/%s", userInfo.about, user.username); } else { text = String.format("https://" + MessagesController.getInstance(currentAccount).linkPrefix + "/%s", user.username); @@ -1201,7 +1206,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. if (chat == null) { return; } - if (chatInfo != null && !TextUtils.isEmpty(chatInfo.about)) { + if (chatInfo != null && !TextUtils.isEmpty(chatInfo.about) && id == share) { text = String.format("%s\nhttps://" + MessagesController.getInstance(currentAccount).linkPrefix + "/%s", chatInfo.about, chat.username); } else { text = String.format("https://" + MessagesController.getInstance(currentAccount).linkPrefix + "/%s", chat.username); @@ -1210,10 +1215,14 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. if (TextUtils.isEmpty(text)) { return; } - Intent intent = new Intent(Intent.ACTION_SEND); - intent.setType("text/plain"); - intent.putExtra(Intent.EXTRA_TEXT, text); - startActivityForResult(Intent.createChooser(intent, LocaleController.getString("BotShare", R.string.BotShare)), 500); + if (id == share) { + Intent intent = new Intent(Intent.ACTION_SEND); + intent.setType("text/plain"); + intent.putExtra(Intent.EXTRA_TEXT, text); + startActivityForResult(Intent.createChooser(intent, LocaleController.getString("BotShare", R.string.BotShare)), 500); + } else { + ProxyUtil.showQrDialog(getParentActivity(), text); + } } catch (Exception e) { FileLog.e(e); } @@ -2381,7 +2390,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. } else if (position == phoneRow) { final TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(user_id); if (user == null || user.phone == null || user.phone.length() == 0 || getParentActivity() == null - || (NekoConfig.hidePhone && user.id == UserConfig.getInstance(currentAccount).getClientUserId())) { + || (NekoConfig.hidePhone && user.id == UserConfig.getInstance(currentAccount).getClientUserId())) { return false; } @@ -3238,7 +3247,9 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. FileLog.e(e); } } - sharedMediaLayout.setCommonGroupsCount(userInfo.common_chats_count); + if (sharedMediaLayout != null) { + sharedMediaLayout.setCommonGroupsCount(userInfo.common_chats_count); + } updateSelectedMediaTabText(); } } else if (id == NotificationCenter.didReceiveNewMessages) { @@ -4221,6 +4232,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. otherItem.addSubItem(invite_to_group, R.drawable.msg_addbot, LocaleController.getString("BotInvite", R.string.BotInvite)); } otherItem.addSubItem(share, R.drawable.msg_share, LocaleController.getString("BotShare", R.string.BotShare)); + otherItem.addSubItem(qr_code, R.drawable.wallet_qr, LocaleController.getString("ShareQRCode", R.string.ShareQRCode)); } else { otherItem.addSubItem(add_contact, R.drawable.msg_addcontact, LocaleController.getString("AddContact", R.string.AddContact)); } @@ -4257,15 +4269,16 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. if (!chat.megagroup && chatInfo != null && chatInfo.can_view_stats) { otherItem.addSubItem(statistics, R.drawable.msg_stats, LocaleController.getString("Statistics", R.string.Statistics)); } + if (!TextUtils.isEmpty(chat.username)) { + otherItem.addSubItem(share, R.drawable.msg_share, LocaleController.getString("BotShare", R.string.BotShare)); + otherItem.addSubItem(qr_code, R.drawable.wallet_qr, LocaleController.getString("ShareQRCode", R.string.ShareQRCode)); + } if (chat.megagroup) { otherItem.addSubItem(search_members, R.drawable.msg_search, LocaleController.getString("SearchMembers", R.string.SearchMembers)); if (!chat.creator && !chat.left && !chat.kicked) { otherItem.addSubItem(leave_group, R.drawable.msg_leave, LocaleController.getString("LeaveMegaMenu", R.string.LeaveMegaMenu)); } } else { - if (!TextUtils.isEmpty(chat.username)) { - otherItem.addSubItem(share, R.drawable.msg_share, LocaleController.getString("BotShare", R.string.BotShare)); - } if (!currentChat.creator && !currentChat.left && !currentChat.kicked) { otherItem.addSubItem(leave_group, R.drawable.msg_leave, LocaleController.getString("LeaveChannelMenu", R.string.LeaveChannelMenu)); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ProxyListActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ProxyListActivity.java index b7117c127..ca940a1c0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ProxyListActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ProxyListActivity.java @@ -8,23 +8,32 @@ package org.telegram.ui; +import android.Manifest; +import android.annotation.SuppressLint; import android.app.Dialog; import android.content.Context; import android.content.SharedPreferences; +import android.content.pm.PackageManager; import android.graphics.Canvas; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.SystemClock; import android.text.TextUtils; +import android.util.Log; import android.util.TypedValue; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; -import android.widget.ImageView; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.DefaultItemAnimator; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.DownloadController; import org.telegram.messenger.LocaleController; @@ -32,10 +41,13 @@ import org.telegram.messenger.MessagesController; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; import org.telegram.messenger.SharedConfig; +import org.telegram.messenger.browser.Browser; import org.telegram.tgnet.ConnectionsManager; import org.telegram.ui.ActionBar.ActionBar; -import org.telegram.ui.ActionBar.AlertDialog; +import org.telegram.ui.ActionBar.ActionBarMenu; +import org.telegram.ui.ActionBar.ActionBarMenuItem; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.BottomSheet; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.ActionBar.ThemeDescription; import org.telegram.ui.Cells.HeaderCell; @@ -46,13 +58,14 @@ import org.telegram.ui.Cells.TextSettingsCell; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.RecyclerListView; -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.DefaultItemAnimator; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - import java.util.List; +import okhttp3.HttpUrl; +import tw.nekomimi.nekogram.ShadowsocksRSettingsActivity; +import tw.nekomimi.nekogram.ShadowsocksSettingsActivity; +import tw.nekomimi.nekogram.VmessSettingsActivity; +import tw.nekomimi.nekogram.utils.ProxyUtil; + public class ProxyListActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { private ListAdapter listAdapter; @@ -71,7 +84,6 @@ public class ProxyListActivity extends BaseFragment implements NotificationCente private int connectionsHeaderRow; private int proxyStartRow; private int proxyEndRow; - private int proxyAddRow; private int proxyDetailRow; private int callsRow; private int callsDetailRow; @@ -80,7 +92,6 @@ public class ProxyListActivity extends BaseFragment implements NotificationCente private TextView textView; private TextView valueTextView; - private ImageView checkImageView; private SharedConfig.ProxyInfo currentInfo; private Drawable checkDrawable; @@ -110,14 +121,6 @@ public class ProxyListActivity extends BaseFragment implements NotificationCente valueTextView.setPadding(0, 0, 0, 0); addView(valueTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, (LocaleController.isRTL ? 56 : 21), 35, (LocaleController.isRTL ? 21 : 56), 0)); - checkImageView = new ImageView(context); - checkImageView.setImageResource(R.drawable.profile_info); - checkImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText3), PorterDuff.Mode.MULTIPLY)); - checkImageView.setScaleType(ImageView.ScaleType.CENTER); - checkImageView.setContentDescription(LocaleController.getString("Edit", R.string.Edit)); - addView(checkImageView, LayoutHelper.createFrame(48, 48, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.TOP, 8, 8, 8, 0)); - checkImageView.setOnClickListener(v -> presentFragment(new ProxySettingsActivity(currentInfo))); - setWillNotDraw(false); } @@ -126,8 +129,30 @@ public class ProxyListActivity extends BaseFragment implements NotificationCente super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(64) + 1, MeasureSpec.EXACTLY)); } + @SuppressLint("SetTextI18n") public void setProxy(SharedConfig.ProxyInfo proxyInfo) { - textView.setText(proxyInfo.address + ":" + proxyInfo.port); + String server; + int port; + if (proxyInfo instanceof SharedConfig.VmessProxy) { + server = "[ Vmess ] " + ((SharedConfig.VmessProxy) proxyInfo).bean.getAddress(); + port = ((SharedConfig.VmessProxy) proxyInfo).bean.getPort(); + } else if (proxyInfo instanceof SharedConfig.ShadowsocksProxy) { + server = "[ SS ] " + ((SharedConfig.ShadowsocksProxy) proxyInfo).bean.getHost(); + port = ((SharedConfig.ShadowsocksProxy) proxyInfo).bean.getRemotePort(); + } else if (proxyInfo instanceof SharedConfig.ShadowsocksRProxy) { + server = "[ SSR ] " + ((SharedConfig.ShadowsocksRProxy) proxyInfo).bean.getHost(); + port = ((SharedConfig.ShadowsocksRProxy) proxyInfo).bean.getRemotePort(); + } else { + server = proxyInfo.address; + port = proxyInfo.port; + } + if (proxyInfo.isInternal && proxyInfo.descripton == null) { + textView.setText(LocaleController.formatString("NekoXProxy", R.string.NekoXProxy)); + } else if (proxyInfo.isInternal) { + textView.setText(LocaleController.formatString("PublicPrefix", R.string.PublicPrefix) + " " + server + ":" + port); + } else { + textView.setText(server + ":" + port); + } currentInfo = proxyInfo; } @@ -210,7 +235,6 @@ public class ProxyListActivity extends BaseFragment implements NotificationCente public boolean onFragmentCreate() { super.onFragmentCreate(); - SharedConfig.loadProxyList(); currentConnectionState = ConnectionsManager.getInstance(currentAccount).getConnectionState(); NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.proxySettingsChanged); @@ -218,7 +242,7 @@ public class ProxyListActivity extends BaseFragment implements NotificationCente NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.didUpdateConnectionState); final SharedPreferences preferences = MessagesController.getGlobalMainSettings(); - useProxySettings = preferences.getBoolean("proxy_enabled", false) && !SharedConfig.proxyList.isEmpty(); + useProxySettings = SharedConfig.proxyEnabled && !SharedConfig.proxyList.isEmpty(); useProxyForCalls = preferences.getBoolean("proxy_enabled_calls", false); updateRows(true); @@ -234,6 +258,15 @@ public class ProxyListActivity extends BaseFragment implements NotificationCente NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.didUpdateConnectionState); } + private int menu_add = 1; + private int menu_add_input_socks = 2; + private int menu_add_input_telegram = 3; + private int menu_add_input_vmess = 4; + private int menu_add_input_ss = 7; + private int menu_add_input_ssr = 8; + private int menu_add_import_from_clipboard = 5; + private int menu_add_scan_qr = 6; + @Override public View createView(Context context) { actionBar.setBackButtonImage(R.drawable.ic_ab_back); @@ -251,6 +284,87 @@ public class ProxyListActivity extends BaseFragment implements NotificationCente } }); + ActionBarMenu menu = actionBar.createMenu(); + + ActionBarMenuItem addItem = menu.addItem(menu_add, R.drawable.add); + + addItem.addSubItem(menu_add_import_from_clipboard, LocaleController.getString("ImportProxyFromClipboard", R.string.ImportProxyFromClipboard)).setOnClickListener((v) -> ProxyUtil.importFromClipboard(context)); + addItem.addSubItem(menu_add_scan_qr, LocaleController.getString("ScanQRCode", R.string.ScanQRCode)).setOnClickListener((v) -> { + + CameraScanActivity.showAsSheet(this, new CameraScanActivity.CameraScanActivityDelegate() { + + @Override + public void didFindQr(String text) { + + BottomSheet.Builder builder = new BottomSheet.Builder(getParentActivity()); + + boolean isUrl = false; + + try { + + HttpUrl.parse(text); + + isUrl = true; + + Browser.openUrl(getParentActivity(), text); + + return; + + } catch (Exception ignored) { + } + + builder.setTitle(text); + + builder.setItems(new String[]{ + + LocaleController.getString("Copy", R.string.Copy), + LocaleController.getString("Cancel", R.string.Cancel) + + }, (v, i) -> { + + if (i == 0) { + + if (Build.VERSION.SDK_INT >= 23) { + if (getParentActivity().checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { + getParentActivity().requestPermissions(new String[]{Manifest.permission.CAMERA}, 22); + return; + } + } + + CameraScanActivity.showAsSheet(ProxyListActivity.this, new CameraScanActivity.CameraScanActivityDelegate() { + + @Override + public void didFindQr(String text) { + + ProxyUtil.showLinkAlert(getParentActivity(), text); + + } + }); + + } + + }); + + showDialog(builder.create()); + + + } + + }); + + }); + + addItem.addSubItem(menu_add_input_socks, LocaleController.getString("AddProxySocks5", R.string.AddProxySocks5)).setOnClickListener((v) -> presentFragment(new ProxySettingsActivity(0))); + addItem.addSubItem(menu_add_input_telegram, LocaleController.getString("AddProxyTelegram", R.string.AddProxyTelegram)).setOnClickListener((v) -> presentFragment(new ProxySettingsActivity(1))); + addItem.addSubItem(menu_add_input_vmess, LocaleController.getString("AddProxyVmess", R.string.AddProxyVmess)).setOnClickListener((v) -> presentFragment(new VmessSettingsActivity())); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + + addItem.addSubItem(menu_add_input_ss, LocaleController.getString("AddProxySS", R.string.AddProxySS)).setOnClickListener((v) -> presentFragment(new ShadowsocksSettingsActivity())); + addItem.addSubItem(menu_add_input_ssr, LocaleController.getString("AddProxySSR", R.string.AddProxySSR)).setOnClickListener((v) -> presentFragment(new ShadowsocksRSettingsActivity())); + + } + listAdapter = new ListAdapter(context); fragmentView = new FrameLayout(context); @@ -267,45 +381,20 @@ public class ProxyListActivity extends BaseFragment implements NotificationCente if (position == useProxyRow) { if (SharedConfig.currentProxy == null) { if (!SharedConfig.proxyList.isEmpty()) { - SharedConfig.currentProxy = SharedConfig.proxyList.get(0); - - if (!useProxySettings) { - SharedPreferences preferences = MessagesController.getGlobalMainSettings(); - SharedPreferences.Editor editor = MessagesController.getGlobalMainSettings().edit(); - editor.putString("proxy_ip", SharedConfig.currentProxy.address); - editor.putString("proxy_pass", SharedConfig.currentProxy.password); - editor.putString("proxy_user", SharedConfig.currentProxy.username); - editor.putInt("proxy_port", SharedConfig.currentProxy.port); - editor.putString("proxy_secret", SharedConfig.currentProxy.secret); - editor.commit(); - } + SharedConfig.setCurrentProxy(SharedConfig.proxyList.get(0)); } else { - presentFragment(new ProxySettingsActivity()); + addProxy(); return; } } - useProxySettings = !useProxySettings; - SharedPreferences preferences = MessagesController.getGlobalMainSettings(); + useProxySettings = !useProxySettings; TextCheckCell textCheckCell = (TextCheckCell) view; textCheckCell.setChecked(useProxySettings); - if (!useProxySettings) { - RecyclerListView.Holder holder = (RecyclerListView.Holder) listView.findViewHolderForAdapterPosition(callsRow); - if (holder != null) { - textCheckCell = (TextCheckCell) holder.itemView; - textCheckCell.setChecked(false); - } - useProxyForCalls = false; - } - SharedPreferences.Editor editor = MessagesController.getGlobalMainSettings().edit(); - editor.putBoolean("proxy_enabled", useProxySettings); - editor.commit(); - - ConnectionsManager.setProxySettings(useProxySettings, SharedConfig.currentProxy.address, SharedConfig.currentProxy.port, SharedConfig.currentProxy.username, SharedConfig.currentProxy.password, SharedConfig.currentProxy.secret); NotificationCenter.getGlobalInstance().removeObserver(ProxyListActivity.this, NotificationCenter.proxySettingsChanged); - NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.proxySettingsChanged); + SharedConfig.setProxyEnable(useProxySettings); NotificationCenter.getGlobalInstance().addObserver(ProxyListActivity.this, NotificationCenter.proxySettingsChanged); for (int a = proxyStartRow; a < proxyEndRow; a++) { @@ -322,23 +411,17 @@ public class ProxyListActivity extends BaseFragment implements NotificationCente textCheckCell.setChecked(useProxyForCalls); SharedPreferences.Editor editor = MessagesController.getGlobalMainSettings().edit(); editor.putBoolean("proxy_enabled_calls", useProxyForCalls); - editor.commit(); + editor.apply(); } else if (position >= proxyStartRow && position < proxyEndRow) { SharedConfig.ProxyInfo info = SharedConfig.proxyList.get(position - proxyStartRow); useProxySettings = true; - SharedPreferences.Editor editor = MessagesController.getGlobalMainSettings().edit(); - editor.putString("proxy_ip", info.address); - editor.putString("proxy_pass", info.password); - editor.putString("proxy_user", info.username); - editor.putInt("proxy_port", info.port); - editor.putString("proxy_secret", info.secret); - editor.putBoolean("proxy_enabled", useProxySettings); if (!info.secret.isEmpty()) { useProxyForCalls = false; + SharedPreferences.Editor editor = MessagesController.getGlobalMainSettings().edit(); editor.putBoolean("proxy_enabled_calls", false); + editor.apply(); } - editor.commit(); - SharedConfig.currentProxy = info; + SharedConfig.setCurrentProxy(info); for (int a = proxyStartRow; a < proxyEndRow; a++) { RecyclerListView.Holder holder = (RecyclerListView.Holder) listView.findViewHolderForAdapterPosition(a); if (holder != null) { @@ -353,37 +436,104 @@ public class ProxyListActivity extends BaseFragment implements NotificationCente TextCheckCell textCheckCell = (TextCheckCell) holder.itemView; textCheckCell.setChecked(true); } - ConnectionsManager.setProxySettings(useProxySettings, SharedConfig.currentProxy.address, SharedConfig.currentProxy.port, SharedConfig.currentProxy.username, SharedConfig.currentProxy.password, SharedConfig.currentProxy.secret); - } else if (position == proxyAddRow) { - presentFragment(new ProxySettingsActivity()); } }); listView.setOnItemLongClickListener((view, position) -> { if (position >= proxyStartRow && position < proxyEndRow) { final SharedConfig.ProxyInfo info = SharedConfig.proxyList.get(position - proxyStartRow); - AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); - builder.setMessage(LocaleController.getString("DeleteProxy", R.string.DeleteProxy)); - builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); - builder.setTitle(LocaleController.getString("AppName", R.string.AppName)); - builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), (dialog, which) -> { - SharedConfig.deleteProxy(info); - if (SharedConfig.currentProxy == null) { - useProxyForCalls = false; - useProxySettings = false; - } - NotificationCenter.getGlobalInstance().removeObserver(ProxyListActivity.this, NotificationCenter.proxySettingsChanged); - NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.proxySettingsChanged); - NotificationCenter.getGlobalInstance().addObserver(ProxyListActivity.this, NotificationCenter.proxySettingsChanged); - updateRows(false); - if (listAdapter != null) { - listAdapter.notifyItemRemoved(position); - if (SharedConfig.currentProxy == null) { - listAdapter.notifyItemChanged(useProxyRow, ListAdapter.PAYLOAD_CHECKED_CHANGED); - listAdapter.notifyItemChanged(callsRow, ListAdapter.PAYLOAD_CHECKED_CHANGED); + + BottomSheet.Builder builder = new BottomSheet.Builder(context); + + builder.setItems(new String[]{ + + info.isInternal ? null : LocaleController.getString("EditProxy", R.string.EditProxy), + (info.isInternal && info.descripton == null) ? null : LocaleController.getString("ShareProxy", R.string.ShareProxy), + (info.isInternal && info.descripton == null) ? null : LocaleController.getString("ShareQRCode", R.string.ShareQRCode), + (info.isInternal && info.descripton == null) ? null : LocaleController.getString("CopyLink", R.string.CopyLink), + (info.isInternal) ? null : LocaleController.getString("ProxyDelete", R.string.ProxyDelete), + LocaleController.getString("Cancel", R.string.Cancel) + + }, new int[]{ + + R.drawable.group_edit, + R.drawable.share, + R.drawable.share, + R.drawable.msg_link, + R.drawable.msg_delete, + R.drawable.msg_cancel + + }, (v, i) -> { + + if (i == 0) { + + if (info instanceof SharedConfig.VmessProxy) { + presentFragment(new VmessSettingsActivity((SharedConfig.VmessProxy) info)); + } else if (info instanceof SharedConfig.ShadowsocksProxy) { + presentFragment(new ShadowsocksSettingsActivity((SharedConfig.ShadowsocksProxy) info)); + } else if (info instanceof SharedConfig.ShadowsocksRProxy) { + presentFragment(new ShadowsocksRSettingsActivity((SharedConfig.ShadowsocksRProxy) info)); + } else { + presentFragment(new ProxySettingsActivity(info)); } + + } else if (i == 1) { + + ProxyUtil.shareProxy(getParentActivity(), info, 0); + + } else if (i == 2) { + + ProxyUtil.shareProxy(getParentActivity(), info, 2); + + } else if (i == 3) { + + ProxyUtil.shareProxy(getParentActivity(), info, 1); + + } else if (i == 4) { + + BottomSheet.Builder del = new BottomSheet.Builder(context); + + del.setItems(new String[]{ + + LocaleController.getString("OK", R.string.OK), + LocaleController.getString("Cancel", R.string.Cancel) + + }, new int[]{ + + R.drawable.msg_delete, + R.drawable.msg_cancel + + }, (dv, di) -> { + + if (di == 0) { + + SharedConfig.deleteProxy(info); + if (SharedConfig.currentProxy == null) { + SharedConfig.setProxyEnable(false); + } + NotificationCenter.getGlobalInstance().removeObserver(ProxyListActivity.this, NotificationCenter.proxySettingsChanged); + NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.proxySettingsChanged); + NotificationCenter.getGlobalInstance().addObserver(ProxyListActivity.this, NotificationCenter.proxySettingsChanged); + updateRows(false); + if (listAdapter != null) { + listAdapter.notifyItemRemoved(position); + if (SharedConfig.currentProxy == null) { + listAdapter.notifyItemChanged(useProxyRow, ListAdapter.PAYLOAD_CHECKED_CHANGED); + listAdapter.notifyItemChanged(callsRow, ListAdapter.PAYLOAD_CHECKED_CHANGED); + } + } + + } + + }); + + showDialog(del.create()); + } + }); + showDialog(builder.create()); + return true; } return false; @@ -392,6 +542,118 @@ public class ProxyListActivity extends BaseFragment implements NotificationCente return fragmentView; } + private void addProxy() { + + BottomSheet.Builder builder = new BottomSheet.Builder(getParentActivity()); + + builder.setItems(new String[]{ + + LocaleController.getString("AddProxySocks5", R.string.AddProxySocks5), + LocaleController.getString("AddProxyTelegram", R.string.AddProxyTelegram), + LocaleController.getString("AddProxyVmess", R.string.AddProxyVmess), + Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP ? null : LocaleController.getString("AddProxySS", R.string.AddProxySS), + Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP ? null : LocaleController.getString("AddProxySSR", R.string.AddProxySSR), + + LocaleController.getString("ImportProxyFromClipboard", R.string.ImportProxyFromClipboard), + LocaleController.getString("ScanQRCode", R.string.ScanQRCode) + + }, (v, i) -> { + + if (i == 0) { + + presentFragment(new ProxySettingsActivity(0)); + + } else if (i == 1) { + + presentFragment(new ProxySettingsActivity(1)); + + } else if (i == 2) { + + presentFragment(new VmessSettingsActivity()); + + } else if (i == 3) { + + presentFragment(new ShadowsocksSettingsActivity()); + + } else if (i == 4) { + + presentFragment(new ShadowsocksRSettingsActivity()); + + } else if (i == 5) { + + ProxyUtil.importFromClipboard(getParentActivity()); + + } else { + + CameraScanActivity.showAsSheet(this, new CameraScanActivity.CameraScanActivityDelegate() { + + @Override + public void didFindQr(String text) { + + BottomSheet.Builder builder = new BottomSheet.Builder(getParentActivity()); + + boolean isUrl = false; + + try { + + HttpUrl.parse(text); + + isUrl = true; + + Browser.openUrl(getParentActivity(), text); + + return; + + } catch (Exception ignored) { + } + + builder.setTitle(text); + + builder.setItems(new String[]{ + + LocaleController.getString("Copy", R.string.Copy), + LocaleController.getString("Cancel", R.string.Cancel) + + }, (v, i) -> { + + if (i == 0) { + + if (Build.VERSION.SDK_INT >= 23) { + if (getParentActivity().checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { + getParentActivity().requestPermissions(new String[]{Manifest.permission.CAMERA}, 22); + return; + } + } + + CameraScanActivity.showAsSheet(ProxyListActivity.this, new CameraScanActivity.CameraScanActivityDelegate() { + + @Override + public void didFindQr(String text) { + + ProxyUtil.showLinkAlert(getParentActivity(), text); + + } + }); + + } + + }); + + showDialog(builder.create()); + + + } + + }); + + } + + }); + + builder.show(); + + } + private void updateRows(boolean notify) { rowCount = 0; useProxyRow = rowCount++; @@ -405,7 +667,6 @@ public class ProxyListActivity extends BaseFragment implements NotificationCente proxyStartRow = -1; proxyEndRow = -1; } - proxyAddRow = rowCount++; proxyDetailRow = rowCount++; if (SharedConfig.currentProxy == null || SharedConfig.currentProxy.secret.isEmpty()) { boolean change = callsRow == -1; @@ -433,10 +694,15 @@ public class ProxyListActivity extends BaseFragment implements NotificationCente private void checkProxyList() { for (int a = 0, count = SharedConfig.proxyList.size(); a < count; a++) { final SharedConfig.ProxyInfo proxyInfo = SharedConfig.proxyList.get(a); - if (proxyInfo.checking || SystemClock.elapsedRealtime() - proxyInfo.availableCheckTime < 2 * 60 * 1000) { + if (proxyInfo.checking || SystemClock.elapsedRealtime() - proxyInfo.availableCheckTime < 2 * 60 * 1000L) { continue; } proxyInfo.checking = true; + + if (proxyInfo instanceof SharedConfig.ExternalSocks5Proxy && proxyInfo != SharedConfig.currentProxy) { + ((SharedConfig.ExternalSocks5Proxy) proxyInfo).start(); + } + proxyInfo.proxyCheckPingId = ConnectionsManager.getInstance(currentAccount).checkProxy(proxyInfo.address, proxyInfo.port, proxyInfo.username, proxyInfo.password, proxyInfo.secret, time -> AndroidUtilities.runOnUIThread(() -> { proxyInfo.availableCheckTime = SystemClock.elapsedRealtime(); proxyInfo.checking = false; @@ -446,6 +712,9 @@ public class ProxyListActivity extends BaseFragment implements NotificationCente } else { proxyInfo.ping = time; proxyInfo.available = true; + if (proxyInfo instanceof SharedConfig.ExternalSocks5Proxy && proxyInfo != SharedConfig.currentProxy) { + ((SharedConfig.ExternalSocks5Proxy) proxyInfo).stop(); + } } NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.proxyCheckDone, proxyInfo); })); @@ -460,9 +729,7 @@ public class ProxyListActivity extends BaseFragment implements NotificationCente @Override public void onResume() { super.onResume(); - if (listAdapter != null) { - listAdapter.notifyDataSetChanged(); - } + updateRows(true); } @Override @@ -474,25 +741,29 @@ public class ProxyListActivity extends BaseFragment implements NotificationCente if (currentConnectionState != state) { currentConnectionState = state; if (listView != null && SharedConfig.currentProxy != null) { - int idx = SharedConfig.proxyList.indexOf(SharedConfig.currentProxy); - if (idx >= 0) { - RecyclerListView.Holder holder = (RecyclerListView.Holder) listView.findViewHolderForAdapterPosition(idx + proxyStartRow); - if (holder != null) { - TextDetailProxyCell cell = (TextDetailProxyCell) holder.itemView; - cell.updateStatus(); + synchronized (SharedConfig.proxyList) { + int idx = SharedConfig.proxyList.indexOf(SharedConfig.currentProxy); + if (idx >= 0) { + RecyclerListView.Holder holder = (RecyclerListView.Holder) listView.findViewHolderForAdapterPosition(idx + proxyStartRow); + if (holder != null) { + TextDetailProxyCell cell = (TextDetailProxyCell) holder.itemView; + cell.updateStatus(); + } } } } } } else if (id == NotificationCenter.proxyCheckDone) { if (listView != null) { - SharedConfig.ProxyInfo proxyInfo = (SharedConfig.ProxyInfo) args[0]; - int idx = SharedConfig.proxyList.indexOf(proxyInfo); - if (idx >= 0) { - RecyclerListView.Holder holder = (RecyclerListView.Holder) listView.findViewHolderForAdapterPosition(idx + proxyStartRow); - if (holder != null) { - TextDetailProxyCell cell = (TextDetailProxyCell) holder.itemView; - cell.updateStatus(); + synchronized (SharedConfig.proxyList) { + SharedConfig.ProxyInfo proxyInfo = (SharedConfig.ProxyInfo) args[0]; + int idx = SharedConfig.proxyList.indexOf(proxyInfo); + if (idx >= 0) { + RecyclerListView.Holder holder = (RecyclerListView.Holder) listView.findViewHolderForAdapterPosition(idx + proxyStartRow); + if (holder != null) { + TextDetailProxyCell cell = (TextDetailProxyCell) holder.itemView; + cell.updateStatus(); + } } } } @@ -525,14 +796,6 @@ public class ProxyListActivity extends BaseFragment implements NotificationCente } break; } - case 1: { - TextSettingsCell textCell = (TextSettingsCell) holder.itemView; - textCell.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); - if (position == proxyAddRow) { - textCell.setText(LocaleController.getString("AddProxy", R.string.AddProxy), false); - } - break; - } case 2: { HeaderCell headerCell = (HeaderCell) holder.itemView; if (position == connectionsHeaderRow) { @@ -543,7 +806,7 @@ public class ProxyListActivity extends BaseFragment implements NotificationCente case 3: { TextCheckCell checkCell = (TextCheckCell) holder.itemView; if (position == useProxyRow) { - checkCell.setTextAndCheck(LocaleController.getString("UseProxySettings", R.string.UseProxySettings), useProxySettings, false); + checkCell.setTextAndCheck(LocaleController.getString("UseProxySettings", R.string.UseProxySettings), useProxySettings, true); } else if (position == callsRow) { checkCell.setTextAndCheck(LocaleController.getString("UseProxyForCalls", R.string.UseProxyForCalls), useProxyForCalls, false); } @@ -598,7 +861,7 @@ public class ProxyListActivity extends BaseFragment implements NotificationCente @Override public boolean isEnabled(RecyclerView.ViewHolder holder) { int position = holder.getAdapterPosition(); - return position == useProxyRow || position == callsRow || position == proxyAddRow || position >= proxyStartRow && position < proxyEndRow; + return position == useProxyRow || position == callsRow || position >= proxyStartRow && position < proxyEndRow; } @Override @@ -637,8 +900,6 @@ public class ProxyListActivity extends BaseFragment implements NotificationCente public int getItemViewType(int position) { if (position == useProxyDetailRow || position == proxyDetailRow) { return 0; - } else if (position == proxyAddRow) { - return 1; } else if (position == useProxyRow || position == callsRow) { return 3; } else if (position == connectionsHeaderRow) { @@ -649,6 +910,7 @@ public class ProxyListActivity extends BaseFragment implements NotificationCente return 4; } } + } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ProxySettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ProxySettingsActivity.java index 42328c38e..ce880a62d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ProxySettingsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ProxySettingsActivity.java @@ -85,13 +85,11 @@ public class ProxySettingsActivity extends BaseFragment { private ScrollView scrollView; private LinearLayout linearLayout2; private LinearLayout inputFieldsContainer; - private HeaderCell headerCell; private ShadowSectionCell[] sectionCell = new ShadowSectionCell[3]; private TextInfoPrivacyCell[] bottomCells = new TextInfoPrivacyCell[2]; - private TextSettingsCell shareCell; private TextSettingsCell pasteCell; private ActionBarMenuItem doneItem; - private RadioCell[] typeCell = new RadioCell[2]; + // private RadioCell[] typeCell = new RadioCell[2]; private int currentType = -1; private int pasteType = -1; @@ -163,10 +161,11 @@ public class ProxySettingsActivity extends BaseFragment { } } - public ProxySettingsActivity() { + public ProxySettingsActivity(int type) { super(); currentProxyInfo = new SharedConfig.ProxyInfo("", 1080, "", "", ""); addingNewProxy = true; + currentType = type; } public ProxySettingsActivity(SharedConfig.ProxyInfo proxyInfo) { @@ -222,25 +221,29 @@ public class ProxySettingsActivity extends BaseFragment { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); - boolean enabled; if (addingNewProxy) { SharedConfig.addProxy(currentProxyInfo); - SharedConfig.currentProxy = currentProxyInfo; - editor.putBoolean("proxy_enabled", true); - enabled = true; + SharedConfig.setCurrentProxy(currentProxyInfo); } else { - enabled = preferences.getBoolean("proxy_enabled", false); - SharedConfig.saveProxyList(); + SharedConfig.setProxyEnable(false); } + SharedConfig.saveProxyList(); if (addingNewProxy || SharedConfig.currentProxy == currentProxyInfo) { editor.putString("proxy_ip", currentProxyInfo.address); editor.putString("proxy_pass", currentProxyInfo.password); editor.putString("proxy_user", currentProxyInfo.username); editor.putInt("proxy_port", currentProxyInfo.port); editor.putString("proxy_secret", currentProxyInfo.secret); - ConnectionsManager.setProxySettings(enabled, currentProxyInfo.address, currentProxyInfo.port, currentProxyInfo.username, currentProxyInfo.password, currentProxyInfo.secret); + if (currentProxyInfo instanceof SharedConfig.VmessProxy) { + editor.putString("vmess_link", ((SharedConfig.VmessProxy) currentProxyInfo).bean.toString()); + } else if (currentProxyInfo instanceof SharedConfig.ShadowsocksProxy) { + editor.putString("vmess_link", ((SharedConfig.ShadowsocksProxy) currentProxyInfo).bean.toString()); + } else if (currentProxyInfo instanceof SharedConfig.ShadowsocksRProxy) { + editor.putString("vmess_link", ((SharedConfig.ShadowsocksRProxy) currentProxyInfo).bean.toString()); + } + ConnectionsManager.setProxySettings(SharedConfig.proxyEnabled, currentProxyInfo.address, currentProxyInfo.port, currentProxyInfo.username, currentProxyInfo.password, currentProxyInfo.secret); } - editor.commit(); + editor.apply(); NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.proxySettingsChanged); @@ -265,23 +268,7 @@ public class ProxySettingsActivity extends BaseFragment { linearLayout2.setOrientation(LinearLayout.VERTICAL); scrollView.addView(linearLayout2, new ScrollView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); - final View.OnClickListener typeCellClickListener = view -> setProxyType((Integer) view.getTag(), true); - - for (int a = 0; a < 2; a++) { - typeCell[a] = new RadioCell(context); - typeCell[a].setBackground(Theme.getSelectorDrawable(true)); - typeCell[a].setTag(a); - if (a == 0) { - typeCell[a].setText(LocaleController.getString("UseProxySocks5", R.string.UseProxySocks5), a == currentType, true); - } else if (a == 1) { - typeCell[a].setText(LocaleController.getString("UseProxyTelegram", R.string.UseProxyTelegram), a == currentType, false); - } - linearLayout2.addView(typeCell[a], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 50)); - typeCell[a].setOnClickListener(typeCellClickListener); - } - sectionCell[0] = new ShadowSectionCell(context); - linearLayout2.addView(sectionCell[0], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); inputFieldsContainer = new LinearLayout(context); inputFieldsContainer.setOrientation(LinearLayout.VERTICAL); @@ -488,64 +475,6 @@ public class ProxySettingsActivity extends BaseFragment { linearLayout2.addView(sectionCell[2], 1, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); sectionCell[2].setVisibility(View.GONE); - shareCell = new TextSettingsCell(context); - shareCell.setBackgroundDrawable(Theme.getSelectorDrawable(true)); - shareCell.setText(LocaleController.getString("ShareFile", R.string.ShareFile), false); - shareCell.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlueText4)); - linearLayout2.addView(shareCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - shareCell.setOnClickListener(v -> { - StringBuilder params = new StringBuilder(); - String address = inputFields[FIELD_IP].getText().toString(); - String password = inputFields[FIELD_PASSWORD].getText().toString(); - String user = inputFields[FIELD_USER].getText().toString(); - String port = inputFields[FIELD_PORT].getText().toString(); - String secret = inputFields[FIELD_SECRET].getText().toString(); - String url; - try { - if (!TextUtils.isEmpty(address)) { - params.append("server=").append(URLEncoder.encode(address, "UTF-8")); - } - if (!TextUtils.isEmpty(port)) { - if (params.length() != 0) { - params.append("&"); - } - params.append("port=").append(URLEncoder.encode(port, "UTF-8")); - } - if (currentType == 1) { - url = "https://t.me/proxy?"; - if (params.length() != 0) { - params.append("&"); - } - params.append("secret=").append(URLEncoder.encode(secret, "UTF-8")); - } else { - url = "https://t.me/socks?"; - if (!TextUtils.isEmpty(user)) { - if (params.length() != 0) { - params.append("&"); - } - params.append("user=").append(URLEncoder.encode(user, "UTF-8")); - } - if (!TextUtils.isEmpty(password)) { - if (params.length() != 0) { - params.append("&"); - } - params.append("pass=").append(URLEncoder.encode(password, "UTF-8")); - } - } - } catch (Exception ignore) { - return; - } - if (params.length() == 0) { - return; - } - Intent shareIntent = new Intent(Intent.ACTION_SEND); - shareIntent.setType("text/plain"); - shareIntent.putExtra(Intent.EXTRA_TEXT, url + params.toString()); - Intent chooserIntent = Intent.createChooser(shareIntent, LocaleController.getString("ShareLink", R.string.ShareLink)); - chooserIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - getParentActivity().startActivity(chooserIntent); - }); - sectionCell[1] = new ShadowSectionCell(context); sectionCell[1].setBackgroundDrawable(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); linearLayout2.addView(sectionCell[1], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); @@ -556,8 +485,19 @@ public class ProxySettingsActivity extends BaseFragment { shareDoneProgress = 1f; checkShareDone(false); - currentType = -1; - setProxyType(TextUtils.isEmpty(currentProxyInfo.secret) ? 0 : 1, false); + if (currentType == -1) { + + setProxyType(TextUtils.isEmpty(currentProxyInfo.secret) ? 0 : 1, false); + + } else { + + int t = currentType; + + currentType = -1; + + setProxyType(t, false); + + } pasteType = -1; pasteString = null; @@ -665,7 +605,6 @@ public class ProxySettingsActivity extends BaseFragment { shareDoneAnimator.setDuration(200); shareDoneAnimator.addUpdateListener(a -> { shareDoneProgress = AndroidUtilities.lerp(shareDoneProgressAnimValues, a.getAnimatedFraction()); - shareCell.setTextColor(ColorUtils.blendARGB(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2), Theme.getColor(Theme.key_windowBackgroundWhiteBlueText4), shareDoneProgress)); doneItem.setAlpha(shareDoneProgress / 2f + 0.5f); }); } @@ -675,17 +614,15 @@ public class ProxySettingsActivity extends BaseFragment { shareDoneAnimator.start(); } else { shareDoneProgress = enabled ? 1f : 0f; - shareCell.setTextColor(enabled ? Theme.getColor(Theme.key_windowBackgroundWhiteBlueText4) : Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2)); doneItem.setAlpha(enabled ? 1f : .5f); } - shareCell.setEnabled(enabled); doneItem.setEnabled(enabled); shareDoneEnabled = enabled; } } private void checkShareDone(boolean animated) { - if (shareCell == null || doneItem == null || inputFields[FIELD_IP] == null || inputFields[FIELD_PORT] == null) { + if (doneItem == null || inputFields[FIELD_IP] == null || inputFields[FIELD_PORT] == null) { return; } setShareDoneEnabled(inputFields[FIELD_IP].length() != 0 && Utilities.parseInt(inputFields[FIELD_PORT].getText().toString()) != 0, animated); @@ -749,8 +686,6 @@ public class ProxySettingsActivity extends BaseFragment { ((View) inputFields[FIELD_PASSWORD].getParent()).setVisibility(View.GONE); ((View) inputFields[FIELD_USER].getParent()).setVisibility(View.GONE); } - typeCell[0].setChecked(currentType == 0, animated); - typeCell[1].setChecked(currentType == 1, animated); } } @@ -765,9 +700,6 @@ public class ProxySettingsActivity extends BaseFragment { @Override public ThemeDescription[] getThemeDescriptions() { final ThemeDescription.ThemeDescriptionDelegate delegate = () -> { - if (shareCell != null && (shareDoneAnimator == null || !shareDoneAnimator.isRunning())) { - shareCell.setTextColor(shareDoneEnabled ? Theme.getColor(Theme.key_windowBackgroundWhiteBlueText4) : Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2)); - } if (inputFields != null) { for (int i = 0; i < inputFields.length; i++) { inputFields[i].setLineColors(Theme.getColor(Theme.key_windowBackgroundWhiteInputField), @@ -788,23 +720,10 @@ public class ProxySettingsActivity extends BaseFragment { arrayList.add(new ThemeDescription(inputFieldsContainer, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundWhite)); arrayList.add(new ThemeDescription(linearLayout2, 0, new Class[]{View.class}, Theme.dividerPaint, null, null, Theme.key_divider)); - arrayList.add(new ThemeDescription(shareCell, ThemeDescription.FLAG_SELECTORWHITE, null, null, null, null, Theme.key_windowBackgroundWhite)); - arrayList.add(new ThemeDescription(shareCell, ThemeDescription.FLAG_SELECTORWHITE, null, null, null, null, Theme.key_listSelector)); - arrayList.add(new ThemeDescription(null, 0, null, null, null, null, delegate, Theme.key_windowBackgroundWhiteBlueText4)); - arrayList.add(new ThemeDescription(null, 0, null, null, null, null, delegate, Theme.key_windowBackgroundWhiteGrayText2)); - arrayList.add(new ThemeDescription(pasteCell, ThemeDescription.FLAG_SELECTORWHITE, null, null, null, null, Theme.key_windowBackgroundWhite)); arrayList.add(new ThemeDescription(pasteCell, ThemeDescription.FLAG_SELECTORWHITE, null, null, null, null, Theme.key_listSelector)); arrayList.add(new ThemeDescription(pasteCell, 0, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueText4)); - for (int a = 0; a < typeCell.length; a++) { - arrayList.add(new ThemeDescription(typeCell[a], ThemeDescription.FLAG_SELECTORWHITE, null, null, null, null, Theme.key_windowBackgroundWhite)); - arrayList.add(new ThemeDescription(typeCell[a], ThemeDescription.FLAG_SELECTORWHITE, null, null, null, null, Theme.key_listSelector)); - arrayList.add(new ThemeDescription(typeCell[a], 0, new Class[]{RadioCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); - arrayList.add(new ThemeDescription(typeCell[a], ThemeDescription.FLAG_CHECKBOX, new Class[]{RadioCell.class}, new String[]{"radioButton"}, null, null, null, Theme.key_radioBackground)); - arrayList.add(new ThemeDescription(typeCell[a], ThemeDescription.FLAG_CHECKBOXCHECK, new Class[]{RadioCell.class}, new String[]{"radioButton"}, null, null, null, Theme.key_radioBackgroundChecked)); - } - if (inputFields != null) { for (int a = 0; a < inputFields.length; a++) { arrayList.add(new ThemeDescription(inputFields[a], ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); @@ -819,8 +738,6 @@ public class ProxySettingsActivity extends BaseFragment { arrayList.add(new ThemeDescription(null, ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); arrayList.add(new ThemeDescription(null, ThemeDescription.FLAG_HINTTEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteHintText)); } - arrayList.add(new ThemeDescription(headerCell, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundWhite)); - arrayList.add(new ThemeDescription(headerCell, 0, new Class[]{HeaderCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueHeader)); for (int a = 0; a < sectionCell.length; a++) { if (sectionCell[a] != null) { arrayList.add(new ThemeDescription(sectionCell[a], ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{ShadowSectionCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SecretMediaViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/SecretMediaViewer.java index e366922f4..67508053f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SecretMediaViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SecretMediaViewer.java @@ -65,6 +65,8 @@ import org.telegram.ui.Components.VideoPlayer; import java.io.File; import java.util.ArrayList; +import tw.nekomimi.nekogram.NekoXConfig; + public class SecretMediaViewer implements NotificationCenter.NotificationCenterDelegate, GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener { private class FrameLayoutDrawer extends FrameLayout { @@ -735,7 +737,9 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD } else { windowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; } - windowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_SECURE; + if (!NekoXConfig.disableFlagSecure) { + windowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_SECURE; + } centerImage.setParentView(containerView); centerImage.setForceCrossfade(true); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java index 3bd40c1ad..048847086 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SettingsActivity.java @@ -131,6 +131,7 @@ import java.util.zip.ZipOutputStream; import tw.nekomimi.nekogram.NekoConfig; import tw.nekomimi.nekogram.NekoSettingsActivity; +import tw.nekomimi.nekogram.NekoXConfig; public class SettingsActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate, ImageUpdater.ImageUpdaterDelegate { @@ -283,7 +284,7 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter rowCount = 0; emptyRow = rowCount++; numberSectionRow = rowCount++; - if(!NekoConfig.hidePhone){ + if (!NekoConfig.hidePhone) { numberRow = rowCount++; } usernameRow = rowCount++; @@ -453,31 +454,44 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter listView.setItemAnimator(null); listView.setLayoutAnimation(null); listView.setClipToPadding(false); - listView.setOnItemClickListener((view, position) -> { - if (position == notificationRow) { - presentFragment(new NotificationsSettingsActivity()); - } else if (position == privacyRow) { - presentFragment(new PrivacySettingsActivity()); - } else if (position == dataRow) { - presentFragment(new DataSettingsActivity()); - } else if (position == chatRow) { - presentFragment(new ThemeActivity(ThemeActivity.THEME_TYPE_BASIC)); - } else if (position == devicesRow) { - presentFragment(new SessionsActivity(0)); - } else if (position == nekoRow) { - presentFragment(new NekoSettingsActivity()); - } else if (position == helpRow) { - showHelpAlert(); - } else if (position == languageRow) { - presentFragment(new LanguageSelectActivity()); - } else if (position == usernameRow) { - presentFragment(new ChangeUsernameActivity()); - } else if (position == bioRow) { - if (userInfo != null) { - presentFragment(new ChangeBioActivity()); + listView.setOnItemClickListener(new RecyclerListView.OnItemClickListener() { + + private int pressCount = 0; + + @Override + public void onItemClick(View view, int position) { + + if (position == notificationRow) { + presentFragment(new NotificationsSettingsActivity()); + } else if (position == privacyRow) { + presentFragment(new PrivacySettingsActivity()); + } else if (position == dataRow) { + presentFragment(new DataSettingsActivity()); + } else if (position == chatRow) { + presentFragment(new ThemeActivity(ThemeActivity.THEME_TYPE_BASIC)); + } else if (position == devicesRow) { + presentFragment(new SessionsActivity(0)); + } else if (position == nekoRow) { + presentFragment(new NekoSettingsActivity()); + } else if (position == helpRow) { + showHelpAlert(); + } else if (position == languageRow) { + presentFragment(new LanguageSelectActivity()); + } else if (position == usernameRow) { + presentFragment(new ChangeUsernameActivity()); + } else if (position == bioRow) { + if (userInfo != null) { + presentFragment(new ChangeBioActivity()); + } + } else if (position == numberRow) { + presentFragment(new ActionIntroActivity(ActionIntroActivity.ACTION_TYPE_CHANGE_PHONE_NUMBER)); + } else if (position == versionRow) { + pressCount++; + if (pressCount == 8) { + NekoXConfig.developerModeEntrance = true; + Toast.makeText(getParentActivity(), "¯\\_(ツ)_/¯", Toast.LENGTH_SHORT).show(); + } } - } else if (position == numberRow) { - presentFragment(new ActionIntroActivity(ActionIntroActivity.ACTION_TYPE_CHANGE_PHONE_NUMBER)); } }); @@ -503,10 +517,12 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter LocaleController.getString("DebugMenuClearMediaCache", R.string.DebugMenuClearMediaCache), LocaleController.getString("DebugMenuCallSettings", R.string.DebugMenuCallSettings), null, - BuildVars.DEBUG_PRIVATE_VERSION ? "Check for app updates" : null, + null, LocaleController.getString("DebugMenuReadAllDialogs", R.string.DebugMenuReadAllDialogs), SharedConfig.pauseMusicOnRecord ? LocaleController.getString("DebugMenuDisablePauseMusic", R.string.DebugMenuDisablePauseMusic) : LocaleController.getString("DebugMenuEnablePauseMusic", R.string.DebugMenuEnablePauseMusic), - BuildVars.DEBUG_VERSION && !AndroidUtilities.isTablet() ? (SharedConfig.smoothKeyboard ? LocaleController.getString("DebugMenuDisableSmoothKeyboard", R.string.DebugMenuDisableSmoothKeyboard) : LocaleController.getString("DebugMenuEnableSmoothKeyboard", R.string.DebugMenuEnableSmoothKeyboard)) : null + BuildVars.DEBUG_VERSION && !AndroidUtilities.isTablet() ? (SharedConfig.smoothKeyboard ? LocaleController.getString("DebugMenuDisableSmoothKeyboard", R.string.DebugMenuDisableSmoothKeyboard) : LocaleController.getString("DebugMenuEnableSmoothKeyboard", R.string.DebugMenuEnableSmoothKeyboard)) : null, + NekoXConfig.developerModeEntrance ? (NekoXConfig.developerMode ? LocaleController.getString("DisableDeveloperMode", R.string.DisableDeveloperMode) : LocaleController.getString("EnableDeveloperMode", R.string.EnableDeveloperMode)) : null + }; builder.setItems(items, (dialog, which) -> { if (which == 0) { @@ -521,6 +537,8 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter MessagesController.getInstance(currentAccount).forceResetDialogs(); } else if (which == 4) { BuildVars.LOGS_ENABLED = !BuildVars.LOGS_ENABLED; + BuildVars.DEBUG_VERSION = BuildVars.LOGS_ENABLED; + BuildVars.DEBUG_PRIVATE_VERSION = BuildVars.LOGS_ENABLED; SharedPreferences sharedPreferences = ApplicationLoader.applicationContext.getSharedPreferences("systemConfig", Context.MODE_PRIVATE); sharedPreferences.edit().putBoolean("logsEnabled", BuildVars.LOGS_ENABLED).commit(); } else if (which == 5) { @@ -536,7 +554,6 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter } else if (which == 8) { SharedConfig.toggleRoundCamera16to9(); } else if (which == 9) { - ((LaunchActivity) getParentActivity()).checkAppUpdate(true); } else if (which == 10) { MessagesStorage.getInstance(currentAccount).readAllDialogs(-1); } else if (which == 11) { @@ -546,6 +563,8 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter if (SharedConfig.smoothKeyboard && getParentActivity() != null) { getParentActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); } + } else if (which == 13) { + NekoXConfig.toggleDeveloperMode(); } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); @@ -1347,8 +1366,8 @@ public class SettingsActivity extends BaseFragment implements NotificationCenter float scale = 1.0f + 0.12f * diff; nameTextView.setScaleX(scale); nameTextView.setScaleY(scale); - idTextView.setTranslationX( -21 * AndroidUtilities.density * diff); - idTextView.setTranslationY( (float) Math.floor(avatarY) + AndroidUtilities.dp(32) + (float)Math.floor(22 * AndroidUtilities.density) * diff); + idTextView.setTranslationX(-21 * AndroidUtilities.density * diff); + idTextView.setTranslationY((float) Math.floor(avatarY) + AndroidUtilities.dp(32) + (float) Math.floor(22 * AndroidUtilities.density) * diff); if (diff > 0.85 && !searchMode) { idTextView.setVisibility(View.VISIBLE); } else { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ThemeActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ThemeActivity.java index f010c9a94..673695f24 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ThemeActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ThemeActivity.java @@ -490,7 +490,7 @@ public class ThemeActivity extends BaseFragment implements NotificationCenter.No emojiRow = rowCount++; raiseToSpeakRow = rowCount++; sendByEnterRow = rowCount++; - saveToGalleryRow = NekoConfig.saveCacheToPrivateDirectory ? -1 : rowCount++; + saveToGalleryRow = NekoConfig.saveCacheToSdcard ? rowCount++ : -1; distanceRow = rowCount++; settings2Row = rowCount++; stickersRow = rowCount++; diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ExternalGcm.java b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ExternalGcm.java new file mode 100644 index 000000000..0a42ed8c4 --- /dev/null +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ExternalGcm.java @@ -0,0 +1,25 @@ +package tw.nekomimi.nekogram; + +import org.telegram.messenger.FileLog; + +public class ExternalGcm { + + public static Interface INSTANCE; static { + + try { + + INSTANCE = (Interface) Class.forName("tw.nekomimi.nekogram.GcmImpl").newInstance(); + + } catch (Exception ex) { + } + + } + + public interface Interface { + + void initPlayServices(); + void sendRegistrationToServer(); + + } + +} diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/FilterPopup.java b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/FilterPopup.java index 4655f4b4a..29d2b61d0 100644 --- a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/FilterPopup.java +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/FilterPopup.java @@ -130,6 +130,26 @@ public class FilterPopup extends BaseController { return dialogs; } + private ArrayList filterUnreadDialogs(ArrayList allDialogs) { + ArrayList dialogs = new ArrayList<>(); + for (TLRPC.Dialog dialog : allDialogs) { + if (dialog instanceof TLRPC.TL_dialogFolder) continue; + if (dialog.unread_count == 0) continue; + dialogs.add(dialog); + } + return dialogs; + } + + private ArrayList filterContacts(ArrayList allDialogs) { + ArrayList dialogs = new ArrayList<>(); + for (TLRPC.Dialog dialog : allDialogs) { + if (getContactsController().isContact((int) dialog.id)) { + dialogs.add(dialog); + } + } + return dialogs; + } + public ArrayList getDialogs(int type, int folderId) { ArrayList allDialogs = new ArrayList<>(getMessagesController().getDialogs(folderId)); ArrayList folders = new ArrayList<>(); @@ -153,6 +173,22 @@ public class FilterPopup extends BaseController { } allDialogs.retainAll(filterUnmutedDialogs(allDialogs)); break; + case DialogType.Unread: + for (int i = 0; i < folders.size(); i++) { + folderDialogs.get(i).retainAll(filterUnreadDialogs(folderDialogs.get(i))); + if (!folderDialogs.get(i).isEmpty()) + dialogs.add(folders.get(i)); + } + allDialogs.retainAll(filterUnreadDialogs(allDialogs)); + break; + case DialogType.UnmutedAndUnread: + for (int i = 0; i < folders.size(); i++) { + folderDialogs.get(i).retainAll(filterUnmutedDialogs(filterUnreadDialogs(folderDialogs.get(i)))); + if (!folderDialogs.get(i).isEmpty()) + dialogs.add(folders.get(i)); + } + allDialogs.retainAll(filterUnmutedDialogs(filterUnreadDialogs(allDialogs))); + break; case DialogType.Users: for (int i = 0; i < folders.size(); i++) { folderDialogs.get(i).retainAll(dialogsUsers); @@ -161,6 +197,14 @@ public class FilterPopup extends BaseController { } allDialogs.retainAll(dialogsUsers); break; + case DialogType.Contacts: + for (int i = 0; i < folders.size(); i++) { + folderDialogs.get(i).retainAll(filterContacts(dialogsUsers)); + if (!folderDialogs.get(i).isEmpty()) + dialogs.add(folders.get(i)); + } + allDialogs.retainAll(filterContacts(dialogsUsers)); + break; case DialogType.Groups: for (int i = 0; i < folders.size(); i++) { folderDialogs.get(i).retainAll(dialogsGroups); @@ -232,15 +276,23 @@ public class FilterPopup extends BaseController { ArrayList temp = new ArrayList<>(allDialogs); temp.retainAll(dialogsUsers); - if (!temp.isEmpty()) { + if (!temp.isEmpty() && NekoXConfig.filterUsers) { items.add(LocaleController.getString("Users", R.string.Users)); options.add(DialogType.Users); unreadCounts.add(getDialogsUnreadCount(temp)); } + temp = new ArrayList<>(allDialogs); + temp.retainAll(filterContacts(dialogsUsers)); + if (!temp.isEmpty() && NekoXConfig.filterContacts) { + items.add(LocaleController.getString("Contacts", R.string.Contacts)); + options.add(DialogType.Contacts); + unreadCounts.add(getDialogsUnreadCount(temp)); + } + temp = new ArrayList<>(allDialogs); temp.retainAll(dialogsGroups); - if (!temp.isEmpty()) { + if (!temp.isEmpty() && NekoXConfig.filterGroups) { items.add(LocaleController.getString("Groups", R.string.Groups)); options.add(DialogType.Groups); unreadCounts.add(getDialogsUnreadCount(temp)); @@ -248,7 +300,7 @@ public class FilterPopup extends BaseController { temp = new ArrayList<>(allDialogs); temp.retainAll(dialogsChannels); - if (!temp.isEmpty()) { + if (!temp.isEmpty() && NekoXConfig.filterChannels) { items.add(LocaleController.getString("Channels", R.string.Channels)); options.add(DialogType.Channels); unreadCounts.add(getDialogsUnreadCount(temp)); @@ -256,7 +308,7 @@ public class FilterPopup extends BaseController { temp = new ArrayList<>(allDialogs); temp.retainAll(dialogsBots); - if (!temp.isEmpty()) { + if (!temp.isEmpty() && NekoXConfig.filterBots) { items.add(LocaleController.getString("Bots", R.string.Bots)); options.add(DialogType.Bots); unreadCounts.add(getDialogsUnreadCount(temp)); @@ -264,7 +316,7 @@ public class FilterPopup extends BaseController { temp = new ArrayList<>(allDialogs); temp.retainAll(dialogsAdmin); - if (!temp.isEmpty()) { + if (!temp.isEmpty() && NekoXConfig.filterAdmins) { items.add(LocaleController.getString("Admins", R.string.Admins)); options.add(DialogType.Admin); unreadCounts.add(getDialogsUnreadCount(temp)); @@ -272,12 +324,28 @@ public class FilterPopup extends BaseController { temp = new ArrayList<>(allDialogs); temp.retainAll(filterUnmutedDialogs(allDialogs)); - if (!temp.isEmpty()) { + if (!temp.isEmpty() && NekoXConfig.filterUnmuted) { items.add(LocaleController.getString("NotificationsUnmuted", R.string.NotificationsUnmuted)); options.add(DialogType.Unmuted); unreadCounts.add(getDialogsUnreadCount(temp)); } + temp = new ArrayList<>(allDialogs); + temp.retainAll(filterUnreadDialogs(allDialogs)); + if (!temp.isEmpty() && NekoXConfig.filterUnread) { + items.add(LocaleController.getString("NotificationsUnread", R.string.NotificationsUnread)); + options.add(DialogType.Unread); + unreadCounts.add(getDialogsUnreadCount(temp)); + } + + temp = new ArrayList<>(allDialogs); + temp.retainAll(filterUnmutedDialogs(filterUnreadDialogs(allDialogs))); + if (!temp.isEmpty() && NekoXConfig.filterUnmutedAndUnread) { + items.add(LocaleController.getString("NotificationsUnmutedAndUnread", R.string.NotificationsUnmutedAndUnread)); + options.add(DialogType.UnmutedAndUnread); + unreadCounts.add(getDialogsUnreadCount(temp)); + } + if (scrimPopupWindow != null) { scrimPopupWindow.dismiss(); scrimPopupWindow = null; @@ -383,7 +451,9 @@ public class FilterPopup extends BaseController { } scrimPopupWindow = null; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - dialogsActivity.getParentActivity().getWindow().getDecorView().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO); + try { + dialogsActivity.getParentActivity().getWindow().getDecorView().setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO); + } catch (NullPointerException ignored) {} } } }; @@ -416,9 +486,13 @@ public class FilterPopup extends BaseController { public static final int Bots = 10; public static final int Admin = 11; public static final int Unmuted = 12; + public static final int Unread = 13; + public static final int UnmutedAndUnread = 14; + public static final int Contacts = 15; + public static boolean isDialogsType(int dialogsType) { - return dialogsType == 0 || (dialogsType >= 7 && dialogsType <= 12); + return dialogsType == 0 || (dialogsType >= 7 && dialogsType <= 15); } } } diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/GuardedProcessPool.kt b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/GuardedProcessPool.kt new file mode 100644 index 000000000..c05de45c5 --- /dev/null +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/GuardedProcessPool.kt @@ -0,0 +1,133 @@ +/******************************************************************************* + * * + * Copyright (C) 2017 by Max Lv * + * Copyright (C) 2017 by Mygod Studio * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +package tw.nekomimi.nekogram + +import android.annotation.SuppressLint +import android.os.Build +import android.os.SystemClock +import android.system.ErrnoException +import android.system.Os +import android.system.OsConstants +import android.util.Log +import androidx.annotation.MainThread +import kotlinx.coroutines.* +import kotlinx.coroutines.channels.Channel +import org.telegram.messenger.ApplicationLoader +import org.telegram.messenger.FileLog +import java.io.File +import java.io.IOException +import java.io.InputStream +import kotlin.concurrent.thread + +class GuardedProcessPool(private val onFatal: suspend (IOException) -> Unit) : CoroutineScope { + companion object { + private const val TAG = "GuardedProcessPool" + private val pid by lazy { + Class.forName("java.lang.ProcessManager\$ProcessImpl").getDeclaredField("pid").apply { isAccessible = true } + } + } + + private inner class Guard(private val cmd: List) { + private lateinit var process: Process + + private fun streamLogger(input: InputStream, logger: (String) -> Unit) = try { + input.bufferedReader().forEachLine(logger) + } catch (_: IOException) { } // ignore + + fun start() { + process = ProcessBuilder(cmd).directory(ApplicationLoader.applicationContext.filesDir).start() + } + + suspend fun looper(onRestartCallback: (suspend () -> Unit)?) { + var running = true + val cmdName = File(cmd.first()).nameWithoutExtension + val exitChannel = Channel() + try { + while (true) { + thread(name = "stderr-$cmdName") { + streamLogger(process.errorStream) { + FileLog.e("[$cmdName]$it") + } + } + thread(name = "stdout-$cmdName") { + streamLogger(process.inputStream) { + FileLog.d("[$cmdName]$it") + } + // this thread also acts as a daemon thread for waitFor + runBlocking { exitChannel.send(process.waitFor()) } + } + val startTime = SystemClock.elapsedRealtime() + val exitCode = exitChannel.receive() + running = false + when { + SystemClock.elapsedRealtime() - startTime < 1000 -> throw IOException( + "$cmdName exits too fast (exit code: $exitCode)") + exitCode == 128 + OsConstants.SIGKILL -> FileLog.w("$cmdName was killed") + else -> FileLog.e(IOException("$cmdName unexpectedly exits with code $exitCode")) + } + start() + running = true + onRestartCallback?.invoke() + } + } catch (e: IOException) { + FileLog.w("error occurred. stop guard: " + cmd.joinToString(" ")) + GlobalScope.launch(Dispatchers.Main) { onFatal(e) } + } finally { + if (running) withContext(NonCancellable) { // clean-up cannot be cancelled + @SuppressLint("NewApi") + if (Build.VERSION.SDK_INT > 21) { + try { + Os.kill(pid.get(process) as Int, OsConstants.SIGTERM) + } catch (e: ErrnoException) { + if (e.errno != OsConstants.ESRCH) FileLog.e(e) + } catch (e: ReflectiveOperationException) { + FileLog.e(e) + } + if (withTimeoutOrNull(500) { exitChannel.receive() } != null) return@withContext + } + process.destroy() // kill the process + if (Build.VERSION.SDK_INT >= 26) { + if (withTimeoutOrNull(1000) { exitChannel.receive() } != null) return@withContext + process.destroyForcibly() // Force to kill the process if it's still alive + } + exitChannel.receive() + } // otherwise process already exited, nothing to be done + } + } + } + + override val coroutineContext = Dispatchers.Main.immediate + Job() + + @MainThread + fun start(cmd: List, onRestartCallback: (suspend () -> Unit)? = null) { + FileLog.d("start process: " + cmd.joinToString (" ")) + Guard(cmd).apply { + start() // if start fails, IOException will be thrown directly + launch { looper(onRestartCallback) } + } + } + + @MainThread + fun close(scope: CoroutineScope) { + cancel() + coroutineContext[Job]!!.also { job -> scope.launch { job.join() } } + } +} diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/MessageHelper.java b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/MessageHelper.java index cb90679d0..c6d87ec52 100644 --- a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/MessageHelper.java +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/MessageHelper.java @@ -9,6 +9,9 @@ import org.telegram.tgnet.TLRPC; import org.telegram.ui.Cells.ChatMessageCell; import java.util.ArrayList; +import java.util.HashSet; + +import tw.nekomimi.nekogram.utils.AlertUtil; public class MessageHelper extends BaseController { @@ -97,4 +100,97 @@ public class MessageHelper extends BaseController { } }), ConnectionsManager.RequestFlagFailOnServerErrors); } + + public void deleteChannelHistory(final long dialog_id, TLRPC.Chat chat, final int offset_id) { + + final TLRPC.TL_messages_getHistory req = new TLRPC.TL_messages_getHistory(); + req.peer = getMessagesController().getInputPeer((int) dialog_id); + if (req.peer == null) { + return; + } + req.limit = 100; + req.offset_id = offset_id; + final int currentReqId = ++lastReqId; + getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + if (error == null) { + int lastMessageId = offset_id; + if (currentReqId == lastReqId) { + if (response != null) { + TLRPC.messages_Messages res = (TLRPC.messages_Messages) response; + int size = res.messages.size(); + if (size == 0) { + return; + } + /* + ArrayList ids = new ArrayList<>(); + ArrayList random_ids = new ArrayList<>(); + int channelId = 0; + for (int a = 0; a < res.messages.size(); a++) { + TLRPC.Message message = res.messages.get(a); + ids.add(message.id); + if (message.random_id != 0) { + random_ids.add(message.random_id); + } + if (message.to_id.channel_id != 0) { + channelId = message.to_id.channel_id; + } + if (message.id > lastMessageId) { + lastMessageId = message.id; + } + } + getMessagesController().deleteMessages(ids, random_ids, null, dialog_id, channelId, true, false); + */ + HashSet ids = new HashSet<>(); + ArrayList msgIds = new ArrayList<>(); + ArrayList random_ids = new ArrayList<>(); + int channelId = 0; + for (int a = 0; a < res.messages.size(); a++) { + TLRPC.Message message = res.messages.get(a); + ids.add(message.id); + if (message.from_id > 0) { + ids.add(message.from_id); + } else { + msgIds.add(message.id); + if (message.random_id != 0) { + random_ids.add(message.random_id); + } + } + if (message.id > lastMessageId) { + lastMessageId = message.id; + } + } + for (int userId : ids) { + deleteUserChannelHistory(chat,userId,0); + } + if (!msgIds.isEmpty()) { + getMessagesController().deleteMessages(msgIds, random_ids, null, dialog_id, channelId, true, false); + } + deleteChannelHistory(dialog_id,chat, lastMessageId); + + } + } + } else { + AlertUtil.showToast(error.code + ": " + error.text); + } + }), ConnectionsManager.RequestFlagFailOnServerErrors); + } + + public void deleteUserChannelHistory(final TLRPC.Chat chat, int userId, int offset) { + if (offset == 0) { + getMessagesStorage().deleteUserChannelHistory(chat.id, userId); + } + TLRPC.TL_channels_deleteUserHistory req = new TLRPC.TL_channels_deleteUserHistory(); + req.channel = getMessagesController().getInputChannel(chat.id); + req.user_id = getMessagesController().getInputUser(userId); + getConnectionsManager().sendRequest(req, (response, error) -> { + if (error == null) { + TLRPC.TL_messages_affectedHistory res = (TLRPC.TL_messages_affectedHistory) response; + if (res.offset > 0) { + deleteUserChannelHistory(chat, userId, res.offset); + } + getMessagesController().processNewChannelDifferenceParams(res.pts, res.pts_count, chat.id); + } + }); + } + } diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/NekoConfig.java b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/NekoConfig.java index d3a81335c..62d01f0b4 100644 --- a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/NekoConfig.java +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/NekoConfig.java @@ -14,15 +14,16 @@ public class NekoConfig { private static final Object sync = new Object(); public static boolean useIPv6 = false; - public static boolean showHiddenFeature = false; + //public static boolean showHiddenFeature = false; public static boolean openFilterByActionBar = true; public static boolean openFilterByFab = false; public static boolean ignoreBlocked = false; public static boolean hideProxySponsorChannel = false; - public static boolean saveCacheToPrivateDirectory = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N; + public static boolean saveCacheToSdcard = false; public static boolean disablePhotoSideAction = true; + public static boolean hideKeyboardOnChatScroll = false; public static int mapPreviewProvider = 0; public static float stickerSize = 14.0f; public static int translationProvider = 1; @@ -47,8 +48,8 @@ public class NekoConfig { public static int eventType = 0; public static boolean newYear = false; public static int actionBarDecoration = 0; - public static boolean unlimitedFavedStickers = true; - public static boolean unlimitedPinnedDialogs = true; + public static boolean unlimitedFavedStickers = false; + public static boolean unlimitedPinnedDialogs = false; public static boolean residentNotification = false; @@ -70,7 +71,7 @@ public class NekoConfig { editor.putBoolean("transparentStatusBar", transparentStatusBar); editor.putBoolean("residentNotification", residentNotification); editor.putBoolean("hideProxySponsorChannel", hideProxySponsorChannel); - editor.putBoolean("saveCacheToPrivateDirectory", saveCacheToPrivateDirectory); + editor.putBoolean("saveCacheToSdcard", saveCacheToSdcard); editor.putBoolean("showAddToSavedMessages", showAddToSavedMessages); editor.putBoolean("showReport", showReport); editor.putBoolean("showPrPr", showPrPr); @@ -85,10 +86,11 @@ public class NekoConfig { editor.putBoolean("unlimitedFavedStickers", unlimitedFavedStickers); editor.putBoolean("unlimitedPinnedDialogs", unlimitedPinnedDialogs); editor.putBoolean("disablePhotoSideAction", disablePhotoSideAction); + editor.putBoolean("hideKeyboardOnChatScroll", hideKeyboardOnChatScroll); editor.putBoolean("openArchiveOnPull", openArchiveOnPull); editor.putBoolean("openFilterByActionBar", openFilterByActionBar); editor.putBoolean("openFilterByFab", openFilterByFab); - editor.putBoolean("showHiddenFeature", showHiddenFeature); + // editor.putBoolean("showHiddenFeature", showHiddenFeature); editor.putFloat("stickerSize", stickerSize); editor.putInt("typeface", typeface); editor.putInt("nameOrder", nameOrder); @@ -120,7 +122,7 @@ public class NekoConfig { transparentStatusBar = preferences.getBoolean("transparentStatusBar", false); residentNotification = preferences.getBoolean("residentNotification", false); hideProxySponsorChannel = preferences.getBoolean("hideProxySponsorChannel", false); - saveCacheToPrivateDirectory = preferences.getBoolean("saveCacheToPrivateDirectory", Build.VERSION.SDK_INT >= Build.VERSION_CODES.N); + saveCacheToSdcard = preferences.getBoolean("saveCacheToSdcard", Build.VERSION.SDK_INT >= Build.VERSION_CODES.N); showAddToSavedMessages = preferences.getBoolean("showAddToSavedMessages", true); showReport = preferences.getBoolean("showReport", false); showPrPr = preferences.getBoolean("showPrPr", false); @@ -135,14 +137,15 @@ public class NekoConfig { actionBarDecoration = preferences.getInt("actionBarDecoration", 0); newYear = preferences.getBoolean("newYear", false); stickerSize = preferences.getFloat("stickerSize", 14.0f); - unlimitedFavedStickers = preferences.getBoolean("unlimitedFavedStickers", true); - unlimitedPinnedDialogs = preferences.getBoolean("unlimitedPinnedDialogs", true); + unlimitedFavedStickers = preferences.getBoolean("unlimitedFavedStickers", false); + unlimitedPinnedDialogs = preferences.getBoolean("unlimitedPinnedDialogs", false); translationProvider = preferences.getInt("translationProvider", 1); disablePhotoSideAction = preferences.getBoolean("disablePhotoSideAction", true); openArchiveOnPull = preferences.getBoolean("openArchiveOnPull", false); openFilterByActionBar = preferences.getBoolean("openFilterByActionBar", true); openFilterByFab = preferences.getBoolean("openFilterByFab", false); - showHiddenFeature = preferences.getBoolean("showHiddenFeature", false); + //showHiddenFeature = preferences.getBoolean("showHiddenFeature", false); + hideKeyboardOnChatScroll = preferences.getBoolean("hideKeyboardOnChatScroll", false); configLoaded = true; } } @@ -302,11 +305,11 @@ public class NekoConfig { editor.commit(); } - public static void toggleSaveCacheToPrivateDirectory() { - saveCacheToPrivateDirectory = !saveCacheToPrivateDirectory; + public static void toggleSaveCacheToSdcard() { + saveCacheToSdcard = !saveCacheToSdcard; SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("nekoconfig", Activity.MODE_PRIVATE); SharedPreferences.Editor editor = preferences.edit(); - editor.putBoolean("saveCacheToPrivateDirectory", saveCacheToPrivateDirectory); + editor.putBoolean("saveCacheToSdcard", saveCacheToSdcard); editor.commit(); } @@ -406,11 +409,20 @@ public class NekoConfig { editor.commit(); } - public static void toggleShowHiddenFeature() { + /*public static void toggleShowHiddenFeature() { showHiddenFeature = !showHiddenFeature; SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("nekoconfig", Activity.MODE_PRIVATE); SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean("showHiddenFeature", showHiddenFeature); editor.commit(); + } */ + + public static void toggleHideKeyboardOnChatScroll() { + hideKeyboardOnChatScroll = !hideKeyboardOnChatScroll; + SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("nekoconfig", Activity.MODE_PRIVATE); + SharedPreferences.Editor editor = preferences.edit(); + editor.putBoolean("hideKeyboardOnChatScroll", hideKeyboardOnChatScroll); + editor.commit(); } -} + +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/NekoSettingsActivity.java b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/NekoSettingsActivity.java index 1d5ab505b..47cee00ec 100644 --- a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/NekoSettingsActivity.java +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/NekoSettingsActivity.java @@ -18,36 +18,13 @@ import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.TextView; - import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; - -import org.telegram.messenger.AndroidUtilities; -import org.telegram.messenger.FileLog; -import org.telegram.messenger.LocaleController; -import org.telegram.messenger.MessagesController; -import org.telegram.messenger.NotificationCenter; -import org.telegram.messenger.R; -import org.telegram.messenger.SharedConfig; -import org.telegram.messenger.UserConfig; +import org.telegram.messenger.*; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLRPC; -import org.telegram.ui.ActionBar.ActionBar; -import org.telegram.ui.ActionBar.ActionBarMenuItem; -import org.telegram.ui.ActionBar.AlertDialog; -import org.telegram.ui.ActionBar.BaseFragment; -import org.telegram.ui.ActionBar.BottomSheet; -import org.telegram.ui.ActionBar.Theme; -import org.telegram.ui.ActionBar.ThemeDescription; -import org.telegram.ui.Cells.EmptyCell; -import org.telegram.ui.Cells.HeaderCell; -import org.telegram.ui.Cells.NotificationsCheckCell; -import org.telegram.ui.Cells.RadioColorCell; -import org.telegram.ui.Cells.ShadowSectionCell; -import org.telegram.ui.Cells.TextCheckCell; -import org.telegram.ui.Cells.TextDetailSettingsCell; -import org.telegram.ui.Cells.TextInfoPrivacyCell; -import org.telegram.ui.Cells.TextSettingsCell; +import org.telegram.ui.ActionBar.*; +import org.telegram.ui.Cells.*; import org.telegram.ui.Components.AlertsCreator; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.RecyclerListView; @@ -68,8 +45,14 @@ public class NekoSettingsActivity extends BaseFragment { private int connectionRow; private int ipv6Row; + private int disableProxyWhenVpnEnabledRow; private int connection2Row; + private int dialogsRow; + private int sortMenuRow; + private int filterMenuRow; + private int dialogs2Row; + private int dialogsFilterRow; private int openFilterByActionBarRow; private int openFilterByFabRow; @@ -77,12 +60,15 @@ public class NekoSettingsActivity extends BaseFragment { private int chatRow; private int inappCameraRow; + private int disableChatActionRow; private int useSystemEmojiRow; private int ignoreBlockedRow; private int hideProxySponsorChannelRow; - private int saveCacheToPrivateDirectoryRow; + private int saveCacheToSdcardRow; + private int skipOpenLinkConfiirm; private int pauseMusicOnRecordRow; private int disablePhotoSideActionRow; + private int hideKeyboardOnChatScrollRow; private int mapPreviewRow; private int stickerSizeRow; private int translationProviderRow; @@ -92,6 +78,7 @@ public class NekoSettingsActivity extends BaseFragment { private int settingsRow; private int typefaceRow; private int hidePhoneRow; + private int disableUndoRow; private int nameOrderRow; private int transparentStatusBarRow; private int forceTabletRow; @@ -101,6 +88,10 @@ public class NekoSettingsActivity extends BaseFragment { private int actionBarDecorationRow; private int needRestartRow; + private int privacyRow; + private int disableSystemAccountRow; + private int privacy2Row; + private int experimentRow; private int smoothKeyboardRow; private int disableFilteringRow; @@ -166,16 +157,31 @@ public class NekoSettingsActivity extends BaseFragment { ConnectionsManager.native_setUseIpv6(a, NekoConfig.useIPv6); } } + } else if (position == disableProxyWhenVpnEnabledRow) { + NekoXConfig.toggleDisableProxyWhenVpnEnabled(); + if (view instanceof TextCheckCell) { + ((TextCheckCell) view).setChecked(NekoXConfig.disableProxyWhenVpnEnabled); + } } else if (position == hidePhoneRow) { NekoConfig.toggleHidePhone(); if (view instanceof TextCheckCell) { ((TextCheckCell) view).setChecked(NekoConfig.hidePhone); } + } else if (position == disableUndoRow) { + NekoXConfig.toggleDisableUndo(); + if (view instanceof TextCheckCell) { + ((TextCheckCell) view).setChecked(NekoXConfig.disableUndo); + } } else if (position == inappCameraRow) { SharedConfig.toggleInappCamera(); if (view instanceof TextCheckCell) { ((TextCheckCell) view).setChecked(SharedConfig.inappCamera); } + } else if (position == disableChatActionRow) { + NekoXConfig.toggleDisableChatAction(); + if (view instanceof TextCheckCell) { + ((TextCheckCell) view).setChecked(NekoXConfig.disableChatAction); + } } else if (position == forceTabletRow) { NekoConfig.toggleForceTablet(); if (view instanceof TextCheckCell) { @@ -202,16 +208,21 @@ public class NekoSettingsActivity extends BaseFragment { MessagesController.getInstance(a).checkProxyInfo(true); } } - } else if (position == saveCacheToPrivateDirectoryRow) { - NekoConfig.toggleSaveCacheToPrivateDirectory(); + } else if (position == saveCacheToSdcardRow) { + NekoConfig.toggleSaveCacheToSdcard(); if (view instanceof TextCheckCell) { - ((TextCheckCell) view).setChecked(NekoConfig.saveCacheToPrivateDirectory); + ((TextCheckCell) view).setChecked(NekoConfig.saveCacheToSdcard); + } + } else if (position == skipOpenLinkConfiirm) { + NekoXConfig.toggleSkipOpenLinkConfirm(); + if (view instanceof TextCheckCell) { + ((TextCheckCell) view).setChecked(NekoXConfig.skipOpenLinkConfirm); } } else if (position == useSystemEmojiRow) { SharedConfig.useSystemEmoji = !SharedConfig.useSystemEmoji; SharedPreferences.Editor editor = MessagesController.getGlobalMainSettings().edit(); editor.putBoolean("useSystemEmoji", SharedConfig.useSystemEmoji); - editor.commit(); + editor.apply(); if (view instanceof TextCheckCell) { ((TextCheckCell) view).setChecked(SharedConfig.useSystemEmoji); } @@ -520,9 +531,21 @@ public class NekoSettingsActivity extends BaseFragment { if (view instanceof TextCheckCell) { ((TextCheckCell) view).setChecked(NekoConfig.openFilterByFab); } - } else if (position == connection2Row) { - NekoConfig.toggleShowHiddenFeature(); - updateRows(); + } else if (position == hideKeyboardOnChatScrollRow) { + NekoConfig.toggleHideKeyboardOnChatScroll(); + if (view instanceof TextCheckCell) { + ((TextCheckCell) view).setChecked(NekoConfig.hideKeyboardOnChatScroll); + } + } else if (position == sortMenuRow) { + showSortMenuAlert(); + } else if (position == filterMenuRow) { + showFilterMenuAlert(); + } else if (position == disableSystemAccountRow) { + NekoXConfig.toggleDisableSystemAccount(); + getContactsController().deleteUnknownAppAccounts(); + if (view instanceof TextCheckCell) { + ((TextCheckCell) view).setChecked(NekoXConfig.disableSystemAccount); + } } }); @@ -541,43 +564,62 @@ public class NekoSettingsActivity extends BaseFragment { private void updateRows() { rowCount = 0; + connectionRow = rowCount++; ipv6Row = rowCount++; + disableProxyWhenVpnEnabledRow = rowCount ++; connection2Row = rowCount++; + + dialogsRow = rowCount++; + sortMenuRow = rowCount++; + filterMenuRow = rowCount++; + dialogs2Row = rowCount++; + dialogsFilterRow = rowCount++; openFilterByActionBarRow = rowCount++; openFilterByFabRow = rowCount++; dialogsFilter2Row = rowCount++; + chatRow = rowCount++; inappCameraRow = rowCount++; + disableChatActionRow = rowCount++; useSystemEmojiRow = rowCount++; ignoreBlockedRow = rowCount++; hideProxySponsorChannelRow = rowCount++; - saveCacheToPrivateDirectoryRow = NekoConfig.showHiddenFeature && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ? rowCount++ : -1; + saveCacheToSdcardRow = rowCount++; + skipOpenLinkConfiirm = rowCount ++; pauseMusicOnRecordRow = rowCount++; disablePhotoSideActionRow = rowCount++; + hideKeyboardOnChatScrollRow = rowCount++; mapPreviewRow = rowCount++; stickerSizeRow = rowCount++; messageMenuRow = rowCount++; translationProviderRow = rowCount++; chat2Row = rowCount++; + settingsRow = rowCount++; hidePhoneRow = rowCount++; - typefaceRow = rowCount++; + disableUndoRow = rowCount++; + typefaceRow = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? rowCount++ : -1; transparentStatusBarRow = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? rowCount++ : -1; forceTabletRow = rowCount++; openArchiveOnPullRow = rowCount++; nameOrderRow = rowCount++; - eventTypeRow = rowCount++; - newYearRow = rowCount++; - actionBarDecorationRow = rowCount++; + eventTypeRow = NekoXConfig.developerMode ? rowCount++ : -1; + newYearRow = NekoXConfig.developerMode ? rowCount++ : -1; + actionBarDecorationRow = NekoXConfig.developerMode ? rowCount++ : -1; needRestartRow = rowCount++; + + privacyRow = rowCount++; + disableSystemAccountRow = rowCount++; + privacy2Row = rowCount++; + experimentRow = rowCount++; smoothKeyboardRow = !AndroidUtilities.isTablet() ? rowCount++ : -1; disableFilteringRow = rowCount++; unlimitedFavedStickersRow = rowCount++; unlimitedPinnedDialogsRow = rowCount++; - deleteAccountRow = NekoConfig.showHiddenFeature ? rowCount++ : -1; + deleteAccountRow = rowCount++; experiment2Row = rowCount++; if (listAdapter != null) { listAdapter.notifyDataSetChanged(); @@ -684,7 +726,7 @@ public class NekoSettingsActivity extends BaseFragment { linearLayoutInviteContainer.setOrientation(LinearLayout.VERTICAL); linearLayout.addView(linearLayoutInviteContainer, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); - int count = 10; + int count = NekoXConfig.developerMode ? 10 : 9; for (int a = 0; a < count; a++) { TextCheckCell textCell = new TextCheckCell(context); switch (a) { @@ -709,25 +751,25 @@ public class NekoSettingsActivity extends BaseFragment { break; } case 5: { - textCell.setTextAndCheck(LocaleController.getString("MessageDetails", R.string.MessageDetails), NekoConfig.showMessageDetails, false); - break; - } - case 6: { textCell.setTextAndCheck(LocaleController.getString("Translate", R.string.Translate), NekoConfig.showTranslate, false); break; } - case 7: { + case 6: { textCell.setTextAndCheck(LocaleController.getString("ReportChat", R.string.ReportChat), NekoConfig.showReport, false); break; } - case 8: { + case 7: { textCell.setTextAndCheck(LocaleController.getString("EditAdminRights", R.string.EditAdminRights), NekoConfig.showAdminActions, false); break; } - case 9: { + case 8: { textCell.setTextAndCheck(LocaleController.getString("ChangePermissions", R.string.ChangePermissions), NekoConfig.showChangePermissions, false); break; } + case 9: { + textCell.setTextAndCheck(LocaleController.getString("MessageDetails", R.string.MessageDetails), NekoConfig.showMessageDetails, false); + break; + } } textCell.setTag(a); textCell.setBackgroundDrawable(Theme.getSelectorDrawable(false)); @@ -761,30 +803,223 @@ public class NekoSettingsActivity extends BaseFragment { break; } case 5: { - NekoConfig.toggleShowMessageDetails(); - textCell.setChecked(NekoConfig.showMessageDetails); - break; - } - case 6: { NekoConfig.toggleShowTranslate(); textCell.setChecked(NekoConfig.showTranslate); break; } - case 7: { + case 6: { NekoConfig.toggleShowReport(); textCell.setChecked(NekoConfig.showReport); break; } - case 8: { + case 7: { NekoConfig.toggleShowAdminActions(); textCell.setChecked(NekoConfig.showAdminActions); break; } - case 9: { + case 8: { NekoConfig.toggleShowChangePermissions(); textCell.setChecked(NekoConfig.showChangePermissions); break; } + case 9: { + NekoConfig.toggleShowMessageDetails(); + textCell.setChecked(NekoConfig.showMessageDetails); + break; + } + } + }); + } + builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null); + builder.setView(linearLayout); + showDialog(builder.create()); + } + + private void showFilterMenuAlert() { + if (getParentActivity() == null) { + return; + } + Context context = getParentActivity(); + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(LocaleController.getString("FilterMenu", R.string.FilterMenu)); + + LinearLayout linearLayout = new LinearLayout(context); + linearLayout.setOrientation(LinearLayout.VERTICAL); + + LinearLayout linearLayoutInviteContainer = new LinearLayout(context); + linearLayoutInviteContainer.setOrientation(LinearLayout.VERTICAL); + linearLayout.addView(linearLayoutInviteContainer, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + + int count = 9; + for (int a = 0; a < count; a++) { + TextCheckCell textCell = new TextCheckCell(context); + switch (a) { + case 0: { + textCell.setTextAndCheck(LocaleController.getString("Users", R.string.Users), NekoXConfig.filterUsers, false); + break; + } + case 1: { + textCell.setTextAndCheck(LocaleController.getString("Contacts", R.string.Contacts), NekoXConfig.filterContacts, false); + break; + } + case 2: { + textCell.setTextAndCheck(LocaleController.getString("Groups", R.string.Groups), NekoXConfig.filterGroups, false); + break; + } + case 3: { + textCell.setTextAndCheck(LocaleController.getString("Channels", R.string.Channels), NekoXConfig.filterChannels, false); + break; + } + case 4: { + textCell.setTextAndCheck(LocaleController.getString("Bots", R.string.Bots), NekoXConfig.filterBots, false); + break; + } + case 5: { + textCell.setTextAndCheck(LocaleController.getString("Admins", R.string.Admins), NekoXConfig.filterAdmins, false); + break; + } + case 6: { + textCell.setTextAndCheck(LocaleController.getString("NotificationsUnmuted", R.string.NotificationsUnmuted), NekoXConfig.filterUnmuted, false); + break; + } + case 7: { + textCell.setTextAndCheck(LocaleController.getString("NotificationsUnread", R.string.NotificationsUnread), NekoXConfig.filterUnread, false); + break; + } + case 8: { + textCell.setTextAndCheck(LocaleController.getString("NotificationsUnmutedAndUnread", R.string.NotificationsUnmutedAndUnread), NekoXConfig.filterUnmutedAndUnread, false); + break; + } + } + textCell.setTag(a); + textCell.setBackgroundDrawable(Theme.getSelectorDrawable(false)); + linearLayoutInviteContainer.addView(textCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + textCell.setOnClickListener(v2 -> { + Integer tag = (Integer) v2.getTag(); + switch (tag) { + case 0: { + NekoXConfig.toggleFilterUsers(); + textCell.setChecked(NekoXConfig.filterUsers); + break; + } + case 1: { + NekoXConfig.toggleFilterContacts(); + textCell.setChecked(NekoXConfig.filterContacts); + break; + } + case 2: { + NekoXConfig.toggleFilterGroups(); + textCell.setChecked(NekoXConfig.filterGroups); + break; + } + case 3: { + NekoXConfig.toggleFilterChannels(); + textCell.setChecked(NekoXConfig.filterChannels); + break; + } + case 4: { + NekoXConfig.toggleFilterBots(); + textCell.setChecked(NekoXConfig.filterBots); + break; + } + case 5: { + NekoXConfig.toggleFilterAdmins(); + textCell.setChecked(NekoXConfig.filterAdmins); + break; + } + case 6: { + NekoXConfig.toggleFilterUnmuted(); + textCell.setChecked(NekoXConfig.filterUnmuted); + break; + } + case 7: { + NekoXConfig.toggleDisableFilterUnread(); + textCell.setChecked(NekoXConfig.filterUnread); + break; + } + case 8: { + NekoXConfig.toggleFilterUnmutedAndUnread(); + textCell.setChecked(NekoXConfig.filterUnmutedAndUnread); + break; + } + } + }); + } + builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null); + builder.setView(linearLayout); + showDialog(builder.create()); + } + + private void showSortMenuAlert() { + if (getParentActivity() == null) { + return; + } + Context context = getParentActivity(); + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(LocaleController.getString("SortMenu", R.string.SortMenu)); + + LinearLayout linearLayout = new LinearLayout(context); + linearLayout.setOrientation(LinearLayout.VERTICAL); + + LinearLayout linearLayoutInviteContainer = new LinearLayout(context); + linearLayoutInviteContainer.setOrientation(LinearLayout.VERTICAL); + linearLayout.addView(linearLayoutInviteContainer, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + + int count = 4; + for (int a = 0; a < count; a++) { + TextCheckCell textCell = new TextCheckCell(context); + switch (a) { + case 0: { + textCell.setTextAndCheck(LocaleController.getString("SortByUnread", R.string.SortByUnread), NekoXConfig.sortByUnread, false); + break; + } + case 1: { + textCell.setTextAndCheck(LocaleController.getString("SortByUnmuted", R.string.SortByUnmuted), NekoXConfig.sortByUnmuted, false); + break; + } + case 2: { + textCell.setTextAndCheck(LocaleController.getString("SortByUser", R.string.SortByUser), NekoXConfig.sortByUser, false); + break; + } + case 3: { + textCell.setTextAndCheck(LocaleController.getString("SortByContacts", R.string.SortByContacts), NekoXConfig.sortByContacts, false); + break; + } + } + textCell.setTag(a); + textCell.setBackgroundDrawable(Theme.getSelectorDrawable(false)); + linearLayoutInviteContainer.addView(textCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + textCell.setOnClickListener(view -> { + Integer tag = (Integer) view.getTag(); + switch (tag) { + case 0: { + NekoXConfig.toggleSortByUnread(); + if (view instanceof TextCheckCell) { + ((TextCheckCell) view).setChecked(NekoXConfig.sortByUnread); + } + break; + } + case 1: { + NekoXConfig.toggleSortByUnmuted(); + if (view instanceof TextCheckCell) { + ((TextCheckCell) view).setChecked(NekoXConfig.sortByUnmuted); + } + break; + } + case 2: { + NekoXConfig.toggleSortByUser(); + if (view instanceof TextCheckCell) { + ((TextCheckCell) view).setChecked(NekoXConfig.sortByUser); + } + break; + } + case 3: { + NekoXConfig.toggleSortByContacts(); + if (view instanceof TextCheckCell) { + ((TextCheckCell) view).setChecked(NekoXConfig.sortByContacts); + } + break; + } } }); } @@ -934,7 +1169,7 @@ public class NekoSettingsActivity extends BaseFragment { value = LocaleController.getString("FirstLast", R.string.FirstLast); break; } - textCell.setTextAndValue(LocaleController.getString("NameOrder", R.string.NameOrder), value, true); + textCell.setTextAndValue(LocaleController.getString("NameOrder", R.string.NameOrder), value, eventTypeRow != -1); } else if (position == mapPreviewRow) { String value; switch (NekoConfig.mapPreviewProvider) { @@ -981,6 +1216,10 @@ public class NekoSettingsActivity extends BaseFragment { textCell.setTextAndValue(LocaleController.getString("StickerSize", R.string.StickerSize), String.valueOf(Math.round(NekoConfig.stickerSize)), true); } else if (position == messageMenuRow) { textCell.setText(LocaleController.getString("MessageMenu", R.string.MessageMenu), true); + } else if (position == sortMenuRow) { + textCell.setText(LocaleController.getString("SortMenu", R.string.SortMenu), true); + } else if (position == filterMenuRow) { + textCell.setText(LocaleController.getString("FilterMenu", R.string.FilterMenu), false); } else if (position == deleteAccountRow) { textCell.setText(LocaleController.getString("DeleteAccount", R.string.DeleteAccount), false); textCell.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText)); @@ -1014,18 +1253,27 @@ public class NekoSettingsActivity extends BaseFragment { case 3: { TextCheckCell textCell = (TextCheckCell) holder.itemView; textCell.setEnabled(true, null); + if (position == ipv6Row) { textCell.setTextAndCheck(LocaleController.getString("IPv6", R.string.IPv6), NekoConfig.useIPv6, false); + } else if (position == disableProxyWhenVpnEnabledRow) { + textCell.setTextAndCheck(LocaleController.getString("DisableProxyWhenVpnEnabled", R.string.DisableProxyWhenVpnEnabled), NekoXConfig.disableProxyWhenVpnEnabled, true); } else if (position == hidePhoneRow) { textCell.setTextAndCheck(LocaleController.getString("HidePhone", R.string.HidePhone), NekoConfig.hidePhone, true); + } else if (position == disableUndoRow) { + textCell.setTextAndCheck(LocaleController.getString("DisableUndo", R.string.DisableUndo), NekoXConfig.disableUndo, true); } else if (position == inappCameraRow) { textCell.setTextAndCheck(LocaleController.getString("DebugMenuEnableCamera", R.string.DebugMenuEnableCamera), SharedConfig.inappCamera, true); + } else if (position == disableChatActionRow) { + textCell.setTextAndCheck(LocaleController.getString("DisableChatAction", R.string.DisableChatAction), NekoXConfig.disableChatAction, true); } else if (position == transparentStatusBarRow) { textCell.setTextAndCheck(LocaleController.getString("TransparentStatusBar", R.string.TransparentStatusBar), NekoConfig.transparentStatusBar, true); } else if (position == hideProxySponsorChannelRow) { textCell.setTextAndCheck(LocaleController.getString("HideProxySponsorChannel", R.string.HideProxySponsorChannel), NekoConfig.hideProxySponsorChannel, true); - } else if (position == saveCacheToPrivateDirectoryRow) { - textCell.setTextAndCheck(LocaleController.getString("SaveCacheToPrivateDirectory", R.string.SaveCacheToPrivateDirectory), NekoConfig.saveCacheToPrivateDirectory, true); + } else if (position == saveCacheToSdcardRow) { + textCell.setTextAndCheck(LocaleController.getString("SaveCacheToSdcard", R.string.SaveCacheToSdcard), NekoConfig.saveCacheToSdcard, true); + } else if (position == skipOpenLinkConfiirm) { + textCell.setTextAndCheck(LocaleController.getString("SkipOpenLinkConfirm", R.string.SkipOpenLinkConfirm), NekoXConfig.skipOpenLinkConfirm, true); } else if (position == useSystemEmojiRow) { textCell.setTextAndCheck(LocaleController.getString("EmojiUseDefault", R.string.EmojiUseDefault), SharedConfig.useSystemEmoji, true); } else if (position == typefaceRow) { @@ -1055,7 +1303,13 @@ public class NekoSettingsActivity extends BaseFragment { textCell.setTextAndCheck(LocaleController.getString("TapOnActionBar", R.string.TapOnActionBar), NekoConfig.openFilterByActionBar, true); } else if (position == openFilterByFabRow) { textCell.setTextAndCheck(LocaleController.getString("TapOnFab", R.string.TapOnFab), NekoConfig.openFilterByFab, false); + } else if (position == hideKeyboardOnChatScrollRow) { + textCell.setTextAndCheck(LocaleController.getString("HideKeyboardOnChatScroll", R.string.HideKeyboardOnChatScroll), NekoConfig.hideKeyboardOnChatScroll, true); + } else if (position == disableSystemAccountRow) { + textCell.setTextAndCheck(LocaleController.getString("DisableSystemAccount", R.string.DisableSystemAccount), NekoXConfig.disableSystemAccount, true); } + + break; } case 4: { @@ -1070,6 +1324,10 @@ public class NekoSettingsActivity extends BaseFragment { headerCell.setText(LocaleController.getString("Experiment", R.string.Experiment)); } else if (position == dialogsFilterRow) { headerCell.setText(LocaleController.getString("OpenDialogsFilterBy", R.string.OpenDialogsFilterBy)); + } else if (position == dialogsRow) { + headerCell.setText(LocaleController.getString("DialogsSettings", R.string.DialogsSettings)); + } else if (position == privacyRow) { + headerCell.setText(LocaleController.getString("PrivacyTitle", R.string.PrivacyTitle)); } break; } @@ -1086,16 +1344,17 @@ public class NekoSettingsActivity extends BaseFragment { @Override public boolean isEnabled(RecyclerView.ViewHolder holder) { int position = holder.getAdapterPosition(); - return position == hidePhoneRow || position == inappCameraRow || position == ignoreBlockedRow || - position == useSystemEmojiRow || position == ipv6Row || position == typefaceRow || position == nameOrderRow || + return position == hidePhoneRow || position == disableUndoRow || position == inappCameraRow || position == disableChatActionRow || position == ignoreBlockedRow || + position == useSystemEmojiRow || position == ipv6Row || position == disableProxyWhenVpnEnabledRow || position == typefaceRow || position == nameOrderRow || position == forceTabletRow || position == mapPreviewRow || position == newYearRow || position == actionBarDecorationRow || position == eventTypeRow || position == transparentStatusBarRow || - position == hideProxySponsorChannelRow || position == saveCacheToPrivateDirectoryRow || + position == hideProxySponsorChannelRow || position == saveCacheToSdcardRow || position == skipOpenLinkConfiirm || position == disableFilteringRow || position == stickerSizeRow || position == unlimitedFavedStickersRow || position == messageMenuRow || position == deleteAccountRow || position == translationProviderRow || position == smoothKeyboardRow || position == pauseMusicOnRecordRow || position == disablePhotoSideActionRow || position == unlimitedPinnedDialogsRow || position == openArchiveOnPullRow || - position == openFilterByActionBarRow || position == openFilterByFabRow || position == connection2Row; + position == openFilterByActionBarRow || position == openFilterByFabRow || position == hideKeyboardOnChatScrollRow || + position == sortMenuRow || position == filterMenuRow || position == disableSystemAccountRow; } @Override @@ -1136,22 +1395,25 @@ public class NekoSettingsActivity extends BaseFragment { @Override public int getItemViewType(int position) { - if (position == connection2Row || position == chat2Row || position == experiment2Row || position == dialogsFilter2Row) { + if (position == connection2Row || position == chat2Row || position == experiment2Row || position == dialogs2Row || position == dialogsFilter2Row + || position == privacy2Row) { return 1; } else if (position == nameOrderRow || position == mapPreviewRow || position == stickerSizeRow || position == messageMenuRow || + position == filterMenuRow || position == sortMenuRow || position == deleteAccountRow || position == translationProviderRow || position == eventTypeRow || position == actionBarDecorationRow) { return 2; - } else if (position == ipv6Row || position == hidePhoneRow || position == inappCameraRow || + } else if (position == ipv6Row || position == disableProxyWhenVpnEnabledRow || position == hidePhoneRow || position == disableUndoRow || position == inappCameraRow || position == disableChatActionRow || position == transparentStatusBarRow || position == hideProxySponsorChannelRow || position == ignoreBlockedRow || position == useSystemEmojiRow || position == typefaceRow || position == forceTabletRow || position == newYearRow || - position == saveCacheToPrivateDirectoryRow || position == unlimitedFavedStickersRow || + position == saveCacheToSdcardRow || position == unlimitedFavedStickersRow || position == skipOpenLinkConfiirm || position == disableFilteringRow || position == smoothKeyboardRow || position == pauseMusicOnRecordRow || position == disablePhotoSideActionRow || position == unlimitedPinnedDialogsRow || position == openArchiveOnPullRow || - position == openFilterByActionBarRow || position == openFilterByFabRow) { + position == openFilterByActionBarRow || position == openFilterByFabRow || position == hideKeyboardOnChatScrollRow || + position == disableSystemAccountRow) { return 3; } else if (position == settingsRow || position == connectionRow || position == chatRow || position == experimentRow || - position == dialogsFilterRow) { + position == dialogsRow || position == dialogsFilterRow || position == privacyRow) { return 4; } else if (position == needRestartRow) { return 7; diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/NekoXConfig.java b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/NekoXConfig.java new file mode 100644 index 000000000..9d65b350f --- /dev/null +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/NekoXConfig.java @@ -0,0 +1,220 @@ +package tw.nekomimi.nekogram; + +import android.content.Context; +import android.content.SharedPreferences; + +import org.telegram.messenger.ApplicationLoader; + +public class NekoXConfig { + + public static int[] DEVELOPER_IDS = {896711046, 1121722278, 899300686, 339984997}; + + private static SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("nekox_config", Context.MODE_PRIVATE); + + public static boolean disableChatAction; + + public static boolean developerModeEntrance; + public static boolean developerMode; + + public static boolean disableFlagSecure; + public static boolean disableScreenshotDetection; + + public static boolean showTestBackend; + public static boolean showBotLogin; + + public static boolean sortByUnread; + public static boolean sortByUnmuted; + public static boolean sortByUser; + public static boolean sortByContacts; + + public static boolean disableUndo; + + public static boolean filterUsers; + public static boolean filterContacts; + public static boolean filterGroups; + public static boolean filterChannels; + public static boolean filterBots; + public static boolean filterAdmins; + public static boolean filterUnmuted; + public static boolean filterUnread; + public static boolean filterUnmutedAndUnread; + + public static boolean disableSystemAccount; + public static boolean disableProxyWhenVpnEnabled; + public static boolean skipOpenLinkConfirm; + + + static { + + disableChatAction = preferences.getBoolean("disable_chat_action", false); + + developerMode = preferences.getBoolean("developer_mode", false); + + disableFlagSecure = preferences.getBoolean("disable_flag_secure", false); + disableScreenshotDetection = preferences.getBoolean("disable_screenshot_detection", false); + + showTestBackend = preferences.getBoolean("show_test_backend", false); + showBotLogin = preferences.getBoolean("show_bot_login", false); + + sortByUnread = preferences.getBoolean("sort_by_unread", true); + sortByUnmuted = preferences.getBoolean("sort_by_unmuted", false); + sortByUser = preferences.getBoolean("sort_by_user", false); + sortByContacts = preferences.getBoolean("sort_by_contacts", false); + + disableUndo = preferences.getBoolean("disable_undo", true); + + filterUsers = preferences.getBoolean("filter_users", true); + filterContacts = preferences.getBoolean("filter_contacts", true); + filterGroups = preferences.getBoolean("filter_groups", true); + filterChannels = preferences.getBoolean("filter_channels", true); + filterBots = preferences.getBoolean("filter_bots", true); + filterAdmins = preferences.getBoolean("filter_admins", true); + filterUnmuted = preferences.getBoolean("filter_unmuted", true); + filterUnread = preferences.getBoolean("filter_unread", true); + filterUnmutedAndUnread = preferences.getBoolean("filter_unmuted_and_unread", true); + + disableSystemAccount = preferences.getBoolean("disable_system_account", false); + disableProxyWhenVpnEnabled = preferences.getBoolean("disable_proxy_when_vpn_enabled", false); + skipOpenLinkConfirm = preferences.getBoolean("skip_open_link_confirm", false); + + } + + public static void toggleDisableChatAction() { + + preferences.edit().putBoolean("disable_chat_action", disableChatAction = !disableChatAction).apply(); + + } + + public static void toggleDeveloperMode() { + + preferences.edit().putBoolean("developer_mode", developerMode = !developerMode).apply(); + + } + + public static void toggleDisableFlagSecure() { + + preferences.edit().putBoolean("disable_flag_secure", disableFlagSecure = !disableFlagSecure).apply(); + + } + + public static void toggleDisableScreenshotDetection() { + + preferences.edit().putBoolean("disable_screenshot_detection", disableScreenshotDetection = !disableScreenshotDetection).apply(); + + } + + public static void toggleShowTestBackend() { + + preferences.edit().putBoolean("show_test_backend", showTestBackend = !showTestBackend).apply(); + + } + + public static void toggleShowBotLogin() { + + preferences.edit().putBoolean("show_bot_login", showBotLogin = !showBotLogin).apply(); + + } + + public static void toggleSortByUnread() { + + preferences.edit().putBoolean("sort_by_unread", sortByUnread = !sortByUnread).apply(); + + } + + public static void toggleSortByUnmuted() { + + preferences.edit().putBoolean("sort_by_unmuted", sortByUnmuted = !sortByUnmuted).apply(); + + } + + public static void toggleSortByUser() { + + preferences.edit().putBoolean("sort_by_user", sortByUser = !sortByUser).apply(); + + } + + public static void toggleSortByContacts() { + + preferences.edit().putBoolean("sort_by_contacts", sortByContacts = !sortByContacts).apply(); + + } + + public static void toggleDisableUndo() { + + preferences.edit().putBoolean("disable_undo", disableUndo = !disableUndo).apply(); + + } + + public static void toggleFilterUsers() { + + preferences.edit().putBoolean("filter_users", filterUsers = !filterUsers).apply(); + + } + + public static void toggleFilterContacts() { + + preferences.edit().putBoolean("filter_contacts", filterContacts = !filterContacts).apply(); + + } + + public static void toggleFilterGroups() { + + preferences.edit().putBoolean("filterGroups", filterGroups = !filterGroups).apply(); + + } + + public static void toggleFilterChannels() { + + preferences.edit().putBoolean("filter_channels", filterChannels = !filterChannels).apply(); + + } + + public static void toggleFilterBots() { + + preferences.edit().putBoolean("filter_bots", filterBots = !filterBots).apply(); + + } + + public static void toggleFilterAdmins() { + + preferences.edit().putBoolean("filter_admins", filterAdmins = !filterAdmins).apply(); + + } + + public static void toggleFilterUnmuted() { + + preferences.edit().putBoolean("filter_unmuted", filterUnmuted = !filterUnmuted).apply(); + + } + + public static void toggleDisableFilterUnread() { + + preferences.edit().putBoolean("filter_unread", filterUnread = !filterUnread).apply(); + + } + + public static void toggleFilterUnmutedAndUnread() { + + preferences.edit().putBoolean("filter_unmuted_and_unread", filterUnmutedAndUnread = !filterUnmutedAndUnread).apply(); + + } + + public static void toggleDisableSystemAccount() { + + preferences.edit().putBoolean("disable_system_account", disableSystemAccount = !disableSystemAccount).apply(); + + } + + public static void toggleDisableProxyWhenVpnEnabled() { + + preferences.edit().putBoolean("disable_proxy_when_vpn_enabled", disableProxyWhenVpnEnabled = !disableProxyWhenVpnEnabled).apply(); + + } + + public static void toggleSkipOpenLinkConfirm() { + + preferences.edit().putBoolean("skip_open_link_confirm", skipOpenLinkConfirm = !skipOpenLinkConfirm).apply(); + + + } +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/NekoXPushService.kt b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/NekoXPushService.kt new file mode 100644 index 000000000..69ac698bf --- /dev/null +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/NekoXPushService.kt @@ -0,0 +1,48 @@ +package tw.nekomimi.nekogram + +import android.annotation.SuppressLint +import android.annotation.TargetApi +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.os.Build +import android.service.notification.NotificationListenerService +import androidx.annotation.RequiresApi +import androidx.core.app.NotificationCompat +import org.telegram.messenger.ApplicationLoader +import org.telegram.messenger.LocaleController +import org.telegram.messenger.R +import org.telegram.ui.LaunchActivity + +@SuppressLint("OverrideAbstract") +@RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR2) +class NekoXPushService : NotificationListenerService() { + + override fun onCreate() { + super.onCreate() + ApplicationLoader.postInitApplication() + if (NekoConfig.residentNotification) { + val activityIntent = Intent(this, LaunchActivity::class.java) + val pendingIntent = PendingIntent.getActivity(this, 0, activityIntent, 0) + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) { + val channel = NotificationChannel("nekogram", LocaleController.getString("NekogramRunning", R.string.NekogramRunning), NotificationManager.IMPORTANCE_DEFAULT) + channel.enableLights(false) + channel.enableVibration(false) + channel.setSound(null, null) + val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.createNotificationChannel(channel) + } + val notification = NotificationCompat.Builder(this, "nekogram") + .setSmallIcon(R.drawable.notification) + .setColor(-0xee5306) + .setContentTitle(LocaleController.getString("NekogramRunning", R.string.NekogramRunning)) + .setContentIntent(pendingIntent) + .setCategory(NotificationCompat.CATEGORY_STATUS) + .build() + startForeground(38264, notification) + } + } + +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/NekoXSettingActivity.java b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/NekoXSettingActivity.java new file mode 100644 index 000000000..787ecfd4e --- /dev/null +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/NekoXSettingActivity.java @@ -0,0 +1,285 @@ +package tw.nekomimi.nekogram; + +import android.animation.AnimatorSet; +import android.annotation.SuppressLint; +import android.content.Context; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.R; +import org.telegram.ui.ActionBar.ActionBar; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.ActionBar.ThemeDescription; +import org.telegram.ui.Cells.EmptyCell; +import org.telegram.ui.Cells.HeaderCell; +import org.telegram.ui.Cells.NotificationsCheckCell; +import org.telegram.ui.Cells.ShadowSectionCell; +import org.telegram.ui.Cells.TextCheckCell; +import org.telegram.ui.Cells.TextDetailSettingsCell; +import org.telegram.ui.Cells.TextInfoPrivacyCell; +import org.telegram.ui.Cells.TextSettingsCell; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.RecyclerListView; + +public class NekoXSettingActivity extends BaseFragment { + + private RecyclerListView listView; + private ListAdapter listAdapter; + + private int rowCount; + + private int developerSettingsRow; + private int disableFlagSecureRow; + private int disableScreenshotDetectionRow; + + private int loginSettingsRow; + private int showTestBackendRow; + private int showBotLoginRow; + + @Override + public boolean onFragmentCreate() { + super.onFragmentCreate(); + updateRows(); + return true; + } + + @SuppressLint("NewApi") + @Override + public View createView(Context context) { + actionBar.setBackButtonImage(R.drawable.ic_ab_back); + actionBar.setTitle(LocaleController.getString("NekoSettings", R.string.NekoSettings)); + + if (AndroidUtilities.isTablet()) { + actionBar.setOccupyStatusBar(false); + } + actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { + @Override + public void onItemClick(int id) { + if (id == -1) { + finishFragment(); + } + } + }); + + listAdapter = new ListAdapter(context); + + fragmentView = new FrameLayout(context); + fragmentView.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundGray)); + FrameLayout frameLayout = (FrameLayout) fragmentView; + + listView = new RecyclerListView(context); + listView.setVerticalScrollBarEnabled(false); + listView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) { + @Override + public boolean supportsPredictiveItemAnimations() { + return false; + } + }); + listView.setGlowColor(Theme.getColor(Theme.key_avatar_backgroundActionBarBlue)); + listView.setAdapter(listAdapter); + listView.setItemAnimator(null); + listView.setLayoutAnimation(null); + frameLayout.addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT)); + listView.setOnItemClickListener((view, position, x, y) -> { + + if (position == disableFlagSecureRow) { + NekoXConfig.toggleDisableFlagSecure(); + if (view instanceof TextCheckCell) { + ((TextCheckCell) view).setChecked(NekoXConfig.disableFlagSecure); + } + } else if (position == disableScreenshotDetectionRow) { + NekoXConfig.toggleDisableScreenshotDetection(); + if (view instanceof TextCheckCell) { + ((TextCheckCell) view).setChecked(NekoXConfig.disableScreenshotDetection); + } + } else if (position == showTestBackendRow) { + NekoXConfig.toggleShowTestBackend(); + if (view instanceof TextCheckCell) { + ((TextCheckCell) view).setChecked(NekoXConfig.showTestBackend); + } + } else if (position == showBotLoginRow) { + NekoXConfig.toggleShowBotLogin(); + if (view instanceof TextCheckCell) { + ((TextCheckCell) view).setChecked(NekoXConfig.showBotLogin); + } + } + + }); + + return fragmentView; + } + + @Override + public void onResume() { + super.onResume(); + if (listAdapter != null) { + listAdapter.notifyDataSetChanged(); + } + } + + private void updateRows() { + rowCount = 0; + developerSettingsRow = rowCount++; + disableFlagSecureRow = rowCount++; + disableScreenshotDetectionRow = rowCount++; + + loginSettingsRow = rowCount++; + showTestBackendRow = rowCount++; + showBotLoginRow = rowCount++; + + if (listAdapter != null) { + listAdapter.notifyDataSetChanged(); + } + } + + @Override + public ThemeDescription[] getThemeDescriptions() { + return new ThemeDescription[]{ + new ThemeDescription(listView, ThemeDescription.FLAG_CELLBACKGROUNDCOLOR, new Class[]{EmptyCell.class, TextSettingsCell.class, TextCheckCell.class, HeaderCell.class, TextDetailSettingsCell.class, NotificationsCheckCell.class}, null, null, null, Theme.key_windowBackgroundWhite), + new ThemeDescription(fragmentView, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundGray), + + new ThemeDescription(actionBar, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_avatar_backgroundActionBarBlue), + new ThemeDescription(listView, ThemeDescription.FLAG_LISTGLOWCOLOR, null, null, null, null, Theme.key_avatar_backgroundActionBarBlue), + new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_ITEMSCOLOR, null, null, null, null, Theme.key_avatar_actionBarIconBlue), + new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_TITLECOLOR, null, null, null, null, Theme.key_actionBarDefaultTitle), + new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SELECTORCOLOR, null, null, null, null, Theme.key_avatar_actionBarSelectorBlue), + new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SUBMENUBACKGROUND, null, null, null, null, Theme.key_actionBarDefaultSubmenuBackground), + new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SUBMENUITEM, null, null, null, null, Theme.key_actionBarDefaultSubmenuItem), + + new ThemeDescription(listView, ThemeDescription.FLAG_SELECTOR, null, null, null, null, Theme.key_listSelector), + + new ThemeDescription(listView, 0, new Class[]{View.class}, Theme.dividerPaint, null, null, Theme.key_divider), + + new ThemeDescription(listView, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{ShadowSectionCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow), + + new ThemeDescription(listView, 0, new Class[]{TextSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText), + new ThemeDescription(listView, 0, new Class[]{TextSettingsCell.class}, new String[]{"valueTextView"}, null, null, null, Theme.key_windowBackgroundWhiteValueText), + + new ThemeDescription(listView, 0, new Class[]{NotificationsCheckCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText), + new ThemeDescription(listView, 0, new Class[]{NotificationsCheckCell.class}, new String[]{"valueTextView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText2), + new ThemeDescription(listView, 0, new Class[]{NotificationsCheckCell.class}, new String[]{"checkBox"}, null, null, null, Theme.key_switchTrack), + new ThemeDescription(listView, 0, new Class[]{NotificationsCheckCell.class}, new String[]{"checkBox"}, null, null, null, Theme.key_switchTrackChecked), + + new ThemeDescription(listView, 0, new Class[]{TextCheckCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText), + new ThemeDescription(listView, 0, new Class[]{TextCheckCell.class}, new String[]{"valueTextView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText2), + new ThemeDescription(listView, 0, new Class[]{TextCheckCell.class}, new String[]{"checkBox"}, null, null, null, Theme.key_switchTrack), + new ThemeDescription(listView, 0, new Class[]{TextCheckCell.class}, new String[]{"checkBox"}, null, null, null, Theme.key_switchTrackChecked), + + new ThemeDescription(listView, 0, new Class[]{HeaderCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueHeader), + + new ThemeDescription(listView, 0, new Class[]{TextDetailSettingsCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText), + new ThemeDescription(listView, 0, new Class[]{TextDetailSettingsCell.class}, new String[]{"valueTextView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText2), + }; + } + + private class ListAdapter extends RecyclerListView.SelectionAdapter { + + private Context mContext; + + public ListAdapter(Context context) { + mContext = context; + } + + @Override + public int getItemCount() { + return rowCount; + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + + // init + + switch (holder.getItemViewType()) { + + case 4: { + HeaderCell headerCell = (HeaderCell) holder.itemView; + if (position == developerSettingsRow) { + headerCell.setText(LocaleController.getString("DeveloperSettings", R.string.DeveloperSettings)); + } else if (position == loginSettingsRow) { + headerCell.setText(LocaleController.getString("LoginSettings", R.string.LoginSettings)); + } + break; + } + + case 3: { + TextCheckCell textCell = (TextCheckCell) holder.itemView; + textCell.setEnabled(true, null); + if (position == disableFlagSecureRow) { + textCell.setTextAndCheck(LocaleController.getString("DisableFlagSecure", R.string.DisableFlagSecure), NekoXConfig.disableFlagSecure, true); + } else if (position == disableScreenshotDetectionRow) { + textCell.setTextAndCheck(LocaleController.getString("DisableScreenshotDetection", R.string.DisableScreenshotDetection), NekoXConfig.disableScreenshotDetection, false); + } else if (position == showTestBackendRow) { + textCell.setTextAndCheck(LocaleController.getString("ShowTestBackend", R.string.ShowTestBackend), NekoXConfig.showTestBackend, true); + } else if (position == showBotLoginRow) { + textCell.setTextAndCheck(LocaleController.getString("ShowBotLogin", R.string.ShowBotLogin), NekoXConfig.showBotLogin, false); + } + break; + } + + } + + } + + @Override + public boolean isEnabled(RecyclerView.ViewHolder holder) { + int position = holder.getAdapterPosition(); + return position == disableFlagSecureRow || position == disableScreenshotDetectionRow || position == showTestBackendRow || position == showBotLoginRow; + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = null; + switch (viewType) { + case 1: + view = new ShadowSectionCell(mContext); + break; + case 2: + view = new TextSettingsCell(mContext); + view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + break; + case 3: + view = new TextCheckCell(mContext); + view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + break; + case 4: + view = new HeaderCell(mContext); + view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + break; + case 5: + view = new NotificationsCheckCell(mContext); + view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + break; + case 6: + view = new TextDetailSettingsCell(mContext); + view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + break; + case 7: + view = new TextInfoPrivacyCell(mContext); + view.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider, Theme.key_windowBackgroundGrayShadow)); + break; + } + view.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT)); + return new RecyclerListView.Holder(view); + } + + @Override + public int getItemViewType(int position) { + if (position == developerSettingsRow || position == loginSettingsRow) { + return 4; + } else if (position == disableFlagSecureRow || position == disableScreenshotDetectionRow || + position == showTestBackendRow || position == showBotLoginRow) { + return 3; + } + return 6; + } + } +} diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ProxyManager.kt b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ProxyManager.kt new file mode 100644 index 000000000..83f949bb1 --- /dev/null +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ProxyManager.kt @@ -0,0 +1,109 @@ +package tw.nekomimi.nekogram + +import android.content.Context +import com.v2ray.ang.dto.AngConfig +import org.telegram.messenger.ApplicationLoader +import java.net.InetSocketAddress +import java.net.ServerSocket +import kotlin.random.Random + +object ProxyManager { + + val pref by lazy { ApplicationLoader.applicationContext.getSharedPreferences("port_cfg", Context.MODE_PRIVATE) } + + @JvmStatic + fun getPortForBean(bean: AngConfig.VmessBean): Int { + + val hash = (bean.address + bean.port + bean.path).hashCode().toString() + + var port = pref.getInt(hash, -1) + + if (!isPorxyAvilable(port)) { + + port = mkNewPort() + + pref.edit().putInt(hash, port).apply() + + } + + return port + + } + + @JvmStatic + fun getPortForBean(bean: ShadowsocksLoader.Bean): Int { + + val hash = bean.hash.toString() + + var port = pref.getInt(hash, -1) + + if (!isPorxyAvilable(port)) { + + port = mkNewPort() + + pref.edit().putInt(hash, port).apply() + + } + + return port + + } + + @JvmStatic + fun getPortForBean(bean: ShadowsocksRLoader.Bean): Int { + + val hash = bean.hash.toString() + + var port = pref.getInt(hash, -1) + + if (!isPorxyAvilable(port)) { + + port = mkNewPort() + + pref.edit().putInt(hash, port).apply() + + } + + return port + + } + + fun mkNewPort(): Int { + + val random = Random(System.currentTimeMillis()) + + var port: Int + + do { + + port = random.nextInt(2048, 32768) + + } while (!isPorxyAvilable(port)) + + return port + + } + + fun isPorxyAvilable(port: Int): Boolean { + + if (port !in 2048..32767) return false + + runCatching { + + val server = ServerSocket() + + server.bind(InetSocketAddress("127.0.0.1",port)) + + server.close() + + }.onFailure { + + return false + + } + + return true + + } + +} diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ShadowsocksLoader.kt b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ShadowsocksLoader.kt new file mode 100644 index 000000000..ad985fda8 --- /dev/null +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ShadowsocksLoader.kt @@ -0,0 +1,197 @@ +package tw.nekomimi.nekogram + +import android.util.Base64 +import android.util.LongSparseArray +import com.v2ray.ang.V2RayConfig.SS_PROTOCOL +import com.v2ray.ang.util.Utils +import kotlinx.coroutines.runBlocking +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull +import org.json.JSONArray +import org.json.JSONObject +import org.telegram.messenger.ApplicationLoader +import org.telegram.messenger.FileLog +import java.io.File +import java.io.FileDescriptor +import kotlin.concurrent.thread +import kotlin.properties.Delegates + +class ShadowsocksLoader { + + lateinit var bean: Bean + var port by Delegates.notNull() + var shadowsocksProcess: GuardedProcessPool? = null + + fun initConfig(bean: Bean,port: Int) { + + this.bean = bean + this.port = port + + } + + fun start() { + + stop() + + val cacheCfg = File(ApplicationLoader.applicationContext.cacheDir,"ss_cfg_${bean.hash}.json") + + cacheCfg.writeText(bean.toJson().toString()) + + shadowsocksProcess = GuardedProcessPool { + + FileLog.e(it) + + }.apply { + + start(listOf("${ApplicationLoader.applicationContext.applicationInfo.nativeLibraryDir}/libss-local.so", + "-b","127.0.0.1", + "-t", "600", + "-c", cacheCfg.path, + "-l", port.toString())) + + } + + } + + fun stop() { + + if (shadowsocksProcess != null) { + + val proc = shadowsocksProcess!! + + thread { + + runCatching { + + runBlocking { proc.close(this) } + + } + + } + + shadowsocksProcess = null + + } + + } + + data class Bean( + var host: String = "", + var remotePort: Int = 443, + var password: String = "", + var method: String = "aes-256-cfb" + ) { + + /* + init { + + if (method !in methods) error("method $method not supported") + + } + */ + + val hash = (host + remotePort + password + method).hashCode() + + fun toJson(): JSONObject = JSONObject().apply { + put("server", host) + put("server_port", remotePort) + put("password", password) + put("remarks", "nekox-cache") + put("route", "all") + put("remote_dns", "8.8.8.8:53") + put("ipv6", true) + put("metered", false) + put("proxy_apps", JSONObject().apply { + put("enabled", false) + }) + put("udpdns", false) + } + + companion object { + + fun parse(url: String): Bean { + + if (url.contains("@")) { + + // ss-android style + + val link = url.replace(SS_PROTOCOL,"https://").toHttpUrlOrNull() ?: error("invalid ss-android link $url") + + if (link.password.isNotBlank()) { + + return Bean( + link.host, + link.port, + link.password, + link.username + ) + + } + + val methodAndPswd = Utils.decode(link.username) + + return Bean( + link.host, + link.port, + methodAndPswd.substringAfter(":"), + methodAndPswd.substringBefore(":") + ) + + } else { + + // v2rayNG style + + var v2Url = url + + if (v2Url.contains("#")) v2Url = v2Url.substringBefore("#") + + val link = ("https://" + Utils.decode(v2Url.substringAfter(SS_PROTOCOL))).toHttpUrlOrNull() ?: error("invalid v2rayNG link $url") + + return Bean( + link.host, + link.port, + link.password, + link.username + ) + + } + + } + + } + + override fun toString(): String { + + return "ss://" + Utils.encode("$method:$password") + "@$host:$remotePort" + + } + + } + + companion object { + + val methods = arrayOf( + + "rc4-md5", + "aes-128-cfb", + "aes-192-cfb", + "aes-256-cfb", + "aes-128-ctr", + "aes-192-ctr", + "aes-256-ctr", + "bf-cfb", + "camellia-128-cfb", + "camellia-192-cfb", + "camellia-256-cfb", + "salsa20", + "chacha20", + "chacha20-ietf", + "aes-128-gcm", + "aes-192-gcm", + "aes-256-gcm", + "chacha20-ietf-poly1305", + "xchacha20-ietf-poly1305" + ) + + } + +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ShadowsocksRLoader.kt b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ShadowsocksRLoader.kt new file mode 100644 index 000000000..d5c204508 --- /dev/null +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ShadowsocksRLoader.kt @@ -0,0 +1,212 @@ +package tw.nekomimi.nekogram + +import android.util.Base64 +import com.v2ray.ang.V2RayConfig.SSR_PROTOCOL +import com.v2ray.ang.util.Utils +import kotlinx.coroutines.runBlocking +import okhttp3.HttpUrl.Companion.toHttpUrl +import org.json.JSONObject +import org.telegram.messenger.ApplicationLoader +import org.telegram.messenger.FileLog +import java.io.File +import java.util.* +import kotlin.concurrent.thread +import kotlin.properties.Delegates + +class ShadowsocksRLoader { + + lateinit var bean: Bean + var port by Delegates.notNull() + var shadowsocksProcess: GuardedProcessPool? = null + + fun initConfig(bean: Bean, port: Int) { + + this.bean = bean + this.port = port + + } + + fun start() { + + stop() + + val cacheCfg = File(ApplicationLoader.applicationContext.cacheDir, "ssr_cfg_${bean.hash}.json") + + cacheCfg.writeText(bean.toJson().toString()) + + shadowsocksProcess = GuardedProcessPool { + + FileLog.e(it) + + }.apply { + + start(listOf("${ApplicationLoader.applicationContext.applicationInfo.nativeLibraryDir}/libssr-local.so", + "-b", "127.0.0.1", + "--host", bean.host, + "-t", "600", + "-c", cacheCfg.path, + "-l", port.toString())) + + } + + } + + fun stop() { + + if (shadowsocksProcess != null) { + + val proc = shadowsocksProcess!! + + thread { + + runCatching { + + runBlocking { proc.close(this) } + + } + + } + + shadowsocksProcess = null + + } + + } + + data class Bean( + var host: String = "", + var remotePort: Int = 443, + var password: String = "", + var protocol: String = "origin", + var protocol_param: String = "", + var obfs: String = "plain", + var obfs_param: String = "", + var method: String = "aes-256-cfb" + ) { + + val hash = (host + remotePort + password + protocol + obfs + method).hashCode() + + /* + init { + + if (method !in methods) error("method $method not supported") + if (protocol !in protocols) error("protocol $protocol not supported") + if (obfs !in obfses) error("obfs $obfs not supported") + + } + */ + + fun toJson(): JSONObject = JSONObject().apply { + put("server", host) + put("server_port", remotePort) + put("password", password) + put("method", method) + put("protocol", protocol) + put("protocol_param", protocol_param) + put("obfs", obfs) + put("obfs_param", obfs_param) + put("remarks", "nekox-cache") + put("route", "all") + put("remote_dns", "8.8.8.8:53") + put("ipv6", true) + put("metered", false) + put("proxy_apps", JSONObject().apply { + put("enabled", false) + }) + put("udpdns", false) + } + + companion object { + + fun parse(url: String): Bean { + + val params = Utils.decode(url.substringAfter(SSR_PROTOCOL)).split(":") + + val bean = Bean(params[0], + params[1].toInt(), + protocol = params[2], + method = params[3], + obfs = params[4], + password = Utils.decode(params[5].substringBefore("/"))) + + val httpUrl = ("https://localhost" + params[5].substringAfter("/")).toHttpUrl() + + runCatching { + + bean.obfs_param = Utils.decode(httpUrl.queryParameter("obfsparam")!!) + + } + + runCatching { + + bean.protocol_param = Utils.decode(httpUrl.queryParameter("protoparam")!!) + + } + + return bean + + } + + } + + override fun toString(): String { + + val flags = Base64.NO_PADDING or Base64.URL_SAFE or Base64.NO_WRAP + return "ssr://" + Base64.encodeToString("%s:%d:%s:%s:%s:%s/?obfsparam=%s&protoparam=%s&remarks=%s".format(Locale.ENGLISH, host, remotePort, protocol, method, obfs, + Base64.encodeToString("%s".format(Locale.ENGLISH, password).toByteArray(), flags), + Base64.encodeToString("%s".format(Locale.ENGLISH, obfs_param).toByteArray(), flags), + Base64.encodeToString("%s".format(Locale.ENGLISH, protocol_param).toByteArray(), flags), + Base64.encodeToString("Exported From NekogramX".format(Locale.ENGLISH, protocol_param).toByteArray(), flags)).toByteArray(), flags) + } + + } + + companion object { + + val methods = arrayOf( + + "none", + "table", + "rc4", + "rc4-md5", + "rc4-md5-6", + "aes-128-cfb", + "aes-192-cfb", + "aes-256-cfb", + "aes-128-ctr", + "aes-192-ctr", + "aes-256-ctr", + "bf-cfb", + "camellia-128-cfb", + "camellia-192-cfb", + "camellia-256-cfb", + "salsa20", + "chacha20", + "chacha20-ietf" + + ) + + val protocols = arrayOf( + "origin", + "verify_simple", + "verify_sha1", + "auth_sha1", + "auth_sha1_v2", + "auth_sha1_v4", + "auth_aes128_sha1", + "auth_aes128_md5", + "auth_chain_a", + "auth_chain_b" + ) + + val obfses = arrayOf( + "plain", + "http_simple", + "http_post", + "tls_simple", + "tls1.2_ticket_auth" + ) + + } + +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ShadowsocksRSettingsActivity.java b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ShadowsocksRSettingsActivity.java new file mode 100644 index 000000000..87160fc78 --- /dev/null +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ShadowsocksRSettingsActivity.java @@ -0,0 +1,451 @@ +/* + * This is the source code of Telegram for Android v. 5.x.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2018. + */ + +package tw.nekomimi.nekogram; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.os.Build; +import android.text.InputType; +import android.text.TextUtils; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.EditorInfo; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ScrollView; +import android.widget.TextView; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.R; +import org.telegram.messenger.SharedConfig; +import org.telegram.messenger.Utilities; +import org.telegram.ui.ActionBar.ActionBar; +import org.telegram.ui.ActionBar.ActionBarMenuItem; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.BottomSheet; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.ActionBar.ThemeDescription; +import org.telegram.ui.Cells.TextInfoPrivacyCell; +import org.telegram.ui.Cells.TextSettingsCell; +import org.telegram.ui.Components.EditTextBoldCursor; +import org.telegram.ui.Components.LayoutHelper; + +import java.util.ArrayList; + +import kotlin.text.StringsKt; + +public class ShadowsocksRSettingsActivity extends BaseFragment { + + private EditTextBoldCursor[] inputFields; + + private EditTextBoldCursor ipField; + private EditTextBoldCursor portField; + private EditTextBoldCursor passwordField; + private TextSettingsCell methodField; + + private TextSettingsCell protocolField; + private EditTextBoldCursor protocolParamField; + + private TextSettingsCell obfsField; + private EditTextBoldCursor obfsParamField; + + private ScrollView scrollView; + private LinearLayout linearLayout2; + private LinearLayout inputFieldsContainer; + + private TextInfoPrivacyCell bottomCell; + + private SharedConfig.ShadowsocksRProxy currentProxyInfo; + private ShadowsocksRLoader.Bean currentBean; + + private boolean ignoreOnTextChange; + + private static final int done_button = 1; + + private static String[] methodSet = ShadowsocksLoader.Companion.getMethods(); + + public class TypeCell extends FrameLayout { + + private TextView textView; + private ImageView checkImage; + private boolean needDivider; + + public TypeCell(Context context) { + super(context); + + setWillNotDraw(false); + + textView = new TextView(context); + textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + textView.setLines(1); + textView.setMaxLines(1); + textView.setSingleLine(true); + textView.setEllipsize(TextUtils.TruncateAt.END); + textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); + addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 23 + 48 : 21, 0, LocaleController.isRTL ? 21 : 23, 0)); + + checkImage = new ImageView(context); + checkImage.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_featuredStickers_addedIcon), PorterDuff.Mode.MULTIPLY)); + checkImage.setImageResource(R.drawable.sticker_added); + addView(checkImage, LayoutHelper.createFrame(19, 14, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.CENTER_VERTICAL, 21, 0, 21, 0)); + + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(50) + (needDivider ? 1 : 0), MeasureSpec.EXACTLY)); + } + + public void setValue(String name, boolean checked, boolean divider) { + textView.setText(name); + checkImage.setVisibility(checked ? VISIBLE : INVISIBLE); + needDivider = divider; + } + + public void setTypeChecked(boolean value) { + checkImage.setVisibility(value ? VISIBLE : INVISIBLE); + } + + @Override + protected void onDraw(Canvas canvas) { + if (needDivider) { + canvas.drawLine(LocaleController.isRTL ? 0 : AndroidUtilities.dp(20), getMeasuredHeight() - 1, getMeasuredWidth() - (LocaleController.isRTL ? AndroidUtilities.dp(20) : 0), getMeasuredHeight() - 1, Theme.dividerPaint); + } + } + } + + public ShadowsocksRSettingsActivity() { + super(); + currentBean = new ShadowsocksRLoader.Bean(); + } + + public ShadowsocksRSettingsActivity(SharedConfig.ShadowsocksRProxy proxyInfo) { + super(); + currentProxyInfo = proxyInfo; + currentBean = proxyInfo.bean; + } + + + @Override + public void onResume() { + super.onResume(); + AndroidUtilities.requestAdjustResize(getParentActivity(), classGuid); + } + + @Override + public View createView(Context context) { + actionBar.setTitle(LocaleController.getString("ProxyDetails", R.string.ProxyDetails)); + actionBar.setBackButtonImage(R.drawable.ic_ab_back); + actionBar.setAllowOverlayTitle(false); + if (AndroidUtilities.isTablet()) { + actionBar.setOccupyStatusBar(false); + } + + actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { + @Override + public void onItemClick(int id) { + if (id == -1) { + finishFragment(); + } else if (id == done_button) { + + if (getParentActivity() == null) { + return; + } + + if (StringsKt.isBlank(ipField.getText())) { + + ipField.requestFocus(); + AndroidUtilities.showKeyboard(ipField); + + return; + + } + + if (StringsKt.isBlank(portField.getText())) { + + portField.requestFocus(); + AndroidUtilities.showKeyboard(portField); + + return; + + } + + if (StringsKt.isBlank(passwordField.getText())) { + + passwordField.requestFocus(); + AndroidUtilities.showKeyboard(passwordField); + + return; + + } + + currentBean.setHost(ipField.getText().toString()); + currentBean.setRemotePort(Utilities.parseInt(portField.getText().toString())); + currentBean.setPassword(passwordField.getText().toString()); + currentBean.setMethod(methodField.getValueTextView().getText().toString()); + currentBean.setProtocol(protocolField.getValueTextView().getText().toString()); + currentBean.setProtocol_param(protocolParamField.getText().toString()); + currentBean.setObfs(obfsField.getValueTextView().getText().toString()); + currentBean.setObfs_param(obfsParamField.getText().toString()); + + if (currentProxyInfo == null) { + currentProxyInfo = new SharedConfig.ShadowsocksRProxy(currentBean); + SharedConfig.addProxy(currentProxyInfo); + SharedConfig.setCurrentProxy(currentProxyInfo); + } else { + SharedConfig.setProxyEnable(false); + } + + SharedConfig.saveProxyList(); + + finishFragment(); + + } + } + }); + + ActionBarMenuItem doneItem = actionBar.createMenu().addItemWithWidth(done_button, R.drawable.ic_done, AndroidUtilities.dp(56)); + doneItem.setContentDescription(LocaleController.getString("Done", R.string.Done)); + + fragmentView = new FrameLayout(context); + FrameLayout frameLayout = (FrameLayout) fragmentView; + fragmentView.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundGray)); + + scrollView = new ScrollView(context); + scrollView.setFillViewport(true); + AndroidUtilities.setScrollViewEdgeEffectColor(scrollView, Theme.getColor(Theme.key_actionBarDefault)); + frameLayout.addView(scrollView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + + linearLayout2 = new LinearLayout(context); + linearLayout2.setOrientation(LinearLayout.VERTICAL); + scrollView.addView(linearLayout2, new ScrollView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + + inputFieldsContainer = new LinearLayout(context); + inputFieldsContainer.setOrientation(LinearLayout.VERTICAL); + inputFieldsContainer.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + // bring to front for transitions + inputFieldsContainer.setElevation(AndroidUtilities.dp(1f)); + inputFieldsContainer.setOutlineProvider(null); + } + linearLayout2.addView(inputFieldsContainer, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + + inputFields = new EditTextBoldCursor[6]; + + for (int a = 0; a < 5; a++) { + FrameLayout container = new FrameLayout(context); + EditTextBoldCursor cursor = mkCursor(); + inputFields[a] = cursor; + cursor.setImeOptions(EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NO_EXTRACT_UI); + switch (a) { + case 0: + ipField = cursor; + cursor.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + cursor.setHintText(LocaleController.getString("UseProxyAddress", R.string.UseProxyAddress)); + cursor.setText(currentBean.getHost()); + break; + case 1: + portField = cursor; + cursor.setInputType(InputType.TYPE_CLASS_NUMBER); + cursor.setHintText(LocaleController.getString("UseProxyPort", R.string.UseProxyPort)); + cursor.setText("" + currentBean.getRemotePort()); + break; + case 2: + passwordField = cursor; + cursor.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + cursor.setHintText(LocaleController.getString("SSPassword", R.string.SSPassword)); + cursor.setText(currentBean.getPassword()); + break; + case 3: + protocolParamField = cursor; + cursor.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + cursor.setHintText(LocaleController.getString("SSRProtocolParams", R.string.SSRProtocolParams)); + cursor.setText(currentBean.getProtocol_param()); + break; + case 4: + obfsParamField = cursor; + cursor.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + cursor.setHintText(LocaleController.getString("SSRObfsParam", R.string.SSRObfsParam)); + cursor.setText(currentBean.getObfs_param()); + break; + } + cursor.setSelection(cursor.length()); + + cursor.setPadding(0, 0, 0, 0); + container.addView(cursor, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 17, a == 0 ? 12 : 0, 17, 0)); + + } + + inputFieldsContainer.addView((View) ipField.getParent(), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 64)); + inputFieldsContainer.addView((View) portField.getParent(), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 64)); + inputFieldsContainer.addView((View) passwordField.getParent(), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 64)); + + FrameLayout container = new FrameLayout(context); + + methodField = new TextSettingsCell(context); + methodField.setBackground(Theme.getSelectorDrawable(false)); + methodField.setTextAndValue(LocaleController.getString("SSMethod", R.string.SSMethod), currentBean.getMethod(), false); + container.addView(methodField, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, 0, 0, 0)); + + methodField.setOnClickListener((v) -> { + + BottomSheet.Builder select = new BottomSheet.Builder(context); + + select.setItems(ShadowsocksRLoader.Companion.getMethods(),(d,i) -> { + + methodField.getValueTextView().setText(ShadowsocksRLoader.Companion.getMethods()[i]); + + }); + + select.show(); + + }); + + inputFieldsContainer.addView(container, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 64)); + + container = new FrameLayout(context); + protocolField = new TextSettingsCell(context); + protocolField.setBackground(Theme.getSelectorDrawable(false)); + protocolField.setTextAndValue(LocaleController.getString("SSRProtocol", R.string.SSRProtocol), currentBean.getProtocol(), false); + container.addView(protocolField, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, 0, 0, 0)); + + protocolField.setOnClickListener((v) -> { + + BottomSheet.Builder select = new BottomSheet.Builder(context); + + select.setItems(ShadowsocksRLoader.Companion.getProtocols(),(d,i) -> { + + protocolField.getValueTextView().setText(ShadowsocksRLoader.Companion.getProtocols()[i]); + + }); + + select.show(); + + }); + + inputFieldsContainer.addView(container, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 64)); + + inputFieldsContainer.addView((View) protocolParamField.getParent(), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 64)); + + container = new FrameLayout(context); + obfsField = new TextSettingsCell(context); + obfsField.setBackground(Theme.getSelectorDrawable(false)); + obfsField.setTextAndValue(LocaleController.getString("SSRObfs", R.string.SSRObfs), currentBean.getObfs(), false); + container.addView(obfsField, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, 0, 0, 0)); + + obfsField.setOnClickListener((v) -> { + + BottomSheet.Builder select = new BottomSheet.Builder(context); + + select.setItems(ShadowsocksRLoader.Companion.getObfses(),(d,i) -> { + + obfsField.getValueTextView().setText(ShadowsocksRLoader.Companion.getObfses()[i]); + + }); + + select.show(); + + }); + + inputFieldsContainer.addView(container, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 64)); + + inputFieldsContainer.addView((View) obfsParamField.getParent(), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 64)); + + bottomCell = new TextInfoPrivacyCell(context); + bottomCell.setBackground(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + bottomCell.setText(LocaleController.getString("ProxyInfoSSR", R.string.ProxyInfoSSR)); + linearLayout2.addView(bottomCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + + return fragmentView; + + } + + EditTextBoldCursor mkCursor() { + + EditTextBoldCursor cursor = new EditTextBoldCursor(getParentActivity()); + cursor.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + cursor.setHintColor(Theme.getColor(Theme.key_windowBackgroundWhiteHintText)); + cursor.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + cursor.setBackground(null); + cursor.setCursorColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + cursor.setCursorSize(AndroidUtilities.dp(20)); + cursor.setCursorWidth(1.5f); + cursor.setSingleLine(true); + cursor.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); + cursor.setHeaderHintColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlueHeader)); + cursor.setTransformHintToHeader(true); + cursor.setLineColors(Theme.getColor(Theme.key_windowBackgroundWhiteInputField), Theme.getColor(Theme.key_windowBackgroundWhiteInputFieldActivated), Theme.getColor(Theme.key_windowBackgroundWhiteRedText3)); + return cursor; + + } + + @Override + protected void onTransitionAnimationEnd(boolean isOpen, boolean backward) { + if (isOpen && !backward && currentProxyInfo == null) { + ipField.requestFocus(); + AndroidUtilities.showKeyboard(ipField); + } + } + + @Override + public ThemeDescription[] getThemeDescriptions() { + final ThemeDescription.ThemeDescriptionDelegate delegate = () -> { + if (inputFields != null) { + for (int i = 0; i < inputFields.length; i++) { + inputFields[i].setLineColors(Theme.getColor(Theme.key_windowBackgroundWhiteInputField), + Theme.getColor(Theme.key_windowBackgroundWhiteInputFieldActivated), + Theme.getColor(Theme.key_windowBackgroundWhiteRedText3)); + } + } + }; + ArrayList arrayList = new ArrayList<>(); + arrayList.add(new ThemeDescription(fragmentView, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundGray)); + arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_actionBarDefault)); + arrayList.add(new ThemeDescription(scrollView, ThemeDescription.FLAG_LISTGLOWCOLOR, null, null, null, null, Theme.key_actionBarDefault)); + arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_ITEMSCOLOR, null, null, null, null, Theme.key_actionBarDefaultIcon)); + arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_TITLECOLOR, null, null, null, null, Theme.key_actionBarDefaultTitle)); + arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SELECTORCOLOR, null, null, null, null, Theme.key_actionBarDefaultSelector)); + arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SEARCH, null, null, null, null, Theme.key_actionBarDefaultSearch)); + arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SEARCHPLACEHOLDER, null, null, null, null, Theme.key_actionBarDefaultSearchPlaceholder)); + arrayList.add(new ThemeDescription(inputFieldsContainer, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundWhite)); + arrayList.add(new ThemeDescription(linearLayout2, 0, new Class[]{View.class}, Theme.dividerPaint, null, null, Theme.key_divider)); + + arrayList.add(new ThemeDescription(null, 0, null, null, null, null, delegate, Theme.key_windowBackgroundWhiteBlueText4)); + arrayList.add(new ThemeDescription(null, 0, null, null, null, null, delegate, Theme.key_windowBackgroundWhiteGrayText2)); + + + if (inputFields != null) { + for (int a = 0; a < inputFields.length; a++) { + arrayList.add(new ThemeDescription(inputFields[a], ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); + arrayList.add(new ThemeDescription(inputFields[a], ThemeDescription.FLAG_HINTTEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteHintText)); + arrayList.add(new ThemeDescription(inputFields[a], ThemeDescription.FLAG_HINTTEXTCOLOR | ThemeDescription.FLAG_PROGRESSBAR, null, null, null, null, Theme.key_windowBackgroundWhiteBlueHeader)); + arrayList.add(new ThemeDescription(inputFields[a], ThemeDescription.FLAG_CURSORCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); + arrayList.add(new ThemeDescription(null, 0, null, null, null, delegate, Theme.key_windowBackgroundWhiteInputField)); + arrayList.add(new ThemeDescription(null, 0, null, null, null, delegate, Theme.key_windowBackgroundWhiteInputFieldActivated)); + arrayList.add(new ThemeDescription(null, 0, null, null, null, delegate, Theme.key_windowBackgroundWhiteRedText3)); + } + } else { + arrayList.add(new ThemeDescription(null, ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); + arrayList.add(new ThemeDescription(null, ThemeDescription.FLAG_HINTTEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteHintText)); + } + + arrayList.add(new ThemeDescription(bottomCell, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{TextInfoPrivacyCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow)); + arrayList.add(new ThemeDescription(bottomCell, 0, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText4)); + arrayList.add(new ThemeDescription(bottomCell, ThemeDescription.FLAG_LINKCOLOR, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteLinkText)); + + return arrayList.toArray(new ThemeDescription[0]); + } +} diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ShadowsocksSettingsActivity.java b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ShadowsocksSettingsActivity.java new file mode 100644 index 000000000..f7f8c7fc1 --- /dev/null +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/ShadowsocksSettingsActivity.java @@ -0,0 +1,385 @@ +/* + * This is the source code of Telegram for Android v. 5.x.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2018. + */ + +package tw.nekomimi.nekogram; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.os.Build; +import android.text.InputType; +import android.text.TextUtils; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.EditorInfo; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ScrollView; +import android.widget.TextView; + +import com.v2ray.ang.V2RayConfig; +import com.v2ray.ang.dto.AngConfig; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.R; +import org.telegram.messenger.SharedConfig; +import org.telegram.messenger.Utilities; +import org.telegram.ui.ActionBar.ActionBar; +import org.telegram.ui.ActionBar.ActionBarMenuItem; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.BottomSheet; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.ActionBar.ThemeDescription; +import org.telegram.ui.Cells.TextCheckCell; +import org.telegram.ui.Cells.TextInfoPrivacyCell; +import org.telegram.ui.Cells.TextSettingsCell; +import org.telegram.ui.Components.EditTextBoldCursor; +import org.telegram.ui.Components.LayoutHelper; + +import java.util.ArrayList; + +import kotlin.text.StringsKt; + +public class ShadowsocksSettingsActivity extends BaseFragment { + + private EditTextBoldCursor[] inputFields; + + private EditTextBoldCursor ipField; + private EditTextBoldCursor portField; + private EditTextBoldCursor passwordField; + private TextSettingsCell methodField; + + private ScrollView scrollView; + private LinearLayout linearLayout2; + private LinearLayout inputFieldsContainer; + + private TextInfoPrivacyCell bottomCell; + + private SharedConfig.ShadowsocksProxy currentProxyInfo; + private ShadowsocksLoader.Bean currentBean; + + private boolean ignoreOnTextChange; + + private static final int done_button = 1; + + private static String[] methodSet = ShadowsocksLoader.Companion.getMethods(); + + public class TypeCell extends FrameLayout { + + private TextView textView; + private ImageView checkImage; + private boolean needDivider; + + public TypeCell(Context context) { + super(context); + + setWillNotDraw(false); + + textView = new TextView(context); + textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + textView.setLines(1); + textView.setMaxLines(1); + textView.setSingleLine(true); + textView.setEllipsize(TextUtils.TruncateAt.END); + textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); + addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 23 + 48 : 21, 0, LocaleController.isRTL ? 21 : 23, 0)); + + checkImage = new ImageView(context); + checkImage.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_featuredStickers_addedIcon), PorterDuff.Mode.MULTIPLY)); + checkImage.setImageResource(R.drawable.sticker_added); + addView(checkImage, LayoutHelper.createFrame(19, 14, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.CENTER_VERTICAL, 21, 0, 21, 0)); + + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(50) + (needDivider ? 1 : 0), MeasureSpec.EXACTLY)); + } + + public void setValue(String name, boolean checked, boolean divider) { + textView.setText(name); + checkImage.setVisibility(checked ? VISIBLE : INVISIBLE); + needDivider = divider; + } + + public void setTypeChecked(boolean value) { + checkImage.setVisibility(value ? VISIBLE : INVISIBLE); + } + + @Override + protected void onDraw(Canvas canvas) { + if (needDivider) { + canvas.drawLine(LocaleController.isRTL ? 0 : AndroidUtilities.dp(20), getMeasuredHeight() - 1, getMeasuredWidth() - (LocaleController.isRTL ? AndroidUtilities.dp(20) : 0), getMeasuredHeight() - 1, Theme.dividerPaint); + } + } + } + + public ShadowsocksSettingsActivity() { + super(); + currentBean = new ShadowsocksLoader.Bean(); + } + + public ShadowsocksSettingsActivity(SharedConfig.ShadowsocksProxy proxyInfo) { + super(); + currentProxyInfo = proxyInfo; + currentBean = proxyInfo.bean; + } + + + @Override + public void onResume() { + super.onResume(); + AndroidUtilities.requestAdjustResize(getParentActivity(), classGuid); + } + + @Override + public View createView(Context context) { + actionBar.setTitle(LocaleController.getString("ProxyDetails", R.string.ProxyDetails)); + actionBar.setBackButtonImage(R.drawable.ic_ab_back); + actionBar.setAllowOverlayTitle(false); + if (AndroidUtilities.isTablet()) { + actionBar.setOccupyStatusBar(false); + } + + actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { + @Override + public void onItemClick(int id) { + if (id == -1) { + finishFragment(); + } else if (id == done_button) { + + if (getParentActivity() == null) { + return; + } + + if (StringsKt.isBlank(ipField.getText())) { + + ipField.requestFocus(); + AndroidUtilities.showKeyboard(ipField); + + return; + + } + + if (StringsKt.isBlank(portField.getText())) { + + portField.requestFocus(); + AndroidUtilities.showKeyboard(portField); + + return; + + } + + if (StringsKt.isBlank(passwordField.getText())) { + + passwordField.requestFocus(); + AndroidUtilities.showKeyboard(passwordField); + + return; + + } + + currentBean.setHost(ipField.getText().toString()); + currentBean.setRemotePort(Utilities.parseInt(portField.getText().toString())); + currentBean.setPassword(passwordField.getText().toString()); + currentBean.setMethod(methodField.getValueTextView().getText().toString()); + + if (currentProxyInfo == null) { + currentProxyInfo = new SharedConfig.ShadowsocksProxy(currentBean); + SharedConfig.addProxy(currentProxyInfo); + SharedConfig.setCurrentProxy(currentProxyInfo); + } else { + SharedConfig.setProxyEnable(false); + } + + SharedConfig.saveProxyList(); + + finishFragment(); + + } + } + }); + + ActionBarMenuItem doneItem = actionBar.createMenu().addItemWithWidth(done_button, R.drawable.ic_done, AndroidUtilities.dp(56)); + doneItem.setContentDescription(LocaleController.getString("Done", R.string.Done)); + + fragmentView = new FrameLayout(context); + FrameLayout frameLayout = (FrameLayout) fragmentView; + fragmentView.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundGray)); + + scrollView = new ScrollView(context); + scrollView.setFillViewport(true); + AndroidUtilities.setScrollViewEdgeEffectColor(scrollView, Theme.getColor(Theme.key_actionBarDefault)); + frameLayout.addView(scrollView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + + linearLayout2 = new LinearLayout(context); + linearLayout2.setOrientation(LinearLayout.VERTICAL); + scrollView.addView(linearLayout2, new ScrollView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + + inputFieldsContainer = new LinearLayout(context); + inputFieldsContainer.setOrientation(LinearLayout.VERTICAL); + inputFieldsContainer.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + // bring to front for transitions + inputFieldsContainer.setElevation(AndroidUtilities.dp(1f)); + inputFieldsContainer.setOutlineProvider(null); + } + linearLayout2.addView(inputFieldsContainer, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + + inputFields = new EditTextBoldCursor[6]; + + for (int a = 0; a < 3; a++) { + FrameLayout container = new FrameLayout(context); + EditTextBoldCursor cursor = mkCursor(); + inputFields[a] = cursor; + cursor.setImeOptions(EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NO_EXTRACT_UI); + switch (a) { + case 0: + ipField = cursor; + cursor.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + cursor.setHintText(LocaleController.getString("UseProxyAddress", R.string.UseProxyAddress)); + cursor.setText(currentBean.getHost()); + break; + case 1: + portField = cursor; + cursor.setInputType(InputType.TYPE_CLASS_NUMBER); + cursor.setHintText(LocaleController.getString("UseProxyPort", R.string.UseProxyPort)); + cursor.setText("" + currentBean.getRemotePort()); + break; + case 2: + passwordField = cursor; + cursor.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + cursor.setHintText(LocaleController.getString("SSPassword", R.string.SSPassword)); + cursor.setText(currentBean.getPassword()); + break; + } + cursor.setSelection(cursor.length()); + + cursor.setPadding(0, 0, 0, 0); + container.addView(cursor, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 17, a == 0 ? 12 : 0, 17, 0)); + + } + + inputFieldsContainer.addView((View) ipField.getParent(), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 64)); + inputFieldsContainer.addView((View) portField.getParent(), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 64)); + inputFieldsContainer.addView((View) passwordField.getParent(), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 64)); + + FrameLayout container = new FrameLayout(context); + + methodField = new TextSettingsCell(context); + methodField.setBackground(Theme.getSelectorDrawable(false)); + methodField.setTextAndValue(LocaleController.getString("SSMethod", R.string.SSMethod), currentBean.getMethod(), false); + container.addView(methodField, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, 0, 0, 0)); + + methodField.setOnClickListener((v) -> { + + BottomSheet.Builder select = new BottomSheet.Builder(context); + + select.setItems(methodSet,(d,i) -> { + + methodField.getValueTextView().setText(methodSet[i]); + + }); + + select.show(); + + }); + + inputFieldsContainer.addView(container, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 64)); + + bottomCell = new TextInfoPrivacyCell(context); + bottomCell.setBackground(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + bottomCell.setText(LocaleController.getString("ProxyInfoSS", R.string.ProxyInfoSS)); + linearLayout2.addView(bottomCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + + return fragmentView; + + } + + EditTextBoldCursor mkCursor() { + + EditTextBoldCursor cursor = new EditTextBoldCursor(getParentActivity()); + cursor.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + cursor.setHintColor(Theme.getColor(Theme.key_windowBackgroundWhiteHintText)); + cursor.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + cursor.setBackground(null); + cursor.setCursorColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + cursor.setCursorSize(AndroidUtilities.dp(20)); + cursor.setCursorWidth(1.5f); + cursor.setSingleLine(true); + cursor.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); + cursor.setHeaderHintColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlueHeader)); + cursor.setTransformHintToHeader(true); + cursor.setLineColors(Theme.getColor(Theme.key_windowBackgroundWhiteInputField), Theme.getColor(Theme.key_windowBackgroundWhiteInputFieldActivated), Theme.getColor(Theme.key_windowBackgroundWhiteRedText3)); + return cursor; + + } + + @Override + protected void onTransitionAnimationEnd(boolean isOpen, boolean backward) { + if (isOpen && !backward && currentProxyInfo == null) { + ipField.requestFocus(); + AndroidUtilities.showKeyboard(ipField); + } + } + + @Override + public ThemeDescription[] getThemeDescriptions() { + final ThemeDescription.ThemeDescriptionDelegate delegate = () -> { + if (inputFields != null) { + for (int i = 0; i < inputFields.length; i++) { + inputFields[i].setLineColors(Theme.getColor(Theme.key_windowBackgroundWhiteInputField), + Theme.getColor(Theme.key_windowBackgroundWhiteInputFieldActivated), + Theme.getColor(Theme.key_windowBackgroundWhiteRedText3)); + } + } + }; + ArrayList arrayList = new ArrayList<>(); + arrayList.add(new ThemeDescription(fragmentView, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundGray)); + arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_actionBarDefault)); + arrayList.add(new ThemeDescription(scrollView, ThemeDescription.FLAG_LISTGLOWCOLOR, null, null, null, null, Theme.key_actionBarDefault)); + arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_ITEMSCOLOR, null, null, null, null, Theme.key_actionBarDefaultIcon)); + arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_TITLECOLOR, null, null, null, null, Theme.key_actionBarDefaultTitle)); + arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SELECTORCOLOR, null, null, null, null, Theme.key_actionBarDefaultSelector)); + arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SEARCH, null, null, null, null, Theme.key_actionBarDefaultSearch)); + arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SEARCHPLACEHOLDER, null, null, null, null, Theme.key_actionBarDefaultSearchPlaceholder)); + arrayList.add(new ThemeDescription(inputFieldsContainer, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundWhite)); + arrayList.add(new ThemeDescription(linearLayout2, 0, new Class[]{View.class}, Theme.dividerPaint, null, null, Theme.key_divider)); + + arrayList.add(new ThemeDescription(null, 0, null, null, null, null, delegate, Theme.key_windowBackgroundWhiteBlueText4)); + arrayList.add(new ThemeDescription(null, 0, null, null, null, null, delegate, Theme.key_windowBackgroundWhiteGrayText2)); + + + if (inputFields != null) { + for (int a = 0; a < inputFields.length; a++) { + arrayList.add(new ThemeDescription(inputFields[a], ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); + arrayList.add(new ThemeDescription(inputFields[a], ThemeDescription.FLAG_HINTTEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteHintText)); + arrayList.add(new ThemeDescription(inputFields[a], ThemeDescription.FLAG_HINTTEXTCOLOR | ThemeDescription.FLAG_PROGRESSBAR, null, null, null, null, Theme.key_windowBackgroundWhiteBlueHeader)); + arrayList.add(new ThemeDescription(inputFields[a], ThemeDescription.FLAG_CURSORCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); + arrayList.add(new ThemeDescription(null, 0, null, null, null, delegate, Theme.key_windowBackgroundWhiteInputField)); + arrayList.add(new ThemeDescription(null, 0, null, null, null, delegate, Theme.key_windowBackgroundWhiteInputFieldActivated)); + arrayList.add(new ThemeDescription(null, 0, null, null, null, delegate, Theme.key_windowBackgroundWhiteRedText3)); + } + } else { + arrayList.add(new ThemeDescription(null, ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); + arrayList.add(new ThemeDescription(null, ThemeDescription.FLAG_HINTTEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteHintText)); + } + + arrayList.add(new ThemeDescription(bottomCell, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{TextInfoPrivacyCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow)); + arrayList.add(new ThemeDescription(bottomCell, 0, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText4)); + arrayList.add(new ThemeDescription(bottomCell, ThemeDescription.FLAG_LINKCOLOR, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteLinkText)); + + return arrayList.toArray(new ThemeDescription[0]); + } +} diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/VmessLoader.kt b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/VmessLoader.kt new file mode 100644 index 000000000..28773a95d --- /dev/null +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/VmessLoader.kt @@ -0,0 +1,432 @@ +package tw.nekomimi.nekogram + +import android.util.Log +import com.google.gson.Gson +import com.v2ray.ang.V2RayConfig +import com.v2ray.ang.V2RayConfig.SOCKS_PROTOCOL +import com.v2ray.ang.V2RayConfig.SS_PROTOCOL +import com.v2ray.ang.V2RayConfig.VMESS1_PROTOCOL +import com.v2ray.ang.V2RayConfig.VMESS_PROTOCOL +import com.v2ray.ang.dto.AngConfig.VmessBean +import com.v2ray.ang.dto.VmessQRCode +import com.v2ray.ang.util.Utils +import com.v2ray.ang.util.V2rayConfigUtil +import libv2ray.Libv2ray +import libv2ray.V2RayPoint +import libv2ray.V2RayVPNServiceSupportsSet +import okhttp3.HttpUrl.Companion.toHttpUrl +import org.telegram.messenger.ApplicationLoader +import org.telegram.messenger.FileLog +import java.lang.IllegalArgumentException +import kotlin.concurrent.thread + +class VmessLoader { + + private val point: V2RayPoint = Libv2ray.newV2RayPoint(EmptyCallback(), true) + + companion object { + + @JvmStatic + val public = VmessBean().apply { + + address = "nekox.me" + port = 443 + configType = V2RayConfig.EConfigType.Vmess + id = "73670f86-6046-4ffd-b468-6cd73cea1f29" + security = "none" + network = "ws" + streamSecurity = "tls" + requestHost = "nekox.me" + path = "/internet" + + } + + fun parseVmess1Link(server: String): VmessBean { + + val lnk = server.substringAfter(VMESS1_PROTOCOL).toHttpUrl() + + val bean = VmessBean() + + bean.configType = V2RayConfig.EConfigType.Vmess + bean.address = lnk.host + bean.port = lnk.port + bean.id = lnk.username + + lnk.queryParameterNames.forEach { + + when (it) { + + "tls" -> if (lnk.queryParameter(it) == "true") bean.streamSecurity = "tls" + + "network" -> { + + bean.network = lnk.queryParameter(it)!! + + if (bean.network in arrayOf("http", "ws")) { + + bean.path = Utils.urlDecode(lnk.encodedPath) + + } + + } + + "header" -> { + + bean.headerType = lnk.queryParameter(it)!! + + } + + } + + } + + return bean + + } + + @JvmStatic + fun parseVmessLink(server: String): VmessBean { + + try { + if (server.isBlank()) error("empty link") + + var vmess = VmessBean() + + if (server.startsWith(VMESS_PROTOCOL)) { + + val indexSplit = server.indexOf("?") + if (indexSplit > 0) { + vmess = resolveSimpleVmess1(server) + } else { + + var result = server.replace(VMESS_PROTOCOL, "") + result = Utils.decode(result) + if (result.isBlank()) { + error("invalid url format") + } + + if (result.contains("= vmess")) { + + vmess = resolveSomeIOSAppShitCsvLink(result) + + } else { + + val vmessQRCode = Gson().fromJson(result, VmessQRCode::class.java) + if (vmessQRCode.add.isBlank() + || vmessQRCode.port.isBlank() + || vmessQRCode.id.isBlank() + || vmessQRCode.aid.isBlank() + || vmessQRCode.net.isBlank() + ) { + error("invalid protocol") + } + + vmess.configType = V2RayConfig.EConfigType.Vmess + vmess.security = "auto" + vmess.network = "tcp" + vmess.headerType = "none" + + vmess.configVersion = Utils.parseInt(vmessQRCode.v) + vmess.remarks = vmessQRCode.ps + vmess.address = vmessQRCode.add + vmess.port = Utils.parseInt(vmessQRCode.port) + vmess.id = vmessQRCode.id + vmess.alterId = Utils.parseInt(vmessQRCode.aid) + vmess.network = vmessQRCode.net + vmess.headerType = vmessQRCode.type + vmess.requestHost = vmessQRCode.host + vmess.path = vmessQRCode.path + vmess.streamSecurity = vmessQRCode.tls + } + } + + upgradeServerVersion(vmess) + + return vmess + } else if (server.startsWith(VMESS1_PROTOCOL)) { + + return parseVmess1Link(server) + + } else if (server.startsWith(SS_PROTOCOL)) { + var result = server.replace(SS_PROTOCOL, "") + val indexSplit = result.indexOf("#") + if (indexSplit > 0) { + try { + vmess.remarks = Utils.urlDecode(result.substring(indexSplit + 1, result.length)) + } catch (e: Exception) { + e.printStackTrace() + } + + result = result.substring(0, indexSplit) + } + + //part decode + val indexS = result.indexOf("@") + if (indexS > 0) { + result = Utils.decode(result.substring(0, indexS)) + result.substring(indexS, result.length) + } else { + result = Utils.decode(result) + } + + val legacyPattern = "^(.+?):(.*)@(.+?):(\\d+?)$".toRegex() + val match = legacyPattern.matchEntire(result) ?: error("invalid protocol") + vmess.security = match.groupValues[1].toLowerCase() + vmess.id = match.groupValues[2] + vmess.address = match.groupValues[3] + if (vmess.address.firstOrNull() == '[' && vmess.address.lastOrNull() == ']') + vmess.address = vmess.address.substring(1, vmess.address.length - 1) + vmess.port = match.groupValues[4].toInt() + + return vmess + + } else if (server.startsWith(SOCKS_PROTOCOL)) { + var result = server.replace(SOCKS_PROTOCOL, "") + val indexSplit = result.indexOf("#") + if (indexSplit > 0) { + try { + vmess.remarks = Utils.urlDecode(result.substring(indexSplit + 1, result.length)) + } catch (e: Exception) { + e.printStackTrace() + } + + result = result.substring(0, indexSplit) + } + + //part decode + val indexS = result.indexOf(":") + if (indexS < 0) { + result = Utils.decode(result) + } + + val legacyPattern = "^(.+?):(\\d+?)$".toRegex() + val match = legacyPattern.matchEntire(result) ?: error("invalid protocol") + vmess.address = match.groupValues[1] + if (vmess.address.firstOrNull() == '[' && vmess.address.lastOrNull() == ']') + vmess.address = vmess.address.substring(1, vmess.address.length - 1) + vmess.port = match.groupValues[2].toInt() + + return vmess + } else { + error("invalid protocol") + } + } catch (e: Exception) { + + throw IllegalArgumentException(e) + + } + + } + + private fun resolveSomeIOSAppShitCsvLink(csv: String): VmessBean { + + val args = csv.split(",") + + val bean = VmessBean() + + bean.configType = V2RayConfig.EConfigType.Vmess + bean.address = args[1] + bean.port = args[2].toInt() + bean.security = args[3] + bean.id = args[4].replace("\"", "") + + args.subList(5, args.size).forEach { + + when { + + it == "over-tls=true" -> { + + bean.streamSecurity = "tls" + + } + + it.startsWith("tls-host=") -> { + + bean.requestHost = it.substringAfter("=") + + } + + it.startsWith("obfs=") -> { + + bean.network = it.substringAfter("=") + + } + + it.startsWith("obfs-path=") || it.contains("Host:") -> { + + runCatching { + + bean.path = it + .substringAfter("obfs-path=\"") + .substringBefore("\"obfs") + + } + + runCatching { + + bean.requestHost = it + .substringAfter("Host:") + .substringBefore("[") + + } + + } + + } + + } + + return bean + + } + + /** + * upgrade + */ + private fun upgradeServerVersion(vmess: VmessBean): Int { + try { + if (vmess.configVersion == 2) { + return 0 + } + + when (vmess.network) { + "kcp" -> { + } + "ws" -> { + var path = "" + var host = "" + val lstParameter = vmess.requestHost.split(";") + if (lstParameter.size > 0) { + path = lstParameter.get(0).trim() + } + if (lstParameter.size > 1) { + path = lstParameter.get(0).trim() + host = lstParameter.get(1).trim() + } + vmess.path = path + vmess.requestHost = host + } + "h2" -> { + var path = "" + var host = "" + val lstParameter = vmess.requestHost.split(";") + if (lstParameter.size > 0) { + path = lstParameter.get(0).trim() + } + if (lstParameter.size > 1) { + path = lstParameter.get(0).trim() + host = lstParameter.get(1).trim() + } + vmess.path = path + vmess.requestHost = host + } + else -> { + } + } + vmess.configVersion = 2 + return 0 + } catch (e: Exception) { + e.printStackTrace() + return -1 + } + } + + private fun resolveSimpleVmess1(server: String): VmessBean { + + val vmess = VmessBean() + + var result = server.replace(VMESS_PROTOCOL, "") + val indexSplit = result.indexOf("?") + if (indexSplit > 0) { + result = result.substring(0, indexSplit) + } + result = Utils.decode(result) + + val arr1 = result.split('@') + if (arr1.count() != 2) { + return vmess + } + val arr21 = arr1[0].split(':') + val arr22 = arr1[1].split(':') + if (arr21.count() != 2 || arr21.count() != 2) { + return vmess + } + + vmess.address = arr22[0] + vmess.port = Utils.parseInt(arr22[1]) + vmess.security = arr21[0] + vmess.id = arr21[1] + + vmess.security = "chacha20-poly1305" + vmess.network = "tcp" + vmess.headerType = "none" + vmess.remarks = "" + vmess.alterId = 0 + + return vmess + } + + } + + fun initConfig(config: VmessBean, port: Int) { + + point.configureFileContent = V2rayConfigUtil.getV2rayConfig(config, port).content + point.domainName = V2rayConfigUtil.currDomain + + Log.d("nekox", point.configureFileContent) + Log.d("nekox", "domainName: " + point.domainName) + + } + + fun start() { + + if (point.isRunning) return + + runCatching { + + point.runLoop(true) + + }.onFailure { + + FileLog.e(it) + + } + + } + + fun stop() { + + thread { + + runCatching { + + point.stopLoop() + + } + + } + + } + + class EmptyCallback : V2RayVPNServiceSupportsSet { + override fun onEmitStatus(p0: Long, p1: String?): Long { + return 0 + } + + override fun setup(p0: String?): Long { + return 0 + } + + override fun prepare(): Long { + return 0 + } + + override fun shutdown(): Long { + return 0 + } + + override fun protect(p0: Long): Boolean { + return true + } + } + +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/VmessSettingsActivity.java b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/VmessSettingsActivity.java new file mode 100644 index 000000000..3a18d3a32 --- /dev/null +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/VmessSettingsActivity.java @@ -0,0 +1,491 @@ +/* + * This is the source code of Telegram for Android v. 5.x.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013-2018. + */ + +package tw.nekomimi.nekogram; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.os.Build; +import android.text.Editable; +import android.text.InputType; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.EditorInfo; +import android.widget.EditText; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ScrollView; +import android.widget.TextView; + +import com.v2ray.ang.V2RayConfig; +import com.v2ray.ang.dto.AngConfig; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.R; +import org.telegram.messenger.SharedConfig; +import org.telegram.messenger.Utilities; +import org.telegram.ui.ActionBar.ActionBar; +import org.telegram.ui.ActionBar.ActionBarMenuItem; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.BottomSheet; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.ActionBar.ThemeDescription; +import org.telegram.ui.Cells.TextCheckCell; +import org.telegram.ui.Cells.TextInfoPrivacyCell; +import org.telegram.ui.Cells.TextSettingsCell; +import org.telegram.ui.Components.AlertsCreator; +import org.telegram.ui.Components.EditTextBoldCursor; +import org.telegram.ui.Components.LayoutHelper; + +import java.util.ArrayList; + +import kotlin.NumbersKt; +import kotlin.text.StringsKt; + +public class VmessSettingsActivity extends BaseFragment { + + private EditTextBoldCursor[] inputFields; + + private EditTextBoldCursor ipField; + private EditTextBoldCursor portField; + private EditTextBoldCursor userIdField; + private EditTextBoldCursor alterIdField; + private TextSettingsCell securityField; + private TextSettingsCell networkField; + private TextSettingsCell headTypeField; + private EditTextBoldCursor requestHostField; + private EditTextBoldCursor pathField; + private TextCheckCell useTlsField; + + private ScrollView scrollView; + private LinearLayout linearLayout2; + private LinearLayout inputFieldsContainer; + + private TextInfoPrivacyCell bottomCell; + private ActionBarMenuItem doneItem; + + private SharedConfig.VmessProxy currentProxyInfo; + private AngConfig.VmessBean currentBean; + + private boolean ignoreOnTextChange; + + private static final int done_button = 1; + + private static String[] securitySet = { "chacha20-poly1305","aes-128-gcm","auto","none" }; + private static String[] networkSet = { "tcp","kcp","ws","h2","quic" }; + private static String[] headTypeSet = { "none","http","srtp","utp","wechat-video","dtls","wireguard" }; + + public class TypeCell extends FrameLayout { + + private TextView textView; + private ImageView checkImage; + private boolean needDivider; + + public TypeCell(Context context) { + super(context); + + setWillNotDraw(false); + + textView = new TextView(context); + textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + textView.setLines(1); + textView.setMaxLines(1); + textView.setSingleLine(true); + textView.setEllipsize(TextUtils.TruncateAt.END); + textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); + addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 23 + 48 : 21, 0, LocaleController.isRTL ? 21 : 23, 0)); + + checkImage = new ImageView(context); + checkImage.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_featuredStickers_addedIcon), PorterDuff.Mode.MULTIPLY)); + checkImage.setImageResource(R.drawable.sticker_added); + addView(checkImage, LayoutHelper.createFrame(19, 14, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.CENTER_VERTICAL, 21, 0, 21, 0)); + + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(50) + (needDivider ? 1 : 0), MeasureSpec.EXACTLY)); + } + + public void setValue(String name, boolean checked, boolean divider) { + textView.setText(name); + checkImage.setVisibility(checked ? VISIBLE : INVISIBLE); + needDivider = divider; + } + + public void setTypeChecked(boolean value) { + checkImage.setVisibility(value ? VISIBLE : INVISIBLE); + } + + @Override + protected void onDraw(Canvas canvas) { + if (needDivider) { + canvas.drawLine(LocaleController.isRTL ? 0 : AndroidUtilities.dp(20), getMeasuredHeight() - 1, getMeasuredWidth() - (LocaleController.isRTL ? AndroidUtilities.dp(20) : 0), getMeasuredHeight() - 1, Theme.dividerPaint); + } + } + } + + public VmessSettingsActivity() { + super(); + currentBean = new AngConfig.VmessBean(); + currentBean.setConfigType(V2RayConfig.EConfigType.Vmess); + } + + public VmessSettingsActivity(SharedConfig.VmessProxy proxyInfo) { + super(); + currentProxyInfo = proxyInfo; + currentBean = proxyInfo.bean; + } + + + @Override + public void onResume() { + super.onResume(); + AndroidUtilities.requestAdjustResize(getParentActivity(), classGuid); + } + + @Override + public View createView(Context context) { + actionBar.setTitle(LocaleController.getString("ProxyDetails", R.string.ProxyDetails)); + actionBar.setBackButtonImage(R.drawable.ic_ab_back); + actionBar.setAllowOverlayTitle(false); + if (AndroidUtilities.isTablet()) { + actionBar.setOccupyStatusBar(false); + } + + actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { + @Override + public void onItemClick(int id) { + if (id == -1) { + finishFragment(); + } else if (id == done_button) { + + if (getParentActivity() == null) { + return; + } + + if (StringsKt.isBlank(ipField.getText())) { + + ipField.requestFocus(); + AndroidUtilities.showKeyboard(ipField); + + return; + + } + + if (StringsKt.isBlank(portField.getText())) { + + portField.requestFocus(); + AndroidUtilities.showKeyboard(portField); + + return; + + } + + if (StringsKt.isBlank(userIdField.getText())) { + + userIdField.requestFocus(); + AndroidUtilities.showKeyboard(userIdField); + + return; + + } + + if (StringsKt.isBlank(alterIdField.getText())) { + + alterIdField.requestFocus(); + AndroidUtilities.showKeyboard(alterIdField); + + return; + + } + + currentBean.setAddress(ipField.getText().toString()); + currentBean.setPort(Utilities.parseInt(portField.getText().toString())); + currentBean.setId(userIdField.getText().toString()); + currentBean.setAlterId(Utilities.parseInt(alterIdField.getText().toString())); + currentBean.setSecurity(securityField.getValueTextView().getText().toString()); + currentBean.setNetwork(networkField.getValueTextView().getText().toString()); + currentBean.setHeaderType(headTypeField.getValueTextView().getText().toString()); + currentBean.setRequestHost(requestHostField.getText().toString()); + currentBean.setPath(pathField.getText().toString()); + currentBean.setStreamSecurity(useTlsField.isChecked() ? "tls" : ""); + + if (currentProxyInfo == null) { + currentProxyInfo = new SharedConfig.VmessProxy(currentBean); + SharedConfig.addProxy(currentProxyInfo); + SharedConfig.setCurrentProxy(currentProxyInfo); + } else { + SharedConfig.setProxyEnable(false); + } + + SharedConfig.saveProxyList(); + + finishFragment(); + + } + } + }); + + doneItem = actionBar.createMenu().addItemWithWidth(done_button, R.drawable.ic_done, AndroidUtilities.dp(56)); + doneItem.setContentDescription(LocaleController.getString("Done", R.string.Done)); + + fragmentView = new FrameLayout(context); + FrameLayout frameLayout = (FrameLayout) fragmentView; + fragmentView.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundGray)); + + scrollView = new ScrollView(context); + scrollView.setFillViewport(true); + AndroidUtilities.setScrollViewEdgeEffectColor(scrollView, Theme.getColor(Theme.key_actionBarDefault)); + frameLayout.addView(scrollView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + + linearLayout2 = new LinearLayout(context); + linearLayout2.setOrientation(LinearLayout.VERTICAL); + scrollView.addView(linearLayout2, new ScrollView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + + inputFieldsContainer = new LinearLayout(context); + inputFieldsContainer.setOrientation(LinearLayout.VERTICAL); + inputFieldsContainer.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + // bring to front for transitions + inputFieldsContainer.setElevation(AndroidUtilities.dp(1f)); + inputFieldsContainer.setOutlineProvider(null); + } + linearLayout2.addView(inputFieldsContainer, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + + inputFields = new EditTextBoldCursor[6]; + + for (int a = 0; a < 6; a++) { + FrameLayout container = new FrameLayout(context); + EditTextBoldCursor cursor = mkCursor(); + inputFields[a] = cursor; + cursor.setImeOptions(EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NO_EXTRACT_UI); + switch (a) { + case 0: + ipField = cursor; + cursor.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + cursor.setHintText(LocaleController.getString("UseProxyAddress", R.string.UseProxyAddress)); + cursor.setText(currentBean.getAddress()); + break; + case 1: + portField = cursor; + cursor.setInputType(InputType.TYPE_CLASS_NUMBER); + cursor.setHintText(LocaleController.getString("UseProxyPort", R.string.UseProxyPort)); + cursor.setText("" + currentBean.getPort()); + break; + case 2: + userIdField = cursor; + cursor.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + cursor.setHintText(LocaleController.getString("VmessUserId", R.string.VmessUserId)); + cursor.setText(currentBean.getId()); + break; + case 3: + alterIdField = cursor; + cursor.setInputType(InputType.TYPE_CLASS_NUMBER); + cursor.setHintText(LocaleController.getString("VmessAlterId", R.string.VmessAlterId)); + cursor.setText("" + currentBean.getAlterId()); + break; + case 4: + requestHostField = cursor; + cursor.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + cursor.setHintText(LocaleController.getString("VmessRequestHost", R.string.VmessRequestHost)); + cursor.setText(currentBean.getRequestHost()); + break; + case 5: + pathField = cursor; + cursor.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + cursor.setHintText(LocaleController.getString("VmessPath", R.string.VmessPath)); + cursor.setText(currentBean.getPath()); + break; + } + cursor.setSelection(cursor.length()); + + cursor.setPadding(0, 0, 0, 0); + container.addView(cursor, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 17, a == 0 ? 12 : 0, 17, 0)); + + } + + inputFieldsContainer.addView((View) ipField.getParent(), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 64)); + inputFieldsContainer.addView((View) portField.getParent(), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 64)); + inputFieldsContainer.addView((View) userIdField.getParent(), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 64)); + inputFieldsContainer.addView((View) alterIdField.getParent(), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 64)); + + FrameLayout container = new FrameLayout(context); + + securityField = new TextSettingsCell(context); + securityField.setBackground(Theme.getSelectorDrawable(false)); + securityField.setTextAndValue(LocaleController.getString("VmessSecurity", R.string.VmessSecurity), currentBean.getSecurity(), false); + container.addView(securityField, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, 0, 0, 0)); + + securityField.setOnClickListener((v) -> { + + BottomSheet.Builder select = new BottomSheet.Builder(context); + + select.setItems(securitySet,(d,i) -> { + + securityField.getValueTextView().setText(securitySet[i]); + + }); + + select.show(); + + }); + + inputFieldsContainer.addView(container, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 64)); + + container = new FrameLayout(context); + networkField = new TextSettingsCell(context); + networkField.setBackground(Theme.getSelectorDrawable(false)); + networkField.setTextAndValue(LocaleController.getString("VmessNetwork", R.string.VmessNetwork), currentBean.getNetwork(), false); + container.addView(networkField, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, 0, 0, 0)); + + networkField.setOnClickListener((v) -> { + + BottomSheet.Builder select = new BottomSheet.Builder(context); + + select.setItems(networkSet,(d,i) -> { + + networkField.getValueTextView().setText(networkSet[i]); + + }); + + select.show(); + + }); + + inputFieldsContainer.addView(container, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 64)); + + container = new FrameLayout(context); + headTypeField = new TextSettingsCell(context); + headTypeField.setBackground(Theme.getSelectorDrawable(false)); + headTypeField.setTextAndValue(LocaleController.getString("VmessHeadType", R.string.VmessHeadType), currentBean.getHeaderType(), false); + container.addView(headTypeField, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, 0, 0, 0)); + + headTypeField.setOnClickListener((v) -> { + + BottomSheet.Builder select = new BottomSheet.Builder(context); + + select.setItems(headTypeSet,(d,i) -> { + + headTypeField.getValueTextView().setText(headTypeSet[i]); + + }); + + select.show(); + + }); + + inputFieldsContainer.addView(container, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 64)); + + inputFieldsContainer.addView((View) requestHostField.getParent(), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 64)); + inputFieldsContainer.addView((View) pathField.getParent(), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 64)); + + container = new FrameLayout(context); + useTlsField = new TextCheckCell(context); + useTlsField.setBackground(Theme.getSelectorDrawable(false)); + useTlsField.setTextAndCheck(LocaleController.getString("VmessTls", R.string.VmessTls), !StringsKt.isBlank(currentBean.getStreamSecurity()), false); + container.addView(useTlsField, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, 0, 0, 0)); + + useTlsField.setOnClickListener((v) -> useTlsField.setChecked(!useTlsField.isChecked())); + + inputFieldsContainer.addView(container, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 64)); + + bottomCell = new TextInfoPrivacyCell(context); + bottomCell.setBackground(Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); + bottomCell.setText(LocaleController.getString("ProxyInfoVmess", R.string.ProxyInfoVmess)); + linearLayout2.addView(bottomCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + + return fragmentView; + + } + + EditTextBoldCursor mkCursor() { + + EditTextBoldCursor cursor = new EditTextBoldCursor(getParentActivity()); + cursor.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + cursor.setHintColor(Theme.getColor(Theme.key_windowBackgroundWhiteHintText)); + cursor.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + cursor.setBackground(null); + cursor.setCursorColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + cursor.setCursorSize(AndroidUtilities.dp(20)); + cursor.setCursorWidth(1.5f); + cursor.setSingleLine(true); + cursor.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); + cursor.setHeaderHintColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlueHeader)); + cursor.setTransformHintToHeader(true); + cursor.setLineColors(Theme.getColor(Theme.key_windowBackgroundWhiteInputField), Theme.getColor(Theme.key_windowBackgroundWhiteInputFieldActivated), Theme.getColor(Theme.key_windowBackgroundWhiteRedText3)); + return cursor; + + } + + @Override + protected void onTransitionAnimationEnd(boolean isOpen, boolean backward) { + if (isOpen && !backward && currentProxyInfo == null) { + ipField.requestFocus(); + AndroidUtilities.showKeyboard(ipField); + } + } + + @Override + public ThemeDescription[] getThemeDescriptions() { + final ThemeDescription.ThemeDescriptionDelegate delegate = () -> { + if (inputFields != null) { + for (int i = 0; i < inputFields.length; i++) { + inputFields[i].setLineColors(Theme.getColor(Theme.key_windowBackgroundWhiteInputField), + Theme.getColor(Theme.key_windowBackgroundWhiteInputFieldActivated), + Theme.getColor(Theme.key_windowBackgroundWhiteRedText3)); + } + } + }; + ArrayList arrayList = new ArrayList<>(); + arrayList.add(new ThemeDescription(fragmentView, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundGray)); + arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_actionBarDefault)); + arrayList.add(new ThemeDescription(scrollView, ThemeDescription.FLAG_LISTGLOWCOLOR, null, null, null, null, Theme.key_actionBarDefault)); + arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_ITEMSCOLOR, null, null, null, null, Theme.key_actionBarDefaultIcon)); + arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_TITLECOLOR, null, null, null, null, Theme.key_actionBarDefaultTitle)); + arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SELECTORCOLOR, null, null, null, null, Theme.key_actionBarDefaultSelector)); + arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SEARCH, null, null, null, null, Theme.key_actionBarDefaultSearch)); + arrayList.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SEARCHPLACEHOLDER, null, null, null, null, Theme.key_actionBarDefaultSearchPlaceholder)); + arrayList.add(new ThemeDescription(inputFieldsContainer, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_windowBackgroundWhite)); + arrayList.add(new ThemeDescription(linearLayout2, 0, new Class[]{View.class}, Theme.dividerPaint, null, null, Theme.key_divider)); + + arrayList.add(new ThemeDescription(null, 0, null, null, null, null, delegate, Theme.key_windowBackgroundWhiteBlueText4)); + arrayList.add(new ThemeDescription(null, 0, null, null, null, null, delegate, Theme.key_windowBackgroundWhiteGrayText2)); + + + if (inputFields != null) { + for (int a = 0; a < inputFields.length; a++) { + arrayList.add(new ThemeDescription(inputFields[a], ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); + arrayList.add(new ThemeDescription(inputFields[a], ThemeDescription.FLAG_HINTTEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteHintText)); + arrayList.add(new ThemeDescription(inputFields[a], ThemeDescription.FLAG_HINTTEXTCOLOR | ThemeDescription.FLAG_PROGRESSBAR, null, null, null, null, Theme.key_windowBackgroundWhiteBlueHeader)); + arrayList.add(new ThemeDescription(inputFields[a], ThemeDescription.FLAG_CURSORCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); + arrayList.add(new ThemeDescription(null, 0, null, null, null, delegate, Theme.key_windowBackgroundWhiteInputField)); + arrayList.add(new ThemeDescription(null, 0, null, null, null, delegate, Theme.key_windowBackgroundWhiteInputFieldActivated)); + arrayList.add(new ThemeDescription(null, 0, null, null, null, delegate, Theme.key_windowBackgroundWhiteRedText3)); + } + } else { + arrayList.add(new ThemeDescription(null, ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); + arrayList.add(new ThemeDescription(null, ThemeDescription.FLAG_HINTTEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteHintText)); + } + + arrayList.add(new ThemeDescription(bottomCell, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{TextInfoPrivacyCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow)); + arrayList.add(new ThemeDescription(bottomCell, 0, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText4)); + arrayList.add(new ThemeDescription(bottomCell, ThemeDescription.FLAG_LINKCOLOR, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteLinkText)); + + return arrayList.toArray(new ThemeDescription[0]); + } +} diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/translator/GoogleWebTranslator.java b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/translator/GoogleWebTranslator.java index 6fda4b8a1..a6a882adb 100644 --- a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/translator/GoogleWebTranslator.java +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/translator/GoogleWebTranslator.java @@ -7,16 +7,14 @@ import org.json.JSONException; import org.json.JSONTokener; import org.telegram.messenger.FileLog; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; -import java.net.URL; -import java.net.URLConnection; import java.util.Arrays; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import okhttp3.Request; import tw.nekomimi.nekogram.NekoConfig; +import tw.nekomimi.nekogram.utils.HttpUtil; public class GoogleWebTranslator extends Translator { @@ -118,45 +116,19 @@ public class GoogleWebTranslator extends Translator { } private String request(String url) { + try { - ByteArrayOutputStream outbuf; - InputStream httpConnectionStream; - URL downloadUrl = new URL(url); - URLConnection httpConnection = downloadUrl.openConnection(); - httpConnection.addRequestProperty("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 10_0 like Mac OS X) AppleWebKit/602.1.38 (KHTML, like Gecko) Version/10.0 Mobile/14A5297c Safari/602.1"); - httpConnection.setConnectTimeout(1000); - httpConnection.setReadTimeout(2000); - httpConnection.connect(); - httpConnectionStream = httpConnection.getInputStream(); - outbuf = new ByteArrayOutputStream(); + return (NekoConfig.translationProvider == 2 ? HttpUtil.okhttpClient : HttpUtil.getOkhttpClientWithCurrProxy()) + .newCall(new Request.Builder() + .url(url) + .header("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 10_0 like Mac OS X) AppleWebKit/602.1.38 (KHTML, like Gecko) Version/10.0 Mobile/14A5297c Safari/602.1") + .build()).execute().body().string(); - byte[] data = new byte[1024 * 32]; - while (true) { - int read = httpConnectionStream.read(data); - if (read > 0) { - outbuf.write(data, 0, read); - } else if (read == -1) { - break; - } else { - break; - } - } - String result = new String(outbuf.toByteArray()); - try { - httpConnectionStream.close(); - } catch (Throwable e) { - FileLog.e(e); - } - try { - outbuf.close(); - } catch (Exception ignore) { - - } - return result; } catch (Throwable e) { FileLog.e(e); return null; } + } } diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/utils/AlertUtil.kt b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/utils/AlertUtil.kt new file mode 100644 index 000000000..97aec5ff0 --- /dev/null +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/utils/AlertUtil.kt @@ -0,0 +1,13 @@ +package tw.nekomimi.nekogram.utils + +import android.content.Context +import android.widget.Toast +import com.google.android.exoplayer2.drm.DecryptionResource +import org.telegram.messenger.ApplicationLoader + +object AlertUtil { + + @JvmStatic + fun showToast(text: String) = Toast.makeText(ApplicationLoader.applicationContext, text.takeIf { it.isNotBlank() } ?: "喵 !", Toast.LENGTH_LONG).show() + +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/utils/DnsFactory.kt b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/utils/DnsFactory.kt new file mode 100644 index 000000000..2bb51f057 --- /dev/null +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/utils/DnsFactory.kt @@ -0,0 +1,53 @@ +package tw.nekomimi.nekogram.utils + +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.dnsoverhttps.DnsOverHttps +import org.telegram.tgnet.ConnectionsManager +import java.net.InetAddress +import java.util.* + +open class DnsFactory { + + companion object : DnsFactory() { + + init { + + addProvider("https://mozilla.cloudflare-dns.com/dns-query") + addProvider("https://cloudflare-dns.com/dns-query") + addProvider("https://dns.google/dns-query") + addProvider("https://dns.twnic.tw/dns-query") + addProvider("https://dns.adguard.com/dns-query") + + } + + } + + val providers = LinkedList() + + fun addProvider(url: String) { + + providers.add(DnsOverHttps.Builder() + .client(HttpUtil.okhttpClient) + .url(url.toHttpUrl()) + .includeIPv6(ConnectionsManager.useIpv6Address()) + .build()) + + } + + fun lookUp(host: String) : Array { + + providers.forEach { + + runCatching { + + return it.lookup(host).toTypedArray() + + } + + } + + return arrayOf() + + } + +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/utils/FileUtil.kt b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/utils/FileUtil.kt new file mode 100644 index 000000000..74691ae1c --- /dev/null +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/utils/FileUtil.kt @@ -0,0 +1,13 @@ +package tw.nekomimi.nekogram.utils + +import java.io.File + +object FileUtil { + + @JvmStatic + fun readUtf8String(file: File) = file.readText() + + @JvmStatic + fun writeUtf8String(text: String, save: File) = save.writeText(text) + +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/utils/HttpUtil.kt b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/utils/HttpUtil.kt new file mode 100644 index 000000000..cc3727399 --- /dev/null +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/utils/HttpUtil.kt @@ -0,0 +1,74 @@ +package tw.nekomimi.nekogram.utils + +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.internal.http2.Header +import org.telegram.messenger.SharedConfig +import java.net.InetSocketAddress +import java.net.Proxy +import java.util.concurrent.TimeUnit + +object HttpUtil { + + @JvmField + val okhttpClient = OkHttpClient().newBuilder().readTimeout(500,TimeUnit.MILLISECONDS).build() + + @JvmStatic + val okhttpClientWithCurrProxy: OkHttpClient get() { + + return if (!SharedConfig.proxyEnabled || SharedConfig.currentProxy?.secret != null ) { + + okhttpClient + + } else { + + okhttpClient.newBuilder() + .proxy(Proxy(Proxy.Type.SOCKS,InetSocketAddress(SharedConfig.currentProxy.address,SharedConfig.currentProxy.port))) + .build() + + } + + } + + @JvmStatic + fun get(url: String): String { + + val request = Request.Builder().url(url).build() + + okhttpClient.newCall(request).execute().apply { + + val body = body + + return body?.string() ?: error("HTTP ERROR $code") + + } + + } + + @JvmStatic + fun get(url: String,ua: String): String { + + val request = Request.Builder().url(url).addHeader("User-Agent",ua).build() + + okhttpClient.newCall(request).execute().apply { + + return body?.string() ?: error("HTTP ERROR $code") + + } + + } + + @JvmStatic + fun getByteArray(url: String): ByteArray { + + val request = Request.Builder().url(url).build() + + okhttpClient.newCall(request).execute().apply { + + return body?.bytes() ?: error("HTTP ERROR $code") + + } + + } + +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/utils/ProxyUtil.kt b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/utils/ProxyUtil.kt new file mode 100644 index 000000000..c4485fbe7 --- /dev/null +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/utils/ProxyUtil.kt @@ -0,0 +1,456 @@ +package tw.nekomimi.nekogram.utils + +import android.Manifest +import android.app.Activity +import android.content.ClipboardManager +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.graphics.Bitmap +import android.os.Build +import android.os.Environment +import android.view.Gravity +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.Toast +import com.google.zxing.* +import com.google.zxing.common.GlobalHistogramBinarizer +import com.google.zxing.qrcode.QRCodeReader +import com.google.zxing.qrcode.QRCodeWriter +import com.v2ray.ang.V2RayConfig +import com.v2ray.ang.V2RayConfig.SSR_PROTOCOL +import com.v2ray.ang.V2RayConfig.SS_PROTOCOL +import com.v2ray.ang.V2RayConfig.VMESS1_PROTOCOL +import com.v2ray.ang.V2RayConfig.VMESS_PROTOCOL +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull +import org.json.JSONArray +import org.telegram.messenger.* +import org.telegram.messenger.browser.Browser +import org.telegram.ui.ActionBar.BottomSheet +import java.io.ByteArrayInputStream +import java.io.File +import java.net.NetworkInterface +import java.util.* + + +object ProxyUtil { + + @JvmStatic + val cacheFile = File(ApplicationLoader.applicationContext.filesDir.parent, "nekox/proxy_list.json") + + @JvmStatic + fun isVPNEnabled(): Boolean { + + val networkList = mutableListOf() + + runCatching { + + Collections.list(NetworkInterface.getNetworkInterfaces()).forEach { + + if (it.isUp) networkList.add(it.name) + + } + + } + + return networkList.contains("tun0") + + } + + @JvmStatic + fun reloadProxyList(): Boolean { + + cacheFile.parentFile?.mkdirs() + + runCatching { + + // 从 GITEE 主站 读取 + + val list = JSONArray(HttpUtil.get("https://gitee.com/nekoshizuku/AwesomeRepo/raw/master/proxy_list.json")).toString() + + if (!cacheFile.isFile || list != cacheFile.readText()) { + + cacheFile.writeText(list) + + return true + + } + + }.recover { + + // 从 GITHUB PAGES 读取 + + val list = JSONArray(HttpUtil.get("https://nekogramx.github.io/ProxyList/proxy_list.json")).toString() + + if (!cacheFile.isFile || list != cacheFile.readText()) { + + cacheFile.writeText(list) + + return true + + } + + }.recover { + + // 从 GITLAB 读取 + + val list = JSONArray(HttpUtil.get("https://gitlab.com/KazamaWataru/nekox-proxy-list/-/raw/master/proxy_list.json")).toString() + + if (!cacheFile.isFile || list != cacheFile.readText()) { + + cacheFile.writeText(list) + + return true + + } + + }.recover { + + // 从 GITHUB 主站 读取 + + val master = HttpUtil.getByteArray("https://github.com/NekogramX/ProxyList/archive/master.zip") + + val list = JSONArray(String(ZipUtil.read(ByteArrayInputStream(master), "ProxyList-master/proxy_list.json"))).toString() + + if (!cacheFile.isFile || list != cacheFile.readText()) { + + cacheFile.writeText(list) + + return true + + } + + } + + return false + + } + + @JvmStatic + fun importFromClipboard(ctx: Context) { + + val clip = ctx.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + + var exists = false + + clip.primaryClip?.getItemAt(0)?.text?.split('\n')?.map { it.split(" ") }?.forEach { + + it.forEach { line -> + + if (line.startsWith("tg://proxy") || + line.startsWith("tg://socks") || + line.startsWith("https://t.me/proxy") || + line.startsWith("https://t.me/socks") || + line.startsWith(VMESS_PROTOCOL) || + line.startsWith(VMESS1_PROTOCOL) || + line.startsWith(SS_PROTOCOL) || + line.startsWith(SSR_PROTOCOL)) { + + exists = true + + import(ctx, line) + + } + + } + + } + + if (!exists) { + + AlertUtil.showToast(LocaleController.getString("BrokenLink", R.string.BrokenLink)) + + } + + } + + fun import(ctx: Context, link: String) { + + runCatching { + + if (link.startsWith(VMESS_PROTOCOL) || link.startsWith(VMESS1_PROTOCOL)) { + + AndroidUtilities.showVmessAlert(ctx, SharedConfig.VmessProxy(link)) + + } else if (link.startsWith(SS_PROTOCOL)) { + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + + AlertUtil.showToast(LocaleController.getString("MinApi21Required", R.string.MinApi21Required)) + + return + + } + + AndroidUtilities.showShadowsocksAlert(ctx, SharedConfig.ShadowsocksProxy(link)) + + } else if (link.startsWith(SSR_PROTOCOL)) { + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + + AlertUtil.showToast(LocaleController.getString("MinApi21Required", R.string.MinApi21Required)) + + return + + } + + AndroidUtilities.showShadowsocksRAlert(ctx, SharedConfig.ShadowsocksRProxy(link)) + + } else { + + val url = link.toHttpUrlOrNull()!! + + AndroidUtilities.showProxyAlert(ctx, + url.queryParameter("server"), + url.queryParameter("port"), + url.queryParameter("user"), + url.queryParameter("pass"), + url.queryParameter("secret")) + + + } + + }.onFailure { + + FileLog.e(it) + + AlertUtil.showToast(LocaleController.getString("BrokenLink", R.string.BrokenLink)) + + } + + } + + + @JvmStatic + fun shareProxy(ctx: Activity, info: SharedConfig.ProxyInfo, type: Int) { + + val url = if (info is SharedConfig.VmessProxy) { + + info.bean.toString() + + } else if (info is SharedConfig.ShadowsocksProxy) { + + info.bean.toString() + + } else if (info is SharedConfig.ShadowsocksRProxy) { + + info.bean.toString() + + } else { + + val httpUrl = (if (info.secret.isEmpty()) { + + "https://t.me/socks" + + } else { + + "https://t.me/proxy" + + }).toHttpUrlOrNull()!!.newBuilder() + + httpUrl.addQueryParameter("server", info.address) + httpUrl.addQueryParameter("port", info.port.toString()) + + if (info.secret.isNotBlank()) { + + httpUrl.addQueryParameter("secret", info.secret) + + } else { + + httpUrl.addQueryParameter("user", info.username) + httpUrl.addQueryParameter("pass", info.password) + + } + + httpUrl.build().toString() + + } + + if (type == 1) { + + AndroidUtilities.addToClipboard(url) + + Toast.makeText(ctx, LocaleController.getString("LinkCopied", R.string.LinkCopied), Toast.LENGTH_LONG).show() + + } else if (type == 0) { + + val shareIntent = Intent(Intent.ACTION_SEND) + + shareIntent.type = "text/plain" + + shareIntent.putExtra(Intent.EXTRA_TEXT, url) + + val chooserIntent = Intent.createChooser(shareIntent, LocaleController.getString("ShareLink", R.string.ShareLink)) + + chooserIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + + ctx.startActivity(chooserIntent) + + } else { + + showQrDialog(ctx, url) + + } + + } + + @JvmStatic + fun showQrDialog(ctx: Activity, text: String) { + + val code = createQRCode(text) + + android.app.AlertDialog.Builder(ctx).setView(LinearLayout(ctx).apply { + + addView(LinearLayout(ctx).apply { + + gravity = Gravity.CENTER + + val width = AndroidUtilities.dp(330f) + + addView(ImageView(ctx).apply { + + setImageBitmap(code) + + scaleType = ImageView.ScaleType.FIT_XY + + setOnLongClickListener { + + BottomSheet.Builder(ctx).setItems(arrayOf( + + LocaleController.getString("SaveToGallery", R.string.SaveToGallery), + LocaleController.getString("Cancel", R.string.Cancel) + + )) { _, i -> + + if (i == 0) { + + if (Build.VERSION.SDK_INT >= 23 && ctx.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + + ctx.requestPermissions(arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 4) + + return@setItems + + } + + val saveTo = File(Environment.getExternalStorageDirectory(), "${Environment.DIRECTORY_PICTURES}/share_${text.hashCode()}.jpg") + + saveTo.parentFile?.mkdirs() + + runCatching { + + saveTo.createNewFile() + + saveTo.outputStream().use { + + code?.compress(Bitmap.CompressFormat.JPEG, 100, it); + + } + + AndroidUtilities.addMediaToGallery(saveTo.path) + + } + + } + + }.show() + + return@setOnLongClickListener true + + } + + }, LinearLayout.LayoutParams(width, width)) + + }, LinearLayout.LayoutParams(-1, -1).apply { + + gravity = Gravity.CENTER + + }) + + }).show() + + } + + fun createQRCode(text: String, size: Int = 800): Bitmap? { + try { + val hints = HashMap() + hints[EncodeHintType.CHARACTER_SET] = "utf-8" + //hints[EncodeHintType.ERROR_CORRECTION] = ErrorCorrectionLevel.H + val bitMatrix = QRCodeWriter().encode(text, BarcodeFormat.QR_CODE, size, size, hints) + val pixels = IntArray(size * size) + for (y in 0 until size) { + for (x in 0 until size) { + if (bitMatrix.get(x, y)) { + pixels[y * size + x] = 0xff000000.toInt() + } else { + pixels[y * size + x] = 0xffffffff.toInt() + } + } + } + val bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888) + bitmap.setPixels(pixels, 0, size, 0, 0, size, size) + return bitmap + } catch (e: WriterException) { + e.printStackTrace() + return null + } + } + + val qrReader = QRCodeReader() + + @JvmStatic + fun tryReadQR(ctx: Activity, bitmap: Bitmap) { + + val intArray = IntArray(bitmap.getWidth() * bitmap.getHeight()) + bitmap.getPixels(intArray, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight()) + val source = RGBLuminanceSource(bitmap.getWidth(), bitmap.getHeight(), intArray) + + val result = qrReader.decode(BinaryBitmap(GlobalHistogramBinarizer(source))) + + if (result == null || result.text.isBlank()) { + + AlertUtil.showToast(LocaleController.getString("NoQrFound", R.string.NoQrFound)) + + } else { + + showLinkAlert(ctx, result.text) + + } + + } + + @JvmStatic + fun showLinkAlert(ctx: Activity, text: String) { + + val builder = BottomSheet.Builder(ctx) + + var isUrl = false + + runCatching { + text.toHttpUrlOrNull()!! + if (Browser.isInternalUrl(text, booleanArrayOf(false))) { + Browser.openUrl(ctx, text) + return + } + isUrl = true + } + + builder.setTitle(text) + + builder.setItems(arrayOf( + if (isUrl) LocaleController.getString("Open", R.string.OpenUrlTitle) else null, + LocaleController.getString("Copy", R.string.Copy), + LocaleController.getString("Cancel", R.string.Cancel) + )) { _, i -> + if (i == 0) { + Browser.openUrl(ctx, text) + } else if (i == 1) { + AndroidUtilities.addToClipboard(text) + Toast.makeText(ApplicationLoader.applicationContext, LocaleController.getString("LinkCopied", R.string.LinkCopied), Toast.LENGTH_LONG).show() + } + } + + builder.show() + + } + +} diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/utils/ThreadUtil.kt b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/utils/ThreadUtil.kt new file mode 100644 index 000000000..dfb1ba890 --- /dev/null +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/utils/ThreadUtil.kt @@ -0,0 +1,8 @@ +package tw.nekomimi.nekogram.utils + +object ThreadUtil { + + @JvmStatic + fun sleep(time: Long) = Thread.sleep(time) + +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/utils/UIUtil.kt b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/utils/UIUtil.kt new file mode 100644 index 000000000..01201175e --- /dev/null +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/utils/UIUtil.kt @@ -0,0 +1,13 @@ +package tw.nekomimi.nekogram.utils + +import android.os.Handler +import android.os.Looper + +object UIUtil { + + val handler = Handler(Looper.getMainLooper()) + + @JvmStatic + fun runOnUIThread(runnable: Runnable) = handler.post(runnable) + +} \ No newline at end of file diff --git a/TMessagesProj/src/main/java/tw/nekomimi/nekogram/utils/ZipUtil.kt b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/utils/ZipUtil.kt new file mode 100644 index 000000000..170cb7d5e --- /dev/null +++ b/TMessagesProj/src/main/java/tw/nekomimi/nekogram/utils/ZipUtil.kt @@ -0,0 +1,58 @@ +package tw.nekomimi.nekogram.utils + +import java.io.File +import java.io.InputStream +import java.util.zip.ZipInputStream + +object ZipUtil { + + fun read(input: InputStream, path: String): ByteArray { + + ZipInputStream(input).use { zip -> + + while (true) { + + val entry = zip.nextEntry ?: break + + if (entry.name == path) return zip.readBytes() + + } + + } + + error("path not found") + + } + + @JvmStatic + fun unzip(input: InputStream, output: File) { + + ZipInputStream(input).use { zip -> + + while (true) { + + val entry = zip.nextEntry ?: break + + val entryFile = File(output, entry.name) + + if (entry.isDirectory) { + + entryFile.mkdirs() + + } else { + + entryFile.outputStream().use { + + zip.copyTo(it) + + } + + } + + } + + } + + } + +} \ No newline at end of file diff --git a/TMessagesProj/src/main/libs/arm64-v8a/libtmessages.30.so b/TMessagesProj/src/main/libs/arm64-v8a/libtmessages.30.so new file mode 100644 index 000000000..9483c2ce3 Binary files /dev/null and b/TMessagesProj/src/main/libs/arm64-v8a/libtmessages.30.so differ diff --git a/TMessagesProj/src/main/libs/armeabi-v7a/libtmessages.30.so b/TMessagesProj/src/main/libs/armeabi-v7a/libtmessages.30.so new file mode 100644 index 000000000..6e07af86f Binary files /dev/null and b/TMessagesProj/src/main/libs/armeabi-v7a/libtmessages.30.so differ diff --git a/TMessagesProj/src/main/libs/x86/libtmessages.30.so b/TMessagesProj/src/main/libs/x86/libtmessages.30.so new file mode 100644 index 000000000..418d677ff Binary files /dev/null and b/TMessagesProj/src/main/libs/x86/libtmessages.30.so differ diff --git a/TMessagesProj/src/main/libs/x86_64/libtmessages.30.so b/TMessagesProj/src/main/libs/x86_64/libtmessages.30.so new file mode 100644 index 000000000..e289216a2 Binary files /dev/null and b/TMessagesProj/src/main/libs/x86_64/libtmessages.30.so differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_launcher_background.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_launcher_background.png new file mode 100644 index 000000000..617ce0115 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/ic_launcher_background.png differ diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_launcher_foreground.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..9aca3bd9e Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-hdpi/ic_launcher_foreground.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_launcher_background.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_launcher_background.png new file mode 100644 index 000000000..513e4f3fa Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/ic_launcher_background.png differ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_launcher_foreground.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..5c8af884c Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-mdpi/ic_launcher_foreground.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_launcher_background.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_launcher_background.png new file mode 100644 index 000000000..4a95993e5 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_launcher_background.png differ diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_launcher_foreground.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..f5d977881 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xhdpi/ic_launcher_foreground.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_launcher_background.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_launcher_background.png new file mode 100644 index 000000000..d6518b8ed Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_launcher_background.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..8d6d3409f Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxxhdpi/ic_launcher_background.png b/TMessagesProj/src/main/res/drawable-xxxhdpi/ic_launcher_background.png new file mode 100644 index 000000000..ca92ecf57 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxxhdpi/ic_launcher_background.png differ diff --git a/TMessagesProj/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png b/TMessagesProj/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..f5161bba9 Binary files /dev/null and b/TMessagesProj/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png differ diff --git a/TMessagesProj/src/main/res/drawable/gem_l.png b/TMessagesProj/src/main/res/drawable/gem_l.png deleted file mode 100644 index 06c378b6f..000000000 Binary files a/TMessagesProj/src/main/res/drawable/gem_l.png and /dev/null differ diff --git a/TMessagesProj/src/main/res/drawable/ic_action_next.xml b/TMessagesProj/src/main/res/drawable/ic_action_next.xml index ad6b6221d..d74a4675f 100755 --- a/TMessagesProj/src/main/res/drawable/ic_action_next.xml +++ b/TMessagesProj/src/main/res/drawable/ic_action_next.xml @@ -1,17 +1,17 @@ - - - - - - + + + + + + \ No newline at end of file diff --git a/TMessagesProj/src/main/res/drawable/ic_action_pause.xml b/TMessagesProj/src/main/res/drawable/ic_action_pause.xml index e720ea761..e53a7b201 100755 --- a/TMessagesProj/src/main/res/drawable/ic_action_pause.xml +++ b/TMessagesProj/src/main/res/drawable/ic_action_pause.xml @@ -1,13 +1,13 @@ - - - - + + + + \ No newline at end of file diff --git a/TMessagesProj/src/main/res/drawable/ic_action_play.xml b/TMessagesProj/src/main/res/drawable/ic_action_play.xml index f38d01941..c4959c2e7 100755 --- a/TMessagesProj/src/main/res/drawable/ic_action_play.xml +++ b/TMessagesProj/src/main/res/drawable/ic_action_play.xml @@ -1,10 +1,10 @@ - - - + + + \ No newline at end of file diff --git a/TMessagesProj/src/main/res/drawable/ic_action_previous.xml b/TMessagesProj/src/main/res/drawable/ic_action_previous.xml index f596ea591..ea30fbec9 100755 --- a/TMessagesProj/src/main/res/drawable/ic_action_previous.xml +++ b/TMessagesProj/src/main/res/drawable/ic_action_previous.xml @@ -1,18 +1,18 @@ - - - - - - + + + + + + \ No newline at end of file diff --git a/TMessagesProj/src/main/res/layout/player_big_notification.xml b/TMessagesProj/src/main/res/layout/player_big_notification.xml index e37aa6d49..0f9ff733f 100755 --- a/TMessagesProj/src/main/res/layout/player_big_notification.xml +++ b/TMessagesProj/src/main/res/layout/player_big_notification.xml @@ -1,137 +1,137 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TMessagesProj/src/main/res/layout/player_small_notification.xml b/TMessagesProj/src/main/res/layout/player_small_notification.xml index 43c2b9d32..55d8fb0bc 100755 --- a/TMessagesProj/src/main/res/layout/player_small_notification.xml +++ b/TMessagesProj/src/main/res/layout/player_small_notification.xml @@ -1,116 +1,116 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TMessagesProj/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/TMessagesProj/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 000000000..036d09bc5 --- /dev/null +++ b/TMessagesProj/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/TMessagesProj/src/main/res/mipmap-hdpi/ic_launcher.png b/TMessagesProj/src/main/res/mipmap-hdpi/ic_launcher.png index 58b105116..3a7ef90eb 100644 Binary files a/TMessagesProj/src/main/res/mipmap-hdpi/ic_launcher.png and b/TMessagesProj/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/TMessagesProj/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/TMessagesProj/src/main/res/mipmap-hdpi/ic_launcher_foreground.png index 23eeab426..eef19650b 100644 Binary files a/TMessagesProj/src/main/res/mipmap-hdpi/ic_launcher_foreground.png and b/TMessagesProj/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/TMessagesProj/src/main/res/mipmap-hdpi/ic_launcher_round.png b/TMessagesProj/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 000000000..3a7ef90eb Binary files /dev/null and b/TMessagesProj/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/TMessagesProj/src/main/res/mipmap-mdpi/ic_launcher.png b/TMessagesProj/src/main/res/mipmap-mdpi/ic_launcher.png index c04762a48..88caa1f71 100644 Binary files a/TMessagesProj/src/main/res/mipmap-mdpi/ic_launcher.png and b/TMessagesProj/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/TMessagesProj/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/TMessagesProj/src/main/res/mipmap-mdpi/ic_launcher_foreground.png index f7dc83ae6..c6d15f121 100644 Binary files a/TMessagesProj/src/main/res/mipmap-mdpi/ic_launcher_foreground.png and b/TMessagesProj/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/TMessagesProj/src/main/res/mipmap-mdpi/ic_launcher_round.png b/TMessagesProj/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 000000000..88caa1f71 Binary files /dev/null and b/TMessagesProj/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/TMessagesProj/src/main/res/mipmap-xhdpi/ic_launcher.png b/TMessagesProj/src/main/res/mipmap-xhdpi/ic_launcher.png index 524fedbaa..cce4768db 100644 Binary files a/TMessagesProj/src/main/res/mipmap-xhdpi/ic_launcher.png and b/TMessagesProj/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/TMessagesProj/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/TMessagesProj/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png index 83986cd84..958b3034c 100644 Binary files a/TMessagesProj/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png and b/TMessagesProj/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/TMessagesProj/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/TMessagesProj/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 000000000..cce4768db Binary files /dev/null and b/TMessagesProj/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/TMessagesProj/src/main/res/mipmap-xxhdpi/ic_launcher.png b/TMessagesProj/src/main/res/mipmap-xxhdpi/ic_launcher.png index 548789111..705df2a52 100644 Binary files a/TMessagesProj/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/TMessagesProj/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/TMessagesProj/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/TMessagesProj/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png index f8d476e80..8d49aff17 100644 Binary files a/TMessagesProj/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png and b/TMessagesProj/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/TMessagesProj/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/TMessagesProj/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..705df2a52 Binary files /dev/null and b/TMessagesProj/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/TMessagesProj/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/TMessagesProj/src/main/res/mipmap-xxxhdpi/ic_launcher.png index f2c0168ad..4ff19ae15 100644 Binary files a/TMessagesProj/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/TMessagesProj/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/TMessagesProj/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/TMessagesProj/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png index 05e28c4ae..fcb3df8e9 100644 Binary files a/TMessagesProj/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png and b/TMessagesProj/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/TMessagesProj/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/TMessagesProj/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..4ff19ae15 Binary files /dev/null and b/TMessagesProj/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/TMessagesProj/src/main/res/values-af/strings_nekox.xml b/TMessagesProj/src/main/res/values-af/strings_nekox.xml new file mode 100644 index 000000000..3ea04e700 --- /dev/null +++ b/TMessagesProj/src/main/res/values-af/strings_nekox.xml @@ -0,0 +1,2 @@ + + diff --git a/TMessagesProj/src/main/res/values-ar/strings_nekox.xml b/TMessagesProj/src/main/res/values-ar/strings_nekox.xml new file mode 100644 index 000000000..3ea04e700 --- /dev/null +++ b/TMessagesProj/src/main/res/values-ar/strings_nekox.xml @@ -0,0 +1,2 @@ + + diff --git a/TMessagesProj/src/main/res/values-b+zh+Duang/strings_neko.xml b/TMessagesProj/src/main/res/values-b+zh+Duang/strings_neko.xml index 0cc726973..a3517e422 100644 --- a/TMessagesProj/src/main/res/values-b+zh+Duang/strings_neko.xml +++ b/TMessagesProj/src/main/res/values-b+zh+Duang/strings_neko.xml @@ -85,4 +85,5 @@ 对话分类打开方式 点按标题栏 点按铅笔按钮 + 当滚动浏览对话时隐藏键盘 diff --git a/TMessagesProj/src/main/res/values-ca/strings_nekox.xml b/TMessagesProj/src/main/res/values-ca/strings_nekox.xml new file mode 100644 index 000000000..3ea04e700 --- /dev/null +++ b/TMessagesProj/src/main/res/values-ca/strings_nekox.xml @@ -0,0 +1,2 @@ + + diff --git a/TMessagesProj/src/main/res/values-cs/strings_nekox.xml b/TMessagesProj/src/main/res/values-cs/strings_nekox.xml new file mode 100644 index 000000000..3ea04e700 --- /dev/null +++ b/TMessagesProj/src/main/res/values-cs/strings_nekox.xml @@ -0,0 +1,2 @@ + + diff --git a/TMessagesProj/src/main/res/values-da/strings_nekox.xml b/TMessagesProj/src/main/res/values-da/strings_nekox.xml new file mode 100644 index 000000000..3ea04e700 --- /dev/null +++ b/TMessagesProj/src/main/res/values-da/strings_nekox.xml @@ -0,0 +1,2 @@ + + diff --git a/TMessagesProj/src/main/res/values-de/strings_nekox.xml b/TMessagesProj/src/main/res/values-de/strings_nekox.xml new file mode 100644 index 000000000..3ea04e700 --- /dev/null +++ b/TMessagesProj/src/main/res/values-de/strings_nekox.xml @@ -0,0 +1,2 @@ + + diff --git a/TMessagesProj/src/main/res/values-el/strings_nekox.xml b/TMessagesProj/src/main/res/values-el/strings_nekox.xml new file mode 100644 index 000000000..3ea04e700 --- /dev/null +++ b/TMessagesProj/src/main/res/values-el/strings_nekox.xml @@ -0,0 +1,2 @@ + + diff --git a/TMessagesProj/src/main/res/values-es/strings_neko.xml b/TMessagesProj/src/main/res/values-es/strings_neko.xml index d83128b16..1568e891b 100644 --- a/TMessagesProj/src/main/res/values-es/strings_neko.xml +++ b/TMessagesProj/src/main/res/values-es/strings_neko.xml @@ -85,4 +85,5 @@ Abrir filtro de chats al Tocar en la barra de título Tocar en el botón del lápiz + Ocultar teclado al desplazar el chat diff --git a/TMessagesProj/src/main/res/values-es/strings_nekox.xml b/TMessagesProj/src/main/res/values-es/strings_nekox.xml new file mode 100644 index 000000000..3ea04e700 --- /dev/null +++ b/TMessagesProj/src/main/res/values-es/strings_nekox.xml @@ -0,0 +1,2 @@ + + diff --git a/TMessagesProj/src/main/res/values-fa/strings_nekox.xml b/TMessagesProj/src/main/res/values-fa/strings_nekox.xml new file mode 100644 index 000000000..5b7671d5e --- /dev/null +++ b/TMessagesProj/src/main/res/values-fa/strings_nekox.xml @@ -0,0 +1,13 @@ + + + پروکسی عمومی نکوX + (عمومی) + ممکن است ده ها ثانیه برای بارگیری پروکسی داخلی نکوX طول بکشد + وضعیت مرا ارسال نکن + تصویر قبل + فعالسازی توسعه دهنده + غیرفعال‌سازی مد توسعه دهنده + تنظیمات توسعه دهنده + غیرفعال‌سازی پرچم امنیتی + عدم شناسایی اسکرین‌شات + diff --git a/TMessagesProj/src/main/res/values-fi/strings_nekox.xml b/TMessagesProj/src/main/res/values-fi/strings_nekox.xml new file mode 100644 index 000000000..3ea04e700 --- /dev/null +++ b/TMessagesProj/src/main/res/values-fi/strings_nekox.xml @@ -0,0 +1,2 @@ + + diff --git a/TMessagesProj/src/main/res/values-fr/strings_nekox.xml b/TMessagesProj/src/main/res/values-fr/strings_nekox.xml new file mode 100644 index 000000000..3ea04e700 --- /dev/null +++ b/TMessagesProj/src/main/res/values-fr/strings_nekox.xml @@ -0,0 +1,2 @@ + + diff --git a/TMessagesProj/src/main/res/values-he/strings_nekox.xml b/TMessagesProj/src/main/res/values-he/strings_nekox.xml new file mode 100644 index 000000000..3ea04e700 --- /dev/null +++ b/TMessagesProj/src/main/res/values-he/strings_nekox.xml @@ -0,0 +1,2 @@ + + diff --git a/TMessagesProj/src/main/res/values-hu/strings_nekox.xml b/TMessagesProj/src/main/res/values-hu/strings_nekox.xml new file mode 100644 index 000000000..3ea04e700 --- /dev/null +++ b/TMessagesProj/src/main/res/values-hu/strings_nekox.xml @@ -0,0 +1,2 @@ + + diff --git a/TMessagesProj/src/main/res/values-id/strings_neko.xml b/TMessagesProj/src/main/res/values-id/strings_neko.xml index 2bbf31158..414e9107c 100644 --- a/TMessagesProj/src/main/res/values-id/strings_neko.xml +++ b/TMessagesProj/src/main/res/values-id/strings_neko.xml @@ -84,4 +84,6 @@ Buka arsip saat ditarik ke bawah Buka filter dialog dengan Ketuk di atas baris judul + Ketuk pada tombol pensil + Sembunyikan keyboard saat men-scroll diff --git a/TMessagesProj/src/main/res/values-it/strings_nekox.xml b/TMessagesProj/src/main/res/values-it/strings_nekox.xml new file mode 100644 index 000000000..3ea04e700 --- /dev/null +++ b/TMessagesProj/src/main/res/values-it/strings_nekox.xml @@ -0,0 +1,2 @@ + + diff --git a/TMessagesProj/src/main/res/values-ja/strings_neko.xml b/TMessagesProj/src/main/res/values-ja/strings_neko.xml index df23f3eca..5ac293388 100644 --- a/TMessagesProj/src/main/res/values-ja/strings_neko.xml +++ b/TMessagesProj/src/main/res/values-ja/strings_neko.xml @@ -83,4 +83,5 @@ チャットフィルタを開く方法 タイトルバーをタップ 鉛筆ボタンをタップ + チャットスクロールでキーボードを非表示にする diff --git a/TMessagesProj/src/main/res/values-ja/strings_nekox.xml b/TMessagesProj/src/main/res/values-ja/strings_nekox.xml new file mode 100644 index 000000000..3ea04e700 --- /dev/null +++ b/TMessagesProj/src/main/res/values-ja/strings_nekox.xml @@ -0,0 +1,2 @@ + + diff --git a/TMessagesProj/src/main/res/values-ko/strings_neko.xml b/TMessagesProj/src/main/res/values-ko/strings_neko.xml index b28384a27..d0eaabe33 100644 --- a/TMessagesProj/src/main/res/values-ko/strings_neko.xml +++ b/TMessagesProj/src/main/res/values-ko/strings_neko.xml @@ -85,4 +85,5 @@ 대화 상자 필터 열기 제목 표시 줄을 누르기 연필 버튼 누르기 + 대화창 스크롤시 키보드 숨기기 diff --git a/TMessagesProj/src/main/res/values-ko/strings_nekox.xml b/TMessagesProj/src/main/res/values-ko/strings_nekox.xml new file mode 100644 index 000000000..3ea04e700 --- /dev/null +++ b/TMessagesProj/src/main/res/values-ko/strings_nekox.xml @@ -0,0 +1,2 @@ + + diff --git a/TMessagesProj/src/main/res/values-nl/strings_nekox.xml b/TMessagesProj/src/main/res/values-nl/strings_nekox.xml new file mode 100644 index 000000000..3ea04e700 --- /dev/null +++ b/TMessagesProj/src/main/res/values-nl/strings_nekox.xml @@ -0,0 +1,2 @@ + + diff --git a/TMessagesProj/src/main/res/values-no/strings_nekox.xml b/TMessagesProj/src/main/res/values-no/strings_nekox.xml new file mode 100644 index 000000000..3ea04e700 --- /dev/null +++ b/TMessagesProj/src/main/res/values-no/strings_nekox.xml @@ -0,0 +1,2 @@ + + diff --git a/TMessagesProj/src/main/res/values-pl/strings_nekox.xml b/TMessagesProj/src/main/res/values-pl/strings_nekox.xml new file mode 100644 index 000000000..3ea04e700 --- /dev/null +++ b/TMessagesProj/src/main/res/values-pl/strings_nekox.xml @@ -0,0 +1,2 @@ + + diff --git a/TMessagesProj/src/main/res/values-pt-rBR/strings_neko.xml b/TMessagesProj/src/main/res/values-pt-rBR/strings_neko.xml index 05742b38d..d8aad4b5b 100644 --- a/TMessagesProj/src/main/res/values-pt-rBR/strings_neko.xml +++ b/TMessagesProj/src/main/res/values-pt-rBR/strings_neko.xml @@ -45,7 +45,7 @@ Teste de back-end Esta conta se conectará ao teste back-end. Esta conta se conectará a produção back-end. - Ocultar canal do patrocinador proxy + Ocultar canal do patrocinador do proxy Ver histórico Miau! Salvar cache no diretório privado* @@ -77,12 +77,13 @@ Lingocloud Desfazer tradução Copiar Detalhes - Desativar Virar Fotos ao tocar + Desativar mudança de fotos ao tocar nos cantos Token Chats fixados ilimitados Fixar chats ilimitados desativar a sincronização. Abrir chats arquivados ao puxar para baixo Abrir filtro de chats por - Toque na barra de título \"Nekogram\" - Toque no botão de lápis + Toque na barra no título \"Nekogram\" + Toque no botão do lápis + Ocultar teclado na rolagem do chat diff --git a/TMessagesProj/src/main/res/values-pt-rBR/strings_nekox.xml b/TMessagesProj/src/main/res/values-pt-rBR/strings_nekox.xml new file mode 100644 index 000000000..27c26e3eb --- /dev/null +++ b/TMessagesProj/src/main/res/values-pt-rBR/strings_nekox.xml @@ -0,0 +1,65 @@ + + + NekoX Público Proxy + [ Público ] + Pode levar dezenas de segundos para carregar o proxy interno do NekoX Público :) + Não enviar meu status de entrada + Pré-visualizar imagem + Salvar cache em sdcard* + Não lida + Não silenciado & Não Lido + Configurações de diálogo + Configurações de diálogo + Classificar por não lidas + Classificar por Não silenciado + Classificar por usuário + Classificar por contatos + Serviço de Notificação NekoX + Por favor ative \"Serviço de Notificação NekoX\" + Por favor desabilite \"Serviço de Notificação NekoX\" + Para usuários do MIUI, ative a permissão de inicialização automática para o NekoX receber notificações normalmente. + Desativar Desfazer + Desativar conta do sistema + Menu de filtro de diálogo + Desativar proxy quando a VPN estiver ativada + Pular confirmação de link aberto + Excluir tudo no chat + Aviso! Isto irá **excluir todas as mensagens** deste chat para **todos** os participantes. + Desbloquear tudos + Você tem certeza que deseja desbloquear **todos os usuários e bots**? + Você não bloqueou ninguém :) + Proxy de Vmesas + Adicionar Proxy Socks5 + Adicionar Proxy MTProto + Adicionar Proxy Vmess + Adicionar Shadowsocks Proxy + Editar Proxy + Compartilhar Proxy + Excluir proxy + Configurações de Proxy Vmess + ID do usuário + Alterar ID + Segurança + Rede + Tipo de cabeça + Host do Pedido / Segurança QUIC + Path / QUIC Key + Usar TLS + Configurações do Proxy Shadowsocks + Senha + Método de criptografia + Desculpe, você precisa pelo menos do Android 5 (API21). + Importar da área de transferência + Link desconhecido / quebrado + QR Code + Escanear QR Code + Nenhum QR Code encontrado + Ativar modo de desenvolvedor + Desativar modo desenvolvedor + Configurações do desenvolvedor + Desativar sinalização segura + Desativar detecção de captura de tela + Configurações de login + Mostrar back-end de teste + Mostrar Login do Bot + diff --git a/TMessagesProj/src/main/res/values-pt/strings_nekox.xml b/TMessagesProj/src/main/res/values-pt/strings_nekox.xml new file mode 100644 index 000000000..3ea04e700 --- /dev/null +++ b/TMessagesProj/src/main/res/values-pt/strings_nekox.xml @@ -0,0 +1,2 @@ + + diff --git a/TMessagesProj/src/main/res/values-ro/strings_nekox.xml b/TMessagesProj/src/main/res/values-ro/strings_nekox.xml new file mode 100644 index 000000000..3ea04e700 --- /dev/null +++ b/TMessagesProj/src/main/res/values-ro/strings_nekox.xml @@ -0,0 +1,2 @@ + + diff --git a/TMessagesProj/src/main/res/values-ru/strings_nekox.xml b/TMessagesProj/src/main/res/values-ru/strings_nekox.xml new file mode 100644 index 000000000..3ea04e700 --- /dev/null +++ b/TMessagesProj/src/main/res/values-ru/strings_nekox.xml @@ -0,0 +1,2 @@ + + diff --git a/TMessagesProj/src/main/res/values-sr/strings_nekox.xml b/TMessagesProj/src/main/res/values-sr/strings_nekox.xml new file mode 100644 index 000000000..3ea04e700 --- /dev/null +++ b/TMessagesProj/src/main/res/values-sr/strings_nekox.xml @@ -0,0 +1,2 @@ + + diff --git a/TMessagesProj/src/main/res/values-sv/strings_nekox.xml b/TMessagesProj/src/main/res/values-sv/strings_nekox.xml new file mode 100644 index 000000000..3ea04e700 --- /dev/null +++ b/TMessagesProj/src/main/res/values-sv/strings_nekox.xml @@ -0,0 +1,2 @@ + + diff --git a/TMessagesProj/src/main/res/values-tr/strings_nekox.xml b/TMessagesProj/src/main/res/values-tr/strings_nekox.xml new file mode 100644 index 000000000..3ea04e700 --- /dev/null +++ b/TMessagesProj/src/main/res/values-tr/strings_nekox.xml @@ -0,0 +1,2 @@ + + diff --git a/TMessagesProj/src/main/res/values-uk/strings_nekox.xml b/TMessagesProj/src/main/res/values-uk/strings_nekox.xml new file mode 100644 index 000000000..3ea04e700 --- /dev/null +++ b/TMessagesProj/src/main/res/values-uk/strings_nekox.xml @@ -0,0 +1,2 @@ + + diff --git a/TMessagesProj/src/main/res/values-vi/strings_nekox.xml b/TMessagesProj/src/main/res/values-vi/strings_nekox.xml new file mode 100644 index 000000000..3ea04e700 --- /dev/null +++ b/TMessagesProj/src/main/res/values-vi/strings_nekox.xml @@ -0,0 +1,2 @@ + + diff --git a/TMessagesProj/src/main/res/values-zh-rCN/strings_neko.xml b/TMessagesProj/src/main/res/values-zh-rCN/strings_neko.xml index b042e59bd..8c4a96293 100644 --- a/TMessagesProj/src/main/res/values-zh-rCN/strings_neko.xml +++ b/TMessagesProj/src/main/res/values-zh-rCN/strings_neko.xml @@ -85,4 +85,5 @@ 对话分类打开方式 点按标题栏 点按铅笔按钮 + 当滚动浏览对话时隐藏键盘 diff --git a/TMessagesProj/src/main/res/values-zh-rCN/strings_nekox.xml b/TMessagesProj/src/main/res/values-zh-rCN/strings_nekox.xml new file mode 100644 index 000000000..62d304390 --- /dev/null +++ b/TMessagesProj/src/main/res/values-zh-rCN/strings_nekox.xml @@ -0,0 +1,65 @@ + + + NekoX 公共代理 + [ 公共 ] + 这可能需要几十秒的时间来加载 NekoX 公共代理 :) + 不要发送我的输入状态 + 假装截屏 + 保存缓存到内部存储* + 未读 + 未读且允许通知 + 对话设置 + 排序设置 + 未读优先排序 + 可通知优先排序 + 用户优先排序 + 联系人优先排序 + NekoX 通知服务 + 请启用 \"NekoX 通知服务\" + 请禁用 \"NekoX 通知服务\" + 对于 MIUI 用户, 请先为 NekoX 启用自启动权限才能正常接收通知. + 禁用五秒内后悔 + 禁用系统帐户 + 分类菜单 + 启用 VPN 时禁用代理 + 跳过打开链接确认 + 删除群组所有消息 + 警告!这将**删除此聊天中所有用户**的**所有消息**。 + 解除所有 + 您确定要解除屏蔽**所有用户和机器人**吗? + 您还没有屏蔽任何人 :) + Vmess 代理 + 添加 Socks5 代理 + 添加 MTProto 代理 + 添加 Vmess 代理 + 添加 Shadowsocks 代理 + 编辑代理 + 分享代理 + 删除代理 + Vmess 代理设置 + 用户ID + 额外ID + 加密方式 + 传输协议 + 伪装类型 + 伪装域名 / QUIC 加密方式 + 路径 / QUIC 加密密钥 + 使用 TLS + Shadowsocks 代理设置 + 密码 + 加密方法 + 抱歉,您至少需要 Android 5 (API21)。 + 从剪贴板导入 + 未知或损坏的链接 + 二维码 + 扫描二维码 + 未找到二维码 + 启用开发者模式 + 禁用开发者模式 + 开发者设置 + 禁用安全标记 + 禁用截屏检测 + 登录设置 + 显示连接到测试后端 + 显示机器人秘钥登录 + diff --git a/TMessagesProj/src/main/res/values-zh-rHK/strings_neko.xml b/TMessagesProj/src/main/res/values-zh-rHK/strings_neko.xml index 66aacfc01..214b9b7a1 100644 --- a/TMessagesProj/src/main/res/values-zh-rHK/strings_neko.xml +++ b/TMessagesProj/src/main/res/values-zh-rHK/strings_neko.xml @@ -85,4 +85,5 @@ 對話分類開啟方式 點按標題欄 點按鉛筆按鈕 + 當滾動瀏覽對話時隱藏鍵盤 diff --git a/TMessagesProj/src/main/res/values-zh-rHK/strings_nekox.xml b/TMessagesProj/src/main/res/values-zh-rHK/strings_nekox.xml new file mode 100644 index 000000000..7049971a9 --- /dev/null +++ b/TMessagesProj/src/main/res/values-zh-rHK/strings_nekox.xml @@ -0,0 +1,65 @@ + + + NekoX 公共代理 + [ 公共 ] + 這可能需要幾十秒的時間來加載 NekoX 公共代理 :) + 不要發送我的輸入狀態 + 假裝截屏 + 保存緩存到存儲卡* + 未讀 + 未讀且可通知 + 對話設定 + 排序設定 + 未讀優先排序 + 可通知優先排序 + 用戶優先排序 + 聯繫人優先排序 + NekoX 通知服務 + 請啓用 \"NekoX 通知服務\" + 請禁用 \"NekoX 通知服務\" + 對於MIUI用戶, 請先為 NekoX 啟用自啟動權限才能正常接收通知. + 禁用五秒內後悔 + 禁用系統賬戶 + 分類菜單 + 啓用 VPN 時禁用代理 + 跳過打開鏈接確認 + 刪除群組所有消息 + 警告!這將**刪除此聊天中所有用戶**的**所有消息**。 + 解除所有 + 您確定要解除屏蔽**所有用戶與機器人**嗎? + 您还没有屏蔽任何人 :) + Vmess 代理 + 添加 Socks5 代理 + 添加 MTProto 代理 + 添加 Vmess 代理 + 添加 Shadowsocks 代理 + 編輯代理 + 分享代理 + 刪除代理 + Vmess 代理設定 + 用戶ID + 額外ID + 加密方式 + 傳輸協議 + 僞裝類型 + 僞裝域名 / QUIC 加密方式 + 路徑 / QUIC 加密密鑰 + 使用 TLS + Shadowsocks 代理設定 + 密碼 + 加密方法 + 抱歉,您至少需要 Android 5 (API21)。 + 從剪切板導入 + 未知或損壞的鏈接 + 二維碼 + 扫描二维码 + 未找到二維碼 + 啓用開發人員模式 + 禁用開發人員模式 + 開發人員設定 + 禁用安全標記 + 禁用截屏檢測 + 登錄設定 + 顯示連接到測試後端 + 顯示機器人秘鑰登錄 + diff --git a/TMessagesProj/src/main/res/values-zh-rTW/strings_neko.xml b/TMessagesProj/src/main/res/values-zh-rTW/strings_neko.xml index 117c77f46..73a3b7380 100644 --- a/TMessagesProj/src/main/res/values-zh-rTW/strings_neko.xml +++ b/TMessagesProj/src/main/res/values-zh-rTW/strings_neko.xml @@ -85,4 +85,5 @@ 對話分類開啟方式 點按標題欄 點按鉛筆按鈕 + 當滾動瀏覽對話時隱藏鍵盤 diff --git a/TMessagesProj/src/main/res/values-zh-rTW/strings_nekox.xml b/TMessagesProj/src/main/res/values-zh-rTW/strings_nekox.xml new file mode 100644 index 000000000..7049971a9 --- /dev/null +++ b/TMessagesProj/src/main/res/values-zh-rTW/strings_nekox.xml @@ -0,0 +1,65 @@ + + + NekoX 公共代理 + [ 公共 ] + 這可能需要幾十秒的時間來加載 NekoX 公共代理 :) + 不要發送我的輸入狀態 + 假裝截屏 + 保存緩存到存儲卡* + 未讀 + 未讀且可通知 + 對話設定 + 排序設定 + 未讀優先排序 + 可通知優先排序 + 用戶優先排序 + 聯繫人優先排序 + NekoX 通知服務 + 請啓用 \"NekoX 通知服務\" + 請禁用 \"NekoX 通知服務\" + 對於MIUI用戶, 請先為 NekoX 啟用自啟動權限才能正常接收通知. + 禁用五秒內後悔 + 禁用系統賬戶 + 分類菜單 + 啓用 VPN 時禁用代理 + 跳過打開鏈接確認 + 刪除群組所有消息 + 警告!這將**刪除此聊天中所有用戶**的**所有消息**。 + 解除所有 + 您確定要解除屏蔽**所有用戶與機器人**嗎? + 您还没有屏蔽任何人 :) + Vmess 代理 + 添加 Socks5 代理 + 添加 MTProto 代理 + 添加 Vmess 代理 + 添加 Shadowsocks 代理 + 編輯代理 + 分享代理 + 刪除代理 + Vmess 代理設定 + 用戶ID + 額外ID + 加密方式 + 傳輸協議 + 僞裝類型 + 僞裝域名 / QUIC 加密方式 + 路徑 / QUIC 加密密鑰 + 使用 TLS + Shadowsocks 代理設定 + 密碼 + 加密方法 + 抱歉,您至少需要 Android 5 (API21)。 + 從剪切板導入 + 未知或損壞的鏈接 + 二維碼 + 扫描二维码 + 未找到二維碼 + 啓用開發人員模式 + 禁用開發人員模式 + 開發人員設定 + 禁用安全標記 + 禁用截屏檢測 + 登錄設定 + 顯示連接到測試後端 + 顯示機器人秘鑰登錄 + diff --git a/TMessagesProj/src/main/res/values/ic_launcher_background.xml b/TMessagesProj/src/main/res/values/ic_launcher_background.xml index 63913ed03..07c2929ad 100644 --- a/TMessagesProj/src/main/res/values/ic_launcher_background.xml +++ b/TMessagesProj/src/main/res/values/ic_launcher_background.xml @@ -1,4 +1,4 @@ - #C1DCFC + #4B6C85 \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values/strings_neko.xml b/TMessagesProj/src/main/res/values/strings_neko.xml index d93ae9a2d..ab651d239 100644 --- a/TMessagesProj/src/main/res/values/strings_neko.xml +++ b/TMessagesProj/src/main/res/values/strings_neko.xml @@ -90,4 +90,7 @@ Open dialogs filter by Tap on the title bar Tap on the pencil button + Hide keyboard on chat scroll + Send Stickers + Send GIFs \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values/strings_nekox.xml b/TMessagesProj/src/main/res/values/strings_nekox.xml new file mode 100644 index 000000000..6e33929f8 --- /dev/null +++ b/TMessagesProj/src/main/res/values/strings_nekox.xml @@ -0,0 +1,95 @@ + + + + NekoX Public Proxy + [ Public ] + It may take tens of seconds to load the NekoX Public built-in proxy :) + + Don\'t send my input status + Pretend screenshot + Save cache to sdcard* + + Unread + Unmuted & Unread + + Dialogs Settings + Dialog Sort Settings + Sort By Unread + Sort By Unmuted + Sort By User + Sort By Contacts + + NekoX Push Service + Please enable "NekoX Push Service" + Please disable "NekoX Push Service" + + For MIUI users, please enable auto-start permission for NekoX to receive notifications normally. + + Disable Undo + Disable System Account + Dialogs Filter Menu + + Disable proxy when VPN enabled + Skip open link confirm + + Delete all in chat + Warning! This will **delete all messages** in this chat for **all** participants. + + Unblock All + Are you sure you want to unblock **all users and bots**? + You haven\'t blocked anyone :) + + Vmess Proxy + Add Socks5 Proxy + Add MTProto Proxy + Add Vmess Proxy + Add Shadowsocks Proxy + Add ShadowsocksR Proxy + + Edit Proxy + Share Proxy + Delete Proxy + + Vmess Proxy Settings + User Id + Alter Id + Security + Network + Head Type + Request Host / QUIC Security + Path / QUIC Key + Use TLS + + Shadowsocks Proxy Settings + Password + Encrypt Method + + ShadowsocksR Proxy Settings + Protocol + Protocol Params + Obfs + Obfs Param + + Sorry, you need at least Android 5 (API21). + + Import From Clipboard + Unknown / Broken Link + + QR Code + Scan QR Code + + No QR Code found + + Enable Developer Mode + Disable Developer Mode + Developer Settings + + Disable Flag Secure + Disable Screenshot Detection + + Login Settings + Show Test Backend + Show Bot Login + + + \ No newline at end of file diff --git a/TMessagesProj/src/main/res/xml/auth.xml b/TMessagesProj/src/main/res/xml/auth.xml index d9e15adb5..89025e555 100644 --- a/TMessagesProj/src/main/res/xml/auth.xml +++ b/TMessagesProj/src/main/res/xml/auth.xml @@ -1,7 +1,7 @@ diff --git a/TMessagesProj/src/main/res/xml/network_security_config.xml b/TMessagesProj/src/main/res/xml/network_security_config.xml new file mode 100644 index 000000000..d7b4192e1 --- /dev/null +++ b/TMessagesProj/src/main/res/xml/network_security_config.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/TMessagesProj/src/main/res/xml/sync_contacts.xml b/TMessagesProj/src/main/res/xml/sync_contacts.xml index 3251a2760..4e8920c60 100644 --- a/TMessagesProj/src/main/res/xml/sync_contacts.xml +++ b/TMessagesProj/src/main/res/xml/sync_contacts.xml @@ -1,3 +1,3 @@ \ No newline at end of file + android:accountType="moe.wataru.nekogram"/> \ No newline at end of file diff --git a/TMessagesProj/src/multidex/java/org/telegram/messenger/MultiDexApplicationLoader.java b/TMessagesProj/src/multidex/java/org/telegram/messenger/MultiDexApplicationLoader.java index 3f1ced45e..b94bd096a 100644 --- a/TMessagesProj/src/multidex/java/org/telegram/messenger/MultiDexApplicationLoader.java +++ b/TMessagesProj/src/multidex/java/org/telegram/messenger/MultiDexApplicationLoader.java @@ -1,13 +1,14 @@ package org.telegram.messenger; import android.content.Context; + import androidx.multidex.MultiDex; -public class MultiDexApplicationLoader extends ApplicationLoader{ +public class MultiDexApplicationLoader extends ApplicationLoader { - @Override - protected void attachBaseContext(Context base){ - super.attachBaseContext(base); - MultiDex.install(this); - } + @Override + protected void attachBaseContext(Context base) { + super.attachBaseContext(base); + MultiDex.install(this); + } } \ No newline at end of file diff --git a/build.gradle b/build.gradle index 8a03d1535..adb4f75dc 100644 --- a/build.gradle +++ b/build.gradle @@ -6,9 +6,18 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.5.3' + classpath 'com.android.tools.build:gradle:3.6.1' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.71" + classpath 'com.google.gms:google-services:4.3.3' + classpath 'com.google.firebase:firebase-crashlytics-gradle:2.0.0-beta03' } } -repositories { - google() -} + +allprojects { + repositories { + // The order in which you list these repositories matter. + jcenter() + mavenCentral() + google() + } +} \ No newline at end of file diff --git a/crowdin.yml b/crowdin.yml index 2f1c24560..511e79dbe 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -1,3 +1,3 @@ files: - - source: /TMessagesProj/src/main/res/values/strings_neko.xml - translation: /TMessagesProj/src/main/res/values-%two_letters_code%/%original_file_name% + - source: /TMessagesProj/src/main/res/values/strings_nekox.xml + translation: /TMessagesProj/src/main/res/values-%two_letters_code%/%original_file_name% \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 8b5e603fe..2ecdd122d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,23 +1,22 @@ -## Project-wide Gradle settings. -# -# For more details on how to configure your build environment visit +## For more details on how to configure your build environment visit # http://www.gradle.org/docs/current/userguide/build_environment.html # # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -# Default value: -Xmx10248m -XX:MaxPermSize=256m +# Default value: -Xmx1024m -XX:MaxPermSize=256m # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 # # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true -RELEASE_KEY_PASSWORD=android +#Fri Mar 20 16:49:47 CST 2020 RELEASE_KEY_ALIAS=androidkey +RELEASE_KEY_PASSWORD=android RELEASE_STORE_PASSWORD=android -org.gradle.jvmargs=-Xmx4096M -XX:MaxPermSize=4096m -org.gradle.daemon=true -org.gradle.parallel=true -org.gradle.configureondemand=false -android.useAndroidX=true android.enableJetifier=true +android.useAndroidX=true +org.gradle.configureondemand=false +org.gradle.daemon=true +org.gradle.jvmargs=-Xmx1536M -Dkotlin.daemon.jvm.options\="-Xmx4096M" -XX\:MaxPermSize\=4096m +org.gradle.parallel=true diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 934dfd3b0..2c1979a1c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Apr 23 19:30:06 MSK 2019 +#Wed Mar 25 07:24:03 CST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip diff --git a/release.sh b/release.sh new file mode 100644 index 000000000..5486d51d8 --- /dev/null +++ b/release.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +git branch -D release +git checkout -b release +git push origin release -f +git checkout master \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 867a4183d..3f0766d03 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,3 @@ +include ':ssr-libev' +include ':ss-libev' include ':TMessagesProj' diff --git a/ss-libev/.gitignore b/ss-libev/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/ss-libev/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/ss-libev/build.gradle b/ss-libev/build.gradle new file mode 100644 index 000000000..2b4b89e62 --- /dev/null +++ b/ss-libev/build.gradle @@ -0,0 +1,32 @@ +plugins { + id 'com.android.library' +} + +android { + compileSdkVersion 29 + buildToolsVersion "29.0.3" + ndkVersion "21.0.6113669" + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 29 + versionCode 1 + versionName "1.0" + + externalNativeBuild { + ndkBuild { + + abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' + arguments "NDK_APPLICATION_MK:=src/main/jni/Application.mk", "APP_PLATFORM:=android-21", "--jobs=8" + + } + } + } + + externalNativeBuild { + ndkBuild { + path 'src/main/jni/Android.mk' + } + } + +} \ No newline at end of file diff --git a/ss-libev/src/main/AndroidManifest.xml b/ss-libev/src/main/AndroidManifest.xml new file mode 100644 index 000000000..0ba4d0e94 --- /dev/null +++ b/ss-libev/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/ss-libev/src/main/jni/Android.mk b/ss-libev/src/main/jni/Android.mk new file mode 100644 index 000000000..f02564bb7 --- /dev/null +++ b/ss-libev/src/main/jni/Android.mk @@ -0,0 +1,293 @@ +# Copyright (C) 2009 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +LOCAL_PATH := $(call my-dir) +ROOT_PATH := $(LOCAL_PATH) + +BUILD_SHARED_EXECUTABLE := $(LOCAL_PATH)/build-shared-executable.mk + +######################################################## +## libsodium +######################################################## + +include $(CLEAR_VARS) + +SODIUM_SOURCE := \ + crypto_aead/aes256gcm/aesni/aead_aes256gcm_aesni.c \ + crypto_aead/chacha20poly1305/sodium/aead_chacha20poly1305.c \ + crypto_aead/xchacha20poly1305/sodium/aead_xchacha20poly1305.c \ + crypto_core/ed25519/ref10/ed25519_ref10.c \ + crypto_core/hchacha20/core_hchacha20.c \ + crypto_core/salsa/ref/core_salsa_ref.c \ + crypto_generichash/blake2b/ref/blake2b-compress-ref.c \ + crypto_generichash/blake2b/ref/blake2b-ref.c \ + crypto_generichash/blake2b/ref/generichash_blake2b.c \ + crypto_onetimeauth/poly1305/onetimeauth_poly1305.c \ + crypto_onetimeauth/poly1305/donna/poly1305_donna.c \ + crypto_pwhash/crypto_pwhash.c \ + crypto_pwhash/argon2/argon2-core.c \ + crypto_pwhash/argon2/argon2.c \ + crypto_pwhash/argon2/argon2-encoding.c \ + crypto_pwhash/argon2/argon2-fill-block-ref.c \ + crypto_pwhash/argon2/blake2b-long.c \ + crypto_pwhash/argon2/pwhash_argon2i.c \ + crypto_scalarmult/curve25519/scalarmult_curve25519.c \ + crypto_scalarmult/curve25519/ref10/x25519_ref10.c \ + crypto_stream/chacha20/stream_chacha20.c \ + crypto_stream/chacha20/ref/chacha20_ref.c \ + crypto_stream/salsa20/stream_salsa20.c \ + crypto_stream/salsa20/ref/salsa20_ref.c \ + crypto_verify/sodium/verify.c \ + randombytes/randombytes.c \ + randombytes/sysrandom/randombytes_sysrandom.c \ + sodium/core.c \ + sodium/runtime.c \ + sodium/utils.c \ + sodium/version.c + +LOCAL_MODULE := sodium +LOCAL_CFLAGS += -I$(LOCAL_PATH)/libsodium/src/libsodium/include \ + -I$(LOCAL_PATH)/include \ + -I$(LOCAL_PATH)/include/sodium \ + -I$(LOCAL_PATH)/libsodium/src/libsodium/include/sodium \ + -DPACKAGE_NAME=\"libsodium\" -DPACKAGE_TARNAME=\"libsodium\" \ + -DPACKAGE_VERSION=\"1.0.15\" -DPACKAGE_STRING=\"libsodium-1.0.15\" \ + -DPACKAGE_BUGREPORT=\"https://github.com/jedisct1/libsodium/issues\" \ + -DPACKAGE_URL=\"https://github.com/jedisct1/libsodium\" \ + -DPACKAGE=\"libsodium\" -DVERSION=\"1.0.15\" \ + -DHAVE_PTHREAD=1 \ + -DSTDC_HEADERS=1 \ + -DHAVE_SYS_TYPES_H=1 \ + -DHAVE_SYS_STAT_H=1 \ + -DHAVE_STDLIB_H=1 \ + -DHAVE_STRING_H=1 \ + -DHAVE_MEMORY_H=1 \ + -DHAVE_STRINGS_H=1 \ + -DHAVE_INTTYPES_H=1 \ + -DHAVE_STDINT_H=1 \ + -DHAVE_UNISTD_H=1 \ + -D__EXTENSIONS__=1 \ + -D_ALL_SOURCE=1 \ + -D_GNU_SOURCE=1 \ + -D_POSIX_PTHREAD_SEMANTICS=1 \ + -D_TANDEM_SOURCE=1 \ + -DHAVE_DLFCN_H=1 \ + -DLT_OBJDIR=\".libs/\" \ + -DHAVE_SYS_MMAN_H=1 \ + -DNATIVE_LITTLE_ENDIAN=1 \ + -DASM_HIDE_SYMBOL=.hidden \ + -DHAVE_WEAK_SYMBOLS=1 \ + -DHAVE_ATOMIC_OPS=1 \ + -DHAVE_ARC4RANDOM=1 \ + -DHAVE_ARC4RANDOM_BUF=1 \ + -DHAVE_MMAP=1 \ + -DHAVE_MLOCK=1 \ + -DHAVE_MADVISE=1 \ + -DHAVE_MPROTECT=1 \ + -DHAVE_NANOSLEEP=1 \ + -DHAVE_POSIX_MEMALIGN=1 \ + -DHAVE_GETPID=1 \ + -DCONFIGURED=1 + +LOCAL_SRC_FILES := $(addprefix libsodium/src/libsodium/,$(SODIUM_SOURCE)) + +include $(BUILD_STATIC_LIBRARY) + +######################################################## +## libancillary +######################################################## + +include $(CLEAR_VARS) + +ANCILLARY_SOURCE := fd_recv.c fd_send.c + +LOCAL_MODULE := libancillary +LOCAL_CFLAGS += -I$(LOCAL_PATH)/libancillary + +LOCAL_SRC_FILES := $(addprefix libancillary/, $(ANCILLARY_SOURCE)) + +include $(BUILD_STATIC_LIBRARY) + +######################################################## +## libbloom +######################################################## + +include $(CLEAR_VARS) + +BLOOM_SOURCE := bloom.c murmur2/MurmurHash2.c + +LOCAL_MODULE := libbloom +LOCAL_CFLAGS += -I$(LOCAL_PATH)/shadowsocks-libev/libbloom \ + -I$(LOCAL_PATH)/shadowsocks-libev/libbloom/murmur2 + +LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/libbloom/, $(BLOOM_SOURCE)) + +include $(BUILD_STATIC_LIBRARY) + +######################################################## +## libipset +######################################################## + +include $(CLEAR_VARS) + +bdd_src = bdd/assignments.c bdd/basics.c bdd/bdd-iterator.c bdd/expanded.c \ + bdd/reachable.c bdd/read.c bdd/write.c +map_src = map/allocation.c map/inspection.c map/ipv4_map.c map/ipv6_map.c \ + map/storage.c +set_src = set/allocation.c set/inspection.c set/ipv4_set.c set/ipv6_set.c \ + set/iterator.c set/storage.c + +IPSET_SOURCE := general.c $(bdd_src) $(map_src) $(set_src) + +LOCAL_MODULE := libipset +LOCAL_CFLAGS += -I$(LOCAL_PATH)/shadowsocks-libev/libipset/include \ + -I$(LOCAL_PATH)/shadowsocks-libev/libcork/include + +LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/libipset/src/libipset/,$(IPSET_SOURCE)) + +include $(BUILD_STATIC_LIBRARY) + +######################################################## +## libcork +######################################################## + +include $(CLEAR_VARS) + +cli_src := cli/commands.c +core_src := core/allocator.c core/error.c core/gc.c \ + core/hash.c core/ip-address.c core/mempool.c \ + core/timestamp.c core/u128.c +ds_src := ds/array.c ds/bitset.c ds/buffer.c ds/dllist.c \ + ds/file-stream.c ds/hash-table.c ds/managed-buffer.c \ + ds/ring-buffer.c ds/slice.c +posix_src := posix/directory-walker.c posix/env.c posix/exec.c \ + posix/files.c posix/process.c posix/subprocess.c +pthreads_src := pthreads/thread.c + +CORK_SOURCE := $(cli_src) $(core_src) $(ds_src) $(posix_src) $(pthreads_src) + +LOCAL_MODULE := libcork +LOCAL_CFLAGS += -I$(LOCAL_PATH)/shadowsocks-libev/libcork/include \ + -DCORK_API=CORK_LOCAL + +LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/libcork/src/libcork/,$(CORK_SOURCE)) + +include $(BUILD_STATIC_LIBRARY) + +######################################################## +## libev +######################################################## + +include $(CLEAR_VARS) + +LOCAL_MODULE := libev +LOCAL_CFLAGS += -DNDEBUG -DHAVE_CONFIG_H \ + -I$(LOCAL_PATH)/include/libev +LOCAL_SRC_FILES := \ + libev/ev.c \ + libev/event.c + +include $(BUILD_STATIC_LIBRARY) + +######################################################## +## shadowsocks-libev local +######################################################## + +include $(CLEAR_VARS) + +SHADOWSOCKS_SOURCES := local.c \ + cache.c udprelay.c utils.c netutils.c json.c jconf.c \ + acl.c http.c tls.c rule.c \ + crypto.c aead.c stream.c base64.c \ + plugin.c ppbloom.c \ + android.c + +LOCAL_MODULE := ss-local +LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/src/, $(SHADOWSOCKS_SOURCES)) +LOCAL_CFLAGS := -Wall -fno-strict-aliasing -DMODULE_LOCAL \ + -DUSE_CRYPTO_MBEDTLS -DHAVE_CONFIG_H \ + -DCONNECT_IN_PROGRESS=EINPROGRESS \ + -I$(LOCAL_PATH)/include/shadowsocks-libev \ + -I$(LOCAL_PATH)/include \ + -I$(LOCAL_PATH)/libancillary \ + -I$(LOCAL_PATH)/mbedtls/include \ + -I$(LOCAL_PATH)/pcre \ + -I$(LOCAL_PATH)/libsodium/src/libsodium/include \ + -I$(LOCAL_PATH)/libsodium/src/libsodium/include/sodium \ + -I$(LOCAL_PATH)/shadowsocks-libev/libcork/include \ + -I$(LOCAL_PATH)/shadowsocks-libev/libipset/include \ + -I$(LOCAL_PATH)/shadowsocks-libev/libbloom \ + -I$(LOCAL_PATH)/libev + +LOCAL_STATIC_LIBRARIES := libev libmbedtls libipset libcork libbloom \ + libsodium libancillary libpcre + +LOCAL_LDLIBS := -llog + +include $(BUILD_SHARED_EXECUTABLE) + +######################################################## +## mbed TLS +######################################################## + +include $(CLEAR_VARS) + +LOCAL_MODULE := mbedtls + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/mbedtls/include + +MBEDTLS_SOURCES := $(wildcard $(LOCAL_PATH)/mbedtls/library/*.c) + +LOCAL_SRC_FILES := $(MBEDTLS_SOURCES:$(LOCAL_PATH)/%=%) + +include $(BUILD_STATIC_LIBRARY) + +######################################################## +## pcre +######################################################## + +include $(CLEAR_VARS) + +LOCAL_MODULE := pcre + +LOCAL_CFLAGS += -DHAVE_CONFIG_H + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/pcre/dist $(LOCAL_PATH)/pcre + +libpcre_src_files := \ + dist/pcre_byte_order.c \ + dist/pcre_compile.c \ + dist/pcre_config.c \ + dist/pcre_dfa_exec.c \ + dist/pcre_exec.c \ + dist/pcre_fullinfo.c \ + dist/pcre_get.c \ + dist/pcre_globals.c \ + dist/pcre_jit_compile.c \ + dist/pcre_maketables.c \ + dist/pcre_newline.c \ + dist/pcre_ord2utf8.c \ + dist/pcre_refcount.c \ + dist/pcre_string_utils.c \ + dist/pcre_study.c \ + dist/pcre_tables.c \ + dist/pcre_ucd.c \ + dist/pcre_valid_utf8.c \ + dist/pcre_version.c \ + dist/pcre_xclass.c + +LOCAL_SRC_FILES := $(addprefix pcre/, $(libpcre_src_files)) $(LOCAL_PATH)/patch/pcre/pcre_chartables.c + +include $(BUILD_STATIC_LIBRARY) diff --git a/ss-libev/src/main/jni/Application.mk b/ss-libev/src/main/jni/Application.mk new file mode 100644 index 000000000..067c76ff0 --- /dev/null +++ b/ss-libev/src/main/jni/Application.mk @@ -0,0 +1 @@ +APP_STL := c++_static \ No newline at end of file diff --git a/ss-libev/src/main/jni/build-shared-executable.mk b/ss-libev/src/main/jni/build-shared-executable.mk new file mode 100644 index 000000000..05239df18 --- /dev/null +++ b/ss-libev/src/main/jni/build-shared-executable.mk @@ -0,0 +1,31 @@ +# Copyright (C) 2009 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# this file is included from Android.mk files to build a target-specific +# executable program +# +# Modified by @Mygod, based on: +# https://android.googlesource.com/platform/ndk/+/a355a4e/build/core/build-shared-library.mk +# https://android.googlesource.com/platform/ndk/+/a355a4e/build/core/build-executable.mk +LOCAL_BUILD_SCRIPT := BUILD_EXECUTABLE +LOCAL_MAKEFILE := $(local-makefile) +$(call check-defined-LOCAL_MODULE,$(LOCAL_BUILD_SCRIPT)) +$(call check-LOCAL_MODULE,$(LOCAL_MAKEFILE)) +$(call check-LOCAL_MODULE_FILENAME) +# we are building target objects +my := TARGET_ +$(call handle-module-filename,lib,$(TARGET_SONAME_EXTENSION)) +$(call handle-module-built) +LOCAL_MODULE_CLASS := EXECUTABLE +include $(BUILD_SYSTEM)/build-module.mk diff --git a/ss-libev/src/main/jni/include/libev/config.h b/ss-libev/src/main/jni/include/libev/config.h new file mode 100644 index 000000000..5c46ab0fa --- /dev/null +++ b/ss-libev/src/main/jni/include/libev/config.h @@ -0,0 +1,126 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the `clock_gettime' function. */ +/* #undef HAVE_CLOCK_GETTIME */ + +/* Define to 1 to use the syscall interface for clock_gettime */ +#define HAVE_CLOCK_SYSCALL 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the `epoll_ctl' function. */ +#define HAVE_EPOLL_CTL 1 + +/* Define to 1 if you have the `eventfd' function. */ +#define HAVE_EVENTFD 1 + +/* Define to 1 if the floor function is available */ +#define HAVE_FLOOR 1 + +/* Define to 1 if you have the `inotify_init' function. */ +#define HAVE_INOTIFY_INIT 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `kqueue' function. */ +/* #undef HAVE_KQUEUE */ + +/* Define to 1 if you have the `rt' library (-lrt). */ +/* #undef HAVE_LIBRT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `nanosleep' function. */ +#define HAVE_NANOSLEEP 1 + +/* Define to 1 if you have the `poll' function. */ +#define HAVE_POLL 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_POLL_H 1 + +/* Define to 1 if you have the `port_create' function. */ +/* #undef HAVE_PORT_CREATE */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_PORT_H */ + +/* Define to 1 if you have the `select' function. */ +#define HAVE_SELECT 1 + +/* Define to 1 if you have the `signalfd' function. */ +#define HAVE_SIGNALFD 0 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_EPOLL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_EVENTFD_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_EVENT_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_INOTIFY_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SELECT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SIGNALFD_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "libev" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "4.11" diff --git a/ss-libev/src/main/jni/include/pdnsd/config.h b/ss-libev/src/main/jni/include/pdnsd/config.h new file mode 100644 index 000000000..3740beae7 --- /dev/null +++ b/ss-libev/src/main/jni/include/pdnsd/config.h @@ -0,0 +1,437 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.in by autoheader. */ +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +/* ONLY EDIT acconfig.h, NEVER config.h or config.h.in! + * config.h MAY BE OVERWRITTEN BY make, config.h.in by autoheader! */ + +/* Define your Target here. Currently defined are TARGET_LINUX (any + * architecture), TARGET_BSD (experimental; tested on FreeBSD, hopefully + * works for other BSD variants) and TARGET_CYGWIN. */ +#define TARGET TARGET_LINUX + +/* change the #undef to #define if you do not want to compile with special + * ISDN support for Linux. Note that the ISDN support will not compile ok on + * unpatched kernerls earlier than 2.2.12 (if you did apply newer isdn patches, + * it may work fine). This is not on by default because it will cause compile + * problems on some systems */ +/* #undef ISDN_SUPPORT */ + +/* The following regulates the IP Protocol support. Supported types are IPv4 + * and IPv6 (aka IPng). You may enable either or both of these protocols. + * Enabling in this context means that support for the respective protocol + * will be in the binary. When running the binary, one of the protocols may + * be activated via command line switches. Note that activating both IPv4 and + * IPv6 is pointless (and will not work because two UDP and two TCP threads + * will be started that concur for ports). Because of that, it is not allowed. + * When pdnsd runs with IPv6 activated it should be able to service queries + * from IPv6 as well as from IPv4 hosts, provided that you host is configured + * properly. + * For each of the protocols there are two options: ENABLE_IPV4 and ENABLE_IPV6 + * control whether support for the respective protocol is available in the + * binary. DEFAULT_IPV4 selects which protocol is enabled on pdnsd + * startup by default. 1 means IPv4, while 0 means IPv6. If support for + * a protocol was included in the executable, you can specify command line + * parameters to activate or deactivate that protocol (the options are -4 and + * -6), but it makes more sense to use the run_ipv4=on/off option in the + * configuration file. + * Make your choice. Note that IPv6 support is experimental in pdnsd. + * In normal operation, you will currently only need IPv4. */ +#define ENABLE_IPV4 1 +#define DEFAULT_IPV4 1 +#undef ENABLE_IPV6 + +/* In all pdnsd versions before 1.0.6, DNS queries were always done over + * TCP. Now, you have the choice. You can control that behaviour using + * the -m command line switch, and you can give a preset here. There + * are 3 different modes: + * UDP_ONLY: This is undoubtedly the fastest query method, because + * no TCP negotiation needs to be done. + * TCP_ONLY: This is slower than uo, but generally more secure + * against DNS spoofing. Note that some name servers on the + * internet do not support TCP queries, notably dnscache. + * TCP_UDP: TCP, then UDP. If the TCP query fails with a "connection refused"- + * error or times out, the query is retried using UDP. + * UDP_TCP: UDP, then TCP. If the UDP reply is truncated (i.e. the tc flag is set), + * the query is retried using TCP. */ +#define M_PRESET TCP_ONLY + +/* In addition to choosing the presets, you may also completely disable + * one of the protocols (TCP for preset UDP_ONLY and UDP for preset TCP_ONLY). + * This saves some executable space. */ +/* #undef NO_UDP_QUERIES */ +/* #undef NO_TCP_QUERIES */ + +/* With the following option, you can disable the TCP server functionality + * of pdnsd. Nearly no program does TCP queries, so you probably can do + * this safely and save some executable space and one thread. + * You also can turn off the TCP server at runtime with the --notcp option. */ +/* #undef NO_TCP_SERVER */ + +/* By undefining the following, you can disable the UDP source address + * discovery code. This is not recommended, but you may need it when + * running into compilation problems. */ +#define SRC_ADDR_DISC 1 + +/* NO_POLL specifies not to use poll(2), but select(2) instead. If you are + * unsure about what this means, just leave this as it is.*/ +/* #undef NO_POLL */ + +/* Define this for "hard" RFC 2181 compliance: this RFC states that + * implementations should discard answers whose RR sets have multiple + * different time stamps. While correct answers are generated, incorrect + * ones are normally tolerated and corrected. Full RFC compliance is + * however only achieved by deactivating this behaviour and thus being + * intolerant. */ +/* #undef RFC2181_ME_HARDER */ + +/* Define this to the device you want to use for getting random numbers. + * Leave this undefined if you wand to use the standard C library random + * function, which basically should be sufficient. + * Linux and FreeBSD have two random number devices: /dev/random and + * /dev/urandom. /dev/urandom might be less secure in some cases, but + * should still be more than sufficient. The use of /dev/random is + * discouraged, as reading from this device blocks when new random bits + * need to be gathered. */ +/* #undef RANDOM_DEVICE */ +#define R_DEFAULT 1 +/* #undef R_RANDOM */ +/* #undef R_ARC4RANDOM */ +/*#define RANDOM_DEVICE "/dev/urandom"*/ + +/* Designate which database manager to use for cacheing. + * default: native; others: gdbm */ +#define CACHE_DBM DBM_NATIVE + +#define CONFDIR "/data/data/com.github.shadowsocks" + +#define CACHEDIR "/data/data/com.github.shadowsocks" + +#define TEMPDIR "/data/data/com.github.shadowsocks/cache"; + +/* This is for various debugging facilities that produce debug output and + * double-check some values. You can enable debug messages with the -g option. + * Normally, you can switch this off safely by setting the number after DEBUG + * to 0. This will increase speed (although only marginally), save space + * in the executable (only about 12kB) and some stack space per thread + * (which may be significant if you have many threads running simultaneously). + * However, it may be an aid when debugging config files. + * The only defined debug levels by now are in the range 0 - 9. + * Define this to 9 if you want hex dumps of all the queries and replies pdnsd + * receives (you must also call pdnsd with -v9 to actually see the hex dumps). + * When in doubt, leave it defined to 1. */ +#define DEBUG 1 + +/* This defines the default verbosity of informational messages you will get. + This has nothing to to with the debug option (-g), but may be set with -v + option. 0 is for normal operation, up to 3 for debugging. + Unlike the debug messages, these messages will also be written to the syslog.*/ +#define VERBOSITY 0 + +/* Redefine this if you want another hash size. + * The number of hash buckets is computed as power of two (1< and it should be used (not on Ultrix). + */ +#define HAVE_ALLOCA_H 1 + +/* Define to 1 if you have the `asprintf' function. */ +#define HAVE_ASPRINTF 1 + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +/* #undef HAVE_DOPRNT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if you have the `getline' function. */ +#if defined(__aarch64__) || defined(__x86_64__) +#define HAVE_GETLINE 1 +#endif + +/* Define to 1 if you have the `getpwnam_r' function. */ +//#define HAVE_GETPWNAM_R 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define to 1 if you have the `inet_ntop' function. */ +#define HAVE_INET_NTOP 1 + +/* Define to 1 if you have the `inet_pton' function. */ +#define HAVE_INET_PTON 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `pthread' library (-lpthread). */ +#define HAVE_LIBPTHREAD 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MALLOC_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `mempcpy' function. */ +//#define HAVE_MEMPCPY 1 + +/* Define to 1 if you have the `mkfifo' function. */ +#define HAVE_MKFIFO 1 + +/* Define to 1 if you have the `nanosleep' function. */ +#define HAVE_NANOSLEEP 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NET_IF_H 1 + +/* Define to 1 if you have the `poll' function. */ +#define HAVE_POLL 1 + +/* Define to 1 if you have the `select' function. */ +#define HAVE_SELECT 1 + +/* Define to 1 if you have the `snprintf' function. */ +#define HAVE_SNPRINTF 1 + +/* Define to 1 if you have the `socket' function. */ +#define HAVE_SOCKET 1 + +/* Define to 1 if the system has the type `socklen_t'. */ +#define HAVE_SOCKLEN_T 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `stpcpy' function. */ +#if defined(__aarch64__) || defined(__x86_64__) +#define HAVE_STPCPY 1 +#endif + +/* Define to 1 if you have the `stpncpy' function. */ +//#define HAVE_STPNCPY 1 + +/* Define to 1 if you have the `strdup' function. */ +#define HAVE_STRDUP 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strlcpy' function. */ +/* #undef HAVE_STRLCPY */ + +/* Define to 1 if you have the `strndup' function. */ +#define HAVE_STRNDUP 1 + +/* Define to 1 if the system has the type `struct ifreq'. */ +#define HAVE_STRUCT_IFREQ 1 + +/* Define to 1 if the system has the type `struct in6_addr'. */ +#define HAVE_STRUCT_IN6_ADDR 1 + +/* Define to 1 if the system has the type `struct in_pktinfo'. */ +#define HAVE_STRUCT_IN_PKTINFO 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYSLOG_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_IOCTL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_POLL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have that is POSIX.1 compatible. */ +#define HAVE_SYS_WAIT_H 1 + +/* Define to 1 if you have the `uname' function. */ +#define HAVE_UNAME 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `vasprintf' function. */ +#define HAVE_VASPRINTF 1 + +/* Define to 1 if you have the `vprintf' function. */ +#define HAVE_VPRINTF 1 + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 + +/* Define to 1 if your C compiler doesn't accept -c and -o together. */ +/* #undef NO_MINUS_C_MINUS_O */ + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* Define as the return type of signal handlers (`int' or `void'). */ +#define RETSIGTYPE void + +/* If using the C implementation of alloca, define if you know the + direction of stack growth for your system; otherwise it will be + automatically deduced at runtime. + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown */ +/* #undef STACK_DIRECTION */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* Define to 1 if your declares `struct tm'. */ +/* #undef TM_IN_SYS_TIME */ + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif + + +/* Define to 1 if on MINIX. */ +/* #undef _MINIX */ + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +/* #undef _POSIX_1_SOURCE */ + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `int' if does not define. */ +/* #undef pid_t */ + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ diff --git a/ss-libev/src/main/jni/include/shadowsocks-libev/config.h b/ss-libev/src/main/jni/include/shadowsocks-libev/config.h new file mode 100644 index 000000000..8594fa0db --- /dev/null +++ b/ss-libev/src/main/jni/include/shadowsocks-libev/config.h @@ -0,0 +1,428 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if building universal (internal helper macro) */ +/* #undef AC_APPLE_UNIVERSAL_BUILD */ + +/* errno for incomplete non-blocking connect(2) */ +#define CONNECT_IN_PROGRESS EINPROGRESS + +/* Override libev default fd conversion macro. */ +/* #undef EV_FD_TO_WIN32_HANDLE */ + +/* Override libev default fd close macro. */ +/* #undef EV_WIN32_CLOSE_FD */ + +/* Override libev default handle conversion macro. */ +/* #undef EV_WIN32_HANDLE_TO_FD */ + +/* Reset max file descriptor size. */ +/* #undef FD_SETSIZE */ + +/* Define to 1 if you have the header file. */ +#define HAVE_ARPA_INET_H 1 + +/* Define to 1 if you have the `CCCryptorCreateWithMode' function. */ +/* #undef HAVE_CCCRYPTORCREATEWITHMODE */ + +/* Define to 1 if you have the `clock_gettime' function. */ +/* #undef HAVE_CLOCK_GETTIME */ + +/* Define to 1 to use the syscall interface for clock_gettime */ +/* #undef HAVE_CLOCK_SYSCALL */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_COMMONCRYPTO_COMMONCRYPTO_H */ + +/* Define to 1 if you have the declaration of `inet_ntop', and to 0 if you + don't. */ +#define HAVE_DECL_INET_NTOP 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LINUX_TCP_H 1 + +/* Define to 1 if you have the `epoll_ctl' function. */ +/* #undef HAVE_EPOLL_CTL */ + +/* Define to 1 if you have the `eventfd' function. */ +/* #undef HAVE_EVENTFD */ + +/* Define to 1 if you have the `EVP_EncryptInit_ex' function. */ +/* #undef HAVE_EVP_ENCRYPTINIT_EX */ + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if the floor function is available */ +#define HAVE_FLOOR 1 + +/* Define to 1 if you have the `fork' function. */ +#define HAVE_FORK 1 + +/* Define to 1 if you have the `getpwnam_r' function. */ +#define HAVE_GETPWNAM_R 1 + +/* Define to 1 if you have the `inet_ntop' function. */ +/* #undef HAVE_INET_NTOP */ + +/* Define to 1 if you have the `inotify_init' function. */ +/* #undef HAVE_INOTIFY_INIT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Enable IPv6 support in libudns */ +#define HAVE_IPv6 1 + +/* Define to 1 if you have the `kqueue' function. */ +#define HAVE_KQUEUE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LANGINFO_H 1 + +/* Define to 1 if you have the `rt' library (-lrt). */ +/* #undef HAVE_LIBRT */ + +/* Define to 1 if you have the `socket' library (-lsocket). */ +/* #undef HAVE_LIBSOCKET */ + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LINUX_IF_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LINUX_NETFILTER_IPV4_H */ + +/* Define to 1 if you have the header + file. */ +/* #undef HAVE_LINUX_NETFILTER_IPV6_IP6_TABLES_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_LOCALE_H 1 + +/* Define to 1 if you have the `malloc' function. */ +#define HAVE_MALLOC 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `memset' function. */ +#define HAVE_MEMSET 1 + +/* Define to 1 if you have the `nanosleep' function. */ +#define HAVE_NANOSLEEP 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETDB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NET_IF_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_OPENSSL_ENGINE_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_OPENSSL_ERR_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_OPENSSL_EVP_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_OPENSSL_PEM_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_OPENSSL_RAND_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_OPENSSL_RSA_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_OPENSSL_SHA_H */ + +/* Define to 1 if you have the `poll' function. */ +#define HAVE_POLL 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_POLL_H 1 + +/* Define to 1 if you have the `port_create' function. */ +/* #undef HAVE_PORT_CREATE */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_PORT_H */ + +/* Have PTHREAD_PRIO_INHERIT. */ +#define HAVE_PTHREAD_PRIO_INHERIT 1 + +/* Define to 1 if you have the `RAND_pseudo_bytes' function. */ +/* #undef HAVE_RAND_PSEUDO_BYTES */ + +/* Define to 1 if you have the 'select' function. */ +#define HAVE_SELECT 1 + +/* Define to 1 if you have the `setresuid' function. */ +/* #undef HAVE_SETRESUID */ + +/* Define to 1 if you have the `setreuid' function. */ +#define HAVE_SETREUID 1 + +/* Define to 1 if you have the `setrlimit' function. */ +#define HAVE_SETRLIMIT 1 + +/* Define to 1 if you have the `signalfd' function. */ +/* #undef HAVE_SIGNALFD */ + +/* Define to 1 if you have the `socket' function. */ +#define HAVE_SOCKET 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_EPOLL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_EVENTFD_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_EVENT_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_INOTIFY_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_IOCTL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SELECT_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SIGNALFD_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have that is POSIX.1 compatible. */ +#define HAVE_SYS_WAIT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `vfork' function. */ +#define HAVE_VFORK 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_VFORK_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_WINDOWS_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_WINSOCK2_H */ + +/* Define to 1 if `fork' works. */ +#define HAVE_WORKING_FORK 1 + +/* Define to 1 if `vfork' works. */ +#define HAVE_WORKING_VFORK 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_WS2TCPIP_H */ + +/* have zlib compression support */ +/* #undef HAVE_ZLIB */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_ZLIB_H */ + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Define to 1 if assertions should be disabled. */ +/* #undef NDEBUG */ + +/* Name of package */ +#define PACKAGE "shadowsocks-libev" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "max.c.lv@gmail.com" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "shadowsocks-libev" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "shadowsocks-libev 2.4.8" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "shadowsocks-libev" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "2.4.8" + +/* Define to necessary symbol if this constant uses a non-standard name on + your system. */ +/* #undef PTHREAD_CREATE_JOINABLE */ + +/* Define as the return type of signal handlers (`int' or `void'). */ +#define RETSIGTYPE void + +/* Define to the type of arg 1 for `select'. */ +#define SELECT_TYPE_ARG1 int + +/* Define to the type of args 2, 3 and 4 for `select'. */ +#define SELECT_TYPE_ARG234 (fd_set *) + +/* Define to the type of arg 5 for `select'. */ +#define SELECT_TYPE_ARG5 (struct timeval *) + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* If the compiler supports a TLS storage class define it to that here */ +#define TLS __thread + +/* Use Apple CommonCrypto library */ +/* #undef USE_CRYPTO_APPLECC */ + +/* Use mbed TLS library */ +#define USE_CRYPTO_MBEDTLS 1 + +/* Use OpenSSL library */ +/* #undef USE_CRYPTO_OPENSSL */ + +/* Use PolarSSL library */ +/* #undef USE_CRYPTO_POLARSSL */ + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif + + +/* Version number of package */ +#define VERSION "2.4.8" + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +/* # undef WORDS_BIGENDIAN */ +# endif +#endif + +/* Define to 1 if on MINIX. */ +/* #undef _MINIX */ + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +/* #undef _POSIX_1_SOURCE */ + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Define for Solaris 2.5.1 so the uint8_t typedef from , + , or is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +/* #undef _UINT8_T */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif + +/* Define to `int' if does not define. */ +/* #undef pid_t */ + +/* Define to the equivalent of the C99 'restrict' keyword, or to + nothing if this is not supported. Do not define if restrict is + supported directly. */ +#define restrict __restrict +/* Work around a bug in Sun C++: it does not support _Restrict or + __restrict__, even though the corresponding Sun C compiler ends up with + "#define restrict _Restrict" or "#define restrict __restrict__" in the + previous line. Perhaps some future version of Sun C++ will work with + restrict; if so, hopefully it defines __RESTRICT like Sun C does. */ +#if defined __SUNPRO_CC && !defined __RESTRICT +# define _Restrict +# define __restrict__ +#endif + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ + +/* Define to `int' if does not define. */ +/* #undef ssize_t */ + +/* Define to the type of an unsigned integer type of width exactly 16 bits if + such a type exists and the standard includes do not define it. */ +/* #undef uint16_t */ + +/* Define to the type of an unsigned integer type of width exactly 8 bits if + such a type exists and the standard includes do not define it. */ +/* #undef uint8_t */ + +/* Define as `fork' if `vfork' does not work. */ +/* #undef vfork */ + +/* Define to 1 if you have the header file. */ +#define HAVE_PCRE_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_PCRE_PCRE_H */ diff --git a/ss-libev/src/main/jni/include/sodium/version.h b/ss-libev/src/main/jni/include/sodium/version.h new file mode 100644 index 000000000..b95c40b52 --- /dev/null +++ b/ss-libev/src/main/jni/include/sodium/version.h @@ -0,0 +1,29 @@ + +#ifndef sodium_version_H +#define sodium_version_H + +#include "export.h" + +#define SODIUM_VERSION_STRING "1.0.7" + +#define SODIUM_LIBRARY_VERSION_MAJOR 9 +#define SODIUM_LIBRARY_VERSION_MINOR 0 + +#ifdef __cplusplus +extern "C" { +#endif + +SODIUM_EXPORT +const char *sodium_version_string(void); + +SODIUM_EXPORT +int sodium_library_version_major(void); + +SODIUM_EXPORT +int sodium_library_version_minor(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ss-libev/src/main/jni/libancillary b/ss-libev/src/main/jni/libancillary new file mode 160000 index 000000000..311e5d14f --- /dev/null +++ b/ss-libev/src/main/jni/libancillary @@ -0,0 +1 @@ +Subproject commit 311e5d14f593f16c785bc6605220517eb1f21f6b diff --git a/ss-libev/src/main/jni/libev b/ss-libev/src/main/jni/libev new file mode 160000 index 000000000..baa59676d --- /dev/null +++ b/ss-libev/src/main/jni/libev @@ -0,0 +1 @@ +Subproject commit baa59676d65008cfc019fdcebf744b77aa5bb31a diff --git a/ss-libev/src/main/jni/libsodium b/ss-libev/src/main/jni/libsodium new file mode 160000 index 000000000..3b689a6ab --- /dev/null +++ b/ss-libev/src/main/jni/libsodium @@ -0,0 +1 @@ +Subproject commit 3b689a6ab443cb7b467c2cb6c8434d97fd807168 diff --git a/ss-libev/src/main/jni/mbedtls b/ss-libev/src/main/jni/mbedtls new file mode 160000 index 000000000..9f4f8eec9 --- /dev/null +++ b/ss-libev/src/main/jni/mbedtls @@ -0,0 +1 @@ +Subproject commit 9f4f8eec93dd1f32d78e0bcceddbef0ca570e66f diff --git a/ss-libev/src/main/jni/patch/pcre/pcre_chartables.c b/ss-libev/src/main/jni/patch/pcre/pcre_chartables.c new file mode 100644 index 000000000..1e20ec29d --- /dev/null +++ b/ss-libev/src/main/jni/patch/pcre/pcre_chartables.c @@ -0,0 +1,198 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* This file contains character tables that are used when no external tables +are passed to PCRE by the application that calls it. The tables are used only +for characters whose code values are less than 256. + +This is a default version of the tables that assumes ASCII encoding. A program +called dftables (which is distributed with PCRE) can be used to build +alternative versions of this file. This is necessary if you are running in an +EBCDIC environment, or if you want to default to a different encoding, for +example ISO-8859-1. When dftables is run, it creates these tables in the +current locale. If PCRE is configured with --enable-rebuild-chartables, this +happens automatically. + +The following #includes are present because without them gcc 4.x may remove the +array definition from the final binary if PCRE is built into a static library +and dead code stripping is activated. This leads to link errors. Pulling in the +header ensures that the array gets flagged as "someone outside this compilation +unit might reference this" and so it will always be supplied to the linker. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + +const pcre_uint8 PRIV(default_tables)[] = { + +/* This table is a lower casing table. */ + + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 97, 98, 99,100,101,102,103, + 104,105,106,107,108,109,110,111, + 112,113,114,115,116,117,118,119, + 120,121,122, 91, 92, 93, 94, 95, + 96, 97, 98, 99,100,101,102,103, + 104,105,106,107,108,109,110,111, + 112,113,114,115,116,117,118,119, + 120,121,122,123,124,125,126,127, + 128,129,130,131,132,133,134,135, + 136,137,138,139,140,141,142,143, + 144,145,146,147,148,149,150,151, + 152,153,154,155,156,157,158,159, + 160,161,162,163,164,165,166,167, + 168,169,170,171,172,173,174,175, + 176,177,178,179,180,181,182,183, + 184,185,186,187,188,189,190,191, + 192,193,194,195,196,197,198,199, + 200,201,202,203,204,205,206,207, + 208,209,210,211,212,213,214,215, + 216,217,218,219,220,221,222,223, + 224,225,226,227,228,229,230,231, + 232,233,234,235,236,237,238,239, + 240,241,242,243,244,245,246,247, + 248,249,250,251,252,253,254,255, + +/* This table is a case flipping table. */ + + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 97, 98, 99,100,101,102,103, + 104,105,106,107,108,109,110,111, + 112,113,114,115,116,117,118,119, + 120,121,122, 91, 92, 93, 94, 95, + 96, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90,123,124,125,126,127, + 128,129,130,131,132,133,134,135, + 136,137,138,139,140,141,142,143, + 144,145,146,147,148,149,150,151, + 152,153,154,155,156,157,158,159, + 160,161,162,163,164,165,166,167, + 168,169,170,171,172,173,174,175, + 176,177,178,179,180,181,182,183, + 184,185,186,187,188,189,190,191, + 192,193,194,195,196,197,198,199, + 200,201,202,203,204,205,206,207, + 208,209,210,211,212,213,214,215, + 216,217,218,219,220,221,222,223, + 224,225,226,227,228,229,230,231, + 232,233,234,235,236,237,238,239, + 240,241,242,243,244,245,246,247, + 248,249,250,251,252,253,254,255, + +/* This table contains bit maps for various character classes. Each map is 32 +bytes long and the bits run from the least significant end of each byte. The +classes that have their own maps are: space, xdigit, digit, upper, lower, word, +graph, print, punct, and cntrl. Other classes are built from combinations. */ + + 0x00,0x3e,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, + 0x7e,0x00,0x00,0x00,0x7e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xfe,0xff,0xff,0x07,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0x07, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, + 0xfe,0xff,0xff,0x87,0xfe,0xff,0xff,0x07, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0xfe,0xff,0x00,0xfc, + 0x01,0x00,0x00,0xf8,0x01,0x00,0x00,0x78, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + +/* This table identifies various classes of character by individual bits: + 0x01 white space character + 0x02 letter + 0x04 decimal digit + 0x08 hexadecimal digit + 0x10 alphanumeric or '_' + 0x80 regular expression metacharacter or binary zero +*/ + + 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 */ + 0x00,0x01,0x01,0x01,0x01,0x01,0x00,0x00, /* 8- 15 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 16- 23 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 24- 31 */ + 0x01,0x00,0x00,0x00,0x80,0x00,0x00,0x00, /* - ' */ + 0x80,0x80,0x80,0x80,0x00,0x00,0x80,0x00, /* ( - / */ + 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, /* 0 - 7 */ + 0x1c,0x1c,0x00,0x00,0x00,0x00,0x00,0x80, /* 8 - ? */ + 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* @ - G */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* H - O */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* P - W */ + 0x12,0x12,0x12,0x80,0x80,0x00,0x80,0x10, /* X - _ */ + 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* ` - g */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* h - o */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* p - w */ + 0x12,0x12,0x12,0x80,0x80,0x00,0x00,0x00, /* x -127 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 128-135 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 136-143 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144-151 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 152-159 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 160-167 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 168-175 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 176-183 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 184-191 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 192-199 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 200-207 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 208-215 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 216-223 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 224-231 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 232-239 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 240-247 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};/* 248-255 */ + +/* End of pcre_chartables.c */ diff --git a/ss-libev/src/main/jni/pcre b/ss-libev/src/main/jni/pcre new file mode 160000 index 000000000..222bbf4b3 --- /dev/null +++ b/ss-libev/src/main/jni/pcre @@ -0,0 +1 @@ +Subproject commit 222bbf4b3fb8e13c21686803e47e31aa3e4ad130 diff --git a/ss-libev/src/main/jni/re2 b/ss-libev/src/main/jni/re2 new file mode 160000 index 000000000..ca93436e5 --- /dev/null +++ b/ss-libev/src/main/jni/re2 @@ -0,0 +1 @@ +Subproject commit ca93436e5b1be02f9f4bfca79b8202c400161994 diff --git a/ss-libev/src/main/jni/shadowsocks-libev b/ss-libev/src/main/jni/shadowsocks-libev new file mode 160000 index 000000000..72d7d9217 --- /dev/null +++ b/ss-libev/src/main/jni/shadowsocks-libev @@ -0,0 +1 @@ +Subproject commit 72d7d9217ce2a8a59dc647e8f1fbbd77f7cd30aa diff --git a/ssr-libev/.gitignore b/ssr-libev/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/ssr-libev/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/ssr-libev/build.gradle b/ssr-libev/build.gradle new file mode 100644 index 000000000..2b4b89e62 --- /dev/null +++ b/ssr-libev/build.gradle @@ -0,0 +1,32 @@ +plugins { + id 'com.android.library' +} + +android { + compileSdkVersion 29 + buildToolsVersion "29.0.3" + ndkVersion "21.0.6113669" + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 29 + versionCode 1 + versionName "1.0" + + externalNativeBuild { + ndkBuild { + + abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' + arguments "NDK_APPLICATION_MK:=src/main/jni/Application.mk", "APP_PLATFORM:=android-21", "--jobs=8" + + } + } + } + + externalNativeBuild { + ndkBuild { + path 'src/main/jni/Android.mk' + } + } + +} \ No newline at end of file diff --git a/ssr-libev/src/main/AndroidManifest.xml b/ssr-libev/src/main/AndroidManifest.xml new file mode 100644 index 000000000..8462535df --- /dev/null +++ b/ssr-libev/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/ssr-libev/src/main/jni/Android.mk b/ssr-libev/src/main/jni/Android.mk new file mode 100644 index 000000000..73160ff32 --- /dev/null +++ b/ssr-libev/src/main/jni/Android.mk @@ -0,0 +1,293 @@ +# Copyright (C) 2009 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +LOCAL_PATH := $(call my-dir) +ROOT_PATH := $(LOCAL_PATH) + +BUILD_SHARED_EXECUTABLE := $(LOCAL_PATH)/build-shared-executable.mk + +######################################################## +## libsodium +######################################################## + +include $(CLEAR_VARS) + +SODIUM_SOURCE := \ + crypto_aead/aes256gcm/aesni/aead_aes256gcm_aesni.c \ + crypto_aead/chacha20poly1305/sodium/aead_chacha20poly1305.c \ + crypto_aead/xchacha20poly1305/sodium/aead_xchacha20poly1305.c \ + crypto_core/ed25519/ref10/ed25519_ref10.c \ + crypto_core/hchacha20/core_hchacha20.c \ + crypto_core/salsa/ref/core_salsa_ref.c \ + crypto_generichash/blake2b/ref/blake2b-compress-ref.c \ + crypto_generichash/blake2b/ref/blake2b-ref.c \ + crypto_generichash/blake2b/ref/generichash_blake2b.c \ + crypto_onetimeauth/poly1305/onetimeauth_poly1305.c \ + crypto_onetimeauth/poly1305/donna/poly1305_donna.c \ + crypto_pwhash/crypto_pwhash.c \ + crypto_pwhash/argon2/argon2-core.c \ + crypto_pwhash/argon2/argon2.c \ + crypto_pwhash/argon2/argon2-encoding.c \ + crypto_pwhash/argon2/argon2-fill-block-ref.c \ + crypto_pwhash/argon2/blake2b-long.c \ + crypto_pwhash/argon2/pwhash_argon2i.c \ + crypto_scalarmult/curve25519/scalarmult_curve25519.c \ + crypto_scalarmult/curve25519/ref10/x25519_ref10.c \ + crypto_stream/chacha20/stream_chacha20.c \ + crypto_stream/chacha20/ref/chacha20_ref.c \ + crypto_stream/salsa20/stream_salsa20.c \ + crypto_stream/salsa20/ref/salsa20_ref.c \ + crypto_verify/sodium/verify.c \ + randombytes/randombytes.c \ + randombytes/sysrandom/randombytes_sysrandom.c \ + sodium/core.c \ + sodium/runtime.c \ + sodium/utils.c \ + sodium/version.c + +LOCAL_MODULE := sodium +LOCAL_CFLAGS += -I$(LOCAL_PATH)/libsodium/src/libsodium/include \ + -I$(LOCAL_PATH)/include \ + -I$(LOCAL_PATH)/include/sodium \ + -I$(LOCAL_PATH)/libsodium/src/libsodium/include/sodium \ + -DPACKAGE_NAME=\"libsodium\" -DPACKAGE_TARNAME=\"libsodium\" \ + -DPACKAGE_VERSION=\"1.0.15\" -DPACKAGE_STRING=\"libsodium-1.0.15\" \ + -DPACKAGE_BUGREPORT=\"https://github.com/jedisct1/libsodium/issues\" \ + -DPACKAGE_URL=\"https://github.com/jedisct1/libsodium\" \ + -DPACKAGE=\"libsodium\" -DVERSION=\"1.0.15\" \ + -DHAVE_PTHREAD=1 \ + -DSTDC_HEADERS=1 \ + -DHAVE_SYS_TYPES_H=1 \ + -DHAVE_SYS_STAT_H=1 \ + -DHAVE_STDLIB_H=1 \ + -DHAVE_STRING_H=1 \ + -DHAVE_MEMORY_H=1 \ + -DHAVE_STRINGS_H=1 \ + -DHAVE_INTTYPES_H=1 \ + -DHAVE_STDINT_H=1 \ + -DHAVE_UNISTD_H=1 \ + -D__EXTENSIONS__=1 \ + -D_ALL_SOURCE=1 \ + -D_GNU_SOURCE=1 \ + -D_POSIX_PTHREAD_SEMANTICS=1 \ + -D_TANDEM_SOURCE=1 \ + -DHAVE_DLFCN_H=1 \ + -DLT_OBJDIR=\".libs/\" \ + -DHAVE_SYS_MMAN_H=1 \ + -DNATIVE_LITTLE_ENDIAN=1 \ + -DASM_HIDE_SYMBOL=.hidden \ + -DHAVE_WEAK_SYMBOLS=1 \ + -DHAVE_ATOMIC_OPS=1 \ + -DHAVE_ARC4RANDOM=1 \ + -DHAVE_ARC4RANDOM_BUF=1 \ + -DHAVE_MMAP=1 \ + -DHAVE_MLOCK=1 \ + -DHAVE_MADVISE=1 \ + -DHAVE_MPROTECT=1 \ + -DHAVE_NANOSLEEP=1 \ + -DHAVE_POSIX_MEMALIGN=1 \ + -DHAVE_GETPID=1 \ + -DCONFIGURED=1 + +LOCAL_SRC_FILES := $(addprefix libsodium/src/libsodium/,$(SODIUM_SOURCE)) + +include $(BUILD_STATIC_LIBRARY) + +######################################################## +## libancillary +######################################################## + +include $(CLEAR_VARS) + +ANCILLARY_SOURCE := fd_recv.c fd_send.c + +LOCAL_MODULE := libancillary +LOCAL_CFLAGS += -I$(LOCAL_PATH)/libancillary + +LOCAL_SRC_FILES := $(addprefix libancillary/, $(ANCILLARY_SOURCE)) + +include $(BUILD_STATIC_LIBRARY) + +######################################################## +## libipset +######################################################## + +include $(CLEAR_VARS) + +bdd_src = bdd/assignments.c bdd/basics.c bdd/bdd-iterator.c bdd/expanded.c \ + bdd/reachable.c bdd/read.c bdd/write.c +map_src = map/allocation.c map/inspection.c map/ipv4_map.c map/ipv6_map.c \ + map/storage.c +set_src = set/allocation.c set/inspection.c set/ipv4_set.c set/ipv6_set.c \ + set/iterator.c set/storage.c + +IPSET_SOURCE := general.c $(bdd_src) $(map_src) $(set_src) + +LOCAL_MODULE := libipset +LOCAL_CFLAGS += -I$(LOCAL_PATH)/shadowsocks-libev/libipset/include \ + -I$(LOCAL_PATH)/shadowsocks-libev/libcork/include + +LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/libipset/,$(IPSET_SOURCE)) + +include $(BUILD_STATIC_LIBRARY) + +######################################################## +## libcork +######################################################## + +include $(CLEAR_VARS) + +cli_src := cli/commands.c +core_src := core/allocator.c core/error.c core/gc.c \ + core/hash.c core/ip-address.c core/mempool.c \ + core/timestamp.c core/u128.c +ds_src := ds/array.c ds/bitset.c ds/buffer.c ds/dllist.c \ + ds/file-stream.c ds/hash-table.c ds/managed-buffer.c \ + ds/ring-buffer.c ds/slice.c +posix_src := posix/directory-walker.c posix/env.c posix/exec.c \ + posix/files.c posix/process.c posix/subprocess.c +pthreads_src := pthreads/thread.c + +CORK_SOURCE := $(cli_src) $(core_src) $(ds_src) $(posix_src) $(pthreads_src) + +LOCAL_MODULE := libcork +LOCAL_CFLAGS += -I$(LOCAL_PATH)/shadowsocks-libev/libcork/include \ + -DCORK_API=CORK_LOCAL + +LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/libcork/,$(CORK_SOURCE)) + +include $(BUILD_STATIC_LIBRARY) + +######################################################## +## libudns +######################################################## + +include $(CLEAR_VARS) + +UDNS_SOURCES := udns_dn.c udns_dntosp.c udns_parse.c udns_resolver.c udns_init.c \ + udns_misc.c udns_XtoX.c \ + udns_rr_a.c udns_rr_ptr.c udns_rr_mx.c udns_rr_txt.c udns_bl.c \ + udns_rr_srv.c udns_rr_naptr.c udns_codes.c udns_jran.c + +LOCAL_MODULE := libudns +LOCAL_CFLAGS += -I$(LOCAL_PATH)/shadowsocks-libev/libudns \ + -DHAVE_DECL_INET_NTOP + +LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/libudns/,$(UDNS_SOURCES)) + +include $(BUILD_STATIC_LIBRARY) + +######################################################## +## libev +######################################################## + +include $(CLEAR_VARS) + +LOCAL_MODULE := libev +LOCAL_CFLAGS += -I$(LOCAL_PATH)/include/libev +LOCAL_SRC_FILES := \ + shadowsocks-libev/libev/ev.c \ + shadowsocks-libev/libev/event.c + +include $(BUILD_STATIC_LIBRARY) + +######################################################## +## shadowsocks-libev local +######################################################## + +include $(CLEAR_VARS) + +SHADOWSOCKS_SOURCES := local.c cache.c udprelay.c encrypt.c \ + utils.c netutils.c json.c jconf.c acl.c http.c tls.c rule.c \ + android.c + +LOCAL_MODULE := ssr-local +LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/src/, $(SHADOWSOCKS_SOURCES)) +LOCAL_CFLAGS := -Wall -fno-strict-aliasing -DMODULE_LOCAL \ + -DUSE_CRYPTO_MBEDTLS -DANDROID -DHAVE_CONFIG_H \ + -DCONNECT_IN_PROGRESS=EINPROGRESS \ + -I$(LOCAL_PATH)/include/shadowsocks-libev \ + -I$(LOCAL_PATH)/include \ + -I$(LOCAL_PATH)/libancillary \ + -I$(LOCAL_PATH)/mbedtls/include \ + -I$(LOCAL_PATH)/pcre \ + -I$(LOCAL_PATH)/shadowsocks-libev/libudns \ + -I$(LOCAL_PATH)/shadowsocks-libev/libcork/include \ + -I$(LOCAL_PATH)/libsodium/src/libsodium/include \ + -I$(LOCAL_PATH)/libsodium/src/libsodium/include/sodium \ + -I$(LOCAL_PATH)/shadowsocks-libev/libipset/include \ + -I$(LOCAL_PATH)/shadowsocks-libev/libev \ + -I$(LOCAL_PATH)/shadowsocks-libev/src + +LOCAL_STATIC_LIBRARIES := libev libmbedtls libipset libcork libudns \ + libsodium libancillary libpcre + +LOCAL_LDLIBS := -llog + +include $(BUILD_SHARED_EXECUTABLE) + +######################################################## +## mbed TLS +######################################################## + +include $(CLEAR_VARS) + +LOCAL_MODULE := mbedtls + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/mbedtls/include + +MBEDTLS_SOURCES := $(wildcard $(LOCAL_PATH)/mbedtls/library/*.c) + +LOCAL_SRC_FILES := $(MBEDTLS_SOURCES:$(LOCAL_PATH)/%=%) + +include $(BUILD_STATIC_LIBRARY) + +######################################################## +## pcre +######################################################## + +include $(CLEAR_VARS) + +LOCAL_MODULE := pcre + +LOCAL_CFLAGS += -DHAVE_CONFIG_H + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/pcre/dist $(LOCAL_PATH)/pcre + +libpcre_src_files := \ + dist/pcre_byte_order.c \ + dist/pcre_compile.c \ + dist/pcre_config.c \ + dist/pcre_dfa_exec.c \ + dist/pcre_exec.c \ + dist/pcre_fullinfo.c \ + dist/pcre_get.c \ + dist/pcre_globals.c \ + dist/pcre_jit_compile.c \ + dist/pcre_maketables.c \ + dist/pcre_newline.c \ + dist/pcre_ord2utf8.c \ + dist/pcre_refcount.c \ + dist/pcre_string_utils.c \ + dist/pcre_study.c \ + dist/pcre_tables.c \ + dist/pcre_ucd.c \ + dist/pcre_valid_utf8.c \ + dist/pcre_version.c \ + dist/pcre_xclass.c + +LOCAL_SRC_FILES := $(addprefix pcre/, $(libpcre_src_files)) $(LOCAL_PATH)/patch/pcre/pcre_chartables.c + +include $(BUILD_STATIC_LIBRARY) \ No newline at end of file diff --git a/ssr-libev/src/main/jni/Application.mk b/ssr-libev/src/main/jni/Application.mk new file mode 100644 index 000000000..4cbaeb0d9 --- /dev/null +++ b/ssr-libev/src/main/jni/Application.mk @@ -0,0 +1,4 @@ +APP_CFLAGS := -fdata-sections -ffunction-sections -fvisibility=hidden -fvisibility-inlines-hidden +APP_LDFLAGS := -Wl,--hash-style=both -Wl,-exclude-libs,ALL -Wl,--gc-sections +APP_THIN_ARCHIVE := true +APP_STL := c++_static \ No newline at end of file diff --git a/ssr-libev/src/main/jni/build-shared-executable.mk b/ssr-libev/src/main/jni/build-shared-executable.mk new file mode 100644 index 000000000..05239df18 --- /dev/null +++ b/ssr-libev/src/main/jni/build-shared-executable.mk @@ -0,0 +1,31 @@ +# Copyright (C) 2009 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# this file is included from Android.mk files to build a target-specific +# executable program +# +# Modified by @Mygod, based on: +# https://android.googlesource.com/platform/ndk/+/a355a4e/build/core/build-shared-library.mk +# https://android.googlesource.com/platform/ndk/+/a355a4e/build/core/build-executable.mk +LOCAL_BUILD_SCRIPT := BUILD_EXECUTABLE +LOCAL_MAKEFILE := $(local-makefile) +$(call check-defined-LOCAL_MODULE,$(LOCAL_BUILD_SCRIPT)) +$(call check-LOCAL_MODULE,$(LOCAL_MAKEFILE)) +$(call check-LOCAL_MODULE_FILENAME) +# we are building target objects +my := TARGET_ +$(call handle-module-filename,lib,$(TARGET_SONAME_EXTENSION)) +$(call handle-module-built) +LOCAL_MODULE_CLASS := EXECUTABLE +include $(BUILD_SYSTEM)/build-module.mk diff --git a/ssr-libev/src/main/jni/include/libev/config.h b/ssr-libev/src/main/jni/include/libev/config.h new file mode 100644 index 000000000..b7cd0b520 --- /dev/null +++ b/ssr-libev/src/main/jni/include/libev/config.h @@ -0,0 +1,129 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the `clock_gettime' function. */ +/* #undef HAVE_CLOCK_GETTIME */ + +/* Define to 1 to use the syscall interface for clock_gettime */ +#define HAVE_CLOCK_SYSCALL 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the `epoll_ctl' function. */ +#define HAVE_EPOLL_CTL 1 + +/* Define to 1 if you have the `eventfd' function. */ +#define HAVE_EVENTFD 1 + +/* Define to 1 if the floor function is available */ +#define HAVE_FLOOR 1 + +/* Define to 1 if you have the `inotify_init' function. */ +#define HAVE_INOTIFY_INIT 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `kqueue' function. */ +/* #undef HAVE_KQUEUE */ + +/* Define to 1 if you have the `rt' library (-lrt). */ +/* #undef HAVE_LIBRT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `nanosleep' function. */ +#define HAVE_NANOSLEEP 1 + +/* Define to 1 if you have the `poll' function. */ +#define HAVE_POLL 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_POLL_H 1 + +/* Define to 1 if you have the `port_create' function. */ +/* #undef HAVE_PORT_CREATE */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_PORT_H */ + +/* Define to 1 if you have the `select' function. */ +#define HAVE_SELECT 1 + +/* Define to 1 if you have the `signalfd' function. */ +#define HAVE_SIGNALFD 0 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_EPOLL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_EVENTFD_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_EVENT_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_INOTIFY_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SELECT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SIGNALFD_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Name of package */ +#define PACKAGE "libev" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Version number of package */ +#define VERSION "4.11" + +#define DNDEBUG 1 +#define HAVE_CONFIG_H 1 diff --git a/ssr-libev/src/main/jni/include/shadowsocks-libev/config.h b/ssr-libev/src/main/jni/include/shadowsocks-libev/config.h new file mode 100644 index 000000000..59451b968 --- /dev/null +++ b/ssr-libev/src/main/jni/include/shadowsocks-libev/config.h @@ -0,0 +1,425 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if building universal (internal helper macro) */ +/* #undef AC_APPLE_UNIVERSAL_BUILD */ + +/* errno for incomplete non-blocking connect(2) */ +#define CONNECT_IN_PROGRESS EINPROGRESS + +/* Override libev default fd conversion macro. */ +/* #undef EV_FD_TO_WIN32_HANDLE */ + +/* Override libev default fd close macro. */ +/* #undef EV_WIN32_CLOSE_FD */ + +/* Override libev default handle conversion macro. */ +/* #undef EV_WIN32_HANDLE_TO_FD */ + +/* Reset max file descriptor size. */ +/* #undef FD_SETSIZE */ + +/* Define to 1 if you have the header file. */ +#define HAVE_ARPA_INET_H 1 + +/* Define to 1 if you have the `CCCryptorCreateWithMode' function. */ +/* #undef HAVE_CCCRYPTORCREATEWITHMODE */ + +/* Define to 1 if you have the `clock_gettime' function. */ +/* #undef HAVE_CLOCK_GETTIME */ + +/* Define to 1 to use the syscall interface for clock_gettime */ +/* #undef HAVE_CLOCK_SYSCALL */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_COMMONCRYPTO_COMMONCRYPTO_H */ + +/* Define to 1 if you have the declaration of `inet_ntop', and to 0 if you + don't. */ +#define HAVE_DECL_INET_NTOP 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the `epoll_ctl' function. */ +/* #undef HAVE_EPOLL_CTL */ + +/* Define to 1 if you have the `eventfd' function. */ +/* #undef HAVE_EVENTFD */ + +/* Define to 1 if you have the `EVP_EncryptInit_ex' function. */ +/* #undef HAVE_EVP_ENCRYPTINIT_EX */ + +/* Define to 1 if you have the header file. */ +#define HAVE_FCNTL_H 1 + +/* Define to 1 if the floor function is available */ +#define HAVE_FLOOR 1 + +/* Define to 1 if you have the `fork' function. */ +#define HAVE_FORK 1 + +/* Define to 1 if you have the `getpwnam_r' function. */ +#define HAVE_GETPWNAM_R 1 + +/* Define to 1 if you have the `inet_ntop' function. */ +/* #undef HAVE_INET_NTOP */ + +/* Define to 1 if you have the `inotify_init' function. */ +/* #undef HAVE_INOTIFY_INIT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Enable IPv6 support in libudns */ +#define HAVE_IPv6 1 + +/* Define to 1 if you have the `kqueue' function. */ +#define HAVE_KQUEUE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LANGINFO_H 1 + +/* Define to 1 if you have the `rt' library (-lrt). */ +/* #undef HAVE_LIBRT */ + +/* Define to 1 if you have the `socket' library (-lsocket). */ +/* #undef HAVE_LIBSOCKET */ + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LINUX_IF_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_LINUX_NETFILTER_IPV4_H */ + +/* Define to 1 if you have the header + file. */ +/* #undef HAVE_LINUX_NETFILTER_IPV6_IP6_TABLES_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_LOCALE_H 1 + +/* Define to 1 if you have the `malloc' function. */ +#define HAVE_MALLOC 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `memset' function. */ +#define HAVE_MEMSET 1 + +/* Define to 1 if you have the `nanosleep' function. */ +#define HAVE_NANOSLEEP 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETDB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NETINET_IN_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_NET_IF_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_OPENSSL_ENGINE_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_OPENSSL_ERR_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_OPENSSL_EVP_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_OPENSSL_PEM_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_OPENSSL_RAND_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_OPENSSL_RSA_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_OPENSSL_SHA_H */ + +/* Define to 1 if you have the `poll' function. */ +#define HAVE_POLL 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_POLL_H 1 + +/* Define to 1 if you have the `port_create' function. */ +/* #undef HAVE_PORT_CREATE */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_PORT_H */ + +/* Have PTHREAD_PRIO_INHERIT. */ +#define HAVE_PTHREAD_PRIO_INHERIT 1 + +/* Define to 1 if you have the `RAND_pseudo_bytes' function. */ +/* #undef HAVE_RAND_PSEUDO_BYTES */ + +/* Define to 1 if you have the 'select' function. */ +#define HAVE_SELECT 1 + +/* Define to 1 if you have the `setresuid' function. */ +/* #undef HAVE_SETRESUID */ + +/* Define to 1 if you have the `setreuid' function. */ +#define HAVE_SETREUID 1 + +/* Define to 1 if you have the `setrlimit' function. */ +#define HAVE_SETRLIMIT 1 + +/* Define to 1 if you have the `signalfd' function. */ +/* #undef HAVE_SIGNALFD */ + +/* Define to 1 if you have the `socket' function. */ +#define HAVE_SOCKET 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_EPOLL_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_EVENTFD_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_EVENT_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_INOTIFY_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_IOCTL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SELECT_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SIGNALFD_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_SOCKET_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have that is POSIX.1 compatible. */ +#define HAVE_SYS_WAIT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `vfork' function. */ +#define HAVE_VFORK 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_VFORK_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_WINDOWS_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_WINSOCK2_H */ + +/* Define to 1 if `fork' works. */ +#define HAVE_WORKING_FORK 1 + +/* Define to 1 if `vfork' works. */ +#define HAVE_WORKING_VFORK 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_WS2TCPIP_H */ + +/* have zlib compression support */ +/* #undef HAVE_ZLIB */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_ZLIB_H */ + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Define to 1 if assertions should be disabled. */ +/* #undef NDEBUG */ + +/* Name of package */ +#define PACKAGE "shadowsocks-libev" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "max.c.lv@gmail.com" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "shadowsocks-libev" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "shadowsocks-libev 2.4.8" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "shadowsocks-libev" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "2.4.8" + +/* Define to necessary symbol if this constant uses a non-standard name on + your system. */ +/* #undef PTHREAD_CREATE_JOINABLE */ + +/* Define as the return type of signal handlers (`int' or `void'). */ +#define RETSIGTYPE void + +/* Define to the type of arg 1 for `select'. */ +#define SELECT_TYPE_ARG1 int + +/* Define to the type of args 2, 3 and 4 for `select'. */ +#define SELECT_TYPE_ARG234 (fd_set *) + +/* Define to the type of arg 5 for `select'. */ +#define SELECT_TYPE_ARG5 (struct timeval *) + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* If the compiler supports a TLS storage class define it to that here */ +#define TLS __thread + +/* Use Apple CommonCrypto library */ +/* #undef USE_CRYPTO_APPLECC */ + +/* Use mbed TLS library */ +#define USE_CRYPTO_MBEDTLS 1 + +/* Use OpenSSL library */ +/* #undef USE_CRYPTO_OPENSSL */ + +/* Use PolarSSL library */ +/* #undef USE_CRYPTO_POLARSSL */ + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif + + +/* Version number of package */ +#define VERSION "2.4.8" + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +/* # undef WORDS_BIGENDIAN */ +# endif +#endif + +/* Define to 1 if on MINIX. */ +/* #undef _MINIX */ + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +/* #undef _POSIX_1_SOURCE */ + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Define for Solaris 2.5.1 so the uint8_t typedef from , + , or is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +/* #undef _UINT8_T */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif + +/* Define to `int' if does not define. */ +/* #undef pid_t */ + +/* Define to the equivalent of the C99 'restrict' keyword, or to + nothing if this is not supported. Do not define if restrict is + supported directly. */ +#define restrict __restrict +/* Work around a bug in Sun C++: it does not support _Restrict or + __restrict__, even though the corresponding Sun C compiler ends up with + "#define restrict _Restrict" or "#define restrict __restrict__" in the + previous line. Perhaps some future version of Sun C++ will work with + restrict; if so, hopefully it defines __RESTRICT like Sun C does. */ +#if defined __SUNPRO_CC && !defined __RESTRICT +# define _Restrict +# define __restrict__ +#endif + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ + +/* Define to `int' if does not define. */ +/* #undef ssize_t */ + +/* Define to the type of an unsigned integer type of width exactly 16 bits if + such a type exists and the standard includes do not define it. */ +/* #undef uint16_t */ + +/* Define to the type of an unsigned integer type of width exactly 8 bits if + such a type exists and the standard includes do not define it. */ +/* #undef uint8_t */ + +/* Define as `fork' if `vfork' does not work. */ +/* #undef vfork */ + +/* Define to 1 if you have the header file. */ +#define HAVE_PCRE_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_PCRE_PCRE_H */ diff --git a/ssr-libev/src/main/jni/include/sodium/version.h b/ssr-libev/src/main/jni/include/sodium/version.h new file mode 100644 index 000000000..b95c40b52 --- /dev/null +++ b/ssr-libev/src/main/jni/include/sodium/version.h @@ -0,0 +1,29 @@ + +#ifndef sodium_version_H +#define sodium_version_H + +#include "export.h" + +#define SODIUM_VERSION_STRING "1.0.7" + +#define SODIUM_LIBRARY_VERSION_MAJOR 9 +#define SODIUM_LIBRARY_VERSION_MINOR 0 + +#ifdef __cplusplus +extern "C" { +#endif + +SODIUM_EXPORT +const char *sodium_version_string(void); + +SODIUM_EXPORT +int sodium_library_version_major(void); + +SODIUM_EXPORT +int sodium_library_version_minor(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/ssr-libev/src/main/jni/libancillary b/ssr-libev/src/main/jni/libancillary new file mode 160000 index 000000000..311e5d14f --- /dev/null +++ b/ssr-libev/src/main/jni/libancillary @@ -0,0 +1 @@ +Subproject commit 311e5d14f593f16c785bc6605220517eb1f21f6b diff --git a/ssr-libev/src/main/jni/libsodium b/ssr-libev/src/main/jni/libsodium new file mode 160000 index 000000000..3b689a6ab --- /dev/null +++ b/ssr-libev/src/main/jni/libsodium @@ -0,0 +1 @@ +Subproject commit 3b689a6ab443cb7b467c2cb6c8434d97fd807168 diff --git a/ssr-libev/src/main/jni/mbedtls b/ssr-libev/src/main/jni/mbedtls new file mode 160000 index 000000000..d414c32a1 --- /dev/null +++ b/ssr-libev/src/main/jni/mbedtls @@ -0,0 +1 @@ +Subproject commit d414c32a160a45725430d99b3c11904760ac8b9c diff --git a/ssr-libev/src/main/jni/patch/pcre/pcre_chartables.c b/ssr-libev/src/main/jni/patch/pcre/pcre_chartables.c new file mode 100644 index 000000000..1e20ec29d --- /dev/null +++ b/ssr-libev/src/main/jni/patch/pcre/pcre_chartables.c @@ -0,0 +1,198 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* This file contains character tables that are used when no external tables +are passed to PCRE by the application that calls it. The tables are used only +for characters whose code values are less than 256. + +This is a default version of the tables that assumes ASCII encoding. A program +called dftables (which is distributed with PCRE) can be used to build +alternative versions of this file. This is necessary if you are running in an +EBCDIC environment, or if you want to default to a different encoding, for +example ISO-8859-1. When dftables is run, it creates these tables in the +current locale. If PCRE is configured with --enable-rebuild-chartables, this +happens automatically. + +The following #includes are present because without them gcc 4.x may remove the +array definition from the final binary if PCRE is built into a static library +and dead code stripping is activated. This leads to link errors. Pulling in the +header ensures that the array gets flagged as "someone outside this compilation +unit might reference this" and so it will always be supplied to the linker. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + +const pcre_uint8 PRIV(default_tables)[] = { + +/* This table is a lower casing table. */ + + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 97, 98, 99,100,101,102,103, + 104,105,106,107,108,109,110,111, + 112,113,114,115,116,117,118,119, + 120,121,122, 91, 92, 93, 94, 95, + 96, 97, 98, 99,100,101,102,103, + 104,105,106,107,108,109,110,111, + 112,113,114,115,116,117,118,119, + 120,121,122,123,124,125,126,127, + 128,129,130,131,132,133,134,135, + 136,137,138,139,140,141,142,143, + 144,145,146,147,148,149,150,151, + 152,153,154,155,156,157,158,159, + 160,161,162,163,164,165,166,167, + 168,169,170,171,172,173,174,175, + 176,177,178,179,180,181,182,183, + 184,185,186,187,188,189,190,191, + 192,193,194,195,196,197,198,199, + 200,201,202,203,204,205,206,207, + 208,209,210,211,212,213,214,215, + 216,217,218,219,220,221,222,223, + 224,225,226,227,228,229,230,231, + 232,233,234,235,236,237,238,239, + 240,241,242,243,244,245,246,247, + 248,249,250,251,252,253,254,255, + +/* This table is a case flipping table. */ + + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 97, 98, 99,100,101,102,103, + 104,105,106,107,108,109,110,111, + 112,113,114,115,116,117,118,119, + 120,121,122, 91, 92, 93, 94, 95, + 96, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90,123,124,125,126,127, + 128,129,130,131,132,133,134,135, + 136,137,138,139,140,141,142,143, + 144,145,146,147,148,149,150,151, + 152,153,154,155,156,157,158,159, + 160,161,162,163,164,165,166,167, + 168,169,170,171,172,173,174,175, + 176,177,178,179,180,181,182,183, + 184,185,186,187,188,189,190,191, + 192,193,194,195,196,197,198,199, + 200,201,202,203,204,205,206,207, + 208,209,210,211,212,213,214,215, + 216,217,218,219,220,221,222,223, + 224,225,226,227,228,229,230,231, + 232,233,234,235,236,237,238,239, + 240,241,242,243,244,245,246,247, + 248,249,250,251,252,253,254,255, + +/* This table contains bit maps for various character classes. Each map is 32 +bytes long and the bits run from the least significant end of each byte. The +classes that have their own maps are: space, xdigit, digit, upper, lower, word, +graph, print, punct, and cntrl. Other classes are built from combinations. */ + + 0x00,0x3e,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, + 0x7e,0x00,0x00,0x00,0x7e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xfe,0xff,0xff,0x07,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0x07, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, + 0xfe,0xff,0xff,0x87,0xfe,0xff,0xff,0x07, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0xfe,0xff,0x00,0xfc, + 0x01,0x00,0x00,0xf8,0x01,0x00,0x00,0x78, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + +/* This table identifies various classes of character by individual bits: + 0x01 white space character + 0x02 letter + 0x04 decimal digit + 0x08 hexadecimal digit + 0x10 alphanumeric or '_' + 0x80 regular expression metacharacter or binary zero +*/ + + 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 */ + 0x00,0x01,0x01,0x01,0x01,0x01,0x00,0x00, /* 8- 15 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 16- 23 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 24- 31 */ + 0x01,0x00,0x00,0x00,0x80,0x00,0x00,0x00, /* - ' */ + 0x80,0x80,0x80,0x80,0x00,0x00,0x80,0x00, /* ( - / */ + 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, /* 0 - 7 */ + 0x1c,0x1c,0x00,0x00,0x00,0x00,0x00,0x80, /* 8 - ? */ + 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* @ - G */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* H - O */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* P - W */ + 0x12,0x12,0x12,0x80,0x80,0x00,0x80,0x10, /* X - _ */ + 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* ` - g */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* h - o */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* p - w */ + 0x12,0x12,0x12,0x80,0x80,0x00,0x00,0x00, /* x -127 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 128-135 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 136-143 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144-151 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 152-159 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 160-167 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 168-175 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 176-183 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 184-191 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 192-199 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 200-207 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 208-215 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 216-223 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 224-231 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 232-239 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 240-247 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};/* 248-255 */ + +/* End of pcre_chartables.c */ diff --git a/ssr-libev/src/main/jni/pcre b/ssr-libev/src/main/jni/pcre new file mode 160000 index 000000000..222bbf4b3 --- /dev/null +++ b/ssr-libev/src/main/jni/pcre @@ -0,0 +1 @@ +Subproject commit 222bbf4b3fb8e13c21686803e47e31aa3e4ad130 diff --git a/ssr-libev/src/main/jni/re2 b/ssr-libev/src/main/jni/re2 new file mode 160000 index 000000000..ca93436e5 --- /dev/null +++ b/ssr-libev/src/main/jni/re2 @@ -0,0 +1 @@ +Subproject commit ca93436e5b1be02f9f4bfca79b8202c400161994 diff --git a/ssr-libev/src/main/jni/shadowsocks-libev b/ssr-libev/src/main/jni/shadowsocks-libev new file mode 160000 index 000000000..70921a02a --- /dev/null +++ b/ssr-libev/src/main/jni/shadowsocks-libev @@ -0,0 +1 @@ +Subproject commit 70921a02a860b0ca2df4069a609951bc4182c245