From 7a86540c60734f4fa54b1096fa01bdec984f9ac0 Mon Sep 17 00:00:00 2001 From: Vlad Mihalachi Date: Thu, 11 Dec 2014 18:28:54 +0100 Subject: [PATCH] First mac commit --- .gitignore | 122 +- .idea/codeStyleSettings.xml | 348 +- .idea/copyright/Copyright_Vlad_Mihalachi.xml | 16 +- .idea/gradle.xml | 46 +- .idea/inspectionProfiles/Project_Default.xml | 26 +- .../inspectionProfiles/profiles_settings.xml | 12 +- LICENSE | 1354 +++--- README.md | 120 +- app-pro/.gitignore | 2 +- app-pro/build.gradle | 111 +- app-pro/proguard-rules.pro | 34 +- .../maskyn/fileeditorpro/ApplicationTest.java | 62 +- app-pro/src/main/AndroidManifest.xml | 240 +- .../maskyn/fileeditorpro/HomeActivity.java | 61 +- app/.gitignore | 2 +- app/build.gradle | 155 +- app/proguard-rules.pro | 34 +- .../fileeditor/ApplicationTest.java | 62 +- app/src/main/AndroidManifest.xml | 258 +- .../java/com/maskyn/fileeditor/AdsHelper.java | 95 +- .../com/maskyn/fileeditor/HomeActivity.java | 107 +- build.gradle | 76 +- build/intermediates/dex-cache/cache.xml | 381 +- build/intermediates/model_data.bin | Bin 483880 -> 520139 bytes gradle.properties | 72 +- gradle/wrapper/gradle-wrapper.properties | 12 +- gradlew | 328 +- gradlew.bat | 180 +- .../faizmalkani/floatingactionbutton/R.java | 346 +- .../faizmalkani/floatingactionbutton/R.java | 346 +- .../floatingactionbutton/test/R.java | 346 +- .../bundles/debug/AndroidManifest.xml | 24 +- .../build/intermediates/bundles/debug/R.txt | 26 +- .../intermediates/bundles/debug/classes.jar | Bin 5873 -> 5873 bytes .../bundles/release/AndroidManifest.xml | 24 +- .../build/intermediates/bundles/release/R.txt | 26 +- .../intermediates/bundles/release/classes.jar | Bin 5801 -> 5801 bytes .../manifests/test/debug/AndroidManifest.xml | 36 +- ... => manifestMerger7569492401838877170.xml} | 0 .../intermediates/symbols/test/debug/R.txt | 26 +- .../aar/FloatingActionButton-debug.aar | Bin 6494 -> 6492 bytes .../aar/FloatingActionButton-release.aar | Bin 6421 -> 6422 bytes .../build/tmp/packageDebugJar/MANIFEST.MF | 4 +- .../build/tmp/packageReleaseJar/MANIFEST.MF | 4 +- libraries/RootCommands/.gitignore | 66 +- libraries/RootCommands/build.gradle | 60 +- .../RootCommands/src/main/AndroidManifest.xml | 28 +- .../rootcommands/Mount.java | 86 +- .../rootcommands/Remounter.java | 352 +- .../rootcommands/RootCommands.java | 72 +- .../rootcommands/Shell.java | 660 +-- .../rootcommands/SystemCommands.java | 216 +- .../rootcommands/Toolbox.java | 1618 +++---- .../rootcommands/command/Command.java | 308 +- .../command/ExecutableCommand.java | 100 +- .../rootcommands/command/SimpleCommand.java | 56 +- .../command/SimpleExecutableCommand.java | 60 +- .../util/BrokenBusyboxException.java | 36 +- .../rootcommands/util/Log.java | 138 +- .../util/RootAccessDeniedException.java | 36 +- .../UnsupportedArchitectureException.java | 32 +- .../rootcommands/util/Utils.java | 178 +- libraries/sharedCode/.gitignore | 2 +- libraries/sharedCode/build.gradle | 155 +- libraries/sharedCode/proguard-rules.pro | 34 +- .../turboeditor/ApplicationTest.java | 62 +- .../sharedCode/src/main/AndroidManifest.xml | 46 +- .../turboeditor/activity/MainActivity.java | 4244 ++++++++--------- .../activity/SelectFileActivity.java | 778 +-- .../adapter/AdapterDetailedList.java | 408 +- .../turboeditor/adapter/AdapterDrawer.java | 260 +- .../turboeditor/adapter/AdapterTwoItem.java | 150 +- .../turboeditor/application/MyApp.java | 88 +- .../dialogfragment/ChangelogDialog.java | 182 +- .../dialogfragment/EditTextDialog.java | 262 +- .../dialogfragment/EncodingDialog.java | 240 +- .../dialogfragment/FileInfoDialog.java | 192 +- .../dialogfragment/FindTextDialog.java | 412 +- .../dialogfragment/NewFileDetailsDialog.java | 194 +- .../dialogfragment/NumberPickerDialog.java | 244 +- .../dialogfragment/SaveFileDialog.java | 224 +- .../preferences/PreferenceHelper.java | 376 +- .../preferences/SettingsFragment.java | 538 +-- .../turboeditor/root/LinuxShell.java | 154 +- .../turboeditor/root/RootUtils.java | 200 +- .../turboeditor/task/SaveFileTask.java | 210 +- .../texteditor/EditTextPadding.java | 78 +- .../turboeditor/texteditor/FileUtils.java | 116 +- .../turboeditor/texteditor/LineUtils.java | 267 +- .../turboeditor/texteditor/PageSystem.java | 368 +- .../texteditor/PageSystemButtons.java | 268 +- .../turboeditor/texteditor/Patterns.java | 184 +- .../turboeditor/texteditor/SearchResult.java | 126 +- .../turboeditor/util/AccessStorageApi.java | 416 +- .../turboeditor/util/AlphanumComparator.java | 276 +- .../turboeditor/util/AnimationUtils.java | 104 +- .../turboeditor/util/AppInfoHelper.java | 84 +- .../turboeditor/util/EventBusEvents.java | 230 +- .../turboeditor/util/IHomeActivity.java | 24 + .../turboeditor/util/MimeTypes.java | 114 +- .../turboeditor/util/PixelDipConverter.java | 112 +- .../turboeditor/util/ProCheckUtils.java | 88 +- .../turboeditor/util/ThemeUtils.java | 110 +- .../util/systemui/SystemUiHelper.java | 580 +-- .../util/systemui/SystemUiHelperImplHC.java | 186 +- .../util/systemui/SystemUiHelperImplICS.java | 120 +- .../util/systemui/SystemUiHelperImplJB.java | 186 +- .../util/systemui/SystemUiHelperImplKK.java | 106 +- .../turboeditor/views/CustomDrawerLayout.java | 98 +- .../turboeditor/views/GoodScrollView.java | 168 +- .../src/main/res/layout/activity_home.xml | 706 +-- .../src/main/res/layout/activity_licenses.xml | 62 +- .../main/res/layout/activity_select_file.xml | 120 +- ...demo_changelog_fragment_dialogstandard.xml | 54 +- .../main/res/layout/dialog_encoding_list.xml | 92 +- .../res/layout/dialog_fragment_edittext.xml | 74 +- .../res/layout/dialog_fragment_file_info.xml | 64 +- .../res/layout/dialog_fragment_find_text.xml | 154 +- .../dialog_fragment_new_file_details.xml | 122 +- .../res/layout/dialog_fragment_seekbar.xml | 66 +- .../res/layout/fragment_reader_listview.xml | 54 +- .../src/main/res/layout/fragment_settings.xml | 532 +-- .../src/main/res/layout/item_drawer_list.xml | 98 +- .../src/main/res/layout/item_file_list.xml | 122 +- .../main/res/layout/item_single_choice.xml | 64 +- .../src/main/res/layout/item_two_lines.xml | 116 +- .../src/main/res/layout/toolbar.xml | 60 +- .../main/res/menu/activity_select_file.xml | 94 +- .../src/main/res/menu/fragment_editor.xml | 142 +- .../main/res/menu/fragment_editor_search.xml | 116 +- .../src/main/res/menu/popup_new_file.xml | 64 +- .../sharedCode/src/main/res/raw/changelog.xml | 336 +- .../src/main/res/values-es-rMX/strings.xml | 306 +- .../src/main/res/values-ms-rMY/strings.xml | 306 +- .../src/main/res/values-sk-rSK/strings.xml | 306 +- .../src/main/res/values-w820dp/dimens.xml | 80 +- .../src/main/res/values-zh-rHK/strings.xml | 306 +- .../sharedCode/src/main/res/values/arrays.xml | 120 +- .../sharedCode/src/main/res/values/attrs.xml | 56 +- .../sharedCode/src/main/res/values/colors.xml | 136 +- .../sharedCode/src/main/res/values/dimens.xml | 86 +- .../sharedCode/src/main/res/values/ids.xml | 176 +- .../src/main/res/values/integers.xml | 46 +- .../src/main/res/values/strings.xml | 150 +- .../sharedCode/src/main/res/values/styles.xml | 174 +- .../src/main/res/xml/extra_options.xml | 138 +- settings.gradle | 40 +- 147 files changed, 14630 insertions(+), 14798 deletions(-) rename libraries/FloatingActionButton/build/intermediates/manifests/tmp/{manifestMerger1672337899454975879.xml => manifestMerger7569492401838877170.xml} (100%) create mode 100644 libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/IHomeActivity.java diff --git a/.gitignore b/.gitignore index bfcf66c..c5f82c3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,62 +1,62 @@ -# Built application files -*.apk -*.ap_ - -# Files for the Dalvik VM -*.dex - -# Java class files -*.class - -# Generated files -bin/ -gen/ -doc/ - -# Proguard folder generated by Eclipse -proguard/ - -# Log Files -*.log - -# Built application files -/*/build/ - -# Crashlytics configuations -com_crashlytics_export_strings.xml -crashlytics-build.properties -crashlytics.properties - -# Local configuration file (sdk path, etc) -local.properties - -# Gradle generated files -.gradle/ - -# Signing files -.signing/ - -# User-specific configurations -.idea/libraries/ -.idea/workspace.xml -.idea/tasks.xml -.idea/.name -.idea/compiler.xml -.idea/copyright/profiles_settings.xml -.idea/encodings.xml -.idea/misc.xml -.idea/modules.xml -.idea/scopes/scope_settings.xml -.idea/vcs.xml -*.iml - -# OS-specific files -.DS_Store -.DS_Store? -._* -.Spotlight-V100 -.Trashes -ehthumbs.db -Thumbs.db -*~ +# Built application files +*.apk +*.ap_ + +# Files for the Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ +doc/ + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Built application files +/*/build/ + +# Crashlytics configuations +com_crashlytics_export_strings.xml +crashlytics-build.properties +crashlytics.properties + +# Local configuration file (sdk path, etc) +local.properties + +# Gradle generated files +.gradle/ + +# Signing files +.signing/ + +# User-specific configurations +.idea/libraries/ +.idea/workspace.xml +.idea/tasks.xml +.idea/.name +.idea/compiler.xml +.idea/copyright/profiles_settings.xml +.idea/encodings.xml +.idea/misc.xml +.idea/modules.xml +.idea/scopes/scope_settings.xml +.idea/vcs.xml +*.iml + +# OS-specific files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db +*~ #*# \ No newline at end of file diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml index 29ab470..f0823bb 100644 --- a/.idea/codeStyleSettings.xml +++ b/.idea/codeStyleSettings.xml @@ -1,174 +1,174 @@ - - - - - - - + + + + + + + diff --git a/.idea/copyright/Copyright_Vlad_Mihalachi.xml b/.idea/copyright/Copyright_Vlad_Mihalachi.xml index 52b8817..866a560 100644 --- a/.idea/copyright/Copyright_Vlad_Mihalachi.xml +++ b/.idea/copyright/Copyright_Vlad_Mihalachi.xml @@ -1,9 +1,9 @@ - - - + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 286ffea..5cb8949 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -1,23 +1,23 @@ - - - - - - - + + + + + + + diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index a259df6..e070203 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -1,14 +1,14 @@ - - - + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml index 3b31283..6933c1e 100644 --- a/.idea/inspectionProfiles/profiles_settings.xml +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -1,7 +1,7 @@ - - - + + + \ No newline at end of file diff --git a/LICENSE b/LICENSE index 3dc9644..eda548a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,678 +1,678 @@ - - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. [http://fsf.org/] - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - 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 -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 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 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. - - 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. - - 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. - - 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. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "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. - - 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 "covered work" means either the unmodified Program or a work based -on the Program. - - 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. - - 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. - - 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. - - 1. Source Code. - - 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. - - 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. - - 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. - - 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. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - 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. - - 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. - - 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. - - 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 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. - - 13. Use with the GNU Affero General Public License. - - 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. - - 14. Revised Versions of this License. - - 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 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. - - 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. - - 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. - - 15. Disclaimer of Warranty. - - 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 - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -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 -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. - - {one line to give the program's name and a brief idea of what it does.} - Copyright (C) {year} {name of author} - - 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 [http://www.gnu.org/licenses/]. - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - {project} Copyright (C) {year} {fullname} - 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, 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 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 -[http://www.gnu.org/licenses/]. - - 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 -[http://www.gnu.org/philosophy/why-not-lgpl.html]. - - + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. [http://fsf.org/] + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + 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 +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 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 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. + + 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. + + 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. + + 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. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "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. + + 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 "covered work" means either the unmodified Program or a work based +on the Program. + + 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. + + 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. + + 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. + + 1. Source Code. + + 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. + + 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. + + 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. + + 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. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + 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. + + 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. + + 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. + + 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 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. + + 13. Use with the GNU Affero General Public License. + + 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. + + 14. Revised Versions of this License. + + 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 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. + + 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. + + 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. + + 15. Disclaimer of Warranty. + + 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 + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +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 +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. + + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {name of author} + + 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 [http://www.gnu.org/licenses/]. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + {project} Copyright (C) {year} {fullname} + 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, 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 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 +[http://www.gnu.org/licenses/]. + + 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 +[http://www.gnu.org/philosophy/why-not-lgpl.html]. + + \ No newline at end of file diff --git a/README.md b/README.md index a9969f3..a3976c7 100644 --- a/README.md +++ b/README.md @@ -1,60 +1,60 @@ -# Turbo Editor -[![Crowdin](https://d322cqt584bo4o.cloudfront.net/turbo-client/localized.png)](https://crowdin.com/project/turbo-client) - -Simple, powerful and Open Source text editor for Android licensed under the GPLv3 license. - -### Download -[![Play Store](http://developer.android.com/images/brand/en_generic_rgb_wo_60.png)](http://play.google.com/store/apps/details?id=com.maskyn.fileeditorpro) - -[![F-Droid](https://lh5.googleusercontent.com/-zezQqsBye0c/VCUpPVjcKEI/AAAAAAAAAIQ/HbcG5f1qMIw/w129-h45-no/getitonfdroid.png)](https://f-droid.org/repository/browse/?fdid=com.maskyn.fileeditorpro) - -### Contribute -You can contribute to this project in many ways: -* Browse our issues, comment on proposals, report bugs. -* Help to translate the application on [Crowdin][crowdin] -* Be a part of the Google Plus [Community][community googleplus] -* Discuss with us on [XDA thread][xda thread] -* Help to maintain this project active and [Donate][donate] - ------- - -###Donate -[![Paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=26VWS2TSAMUJA) - -[![Flattr this git repo](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=vmihalachi&url=https://github.com/vmihalachi/turbo-editor&title=Turbo Editor&language=java&tags=github&category=software) - ------- - -###Images -![](https://lh3.googleusercontent.com/-0GHukwGQPW4/VCUpEhKnZCI/AAAAAAAAAH4/cclI70K79_Q/w347-h520-no/PhoneCustom_7.png)![](https://lh3.googleusercontent.com/-OvazluFl_QQ/VCUo9DAje9I/AAAAAAAAAHQ/i7n1uCpU1hE/w347-h520-no/PhoneCustom_1.png)![](https://lh4.googleusercontent.com/-zh4CYdQPecg/VCUpD3QXpAI/AAAAAAAAAHw/ulL5-V0iJUw/w347-h520-no/PhoneCustom_6.png)![](https://lh4.googleusercontent.com/-LT3k4z9JHo8/VCUo_0jnZRI/AAAAAAAAAHg/Npk9UlkXIV8/w347-h520-no/PhoneCustom_4.png)![](https://lh5.googleusercontent.com/-hXvsf-WYvBs/VCUo9sYfR-I/AAAAAAAAAHY/TTfAgiV_7ko/w347-h520-no/PhoneCustom_3.png)![](https://lh6.googleusercontent.com/-Qib82pK6mZU/VCUpAgYmUdI/AAAAAAAAAHo/zoPVmwcatbQ/w347-h520-no/PhoneCustom_5.png)![](https://lh5.googleusercontent.com/-SERL7X-JHuc/VCax0QSlCGI/AAAAAAAAAJA/hu8dvbvJGBM/w375-h563-no/PhoneCustom_2.png) ------- - -### Developer -[Vlad Mihalachi][developer site] - ------- - -### A special thanks to.. -* [Dumitru Grubii][contributor dumitru grubii] for the icon -* All the translators -* You? - ------- - -### License -Turbo Client is made available under the terms of the [GPLv3][gplv3]. - -See the [LICENSE][license] file that accompanies this distribution for the full text of the license. - -[gplv3]: http://www.gnu.org/licenses/gpl.html -[license]: https://github.com/vmihalachi/turbo-editor/LICENSE -[donate]: https://github.com/vmihalachi/turbo-editor#donate -[donate paypal]: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=PUQXSX6MTXHZ2 -[xda thread]: http://forum.xda-developers.com/android/apps-games/app-turbo-editor-text-editor-t2832016 -[community googleplus]: https://plus.google.com/u/0/communities/111974095419108178946 -[crowdin]: https://crowdin.net/project/turbo-client -[developer site]: http://vmihalachi.com/ -[crowdin]: https://crowdin.net/project/turbo-client -[contributor dumitru grubii]: https://twitter.com/DumitruGrubii -[project issues]: https://github.com/vmihalachi/turbo-editor/issues -[project wiki]: https://github.com/vmihalachi/turbo-editor/wiki +# Turbo Editor +[![Crowdin](https://d322cqt584bo4o.cloudfront.net/turbo-client/localized.png)](https://crowdin.com/project/turbo-client) + +Simple, powerful and Open Source text editor for Android licensed under the GPLv3 license. + +### Download +[![Play Store](http://developer.android.com/images/brand/en_generic_rgb_wo_60.png)](http://play.google.com/store/apps/details?id=com.maskyn.fileeditorpro) + +[![F-Droid](https://lh5.googleusercontent.com/-zezQqsBye0c/VCUpPVjcKEI/AAAAAAAAAIQ/HbcG5f1qMIw/w129-h45-no/getitonfdroid.png)](https://f-droid.org/repository/browse/?fdid=com.maskyn.fileeditorpro) + +### Contribute +You can contribute to this project in many ways: +* Browse our issues, comment on proposals, report bugs. +* Help to translate the application on [Crowdin][crowdin] +* Be a part of the Google Plus [Community][community googleplus] +* Discuss with us on [XDA thread][xda thread] +* Help to maintain this project active and [Donate][donate] + +------ + +###Donate +[![Paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=26VWS2TSAMUJA) + +[![Flattr this git repo](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=vmihalachi&url=https://github.com/vmihalachi/turbo-editor&title=Turbo Editor&language=java&tags=github&category=software) + +------ + +###Images +![](https://lh3.googleusercontent.com/-0GHukwGQPW4/VCUpEhKnZCI/AAAAAAAAAH4/cclI70K79_Q/w347-h520-no/PhoneCustom_7.png)![](https://lh3.googleusercontent.com/-OvazluFl_QQ/VCUo9DAje9I/AAAAAAAAAHQ/i7n1uCpU1hE/w347-h520-no/PhoneCustom_1.png)![](https://lh4.googleusercontent.com/-zh4CYdQPecg/VCUpD3QXpAI/AAAAAAAAAHw/ulL5-V0iJUw/w347-h520-no/PhoneCustom_6.png)![](https://lh4.googleusercontent.com/-LT3k4z9JHo8/VCUo_0jnZRI/AAAAAAAAAHg/Npk9UlkXIV8/w347-h520-no/PhoneCustom_4.png)![](https://lh5.googleusercontent.com/-hXvsf-WYvBs/VCUo9sYfR-I/AAAAAAAAAHY/TTfAgiV_7ko/w347-h520-no/PhoneCustom_3.png)![](https://lh6.googleusercontent.com/-Qib82pK6mZU/VCUpAgYmUdI/AAAAAAAAAHo/zoPVmwcatbQ/w347-h520-no/PhoneCustom_5.png)![](https://lh5.googleusercontent.com/-SERL7X-JHuc/VCax0QSlCGI/AAAAAAAAAJA/hu8dvbvJGBM/w375-h563-no/PhoneCustom_2.png) +------ + +### Developer +[Vlad Mihalachi][developer site] + +------ + +### A special thanks to.. +* [Dumitru Grubii][contributor dumitru grubii] for the icon +* All the translators +* You? + +------ + +### License +Turbo Client is made available under the terms of the [GPLv3][gplv3]. + +See the [LICENSE][license] file that accompanies this distribution for the full text of the license. + +[gplv3]: http://www.gnu.org/licenses/gpl.html +[license]: https://github.com/vmihalachi/turbo-editor/LICENSE +[donate]: https://github.com/vmihalachi/turbo-editor#donate +[donate paypal]: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=PUQXSX6MTXHZ2 +[xda thread]: http://forum.xda-developers.com/android/apps-games/app-turbo-editor-text-editor-t2832016 +[community googleplus]: https://plus.google.com/u/0/communities/111974095419108178946 +[crowdin]: https://crowdin.net/project/turbo-client +[developer site]: http://vmihalachi.com/ +[crowdin]: https://crowdin.net/project/turbo-client +[contributor dumitru grubii]: https://twitter.com/DumitruGrubii +[project issues]: https://github.com/vmihalachi/turbo-editor/issues +[project wiki]: https://github.com/vmihalachi/turbo-editor/wiki diff --git a/app-pro/.gitignore b/app-pro/.gitignore index 796b96d..3543521 100644 --- a/app-pro/.gitignore +++ b/app-pro/.gitignore @@ -1 +1 @@ -/build +/build diff --git a/app-pro/build.gradle b/app-pro/build.gradle index 215d6a5..7c72321 100644 --- a/app-pro/build.gradle +++ b/app-pro/build.gradle @@ -1,56 +1,55 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 . - */ - -apply plugin: 'com.android.application' - -android { - compileSdkVersion 21 - buildToolsVersion '21.0.2' - - defaultConfig { - applicationId "com.maskyn.fileeditorpro" - minSdkVersion 11 - targetSdkVersion 21 - versionCode 35 - versionName "1.13.2" - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_7 - targetCompatibility JavaVersion.VERSION_1_7 - } - buildTypes { - release { - runProguard false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } - lintOptions { - abortOnError false - } - packagingOptions { - exclude 'META-INF/LICENSE.txt' - exclude 'META-INF/NOTICE.txt' - } -} - -dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - compile project(':libraries:sharedCode') -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 . + */ + +apply plugin: 'com.android.application' + +android { + compileSdkVersion 21 + buildToolsVersion '21.0.2' + + defaultConfig { + applicationId "com.maskyn.fileeditorpro" + minSdkVersion 11 + targetSdkVersion 21 + versionCode 35 + versionName "1.13.2" + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 + } + buildTypes { + release { + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + lintOptions { + abortOnError false + } + packagingOptions { + exclude 'META-INF/LICENSE.txt' + exclude 'META-INF/NOTICE.txt' + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile project(':libraries:sharedCode') +} diff --git a/app-pro/proguard-rules.pro b/app-pro/proguard-rules.pro index 9f604d1..a946ab9 100644 --- a/app-pro/proguard-rules.pro +++ b/app-pro/proguard-rules.pro @@ -1,17 +1,17 @@ -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in C:/Users/Vlad/AppData/Local/Android/android-sdk/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the proguardFiles -# directive in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in C:/Users/Vlad/AppData/Local/Android/android-sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/app-pro/src/androidTest/java/com/maskyn/fileeditorpro/ApplicationTest.java b/app-pro/src/androidTest/java/com/maskyn/fileeditorpro/ApplicationTest.java index e85c652..aba8314 100644 --- a/app-pro/src/androidTest/java/com/maskyn/fileeditorpro/ApplicationTest.java +++ b/app-pro/src/androidTest/java/com/maskyn/fileeditorpro/ApplicationTest.java @@ -1,32 +1,32 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 com.maskyn.fileeditorpro; - -import android.app.Application; -import android.test.ApplicationTestCase; - -/** - * Testing Fundamentals - */ -public class ApplicationTest extends ApplicationTestCase { - public ApplicationTest() { - super(Application.class); - } +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 com.maskyn.fileeditorpro; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } } \ No newline at end of file diff --git a/app-pro/src/main/AndroidManifest.xml b/app-pro/src/main/AndroidManifest.xml index 5d385cf..68761ef 100644 --- a/app-pro/src/main/AndroidManifest.xml +++ b/app-pro/src/main/AndroidManifest.xml @@ -1,120 +1,120 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app-pro/src/main/java/com/maskyn/fileeditorpro/HomeActivity.java b/app-pro/src/main/java/com/maskyn/fileeditorpro/HomeActivity.java index 6a80ed7..2836ac7 100644 --- a/app-pro/src/main/java/com/maskyn/fileeditorpro/HomeActivity.java +++ b/app-pro/src/main/java/com/maskyn/fileeditorpro/HomeActivity.java @@ -1,30 +1,31 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 com.maskyn.fileeditorpro; - -import sharedcode.turboeditor.activity.MainActivity; - -public class HomeActivity extends MainActivity { - - @Override - public void displayInterstitial() { - // nothing to do here - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 com.maskyn.fileeditorpro; + +import sharedcode.turboeditor.activity.MainActivity; + +public class HomeActivity extends MainActivity { + + @Override + public boolean showInterstitial() { + // nothing to do here + return false; + } +} diff --git a/app/.gitignore b/app/.gitignore index 796b96d..3543521 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -1 +1 @@ -/build +/build diff --git a/app/build.gradle b/app/build.gradle index e4bf000..a7af312 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,78 +1,77 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 . - */ - -buildscript { - repositories { - mavenCentral() - maven { url 'https://maven.fabric.io/repo' } - } - - dependencies { - classpath 'io.fabric.tools:gradle:1.+' - } - -} - -apply plugin: 'com.android.application' -apply plugin: 'io.fabric' - -repositories { - mavenCentral() - maven { url 'https://maven.fabric.io/repo' } -} - - - -android { - compileSdkVersion 21 - buildToolsVersion '21.0.2' - defaultConfig { - applicationId "com.maskyn.fileeditor" - minSdkVersion 11 - targetSdkVersion 21 - versionCode 35 - versionName "1.13.2" - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_7 - targetCompatibility JavaVersion.VERSION_1_7 - } - buildTypes { - release { - runProguard false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } - lintOptions { - abortOnError false - } - packagingOptions { - exclude 'META-INF/LICENSE.txt' - exclude 'META-INF/NOTICE.txt' - } -} - -dependencies { - compile fileTree(dir: 'libs', include: '*.jar') - compile project(':libraries:sharedCode') - compile 'com.google.android.gms:play-services:6.1.11' - compile('com.crashlytics.sdk.android:crashlytics:2.+@aar') { - transitive = true; - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 . + */ + +buildscript { + repositories { + mavenCentral() + maven { url 'https://maven.fabric.io/repo' } + } + + dependencies { + classpath 'io.fabric.tools:gradle:1.+' + } + +} + +apply plugin: 'com.android.application' +apply plugin: 'io.fabric' + +repositories { + mavenCentral() + maven { url 'https://maven.fabric.io/repo' } +} + + + +android { + compileSdkVersion 21 + buildToolsVersion '21.0.2' + defaultConfig { + applicationId "com.maskyn.fileeditor" + minSdkVersion 11 + targetSdkVersion 21 + versionCode 35 + versionName "1.13.2" + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 + } + buildTypes { + release { + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + lintOptions { + abortOnError false + } + packagingOptions { + exclude 'META-INF/LICENSE.txt' + exclude 'META-INF/NOTICE.txt' + } +} + +dependencies { + compile fileTree(dir: 'libs', include: '*.jar') + compile project(':libraries:sharedCode') + compile 'com.google.android.gms:play-services:6.1.11' + compile('com.crashlytics.sdk.android:crashlytics:2.+@aar') { + transitive = true; + } +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 9e22422..f65387a 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,17 +1,17 @@ -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in C:\Users\Vlad\AppData\Local\Android\android-sdk/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the proguardFiles -# directive in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in C:\Users\Vlad\AppData\Local\Android\android-sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/app/src/androidTest/java/com/vmihalachi/fileeditor/ApplicationTest.java b/app/src/androidTest/java/com/vmihalachi/fileeditor/ApplicationTest.java index 0ee29b9..689f300 100644 --- a/app/src/androidTest/java/com/vmihalachi/fileeditor/ApplicationTest.java +++ b/app/src/androidTest/java/com/vmihalachi/fileeditor/ApplicationTest.java @@ -1,32 +1,32 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 com.maskyn.fileeditor; - -import android.app.Application; -import android.test.ApplicationTestCase; - -/** - * Testing Fundamentals - */ -public class ApplicationTest extends ApplicationTestCase { - public ApplicationTest() { - super(Application.class); - } +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 com.maskyn.fileeditor; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2eef55b..53f483c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,129 +1,129 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/maskyn/fileeditor/AdsHelper.java b/app/src/main/java/com/maskyn/fileeditor/AdsHelper.java index 0cf91c2..c848d74 100644 --- a/app/src/main/java/com/maskyn/fileeditor/AdsHelper.java +++ b/app/src/main/java/com/maskyn/fileeditor/AdsHelper.java @@ -1,50 +1,45 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 com.maskyn.fileeditor; - -import android.app.Activity; -import android.preference.PreferenceManager; - -import com.google.android.gms.ads.AdRequest; -import com.google.android.gms.ads.InterstitialAd; - -import java.util.Calendar; - -import sharedcode.turboeditor.preferences.PreferenceHelper; - -public class AdsHelper { - private InterstitialAd interstitial; - - public AdsHelper(Activity activity) { - - interstitial = new InterstitialAd(activity); - interstitial.setAdUnitId("ca-app-pub-5679083452234719/7178038180"); - - // Create ad request. - AdRequest adRequest = new AdRequest.Builder().build(); - - // Begin loading your interstitial. - interstitial.loadAd(adRequest); - } - - public void displayInterstitial() { - interstitial.show(); - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 com.maskyn.fileeditor; + +import android.app.Activity; + +import com.google.android.gms.ads.AdRequest; +import com.google.android.gms.ads.InterstitialAd; + +public class AdsHelper { + private InterstitialAd interstitial; + + public AdsHelper(Activity activity) { + + interstitial = new InterstitialAd(activity); + interstitial.setAdUnitId("ca-app-pub-5679083452234719/7178038180"); + + // Create ad request. + AdRequest adRequest = new AdRequest.Builder().build(); + + // Begin loading your interstitial. + interstitial.loadAd(adRequest); + } + + public void displayInterstitial() { + interstitial.show(); + } +} diff --git a/app/src/main/java/com/maskyn/fileeditor/HomeActivity.java b/app/src/main/java/com/maskyn/fileeditor/HomeActivity.java index 660f3d6..a0ff52e 100644 --- a/app/src/main/java/com/maskyn/fileeditor/HomeActivity.java +++ b/app/src/main/java/com/maskyn/fileeditor/HomeActivity.java @@ -1,51 +1,56 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 com.maskyn.fileeditor; - -import android.os.Bundle; - -import com.crashlytics.android.Crashlytics; - -import io.fabric.sdk.android.Fabric; -import sharedcode.turboeditor.activity.MainActivity; -import sharedcode.turboeditor.preferences.PreferenceHelper; -import sharedcode.turboeditor.util.ProCheckUtils; - -public class HomeActivity extends MainActivity { - - private AdsHelper adsHelper; - - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if(PreferenceHelper.getSendErrorReports(this)) - Fabric.with(this, new Crashlytics()); - // setup the ads - if(!ProCheckUtils.isPro(this)) - adsHelper = new AdsHelper(this); - } - - @Override - public void displayInterstitial() { - if(adsHelper != null && !ProCheckUtils.isPro(this)) - adsHelper.displayInterstitial(); - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 com.maskyn.fileeditor; + +import android.os.Bundle; + +import com.crashlytics.android.Crashlytics; + +import io.fabric.sdk.android.Fabric; +import sharedcode.turboeditor.activity.MainActivity; + +public class HomeActivity extends MainActivity { + + private AdsHelper adsHelper; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if(sharedcode.turboeditor.preferences.PreferenceHelper.getSendErrorReports(this)) + Fabric.with(this, new Crashlytics()); + // setup the ads + if(!sharedcode.turboeditor.util.ProCheckUtils.isPro(this)) + adsHelper = new AdsHelper(this); + } + + @Override + public boolean showInterstitial() { + if(adsHelper != null && !sharedcode.turboeditor.util.ProCheckUtils.isPro(this)) { + adsHelper.displayInterstitial(); + return true; + } + else { + return false; + } + } + + +} diff --git a/build.gradle b/build.gradle index 79ad207..b18ba7d 100644 --- a/build.gradle +++ b/build.gradle @@ -1,38 +1,38 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 . - */ - -// Top-level build file where you can add configuration options common to all sub-projects/modules. - -buildscript { - repositories { - jcenter() - } - dependencies { - classpath 'com.android.tools.build:gradle:0.13.+' - - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files - } -} - -allprojects { - repositories { - jcenter() - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 . + */ + +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:+' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + jcenter() + } +} diff --git a/build/intermediates/dex-cache/cache.xml b/build/intermediates/dex-cache/cache.xml index 5db681f..bf62108 100644 --- a/build/intermediates/dex-cache/cache.xml +++ b/build/intermediates/dex-cache/cache.xml @@ -1,287 +1,96 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/intermediates/model_data.bin b/build/intermediates/model_data.bin index 83aca2ff2a1d4c162a919db4e062407a9c6f9951..da77581fe132391beda1ab0b47581b2d63fd2619 100644 GIT binary patch literal 520139 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

WyR3tW-6ObESqH%J0SH? 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 E|Lbz!WdHyG diff --git a/gradle.properties b/gradle.properties index 79a6388..5054f97 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,37 +1,37 @@ -# -# Copyright (C) 2014 Vlad Mihalachi -# -# This file is part of Turbo Editor. -# -# Turbo Editor 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. -# -# Turbo Editor 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 . -# - -# Project-wide Gradle settings. - -# IDE (e.g. Android Studio) users: -# Settings specified in this file will override any Gradle settings -# configured through the IDE. - -# 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 -# 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 +# +# Copyright (C) 2014 Vlad Mihalachi +# +# This file is part of Turbo Editor. +# +# Turbo Editor 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. +# +# Turbo Editor 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 . +# + +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Settings specified in this file will override any Gradle settings +# configured through the IDE. + +# 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 +# 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 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0b8241e..3f7e27a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Oct 22 18:10:25 CEST 2014 -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.1-all.zip +#Wed Oct 22 18:10:25 CEST 2014 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.1-all.zip diff --git a/gradlew b/gradlew index 91a7e26..583ae08 100644 --- a/gradlew +++ b/gradlew @@ -1,164 +1,164 @@ -#!/usr/bin/env bash - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn ( ) { - echo "$*" -} - -die ( ) { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; -esac - -# For Cygwin, ensure paths are in UNIX format before anything is touched. -if $cygwin ; then - [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` -fi - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >&- -APP_HOME="`pwd -P`" -cd "$SAVED" >&- - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" - -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat index 8a0b282..aec9973 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,90 +1,90 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/libraries/FloatingActionButton/build/generated/source/r/debug/com/faizmalkani/floatingactionbutton/R.java b/libraries/FloatingActionButton/build/generated/source/r/debug/com/faizmalkani/floatingactionbutton/R.java index 4d9193b..08a7328 100644 --- a/libraries/FloatingActionButton/build/generated/source/r/debug/com/faizmalkani/floatingactionbutton/R.java +++ b/libraries/FloatingActionButton/build/generated/source/r/debug/com/faizmalkani/floatingactionbutton/R.java @@ -1,173 +1,173 @@ -/* AUTO-GENERATED FILE. DO NOT MODIFY. - * - * This class was automatically generated by the - * aapt tool from the resource data it found. It - * should not be modified by hand. - */ - -package com.faizmalkani.floatingactionbutton; - -public final class R { - public static final class attr { - /**

Must be a color value, in the form of "#rgb", "#argb", -"#rrggbb", or "#aarrggbb". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - */ - public static int colour=0x7f010001; - /**

Must be an integer value, such as "100". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - */ - public static int drawable=0x7f010000; - /**

Must be an integer value, such as "100". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - */ - public static int shadowColor=0x7f010005; - /**

Must be a floating point value, such as "1.2". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - */ - public static int shadowDx=0x7f010003; - /**

Must be a floating point value, such as "1.2". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - */ - public static int shadowDy=0x7f010004; - /**

Must be a floating point value, such as "1.2". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - */ - public static int shadowRadius=0x7f010002; - } - public static final class styleable { - /** Attributes that can be used with a FloatingActionButton. -

Includes the following attributes:

- - - - - - - - - - -
AttributeDescription
{@link #FloatingActionButton_colour com.faizmalkani.floatingactionbutton:colour}
{@link #FloatingActionButton_drawable com.faizmalkani.floatingactionbutton:drawable}
{@link #FloatingActionButton_shadowColor com.faizmalkani.floatingactionbutton:shadowColor}
{@link #FloatingActionButton_shadowDx com.faizmalkani.floatingactionbutton:shadowDx}
{@link #FloatingActionButton_shadowDy com.faizmalkani.floatingactionbutton:shadowDy}
{@link #FloatingActionButton_shadowRadius com.faizmalkani.floatingactionbutton:shadowRadius}
- @see #FloatingActionButton_colour - @see #FloatingActionButton_drawable - @see #FloatingActionButton_shadowColor - @see #FloatingActionButton_shadowDx - @see #FloatingActionButton_shadowDy - @see #FloatingActionButton_shadowRadius - */ - public static final int[] FloatingActionButton = { - 0x7f010000, 0x7f010001, 0x7f010002, 0x7f010003, - 0x7f010004, 0x7f010005 - }; - /** -

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#colour} - attribute's value can be found in the {@link #FloatingActionButton} array. - - -

Must be a color value, in the form of "#rgb", "#argb", -"#rrggbb", or "#aarrggbb". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - @attr name com.faizmalkani.floatingactionbutton:colour - */ - public static int FloatingActionButton_colour = 1; - /** -

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#drawable} - attribute's value can be found in the {@link #FloatingActionButton} array. - - -

Must be an integer value, such as "100". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - @attr name com.faizmalkani.floatingactionbutton:drawable - */ - public static int FloatingActionButton_drawable = 0; - /** -

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#shadowColor} - attribute's value can be found in the {@link #FloatingActionButton} array. - - -

Must be an integer value, such as "100". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - @attr name com.faizmalkani.floatingactionbutton:shadowColor - */ - public static int FloatingActionButton_shadowColor = 5; - /** -

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#shadowDx} - attribute's value can be found in the {@link #FloatingActionButton} array. - - -

Must be a floating point value, such as "1.2". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - @attr name com.faizmalkani.floatingactionbutton:shadowDx - */ - public static int FloatingActionButton_shadowDx = 3; - /** -

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#shadowDy} - attribute's value can be found in the {@link #FloatingActionButton} array. - - -

Must be a floating point value, such as "1.2". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - @attr name com.faizmalkani.floatingactionbutton:shadowDy - */ - public static int FloatingActionButton_shadowDy = 4; - /** -

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#shadowRadius} - attribute's value can be found in the {@link #FloatingActionButton} array. - - -

Must be a floating point value, such as "1.2". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - @attr name com.faizmalkani.floatingactionbutton:shadowRadius - */ - public static int FloatingActionButton_shadowRadius = 2; - }; -} +/* AUTO-GENERATED FILE. DO NOT MODIFY. + * + * This class was automatically generated by the + * aapt tool from the resource data it found. It + * should not be modified by hand. + */ + +package com.faizmalkani.floatingactionbutton; + +public final class R { + public static final class attr { + /**

Must be a color value, in the form of "#rgb", "#argb", +"#rrggbb", or "#aarrggbb". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + */ + public static int colour=0x7f010001; + /**

Must be an integer value, such as "100". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + */ + public static int drawable=0x7f010000; + /**

Must be an integer value, such as "100". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + */ + public static int shadowColor=0x7f010005; + /**

Must be a floating point value, such as "1.2". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + */ + public static int shadowDx=0x7f010003; + /**

Must be a floating point value, such as "1.2". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + */ + public static int shadowDy=0x7f010004; + /**

Must be a floating point value, such as "1.2". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + */ + public static int shadowRadius=0x7f010002; + } + public static final class styleable { + /** Attributes that can be used with a FloatingActionButton. +

Includes the following attributes:

+ + + + + + + + + + +
AttributeDescription
{@link #FloatingActionButton_colour com.faizmalkani.floatingactionbutton:colour}
{@link #FloatingActionButton_drawable com.faizmalkani.floatingactionbutton:drawable}
{@link #FloatingActionButton_shadowColor com.faizmalkani.floatingactionbutton:shadowColor}
{@link #FloatingActionButton_shadowDx com.faizmalkani.floatingactionbutton:shadowDx}
{@link #FloatingActionButton_shadowDy com.faizmalkani.floatingactionbutton:shadowDy}
{@link #FloatingActionButton_shadowRadius com.faizmalkani.floatingactionbutton:shadowRadius}
+ @see #FloatingActionButton_colour + @see #FloatingActionButton_drawable + @see #FloatingActionButton_shadowColor + @see #FloatingActionButton_shadowDx + @see #FloatingActionButton_shadowDy + @see #FloatingActionButton_shadowRadius + */ + public static final int[] FloatingActionButton = { + 0x7f010000, 0x7f010001, 0x7f010002, 0x7f010003, + 0x7f010004, 0x7f010005 + }; + /** +

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#colour} + attribute's value can be found in the {@link #FloatingActionButton} array. + + +

Must be a color value, in the form of "#rgb", "#argb", +"#rrggbb", or "#aarrggbb". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + @attr name com.faizmalkani.floatingactionbutton:colour + */ + public static int FloatingActionButton_colour = 1; + /** +

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#drawable} + attribute's value can be found in the {@link #FloatingActionButton} array. + + +

Must be an integer value, such as "100". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + @attr name com.faizmalkani.floatingactionbutton:drawable + */ + public static int FloatingActionButton_drawable = 0; + /** +

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#shadowColor} + attribute's value can be found in the {@link #FloatingActionButton} array. + + +

Must be an integer value, such as "100". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + @attr name com.faizmalkani.floatingactionbutton:shadowColor + */ + public static int FloatingActionButton_shadowColor = 5; + /** +

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#shadowDx} + attribute's value can be found in the {@link #FloatingActionButton} array. + + +

Must be a floating point value, such as "1.2". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + @attr name com.faizmalkani.floatingactionbutton:shadowDx + */ + public static int FloatingActionButton_shadowDx = 3; + /** +

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#shadowDy} + attribute's value can be found in the {@link #FloatingActionButton} array. + + +

Must be a floating point value, such as "1.2". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + @attr name com.faizmalkani.floatingactionbutton:shadowDy + */ + public static int FloatingActionButton_shadowDy = 4; + /** +

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#shadowRadius} + attribute's value can be found in the {@link #FloatingActionButton} array. + + +

Must be a floating point value, such as "1.2". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + @attr name com.faizmalkani.floatingactionbutton:shadowRadius + */ + public static int FloatingActionButton_shadowRadius = 2; + }; +} diff --git a/libraries/FloatingActionButton/build/generated/source/r/release/com/faizmalkani/floatingactionbutton/R.java b/libraries/FloatingActionButton/build/generated/source/r/release/com/faizmalkani/floatingactionbutton/R.java index 4d9193b..08a7328 100644 --- a/libraries/FloatingActionButton/build/generated/source/r/release/com/faizmalkani/floatingactionbutton/R.java +++ b/libraries/FloatingActionButton/build/generated/source/r/release/com/faizmalkani/floatingactionbutton/R.java @@ -1,173 +1,173 @@ -/* AUTO-GENERATED FILE. DO NOT MODIFY. - * - * This class was automatically generated by the - * aapt tool from the resource data it found. It - * should not be modified by hand. - */ - -package com.faizmalkani.floatingactionbutton; - -public final class R { - public static final class attr { - /**

Must be a color value, in the form of "#rgb", "#argb", -"#rrggbb", or "#aarrggbb". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - */ - public static int colour=0x7f010001; - /**

Must be an integer value, such as "100". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - */ - public static int drawable=0x7f010000; - /**

Must be an integer value, such as "100". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - */ - public static int shadowColor=0x7f010005; - /**

Must be a floating point value, such as "1.2". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - */ - public static int shadowDx=0x7f010003; - /**

Must be a floating point value, such as "1.2". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - */ - public static int shadowDy=0x7f010004; - /**

Must be a floating point value, such as "1.2". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - */ - public static int shadowRadius=0x7f010002; - } - public static final class styleable { - /** Attributes that can be used with a FloatingActionButton. -

Includes the following attributes:

- - - - - - - - - - -
AttributeDescription
{@link #FloatingActionButton_colour com.faizmalkani.floatingactionbutton:colour}
{@link #FloatingActionButton_drawable com.faizmalkani.floatingactionbutton:drawable}
{@link #FloatingActionButton_shadowColor com.faizmalkani.floatingactionbutton:shadowColor}
{@link #FloatingActionButton_shadowDx com.faizmalkani.floatingactionbutton:shadowDx}
{@link #FloatingActionButton_shadowDy com.faizmalkani.floatingactionbutton:shadowDy}
{@link #FloatingActionButton_shadowRadius com.faizmalkani.floatingactionbutton:shadowRadius}
- @see #FloatingActionButton_colour - @see #FloatingActionButton_drawable - @see #FloatingActionButton_shadowColor - @see #FloatingActionButton_shadowDx - @see #FloatingActionButton_shadowDy - @see #FloatingActionButton_shadowRadius - */ - public static final int[] FloatingActionButton = { - 0x7f010000, 0x7f010001, 0x7f010002, 0x7f010003, - 0x7f010004, 0x7f010005 - }; - /** -

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#colour} - attribute's value can be found in the {@link #FloatingActionButton} array. - - -

Must be a color value, in the form of "#rgb", "#argb", -"#rrggbb", or "#aarrggbb". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - @attr name com.faizmalkani.floatingactionbutton:colour - */ - public static int FloatingActionButton_colour = 1; - /** -

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#drawable} - attribute's value can be found in the {@link #FloatingActionButton} array. - - -

Must be an integer value, such as "100". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - @attr name com.faizmalkani.floatingactionbutton:drawable - */ - public static int FloatingActionButton_drawable = 0; - /** -

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#shadowColor} - attribute's value can be found in the {@link #FloatingActionButton} array. - - -

Must be an integer value, such as "100". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - @attr name com.faizmalkani.floatingactionbutton:shadowColor - */ - public static int FloatingActionButton_shadowColor = 5; - /** -

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#shadowDx} - attribute's value can be found in the {@link #FloatingActionButton} array. - - -

Must be a floating point value, such as "1.2". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - @attr name com.faizmalkani.floatingactionbutton:shadowDx - */ - public static int FloatingActionButton_shadowDx = 3; - /** -

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#shadowDy} - attribute's value can be found in the {@link #FloatingActionButton} array. - - -

Must be a floating point value, such as "1.2". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - @attr name com.faizmalkani.floatingactionbutton:shadowDy - */ - public static int FloatingActionButton_shadowDy = 4; - /** -

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#shadowRadius} - attribute's value can be found in the {@link #FloatingActionButton} array. - - -

Must be a floating point value, such as "1.2". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - @attr name com.faizmalkani.floatingactionbutton:shadowRadius - */ - public static int FloatingActionButton_shadowRadius = 2; - }; -} +/* AUTO-GENERATED FILE. DO NOT MODIFY. + * + * This class was automatically generated by the + * aapt tool from the resource data it found. It + * should not be modified by hand. + */ + +package com.faizmalkani.floatingactionbutton; + +public final class R { + public static final class attr { + /**

Must be a color value, in the form of "#rgb", "#argb", +"#rrggbb", or "#aarrggbb". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + */ + public static int colour=0x7f010001; + /**

Must be an integer value, such as "100". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + */ + public static int drawable=0x7f010000; + /**

Must be an integer value, such as "100". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + */ + public static int shadowColor=0x7f010005; + /**

Must be a floating point value, such as "1.2". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + */ + public static int shadowDx=0x7f010003; + /**

Must be a floating point value, such as "1.2". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + */ + public static int shadowDy=0x7f010004; + /**

Must be a floating point value, such as "1.2". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + */ + public static int shadowRadius=0x7f010002; + } + public static final class styleable { + /** Attributes that can be used with a FloatingActionButton. +

Includes the following attributes:

+ + + + + + + + + + +
AttributeDescription
{@link #FloatingActionButton_colour com.faizmalkani.floatingactionbutton:colour}
{@link #FloatingActionButton_drawable com.faizmalkani.floatingactionbutton:drawable}
{@link #FloatingActionButton_shadowColor com.faizmalkani.floatingactionbutton:shadowColor}
{@link #FloatingActionButton_shadowDx com.faizmalkani.floatingactionbutton:shadowDx}
{@link #FloatingActionButton_shadowDy com.faizmalkani.floatingactionbutton:shadowDy}
{@link #FloatingActionButton_shadowRadius com.faizmalkani.floatingactionbutton:shadowRadius}
+ @see #FloatingActionButton_colour + @see #FloatingActionButton_drawable + @see #FloatingActionButton_shadowColor + @see #FloatingActionButton_shadowDx + @see #FloatingActionButton_shadowDy + @see #FloatingActionButton_shadowRadius + */ + public static final int[] FloatingActionButton = { + 0x7f010000, 0x7f010001, 0x7f010002, 0x7f010003, + 0x7f010004, 0x7f010005 + }; + /** +

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#colour} + attribute's value can be found in the {@link #FloatingActionButton} array. + + +

Must be a color value, in the form of "#rgb", "#argb", +"#rrggbb", or "#aarrggbb". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + @attr name com.faizmalkani.floatingactionbutton:colour + */ + public static int FloatingActionButton_colour = 1; + /** +

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#drawable} + attribute's value can be found in the {@link #FloatingActionButton} array. + + +

Must be an integer value, such as "100". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + @attr name com.faizmalkani.floatingactionbutton:drawable + */ + public static int FloatingActionButton_drawable = 0; + /** +

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#shadowColor} + attribute's value can be found in the {@link #FloatingActionButton} array. + + +

Must be an integer value, such as "100". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + @attr name com.faizmalkani.floatingactionbutton:shadowColor + */ + public static int FloatingActionButton_shadowColor = 5; + /** +

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#shadowDx} + attribute's value can be found in the {@link #FloatingActionButton} array. + + +

Must be a floating point value, such as "1.2". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + @attr name com.faizmalkani.floatingactionbutton:shadowDx + */ + public static int FloatingActionButton_shadowDx = 3; + /** +

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#shadowDy} + attribute's value can be found in the {@link #FloatingActionButton} array. + + +

Must be a floating point value, such as "1.2". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + @attr name com.faizmalkani.floatingactionbutton:shadowDy + */ + public static int FloatingActionButton_shadowDy = 4; + /** +

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.R.attr#shadowRadius} + attribute's value can be found in the {@link #FloatingActionButton} array. + + +

Must be a floating point value, such as "1.2". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + @attr name com.faizmalkani.floatingactionbutton:shadowRadius + */ + public static int FloatingActionButton_shadowRadius = 2; + }; +} diff --git a/libraries/FloatingActionButton/build/generated/source/r/test/debug/com/faizmalkani/floatingactionbutton/test/R.java b/libraries/FloatingActionButton/build/generated/source/r/test/debug/com/faizmalkani/floatingactionbutton/test/R.java index c2a3510..fb8b605 100644 --- a/libraries/FloatingActionButton/build/generated/source/r/test/debug/com/faizmalkani/floatingactionbutton/test/R.java +++ b/libraries/FloatingActionButton/build/generated/source/r/test/debug/com/faizmalkani/floatingactionbutton/test/R.java @@ -1,173 +1,173 @@ -/* AUTO-GENERATED FILE. DO NOT MODIFY. - * - * This class was automatically generated by the - * aapt tool from the resource data it found. It - * should not be modified by hand. - */ - -package com.faizmalkani.floatingactionbutton.test; - -public final class R { - public static final class attr { - /**

Must be a color value, in the form of "#rgb", "#argb", -"#rrggbb", or "#aarrggbb". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - */ - public static final int colour=0x7f010001; - /**

Must be an integer value, such as "100". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - */ - public static final int drawable=0x7f010000; - /**

Must be an integer value, such as "100". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - */ - public static final int shadowColor=0x7f010005; - /**

Must be a floating point value, such as "1.2". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - */ - public static final int shadowDx=0x7f010003; - /**

Must be a floating point value, such as "1.2". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - */ - public static final int shadowDy=0x7f010004; - /**

Must be a floating point value, such as "1.2". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - */ - public static final int shadowRadius=0x7f010002; - } - public static final class styleable { - /** Attributes that can be used with a FloatingActionButton. -

Includes the following attributes:

- - - - - - - - - - -
AttributeDescription
{@link #FloatingActionButton_colour com.faizmalkani.floatingactionbutton.test:colour}
{@link #FloatingActionButton_drawable com.faizmalkani.floatingactionbutton.test:drawable}
{@link #FloatingActionButton_shadowColor com.faizmalkani.floatingactionbutton.test:shadowColor}
{@link #FloatingActionButton_shadowDx com.faizmalkani.floatingactionbutton.test:shadowDx}
{@link #FloatingActionButton_shadowDy com.faizmalkani.floatingactionbutton.test:shadowDy}
{@link #FloatingActionButton_shadowRadius com.faizmalkani.floatingactionbutton.test:shadowRadius}
- @see #FloatingActionButton_colour - @see #FloatingActionButton_drawable - @see #FloatingActionButton_shadowColor - @see #FloatingActionButton_shadowDx - @see #FloatingActionButton_shadowDy - @see #FloatingActionButton_shadowRadius - */ - public static final int[] FloatingActionButton = { - 0x7f010000, 0x7f010001, 0x7f010002, 0x7f010003, - 0x7f010004, 0x7f010005 - }; - /** -

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.test.R.attr#colour} - attribute's value can be found in the {@link #FloatingActionButton} array. - - -

Must be a color value, in the form of "#rgb", "#argb", -"#rrggbb", or "#aarrggbb". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - @attr name com.faizmalkani.floatingactionbutton.test:colour - */ - public static final int FloatingActionButton_colour = 1; - /** -

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.test.R.attr#drawable} - attribute's value can be found in the {@link #FloatingActionButton} array. - - -

Must be an integer value, such as "100". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - @attr name com.faizmalkani.floatingactionbutton.test:drawable - */ - public static final int FloatingActionButton_drawable = 0; - /** -

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.test.R.attr#shadowColor} - attribute's value can be found in the {@link #FloatingActionButton} array. - - -

Must be an integer value, such as "100". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - @attr name com.faizmalkani.floatingactionbutton.test:shadowColor - */ - public static final int FloatingActionButton_shadowColor = 5; - /** -

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.test.R.attr#shadowDx} - attribute's value can be found in the {@link #FloatingActionButton} array. - - -

Must be a floating point value, such as "1.2". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - @attr name com.faizmalkani.floatingactionbutton.test:shadowDx - */ - public static final int FloatingActionButton_shadowDx = 3; - /** -

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.test.R.attr#shadowDy} - attribute's value can be found in the {@link #FloatingActionButton} array. - - -

Must be a floating point value, such as "1.2". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - @attr name com.faizmalkani.floatingactionbutton.test:shadowDy - */ - public static final int FloatingActionButton_shadowDy = 4; - /** -

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.test.R.attr#shadowRadius} - attribute's value can be found in the {@link #FloatingActionButton} array. - - -

Must be a floating point value, such as "1.2". -

This may also be a reference to a resource (in the form -"@[package:]type:name") or -theme attribute (in the form -"?[package:][type:]name") -containing a value of this type. - @attr name com.faizmalkani.floatingactionbutton.test:shadowRadius - */ - public static final int FloatingActionButton_shadowRadius = 2; - }; -} +/* AUTO-GENERATED FILE. DO NOT MODIFY. + * + * This class was automatically generated by the + * aapt tool from the resource data it found. It + * should not be modified by hand. + */ + +package com.faizmalkani.floatingactionbutton.test; + +public final class R { + public static final class attr { + /**

Must be a color value, in the form of "#rgb", "#argb", +"#rrggbb", or "#aarrggbb". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + */ + public static final int colour=0x7f010001; + /**

Must be an integer value, such as "100". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + */ + public static final int drawable=0x7f010000; + /**

Must be an integer value, such as "100". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + */ + public static final int shadowColor=0x7f010005; + /**

Must be a floating point value, such as "1.2". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + */ + public static final int shadowDx=0x7f010003; + /**

Must be a floating point value, such as "1.2". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + */ + public static final int shadowDy=0x7f010004; + /**

Must be a floating point value, such as "1.2". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + */ + public static final int shadowRadius=0x7f010002; + } + public static final class styleable { + /** Attributes that can be used with a FloatingActionButton. +

Includes the following attributes:

+ + + + + + + + + + +
AttributeDescription
{@link #FloatingActionButton_colour com.faizmalkani.floatingactionbutton.test:colour}
{@link #FloatingActionButton_drawable com.faizmalkani.floatingactionbutton.test:drawable}
{@link #FloatingActionButton_shadowColor com.faizmalkani.floatingactionbutton.test:shadowColor}
{@link #FloatingActionButton_shadowDx com.faizmalkani.floatingactionbutton.test:shadowDx}
{@link #FloatingActionButton_shadowDy com.faizmalkani.floatingactionbutton.test:shadowDy}
{@link #FloatingActionButton_shadowRadius com.faizmalkani.floatingactionbutton.test:shadowRadius}
+ @see #FloatingActionButton_colour + @see #FloatingActionButton_drawable + @see #FloatingActionButton_shadowColor + @see #FloatingActionButton_shadowDx + @see #FloatingActionButton_shadowDy + @see #FloatingActionButton_shadowRadius + */ + public static final int[] FloatingActionButton = { + 0x7f010000, 0x7f010001, 0x7f010002, 0x7f010003, + 0x7f010004, 0x7f010005 + }; + /** +

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.test.R.attr#colour} + attribute's value can be found in the {@link #FloatingActionButton} array. + + +

Must be a color value, in the form of "#rgb", "#argb", +"#rrggbb", or "#aarrggbb". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + @attr name com.faizmalkani.floatingactionbutton.test:colour + */ + public static final int FloatingActionButton_colour = 1; + /** +

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.test.R.attr#drawable} + attribute's value can be found in the {@link #FloatingActionButton} array. + + +

Must be an integer value, such as "100". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + @attr name com.faizmalkani.floatingactionbutton.test:drawable + */ + public static final int FloatingActionButton_drawable = 0; + /** +

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.test.R.attr#shadowColor} + attribute's value can be found in the {@link #FloatingActionButton} array. + + +

Must be an integer value, such as "100". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + @attr name com.faizmalkani.floatingactionbutton.test:shadowColor + */ + public static final int FloatingActionButton_shadowColor = 5; + /** +

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.test.R.attr#shadowDx} + attribute's value can be found in the {@link #FloatingActionButton} array. + + +

Must be a floating point value, such as "1.2". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + @attr name com.faizmalkani.floatingactionbutton.test:shadowDx + */ + public static final int FloatingActionButton_shadowDx = 3; + /** +

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.test.R.attr#shadowDy} + attribute's value can be found in the {@link #FloatingActionButton} array. + + +

Must be a floating point value, such as "1.2". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + @attr name com.faizmalkani.floatingactionbutton.test:shadowDy + */ + public static final int FloatingActionButton_shadowDy = 4; + /** +

This symbol is the offset where the {@link com.faizmalkani.floatingactionbutton.test.R.attr#shadowRadius} + attribute's value can be found in the {@link #FloatingActionButton} array. + + +

Must be a floating point value, such as "1.2". +

This may also be a reference to a resource (in the form +"@[package:]type:name") or +theme attribute (in the form +"?[package:][type:]name") +containing a value of this type. + @attr name com.faizmalkani.floatingactionbutton.test:shadowRadius + */ + public static final int FloatingActionButton_shadowRadius = 2; + }; +} diff --git a/libraries/FloatingActionButton/build/intermediates/bundles/debug/AndroidManifest.xml b/libraries/FloatingActionButton/build/intermediates/bundles/debug/AndroidManifest.xml index 28dac35..77c4652 100644 --- a/libraries/FloatingActionButton/build/intermediates/bundles/debug/AndroidManifest.xml +++ b/libraries/FloatingActionButton/build/intermediates/bundles/debug/AndroidManifest.xml @@ -1,14 +1,14 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/libraries/FloatingActionButton/build/intermediates/bundles/debug/R.txt b/libraries/FloatingActionButton/build/intermediates/bundles/debug/R.txt index a11b389..41bb8b7 100644 --- a/libraries/FloatingActionButton/build/intermediates/bundles/debug/R.txt +++ b/libraries/FloatingActionButton/build/intermediates/bundles/debug/R.txt @@ -1,13 +1,13 @@ -int attr colour 0x7f010001 -int attr drawable 0x7f010000 -int attr shadowColor 0x7f010005 -int attr shadowDx 0x7f010003 -int attr shadowDy 0x7f010004 -int attr shadowRadius 0x7f010002 -int[] styleable FloatingActionButton { 0x7f010000, 0x7f010001, 0x7f010002, 0x7f010003, 0x7f010004, 0x7f010005 } -int styleable FloatingActionButton_colour 1 -int styleable FloatingActionButton_drawable 0 -int styleable FloatingActionButton_shadowColor 5 -int styleable FloatingActionButton_shadowDx 3 -int styleable FloatingActionButton_shadowDy 4 -int styleable FloatingActionButton_shadowRadius 2 +int attr colour 0x7f010001 +int attr drawable 0x7f010000 +int attr shadowColor 0x7f010005 +int attr shadowDx 0x7f010003 +int attr shadowDy 0x7f010004 +int attr shadowRadius 0x7f010002 +int[] styleable FloatingActionButton { 0x7f010000, 0x7f010001, 0x7f010002, 0x7f010003, 0x7f010004, 0x7f010005 } +int styleable FloatingActionButton_colour 1 +int styleable FloatingActionButton_drawable 0 +int styleable FloatingActionButton_shadowColor 5 +int styleable FloatingActionButton_shadowDx 3 +int styleable FloatingActionButton_shadowDy 4 +int styleable FloatingActionButton_shadowRadius 2 diff --git a/libraries/FloatingActionButton/build/intermediates/bundles/debug/classes.jar b/libraries/FloatingActionButton/build/intermediates/bundles/debug/classes.jar index 34fee555243379b97ce2642f14dad54b2c6344b5..c028b84b58682ce90e251a5fb4c5a8181b9fdc39 100644 GIT binary patch delta 140 zcmeyU`%#xSz?+$ci-CcIgJF7L(nMZ0W)NlO1*V!6!PIhHFm>GtL~WK~Okx5v8kxgc xz>M4M{X8JX?9!x(I(FfBKqSjzKOf&#YuM|xL)4XE2V7gYU1OQgdE+_y1 delta 140 zcmeyU`%#xSz?+$ci-CcIgJH^yu!+2C%pl6li#ZxdH7kOt<+@<%x)X@nEW?<@1ZFfc zhqHhgx7qu7K-wnfikN`uB_j49iOJ7I^ue@*s5O`l6Ab{ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/libraries/FloatingActionButton/build/intermediates/bundles/release/R.txt b/libraries/FloatingActionButton/build/intermediates/bundles/release/R.txt index a11b389..41bb8b7 100644 --- a/libraries/FloatingActionButton/build/intermediates/bundles/release/R.txt +++ b/libraries/FloatingActionButton/build/intermediates/bundles/release/R.txt @@ -1,13 +1,13 @@ -int attr colour 0x7f010001 -int attr drawable 0x7f010000 -int attr shadowColor 0x7f010005 -int attr shadowDx 0x7f010003 -int attr shadowDy 0x7f010004 -int attr shadowRadius 0x7f010002 -int[] styleable FloatingActionButton { 0x7f010000, 0x7f010001, 0x7f010002, 0x7f010003, 0x7f010004, 0x7f010005 } -int styleable FloatingActionButton_colour 1 -int styleable FloatingActionButton_drawable 0 -int styleable FloatingActionButton_shadowColor 5 -int styleable FloatingActionButton_shadowDx 3 -int styleable FloatingActionButton_shadowDy 4 -int styleable FloatingActionButton_shadowRadius 2 +int attr colour 0x7f010001 +int attr drawable 0x7f010000 +int attr shadowColor 0x7f010005 +int attr shadowDx 0x7f010003 +int attr shadowDy 0x7f010004 +int attr shadowRadius 0x7f010002 +int[] styleable FloatingActionButton { 0x7f010000, 0x7f010001, 0x7f010002, 0x7f010003, 0x7f010004, 0x7f010005 } +int styleable FloatingActionButton_colour 1 +int styleable FloatingActionButton_drawable 0 +int styleable FloatingActionButton_shadowColor 5 +int styleable FloatingActionButton_shadowDx 3 +int styleable FloatingActionButton_shadowDy 4 +int styleable FloatingActionButton_shadowRadius 2 diff --git a/libraries/FloatingActionButton/build/intermediates/bundles/release/classes.jar b/libraries/FloatingActionButton/build/intermediates/bundles/release/classes.jar index 1056848e62136173f3c89c7deda43971424c21a8..364809322d6ad2c259b125411327955e7cb23780 100644 GIT binary patch delta 140 zcmZ3fyHb}oz?+$ci-CcIgJF7L(nMZ0W)NlO1*V!6!PIhHFm>GtL~WK~lwtxiESbYu xz>LZ4{X8JXWK|IpFdZXe52ojd=!5ACBGzD7$~#VA@oy1OTu(EPen0 delta 140 zcmZ3fyHb}oz?+$ci-CcIgCTlG*hF46W)NlO1*V!6!PIhHFm>GtL~WK~lwtxiESbYu xz>LZ4{X8JXWK|IpFdZXe52ojd=!5ACBGzD7$~#VA@oy1OTZMEN%b* diff --git a/libraries/FloatingActionButton/build/intermediates/manifests/test/debug/AndroidManifest.xml b/libraries/FloatingActionButton/build/intermediates/manifests/test/debug/AndroidManifest.xml index fb4c5e3..d057938 100644 --- a/libraries/FloatingActionButton/build/intermediates/manifests/test/debug/AndroidManifest.xml +++ b/libraries/FloatingActionButton/build/intermediates/manifests/test/debug/AndroidManifest.xml @@ -1,20 +1,20 @@ - - - - - - - - - - + + + + + + + + + + \ No newline at end of file diff --git a/libraries/FloatingActionButton/build/intermediates/manifests/tmp/manifestMerger1672337899454975879.xml b/libraries/FloatingActionButton/build/intermediates/manifests/tmp/manifestMerger7569492401838877170.xml similarity index 100% rename from libraries/FloatingActionButton/build/intermediates/manifests/tmp/manifestMerger1672337899454975879.xml rename to libraries/FloatingActionButton/build/intermediates/manifests/tmp/manifestMerger7569492401838877170.xml diff --git a/libraries/FloatingActionButton/build/intermediates/symbols/test/debug/R.txt b/libraries/FloatingActionButton/build/intermediates/symbols/test/debug/R.txt index a11b389..41bb8b7 100644 --- a/libraries/FloatingActionButton/build/intermediates/symbols/test/debug/R.txt +++ b/libraries/FloatingActionButton/build/intermediates/symbols/test/debug/R.txt @@ -1,13 +1,13 @@ -int attr colour 0x7f010001 -int attr drawable 0x7f010000 -int attr shadowColor 0x7f010005 -int attr shadowDx 0x7f010003 -int attr shadowDy 0x7f010004 -int attr shadowRadius 0x7f010002 -int[] styleable FloatingActionButton { 0x7f010000, 0x7f010001, 0x7f010002, 0x7f010003, 0x7f010004, 0x7f010005 } -int styleable FloatingActionButton_colour 1 -int styleable FloatingActionButton_drawable 0 -int styleable FloatingActionButton_shadowColor 5 -int styleable FloatingActionButton_shadowDx 3 -int styleable FloatingActionButton_shadowDy 4 -int styleable FloatingActionButton_shadowRadius 2 +int attr colour 0x7f010001 +int attr drawable 0x7f010000 +int attr shadowColor 0x7f010005 +int attr shadowDx 0x7f010003 +int attr shadowDy 0x7f010004 +int attr shadowRadius 0x7f010002 +int[] styleable FloatingActionButton { 0x7f010000, 0x7f010001, 0x7f010002, 0x7f010003, 0x7f010004, 0x7f010005 } +int styleable FloatingActionButton_colour 1 +int styleable FloatingActionButton_drawable 0 +int styleable FloatingActionButton_shadowColor 5 +int styleable FloatingActionButton_shadowDx 3 +int styleable FloatingActionButton_shadowDy 4 +int styleable FloatingActionButton_shadowRadius 2 diff --git a/libraries/FloatingActionButton/build/outputs/aar/FloatingActionButton-debug.aar b/libraries/FloatingActionButton/build/outputs/aar/FloatingActionButton-debug.aar index b42693cc0a75771622d47676c20be47a1825ec73..61d4432d7071a754b28fc2225b9dfa8d54094e59 100644 GIT binary patch delta 2262 zcmZ9Oc{J1u8^?#S3xzSmIEkUe$TIfHGFN2p3K3?JDk2JOInC=Vc9&V&CPL&{E6`o;_Mueu%nP*&{ZcT5w zh!06`x#5cVRwELQVqTueo`5x{99lq@7Zhu&cGX&STpj~mK$fr_b`&N-v@>i2kNSCkQo? z^gXumg&uIDw~S+ngqI${hJ{L7*@p#c0_4ZH-nGVJL&c?4t6QIWi6Ht{%5hO9wn&@h zrSV}EgG0~u;A!t*D?-rwkAI{r^@>i=skxF@DlZ!3lvb;va(l9^KV{;g@h8ul6}Mz& zXw*}1Gk(*ZJw&u!A=8Ma)&o)5FIgkgIK^`!tpLf7ycLtNUhQ99@m4VNdI9$-E35+oCfSPtscNO3Vghib?q*fcL9!Js2R_`)SmRe7?cpurqCESf_vF{W>Jfz;Jx zThhyZ!Avu7rvNM<20cACxUwU0QPl}^*>~y^6^9gYJ^eUf<4O5~p!J(a-&yzL_aMf( zOMs@O7Sd69+ba?~`C9TEKUu8Sv%l_|vQZEuhL9?K7>>pA&`y5w){0#-Nb zyo|bD@eg2(w00;^ago%ek97?KlUCv}mA2j)QWKHW+>!I8b+uYfgrO(NrlrD1^iHV= z(+^JC1u0=j^#0o*IfMQ)WJv#FcyJDe0vzR*Djx-xu+yzEu{|}SL-2c@RWs22G+I7n zPeuhwZRbvQ+AxiO>Qc~bNpswl@Y$Y{a^GH+dABLl%x#Hu{1EbGE$9n(*j*1&)GmuX z(>`mn+b8E2-D7c6dD+MWI6VBYs#VGV^QV`L+jz2_=JTU1KDFR#JqhPb^MT@8pg4)) zSao5gFCa*CembT&|P(*n@L+Ss#^DJ}3o}UWIm(|{EgSQ`nP|4tD$mI2R0vuwO z!V&6)vizRq(3Ii@KlE~giUt&8BvGk@yr>nl^2p609*jme-}Wiuw9cMOQ|Vmq;L!L) zdPLkjmwm!FdwWyMC@C+FZ_N~laBDt0HU*ik;SE&qD;dHP7GB|V+^HQQu6Mq>pTDRKpc@EA?HVuPUR7I=KplQ~*IRPl{SA(6xAN@%X5udJfzU zaSewwNGsj22oAN))=s%PRN=xHHPKe_bFls$xg8)iMB3W;j52gbQr6Y3;8xA^l5QFHM^^_<&>dtuv3fc}rSAuuhF1$K`N^V46ZY0BP4N&rbq1OQ!*nTEy1Ua* zq9kz+T)re`@(Z6v%^|8Uy;=VD>DQ)%c0x31X7mrC#(4gcGwVX?J{1z$gJltpVZvR$ z8Dp7c3*I!qKwjX%C0VXl8#8!bt&f?JLu#z3$K9A*Q9fQGYRJpF-1Nmy+dK z&HduxP$r$b4e>hA5ZXz6*$xUli={f?8C7CG9F+1zN~=p#+|+`I^mmb?J0?R&F^wuF zz-d_gy1Q3iV&~^e(TRIa4(ki!wgK#|uC2)w^lXoV9yuor;V>r{?0F(!-M8z+bh5*N z)BMeQ8mT0{BT?px>Y@Vva7;^%7)$Cbwi}x*kV`Ft?Wh>-tv!1m3!sb4#M<`a%Fe}Y z(43kw246JoQ{c`fEdC}<4i@99rHNu^2eroHA7rOS6%06b+SoP3;XvXd<2!@rd3GD+ z6DC)TN@voBnx(1|IABaHg{=MSRj)Zua{~;T{L%2>zGU>)Wr4N-R60&|0tO`c1qM~? zC_`Z+@6k=bG4Y7-aOVo7l;NDQ_r|+c$C{5ZrBAczaqRB~ZZeNKvzvL(~ zki`%ipp;{qZnrPC&NU3iI>6POxN(;uzxOD lfx%VPpdgTQV4!=@?}En%|0c-#o1g<;R~W_?b-W4!{TEraF0lXr delta 2242 zcmZXWXH?S*62(JMiqat@80iQ+kPe}Uut-siLTJ)Ss8K=;B@`*|{~jn!dI;T6R=PAn z1nDh^qEu;0ldgcGhR|7-w`X_H?)@;oGiT17`FQ7(@{BS&3dwlp903}EZ(Hy826}G1C^Om6YT7op=hd>Rw?q$0CW zGf`=Hu^GaVnhYi9~3q+;)Asd#L8^ z&H8xB)6p^H6}3b-e&W6TyM&u$lRz65^RP_UCc$RXqH$VpvLdYL$BZQ$68~LtBEbo>tWj^dI3B^=#IY$pA zWgdK@XF$*U@3t2jV>xt*n9mEH;`_UT=!d;bmsg`JvRSl~3|;#f&F;5u(Q*nr$%-u) zv{~D=QVYNlTNbtSc)68bp*q%Q+VfoYy-iGa0-p&RzDQ(F{+++=17vQy;N{1>e5}$B z4lv=dUlDR^4VImn(xuF~dtwIS=3zUC!0>FDZWKSV;<+1qFoa=Hn>JR@e9P+9R`PFZ zU2W41-HD~peknfRM0e~gxw5B3K%%ig;K@LON;ELlrb4F9a?tWxWL8m)Y5Ku`e;>5H z?}APdthUa3W?40h(+ z>I(?mn&&RRF?9QvO2LuUWjZE%N%v?9n_mA>f8Y|8U>r zC@?mdb)q47g9aY{)r`kpeJQyjCT(o69rdU4m$*JI(bUx0H0I1v#BOD0N^2&j)Uq}2++W9s z-g0ydjdee-0ea7Ob#he9+&{N1b}CYV=|=sH^#aTKYFaTb6YBxN;6tB}q7I+*7u9n^ z!sPnUKN?)(s>Gm%k+N?%IB_G)<~OzOtfM7q{NR11@B6Zh>8@>}_MC2b-aG^l6ynD8 zbx++5pY z?kGMa+o@_^H^zI|Yt$bJKoOZc1L{z&ud(d*HnqMXS-IV^_Vr8hIoX=FZ?tu*URizZ zrZf6&T%D!VYCC%t1yzG_7v=!_&ov#v?Qp8wH$}W0x&iG-s8j20bX(xN%gII^Miuky z)6Ns9R$7m&aw0mUr zC1d8UMM5vdDAhzacbLjgqxwEx^b0h2s&QK}m>{TAwAfekIv>L9PXpLO*(b*471z_2 zKjbwh{El*Wjy_KC^)y{=&`2s^1L5E^T~(2j(;`yr4I+))juF-aY?P}-=qmVPC%5=& zhEe^qxUk4B^)sSesS?pmd!ohlKxF0*de6Is)HU0;;ev*k-f0(WGcd$p28@-v)4h`U z;Tuk2A)`Y1V+ze79++NrFG##=m`^+k8P3N}@SygJFihNtRBqC zZhy1yD|10vHTHf^+j}Oc+>BDz;@R(a@j(hjDV!9M^?TNKak^w@>GXZpblC>1(tdOT5;+Lo0JIDonLj>obN ztQM>lzHqW*`;R%XU#wfu>Z7{9sNTl|`(;@?i1z?#{_vm~aLVZ*RQyE$C(Id7(wt-n z=|3eOzvwA7_@z(j%0F{T1E|C)asOfpROi3be+sH_I%b2xVOkIn$XuQnL_{GOz9-UZ0pnm{r%pZFI diff --git a/libraries/FloatingActionButton/build/outputs/aar/FloatingActionButton-release.aar b/libraries/FloatingActionButton/build/outputs/aar/FloatingActionButton-release.aar index 6c0433bae4b4d34b29fec9286b5fbf5a5ce27ad5..7f50b239950a56a592db4340f19bec2be7b925b6 100644 GIT binary patch delta 5052 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 delta 5044 zcmZXYbyyVKwufg3rBfJW=o0BJ2?5}e{mX0B%q#Nlv zeBV9yJkPyn?LXdU@88JO#HpFycQVBv14#U9= z4gho#*nf*W>MTd_-Pwo8n5a<{vS*YMl67>C+>)0s<4yrPi2)15`8Uj3YTCxMIe3A1 zut1IP-DbhU*jUe8?C}npX~8ZNAQY+Tg2}J0s{%B=p__kQOwmaz=^d&36}Wp5Z~ed# z8(!%ic*}}9ky+YXE`4Lh2YSp2=1ne&s;*5+DjH~|FeDQA?JF4+5m<4Z9e44I8_AB* zfB=-B27W`AgBD>l8z>gNwe6YGnuc}s9|hK*T5Az;K6vqQzLkYOmUOvnb$zml8Rjie z31L|#T^*iHZ!qP#hWRa9r3-(6Ay(wsN;UxG!Gst@6(S}kD(#XZWvXo zugT_SCi1SnB!Va{rk@~HB3pvdf8FvRTys=j+}?s4-?IwLFzZ|j?hnO!qWANF?x^IE{mcnhu6!0vhxI4428S`7H&!{IuNw}< zqnfE_3j(5En$3sdBH)YO`oRWCK9H{3cB+q=nseWxBl^~Dr~Lk}&uF{G#p+cARcd(y zD@;bnhWG4nRQ2`p*jcz=QsuZ3q&V0Mzj+B|dCz%M&%WI0c45vCWgHwKpFvFbxKUpTDDcMFV`vT3ujWe$zs9n`$l!NIn>3-dYgYo)W; z2Z`^aP*Lj41QGA9N6He2h zXCrEi8_n+Oywb|DoM&qHG?=*qZUzgt5SV|aE-9G(wFz zxd@@tiX*6whJ?*$Z79KNO8{R%I)PN)}0Rqpz10+et!sKM0eQSi(?m+suj& z@Q&p%1XbjPN-T+6FR?iguuS2sP+b%LzdQpwb;nvhZ(s-SBELT7M99LvC=JT4d}^t{ z<$h1kxj-Qn{;0!b%l-!)X%&SM2F1m`hcB

iJlBU9Vo8^yCTgj+Gl-fP|D5F2hMG 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 ppw#}BX`cT*d!7nR4+1^J_BM_Wi(x3o - - - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Mount.java b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Mount.java index 9062d54..d3277dd 100644 --- a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Mount.java +++ b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Mount.java @@ -1,43 +1,43 @@ - - -package org.sufficientlysecure.rootcommands; - -import java.io.File; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -public class Mount { - protected final File mDevice; - protected final File mMountPoint; - protected final String mType; - protected final Set mFlags; - - Mount(File device, File path, String type, String flagsStr) { - mDevice = device; - mMountPoint = path; - mType = type; - mFlags = new HashSet(Arrays.asList(flagsStr.split(","))); - } - - public File getDevice() { - return mDevice; - } - - public File getMountPoint() { - return mMountPoint; - } - - public String getType() { - return mType; - } - - public Set getFlags() { - return mFlags; - } - - @Override - public String toString() { - return String.format("%s on %s type %s %s", mDevice, mMountPoint, mType, mFlags); - } -} + + +package org.sufficientlysecure.rootcommands; + +import java.io.File; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class Mount { + protected final File mDevice; + protected final File mMountPoint; + protected final String mType; + protected final Set mFlags; + + Mount(File device, File path, String type, String flagsStr) { + mDevice = device; + mMountPoint = path; + mType = type; + mFlags = new HashSet(Arrays.asList(flagsStr.split(","))); + } + + public File getDevice() { + return mDevice; + } + + public File getMountPoint() { + return mMountPoint; + } + + public String getType() { + return mType; + } + + public Set getFlags() { + return mFlags; + } + + @Override + public String toString() { + return String.format("%s on %s type %s %s", mDevice, mMountPoint, mType, mFlags); + } +} diff --git a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Remounter.java b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Remounter.java index 2ec1cbf..4ffa893 100644 --- a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Remounter.java +++ b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Remounter.java @@ -1,176 +1,176 @@ - - -package org.sufficientlysecure.rootcommands; - -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.io.LineNumberReader; -import java.util.ArrayList; -import java.util.Locale; - -import org.sufficientlysecure.rootcommands.command.SimpleCommand; -import org.sufficientlysecure.rootcommands.util.Log; - -//no modifier, this means it is package-private. Only our internal classes can use this. -class Remounter { - - private Shell shell; - - public Remounter(Shell shell) { - super(); - this.shell = shell; - } - - /** - * This will take a path, which can contain the file name as well, and attempt to remount the - * underlying partition. - *

- * For example, passing in the following string: - * "/system/bin/some/directory/that/really/would/never/exist" will result in /system ultimately - * being remounted. However, keep in mind that the longer the path you supply, the more work - * this has to do, and the slower it will run. - * - * @param file - * file path - * @param mountType - * mount type: pass in RO (Read only) or RW (Read Write) - * @return a boolean which indicates whether or not the partition has been - * remounted as specified. - */ - protected boolean remount(String file, String mountType) { - - // if the path has a trailing slash get rid of it. - if (file.endsWith("/") && !file.equals("/")) { - file = file.substring(0, file.lastIndexOf("/")); - } - // Make sure that what we are trying to remount is in the mount list. - boolean foundMount = false; - while (!foundMount) { - try { - for (Mount mount : getMounts()) { - Log.d(RootCommands.TAG, mount.getMountPoint().toString()); - - if (file.equals(mount.getMountPoint().toString())) { - foundMount = true; - break; - } - } - } catch (Exception e) { - Log.e(RootCommands.TAG, "Exception", e); - return false; - } - if (!foundMount) { - try { - file = (new File(file).getParent()).toString(); - } catch (Exception e) { - Log.e(RootCommands.TAG, "Exception", e); - return false; - } - } - } - Mount mountPoint = findMountPointRecursive(file); - - Log.d(RootCommands.TAG, "Remounting " + mountPoint.getMountPoint().getAbsolutePath() - + " as " + mountType.toLowerCase(Locale.US)); - final boolean isMountMode = mountPoint.getFlags().contains(mountType.toLowerCase(Locale.US)); - - if (!isMountMode) { - // grab an instance of the internal class - try { - SimpleCommand command = new SimpleCommand("busybox mount -o remount," - + mountType.toLowerCase(Locale.US) + " " + mountPoint.getDevice().getAbsolutePath() - + " " + mountPoint.getMountPoint().getAbsolutePath(), - "toolbox mount -o remount," + mountType.toLowerCase(Locale.US) + " " - + mountPoint.getDevice().getAbsolutePath() + " " - + mountPoint.getMountPoint().getAbsolutePath(), "mount -o remount," - + mountType.toLowerCase(Locale.US) + " " - + mountPoint.getDevice().getAbsolutePath() + " " - + mountPoint.getMountPoint().getAbsolutePath(), - "/system/bin/toolbox mount -o remount," + mountType.toLowerCase(Locale.US) + " " - + mountPoint.getDevice().getAbsolutePath() + " " - + mountPoint.getMountPoint().getAbsolutePath()); - - // execute on shell - shell.add(command).waitForFinish(); - - } catch (Exception e) { - } - - mountPoint = findMountPointRecursive(file); - } - - if (mountPoint != null) { - Log.d(RootCommands.TAG, mountPoint.getFlags() + " AND " + mountType.toLowerCase(Locale.US)); - if (mountPoint.getFlags().contains(mountType.toLowerCase(Locale.US))) { - Log.d(RootCommands.TAG, mountPoint.getFlags().toString()); - return true; - } else { - Log.d(RootCommands.TAG, mountPoint.getFlags().toString()); - } - } else { - Log.d(RootCommands.TAG, "mountPoint is null"); - } - return false; - } - - private Mount findMountPointRecursive(String file) { - try { - ArrayList mounts = getMounts(); - for (File path = new File(file); path != null;) { - for (Mount mount : mounts) { - if (mount.getMountPoint().equals(path)) { - return mount; - } - } - } - return null; - } catch (IOException e) { - throw new RuntimeException(e); - } catch (Exception e) { - Log.e(RootCommands.TAG, "Exception", e); - } - return null; - } - - /** - * This will return an ArrayList of the class Mount. The class mount contains the following - * property's: device mountPoint type flags - *

- * These will provide you with any information you need to work with the mount points. - * - * @return ArrayList an ArrayList of the class Mount. - * @throws Exception - * if we cannot return the mount points. - */ - protected static ArrayList getMounts() throws Exception { - - final String tempFile = "/data/local/RootToolsMounts"; - - // copy /proc/mounts to tempfile. Directly reading it does not work on 4.3 - Shell shell = Shell.startRootShell(); - Toolbox tb = new Toolbox(shell); - tb.copyFile("/proc/mounts", tempFile, false, false); - tb.setFilePermissions(tempFile, "777"); - shell.close(); - - LineNumberReader lnr = null; - lnr = new LineNumberReader(new FileReader(tempFile)); - String line; - ArrayList mounts = new ArrayList(); - while ((line = lnr.readLine()) != null) { - - Log.d(RootCommands.TAG, line); - - String[] fields = line.split(" "); - mounts.add(new Mount(new File(fields[0]), // device - new File(fields[1]), // mountPoint - fields[2], // fstype - fields[3] // flags - )); - } - lnr.close(); - - return mounts; - } -} + + +package org.sufficientlysecure.rootcommands; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.util.ArrayList; +import java.util.Locale; + +import org.sufficientlysecure.rootcommands.command.SimpleCommand; +import org.sufficientlysecure.rootcommands.util.Log; + +//no modifier, this means it is package-private. Only our internal classes can use this. +class Remounter { + + private Shell shell; + + public Remounter(Shell shell) { + super(); + this.shell = shell; + } + + /** + * This will take a path, which can contain the file name as well, and attempt to remount the + * underlying partition. + *

+ * For example, passing in the following string: + * "/system/bin/some/directory/that/really/would/never/exist" will result in /system ultimately + * being remounted. However, keep in mind that the longer the path you supply, the more work + * this has to do, and the slower it will run. + * + * @param file + * file path + * @param mountType + * mount type: pass in RO (Read only) or RW (Read Write) + * @return a boolean which indicates whether or not the partition has been + * remounted as specified. + */ + protected boolean remount(String file, String mountType) { + + // if the path has a trailing slash get rid of it. + if (file.endsWith("/") && !file.equals("/")) { + file = file.substring(0, file.lastIndexOf("/")); + } + // Make sure that what we are trying to remount is in the mount list. + boolean foundMount = false; + while (!foundMount) { + try { + for (Mount mount : getMounts()) { + Log.d(RootCommands.TAG, mount.getMountPoint().toString()); + + if (file.equals(mount.getMountPoint().toString())) { + foundMount = true; + break; + } + } + } catch (Exception e) { + Log.e(RootCommands.TAG, "Exception", e); + return false; + } + if (!foundMount) { + try { + file = (new File(file).getParent()).toString(); + } catch (Exception e) { + Log.e(RootCommands.TAG, "Exception", e); + return false; + } + } + } + Mount mountPoint = findMountPointRecursive(file); + + Log.d(RootCommands.TAG, "Remounting " + mountPoint.getMountPoint().getAbsolutePath() + + " as " + mountType.toLowerCase(Locale.US)); + final boolean isMountMode = mountPoint.getFlags().contains(mountType.toLowerCase(Locale.US)); + + if (!isMountMode) { + // grab an instance of the internal class + try { + SimpleCommand command = new SimpleCommand("busybox mount -o remount," + + mountType.toLowerCase(Locale.US) + " " + mountPoint.getDevice().getAbsolutePath() + + " " + mountPoint.getMountPoint().getAbsolutePath(), + "toolbox mount -o remount," + mountType.toLowerCase(Locale.US) + " " + + mountPoint.getDevice().getAbsolutePath() + " " + + mountPoint.getMountPoint().getAbsolutePath(), "mount -o remount," + + mountType.toLowerCase(Locale.US) + " " + + mountPoint.getDevice().getAbsolutePath() + " " + + mountPoint.getMountPoint().getAbsolutePath(), + "/system/bin/toolbox mount -o remount," + mountType.toLowerCase(Locale.US) + " " + + mountPoint.getDevice().getAbsolutePath() + " " + + mountPoint.getMountPoint().getAbsolutePath()); + + // execute on shell + shell.add(command).waitForFinish(); + + } catch (Exception e) { + } + + mountPoint = findMountPointRecursive(file); + } + + if (mountPoint != null) { + Log.d(RootCommands.TAG, mountPoint.getFlags() + " AND " + mountType.toLowerCase(Locale.US)); + if (mountPoint.getFlags().contains(mountType.toLowerCase(Locale.US))) { + Log.d(RootCommands.TAG, mountPoint.getFlags().toString()); + return true; + } else { + Log.d(RootCommands.TAG, mountPoint.getFlags().toString()); + } + } else { + Log.d(RootCommands.TAG, "mountPoint is null"); + } + return false; + } + + private Mount findMountPointRecursive(String file) { + try { + ArrayList mounts = getMounts(); + for (File path = new File(file); path != null;) { + for (Mount mount : mounts) { + if (mount.getMountPoint().equals(path)) { + return mount; + } + } + } + return null; + } catch (IOException e) { + throw new RuntimeException(e); + } catch (Exception e) { + Log.e(RootCommands.TAG, "Exception", e); + } + return null; + } + + /** + * This will return an ArrayList of the class Mount. The class mount contains the following + * property's: device mountPoint type flags + *

+ * These will provide you with any information you need to work with the mount points. + * + * @return ArrayList an ArrayList of the class Mount. + * @throws Exception + * if we cannot return the mount points. + */ + protected static ArrayList getMounts() throws Exception { + + final String tempFile = "/data/local/RootToolsMounts"; + + // copy /proc/mounts to tempfile. Directly reading it does not work on 4.3 + Shell shell = Shell.startRootShell(); + Toolbox tb = new Toolbox(shell); + tb.copyFile("/proc/mounts", tempFile, false, false); + tb.setFilePermissions(tempFile, "777"); + shell.close(); + + LineNumberReader lnr = null; + lnr = new LineNumberReader(new FileReader(tempFile)); + String line; + ArrayList mounts = new ArrayList(); + while ((line = lnr.readLine()) != null) { + + Log.d(RootCommands.TAG, line); + + String[] fields = line.split(" "); + mounts.add(new Mount(new File(fields[0]), // device + new File(fields[1]), // mountPoint + fields[2], // fstype + fields[3] // flags + )); + } + lnr.close(); + + return mounts; + } +} diff --git a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/RootCommands.java b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/RootCommands.java index 50ebe18..4b12ae3 100644 --- a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/RootCommands.java +++ b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/RootCommands.java @@ -1,36 +1,36 @@ - - -package org.sufficientlysecure.rootcommands; - -import org.sufficientlysecure.rootcommands.util.Log; - -public class RootCommands { - public static boolean DEBUG = false; - public static int DEFAULT_TIMEOUT = 10000; - - public static final String TAG = "RootCommands"; - - /** - * General method to check if user has su binary and accepts root access for this program! - * - * @return true if everything worked - */ - public static boolean rootAccessGiven() { - boolean rootAccess = false; - - try { - Shell rootShell = Shell.startRootShell(); - - Toolbox tb = new Toolbox(rootShell); - if (tb.isRootAccessGiven()) { - rootAccess = true; - } - - rootShell.close(); - } catch (Exception e) { - Log.e(TAG, "Problem while checking for root access!", e); - } - - return rootAccess; - } -} + + +package org.sufficientlysecure.rootcommands; + +import org.sufficientlysecure.rootcommands.util.Log; + +public class RootCommands { + public static boolean DEBUG = false; + public static int DEFAULT_TIMEOUT = 10000; + + public static final String TAG = "RootCommands"; + + /** + * General method to check if user has su binary and accepts root access for this program! + * + * @return true if everything worked + */ + public static boolean rootAccessGiven() { + boolean rootAccess = false; + + try { + Shell rootShell = Shell.startRootShell(); + + Toolbox tb = new Toolbox(rootShell); + if (tb.isRootAccessGiven()) { + rootAccess = true; + } + + rootShell.close(); + } catch (Exception e) { + Log.e(TAG, "Problem while checking for root access!", e); + } + + return rootAccess; + } +} diff --git a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Shell.java b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Shell.java index 0b749ca..b5c8ab6 100644 --- a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Shell.java +++ b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Shell.java @@ -1,331 +1,331 @@ - - -package org.sufficientlysecure.rootcommands; - -import java.io.BufferedReader; -import java.io.Closeable; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.List; - -import org.sufficientlysecure.rootcommands.command.Command; -import org.sufficientlysecure.rootcommands.util.Log; -import org.sufficientlysecure.rootcommands.util.RootAccessDeniedException; -import org.sufficientlysecure.rootcommands.util.Utils; - -public class Shell implements Closeable { - private final Process shellProcess; - private final BufferedReader stdOutErr; - private final DataOutputStream outputStream; - private final List commands = new ArrayList(); - private boolean close = false; - - private static final String LD_LIBRARY_PATH = System.getenv("LD_LIBRARY_PATH"); - private static final String token = "F*D^W@#FGF"; - - /** - * Start root shell - * - * @param customEnv - * @param baseDirectory - * @return - * @throws java.io.IOException - */ - public static Shell startRootShell(ArrayList customEnv, String baseDirectory) - throws IOException, RootAccessDeniedException { - Log.d(RootCommands.TAG, "Starting Root Shell!"); - - // On some versions of Android (ICS) LD_LIBRARY_PATH is unset when using su - // We need to pass LD_LIBRARY_PATH over su for some commands to work correctly. - if (customEnv == null) { - customEnv = new ArrayList(); - } - customEnv.add("LD_LIBRARY_PATH=" + LD_LIBRARY_PATH); - - Shell shell = new Shell(Utils.getSuPath(), customEnv, baseDirectory); - - return shell; - } - - /** - * Start root shell without custom environment and base directory - * - * @return - * @throws java.io.IOException - */ - public static Shell startRootShell() throws IOException, RootAccessDeniedException { - return startRootShell(null, null); - } - - /** - * Start default sh shell - * - * @param customEnv - * @param baseDirectory - * @return - * @throws java.io.IOException - */ - public static Shell startShell(ArrayList customEnv, String baseDirectory) - throws IOException { - Log.d(RootCommands.TAG, "Starting Shell!"); - Shell shell = new Shell("sh", customEnv, baseDirectory); - return shell; - } - - /** - * Start default sh shell without custom environment and base directory - * - * @return - * @throws java.io.IOException - */ - public static Shell startShell() throws IOException { - return startShell(null, null); - } - - /** - * Start custom shell defined by shellPath - * - * @param shellPath - * @param customEnv - * @param baseDirectory - * @return - * @throws java.io.IOException - */ - public static Shell startCustomShell(String shellPath, ArrayList customEnv, - String baseDirectory) throws IOException { - Log.d(RootCommands.TAG, "Starting Custom Shell!"); - Shell shell = new Shell(shellPath, customEnv, baseDirectory); - - return shell; - } - - /** - * Start custom shell without custom environment and base directory - * - * @param shellPath - * @return - * @throws java.io.IOException - */ - public static Shell startCustomShell(String shellPath) throws IOException { - return startCustomShell(shellPath, null, null); - } - - private Shell(String shell, ArrayList customEnv, String baseDirectory) - throws IOException, RootAccessDeniedException { - Log.d(RootCommands.TAG, "Starting shell: " + shell); - - // start shell process! - shellProcess = Utils.runWithEnv(shell, customEnv, baseDirectory); - - // StdErr is redirected to StdOut, defined in Command.getCommand() - stdOutErr = new BufferedReader(new InputStreamReader(shellProcess.getInputStream())); - outputStream = new DataOutputStream(shellProcess.getOutputStream()); - - outputStream.write("echo Started\n".getBytes()); - outputStream.flush(); - - while (true) { - String line = stdOutErr.readLine(); - if (line == null) - throw new RootAccessDeniedException( - "stdout line is null! Access was denied or this executeable is not a shell!"); - if ("".equals(line)) - continue; - if ("Started".equals(line)) - break; - - destroyShellProcess(); - throw new IOException("Unable to start shell, unexpected output \"" + line + "\""); - } - - new Thread(inputRunnable, "Shell Input").start(); - new Thread(outputRunnable, "Shell Output").start(); - } - - private Runnable inputRunnable = new Runnable() { - public void run() { - try { - writeCommands(); - } catch (IOException e) { - Log.e(RootCommands.TAG, "IO Exception", e); - } - } - }; - - private Runnable outputRunnable = new Runnable() { - public void run() { - try { - readOutput(); - } catch (IOException e) { - Log.e(RootCommands.TAG, "IOException", e); - } catch (InterruptedException e) { - Log.e(RootCommands.TAG, "InterruptedException", e); - } - } - }; - - /** - * Destroy shell process considering that the process could already be terminated - */ - private void destroyShellProcess() { - try { - // Yes, this really is the way to check if the process is - // still running. - shellProcess.exitValue(); - } catch (IllegalThreadStateException e) { - // Only call destroy() if the process is still running; - // Calling it for a terminated process will not crash, but - // (starting with at least ICS/4.0) spam the log with INFO - // messages ala "Failed to destroy process" and "kill - // failed: ESRCH (No such process)". - shellProcess.destroy(); - } - - Log.d(RootCommands.TAG, "Shell destroyed"); - } - - /** - * Writes queued commands one after another into the opened shell. After an execution a token is - * written to seperate command output on read - * - * @throws java.io.IOException - */ - private void writeCommands() throws IOException { - try { - int commandIndex = 0; - while (true) { - DataOutputStream out; - synchronized (commands) { - while (!close && commandIndex >= commands.size()) { - commands.wait(); - } - out = this.outputStream; - } - if (commandIndex < commands.size()) { - Command next = commands.get(commandIndex); - next.writeCommand(out); - String line = "\necho " + token + " " + commandIndex + " $?\n"; - out.write(line.getBytes()); - out.flush(); - commandIndex++; - } else if (close) { - out.write("\nexit 0\n".getBytes()); - out.flush(); - out.close(); - Log.d(RootCommands.TAG, "Closing shell"); - return; - } - } - } catch (InterruptedException e) { - Log.e(RootCommands.TAG, "interrupted while writing command", e); - } - } - - /** - * Reads output line by line, seperated by token written after every command - * - * @throws java.io.IOException - * @throws InterruptedException - */ - private void readOutput() throws IOException, InterruptedException { - Command command = null; - - // index of current command - int commandIndex = 0; - while (true) { - String lineStdOut = stdOutErr.readLine(); - - // terminate on EOF - if (lineStdOut == null) - break; - - if (command == null) { - - // break on close after last command - if (commandIndex >= commands.size()) { - if (close) - break; - continue; - } - - // get current command - command = commands.get(commandIndex); - } - - int pos = lineStdOut.indexOf(token); - if (pos > 0) { - command.processOutput(lineStdOut.substring(0, pos)); - } - if (pos >= 0) { - lineStdOut = lineStdOut.substring(pos); - String fields[] = lineStdOut.split(" "); - int id = Integer.parseInt(fields[1]); - if (id == commandIndex) { - command.setExitCode(Integer.parseInt(fields[2])); - - // go to next command - commandIndex++; - command = null; - continue; - } - } - command.processOutput(lineStdOut); - } - Log.d(RootCommands.TAG, "Read all output"); - shellProcess.waitFor(); - destroyShellProcess(); - - while (commandIndex < commands.size()) { - if (command == null) { - command = commands.get(commandIndex); - } - command.terminated("Unexpected Termination!"); - commandIndex++; - command = null; - } - } - - /** - * Add command to shell queue - * - * @param command - * @return - * @throws java.io.IOException - */ - public Command add(Command command) throws IOException { - if (close) - throw new IOException("Unable to add commands to a closed shell"); - synchronized (commands) { - commands.add(command); - // set shell on the command object, to know where the command is running on - command.addedToShell(this, (commands.size() - 1)); - commands.notifyAll(); - } - - return command; - } - - /** - * Close shell - * - * @throws java.io.IOException - */ - public void close() throws IOException { - synchronized (commands) { - this.close = true; - commands.notifyAll(); - } - } - - /** - * Returns number of queued commands - * - * @return - */ - public int getCommandsSize() { - return commands.size(); - } - + + +package org.sufficientlysecure.rootcommands; + +import java.io.BufferedReader; +import java.io.Closeable; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; + +import org.sufficientlysecure.rootcommands.command.Command; +import org.sufficientlysecure.rootcommands.util.Log; +import org.sufficientlysecure.rootcommands.util.RootAccessDeniedException; +import org.sufficientlysecure.rootcommands.util.Utils; + +public class Shell implements Closeable { + private final Process shellProcess; + private final BufferedReader stdOutErr; + private final DataOutputStream outputStream; + private final List commands = new ArrayList(); + private boolean close = false; + + private static final String LD_LIBRARY_PATH = System.getenv("LD_LIBRARY_PATH"); + private static final String token = "F*D^W@#FGF"; + + /** + * Start root shell + * + * @param customEnv + * @param baseDirectory + * @return + * @throws java.io.IOException + */ + public static Shell startRootShell(ArrayList customEnv, String baseDirectory) + throws IOException, RootAccessDeniedException { + Log.d(RootCommands.TAG, "Starting Root Shell!"); + + // On some versions of Android (ICS) LD_LIBRARY_PATH is unset when using su + // We need to pass LD_LIBRARY_PATH over su for some commands to work correctly. + if (customEnv == null) { + customEnv = new ArrayList(); + } + customEnv.add("LD_LIBRARY_PATH=" + LD_LIBRARY_PATH); + + Shell shell = new Shell(Utils.getSuPath(), customEnv, baseDirectory); + + return shell; + } + + /** + * Start root shell without custom environment and base directory + * + * @return + * @throws java.io.IOException + */ + public static Shell startRootShell() throws IOException, RootAccessDeniedException { + return startRootShell(null, null); + } + + /** + * Start default sh shell + * + * @param customEnv + * @param baseDirectory + * @return + * @throws java.io.IOException + */ + public static Shell startShell(ArrayList customEnv, String baseDirectory) + throws IOException { + Log.d(RootCommands.TAG, "Starting Shell!"); + Shell shell = new Shell("sh", customEnv, baseDirectory); + return shell; + } + + /** + * Start default sh shell without custom environment and base directory + * + * @return + * @throws java.io.IOException + */ + public static Shell startShell() throws IOException { + return startShell(null, null); + } + + /** + * Start custom shell defined by shellPath + * + * @param shellPath + * @param customEnv + * @param baseDirectory + * @return + * @throws java.io.IOException + */ + public static Shell startCustomShell(String shellPath, ArrayList customEnv, + String baseDirectory) throws IOException { + Log.d(RootCommands.TAG, "Starting Custom Shell!"); + Shell shell = new Shell(shellPath, customEnv, baseDirectory); + + return shell; + } + + /** + * Start custom shell without custom environment and base directory + * + * @param shellPath + * @return + * @throws java.io.IOException + */ + public static Shell startCustomShell(String shellPath) throws IOException { + return startCustomShell(shellPath, null, null); + } + + private Shell(String shell, ArrayList customEnv, String baseDirectory) + throws IOException, RootAccessDeniedException { + Log.d(RootCommands.TAG, "Starting shell: " + shell); + + // start shell process! + shellProcess = Utils.runWithEnv(shell, customEnv, baseDirectory); + + // StdErr is redirected to StdOut, defined in Command.getCommand() + stdOutErr = new BufferedReader(new InputStreamReader(shellProcess.getInputStream())); + outputStream = new DataOutputStream(shellProcess.getOutputStream()); + + outputStream.write("echo Started\n".getBytes()); + outputStream.flush(); + + while (true) { + String line = stdOutErr.readLine(); + if (line == null) + throw new RootAccessDeniedException( + "stdout line is null! Access was denied or this executeable is not a shell!"); + if ("".equals(line)) + continue; + if ("Started".equals(line)) + break; + + destroyShellProcess(); + throw new IOException("Unable to start shell, unexpected output \"" + line + "\""); + } + + new Thread(inputRunnable, "Shell Input").start(); + new Thread(outputRunnable, "Shell Output").start(); + } + + private Runnable inputRunnable = new Runnable() { + public void run() { + try { + writeCommands(); + } catch (IOException e) { + Log.e(RootCommands.TAG, "IO Exception", e); + } + } + }; + + private Runnable outputRunnable = new Runnable() { + public void run() { + try { + readOutput(); + } catch (IOException e) { + Log.e(RootCommands.TAG, "IOException", e); + } catch (InterruptedException e) { + Log.e(RootCommands.TAG, "InterruptedException", e); + } + } + }; + + /** + * Destroy shell process considering that the process could already be terminated + */ + private void destroyShellProcess() { + try { + // Yes, this really is the way to check if the process is + // still running. + shellProcess.exitValue(); + } catch (IllegalThreadStateException e) { + // Only call destroy() if the process is still running; + // Calling it for a terminated process will not crash, but + // (starting with at least ICS/4.0) spam the log with INFO + // messages ala "Failed to destroy process" and "kill + // failed: ESRCH (No such process)". + shellProcess.destroy(); + } + + Log.d(RootCommands.TAG, "Shell destroyed"); + } + + /** + * Writes queued commands one after another into the opened shell. After an execution a token is + * written to seperate command output on read + * + * @throws java.io.IOException + */ + private void writeCommands() throws IOException { + try { + int commandIndex = 0; + while (true) { + DataOutputStream out; + synchronized (commands) { + while (!close && commandIndex >= commands.size()) { + commands.wait(); + } + out = this.outputStream; + } + if (commandIndex < commands.size()) { + Command next = commands.get(commandIndex); + next.writeCommand(out); + String line = "\necho " + token + " " + commandIndex + " $?\n"; + out.write(line.getBytes()); + out.flush(); + commandIndex++; + } else if (close) { + out.write("\nexit 0\n".getBytes()); + out.flush(); + out.close(); + Log.d(RootCommands.TAG, "Closing shell"); + return; + } + } + } catch (InterruptedException e) { + Log.e(RootCommands.TAG, "interrupted while writing command", e); + } + } + + /** + * Reads output line by line, seperated by token written after every command + * + * @throws java.io.IOException + * @throws InterruptedException + */ + private void readOutput() throws IOException, InterruptedException { + Command command = null; + + // index of current command + int commandIndex = 0; + while (true) { + String lineStdOut = stdOutErr.readLine(); + + // terminate on EOF + if (lineStdOut == null) + break; + + if (command == null) { + + // break on close after last command + if (commandIndex >= commands.size()) { + if (close) + break; + continue; + } + + // get current command + command = commands.get(commandIndex); + } + + int pos = lineStdOut.indexOf(token); + if (pos > 0) { + command.processOutput(lineStdOut.substring(0, pos)); + } + if (pos >= 0) { + lineStdOut = lineStdOut.substring(pos); + String fields[] = lineStdOut.split(" "); + int id = Integer.parseInt(fields[1]); + if (id == commandIndex) { + command.setExitCode(Integer.parseInt(fields[2])); + + // go to next command + commandIndex++; + command = null; + continue; + } + } + command.processOutput(lineStdOut); + } + Log.d(RootCommands.TAG, "Read all output"); + shellProcess.waitFor(); + destroyShellProcess(); + + while (commandIndex < commands.size()) { + if (command == null) { + command = commands.get(commandIndex); + } + command.terminated("Unexpected Termination!"); + commandIndex++; + command = null; + } + } + + /** + * Add command to shell queue + * + * @param command + * @return + * @throws java.io.IOException + */ + public Command add(Command command) throws IOException { + if (close) + throw new IOException("Unable to add commands to a closed shell"); + synchronized (commands) { + commands.add(command); + // set shell on the command object, to know where the command is running on + command.addedToShell(this, (commands.size() - 1)); + commands.notifyAll(); + } + + return command; + } + + /** + * Close shell + * + * @throws java.io.IOException + */ + public void close() throws IOException { + synchronized (commands) { + this.close = true; + commands.notifyAll(); + } + } + + /** + * Returns number of queued commands + * + * @return + */ + public int getCommandsSize() { + return commands.size(); + } + } \ No newline at end of file diff --git a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/SystemCommands.java b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/SystemCommands.java index fa4a551..84b4f80 100644 --- a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/SystemCommands.java +++ b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/SystemCommands.java @@ -1,108 +1,108 @@ - - -package org.sufficientlysecure.rootcommands; - -import android.annotation.TargetApi; -import android.content.ContentResolver; -import android.content.Context; -import android.location.LocationManager; -import android.os.PowerManager; -import android.provider.Settings; - -/** - * This methods work when the apk is installed as a system app (under /system/app) - */ -public class SystemCommands { - Context context; - - public SystemCommands(Context context) { - super(); - this.context = context; - } - - /** - * Get GPS status - * - * @return - */ - public boolean getGPS() { - return ((LocationManager) context.getSystemService(Context.LOCATION_SERVICE)) - .isProviderEnabled(LocationManager.GPS_PROVIDER); - } - - /** - * Enable/Disable GPS - * - * @param value - */ - @TargetApi(8) - public void setGPS(boolean value) { - ContentResolver localContentResolver = context.getContentResolver(); - Settings.Secure.setLocationProviderEnabled(localContentResolver, - LocationManager.GPS_PROVIDER, value); - } - - /** - * TODO: Not ready yet - */ - @TargetApi(8) - public void reboot() { - PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); - pm.reboot("recovery"); - pm.reboot(null); - - // not working: - // reboot(null); - } - - /** - * Reboot the device immediately, passing 'reason' (may be null) to the underlying __reboot - * system call. Should not return. - * - * Taken from com.android.server.PowerManagerService.reboot - */ - // public void reboot(String reason) { - // - // // final String finalReason = reason; - // Runnable runnable = new Runnable() { - // public void run() { - // synchronized (this) { - // // ShutdownThread.reboot(mContext, finalReason, false); - // try { - // Class clazz = Class.forName("com.android.internal.app.ShutdownThread"); - // - // // if (mReboot) { - // Method method = clazz.getMethod("reboot", Context.class, String.class, - // Boolean.TYPE); - // method.invoke(null, context, null, false); - // - // // if (mReboot) { - // // Method method = clazz.getMethod("reboot", Context.class, String.class, - // // Boolean.TYPE); - // // method.invoke(null, mContext, mReason, mConfirm); - // // } else { - // // Method method = clazz.getMethod("shutdown", Context.class, Boolean.TYPE); - // // method.invoke(null, mContext, mConfirm); - // // } - // } catch (Exception e) { - // e.printStackTrace(); - // } - // } - // - // } - // }; - // // ShutdownThread must run on a looper capable of displaying the UI. - // mHandler.post(runnable); - // - // // PowerManager.reboot() is documented not to return so just wait for the inevitable. - // // synchronized (runnable) { - // // while (true) { - // // try { - // // runnable.wait(); - // // } catch (InterruptedException e) { - // // } - // // } - // // } - // } - -} + + +package org.sufficientlysecure.rootcommands; + +import android.annotation.TargetApi; +import android.content.ContentResolver; +import android.content.Context; +import android.location.LocationManager; +import android.os.PowerManager; +import android.provider.Settings; + +/** + * This methods work when the apk is installed as a system app (under /system/app) + */ +public class SystemCommands { + Context context; + + public SystemCommands(Context context) { + super(); + this.context = context; + } + + /** + * Get GPS status + * + * @return + */ + public boolean getGPS() { + return ((LocationManager) context.getSystemService(Context.LOCATION_SERVICE)) + .isProviderEnabled(LocationManager.GPS_PROVIDER); + } + + /** + * Enable/Disable GPS + * + * @param value + */ + @TargetApi(8) + public void setGPS(boolean value) { + ContentResolver localContentResolver = context.getContentResolver(); + Settings.Secure.setLocationProviderEnabled(localContentResolver, + LocationManager.GPS_PROVIDER, value); + } + + /** + * TODO: Not ready yet + */ + @TargetApi(8) + public void reboot() { + PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + pm.reboot("recovery"); + pm.reboot(null); + + // not working: + // reboot(null); + } + + /** + * Reboot the device immediately, passing 'reason' (may be null) to the underlying __reboot + * system call. Should not return. + * + * Taken from com.android.server.PowerManagerService.reboot + */ + // public void reboot(String reason) { + // + // // final String finalReason = reason; + // Runnable runnable = new Runnable() { + // public void run() { + // synchronized (this) { + // // ShutdownThread.reboot(mContext, finalReason, false); + // try { + // Class clazz = Class.forName("com.android.internal.app.ShutdownThread"); + // + // // if (mReboot) { + // Method method = clazz.getMethod("reboot", Context.class, String.class, + // Boolean.TYPE); + // method.invoke(null, context, null, false); + // + // // if (mReboot) { + // // Method method = clazz.getMethod("reboot", Context.class, String.class, + // // Boolean.TYPE); + // // method.invoke(null, mContext, mReason, mConfirm); + // // } else { + // // Method method = clazz.getMethod("shutdown", Context.class, Boolean.TYPE); + // // method.invoke(null, mContext, mConfirm); + // // } + // } catch (Exception e) { + // e.printStackTrace(); + // } + // } + // + // } + // }; + // // ShutdownThread must run on a looper capable of displaying the UI. + // mHandler.post(runnable); + // + // // PowerManager.reboot() is documented not to return so just wait for the inevitable. + // // synchronized (runnable) { + // // while (true) { + // // try { + // // runnable.wait(); + // // } catch (InterruptedException e) { + // // } + // // } + // // } + // } + +} diff --git a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Toolbox.java b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Toolbox.java index 177b016..af869d9 100644 --- a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Toolbox.java +++ b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/Toolbox.java @@ -1,809 +1,809 @@ - - -package org.sufficientlysecure.rootcommands; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.ArrayList; -import java.util.concurrent.TimeoutException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.sufficientlysecure.rootcommands.command.ExecutableCommand; -import org.sufficientlysecure.rootcommands.command.Command; -import org.sufficientlysecure.rootcommands.command.SimpleCommand; -import org.sufficientlysecure.rootcommands.util.BrokenBusyboxException; -import org.sufficientlysecure.rootcommands.util.Log; - -import android.os.StatFs; -import android.os.SystemClock; - -/** - * All methods in this class are working with Androids toolbox. Toolbox is similar to busybox, but - * normally shipped on every Android OS. You can find toolbox commands on - * https://github.com/CyanogenMod/android_system_core/tree/ics/toolbox - * - * This means that these commands are designed to work on every Android OS, with a _working_ toolbox - * binary on it. They don't require busybox! - * - */ -public class Toolbox { - private Shell shell; - - /** - * All methods in this class are working with Androids toolbox. Toolbox is similar to busybox, - * but normally shipped on every Android OS. - * - * @param shell - * where to execute commands on - */ - public Toolbox(Shell shell) { - super(); - this.shell = shell; - } - - /** - * Checks if user accepted root access - * - * (commands: id) - * - * @return true if user has given root access - * @throws java.io.IOException - * @throws java.util.concurrent.TimeoutException - * @throws BrokenBusyboxException - */ - public boolean isRootAccessGiven() throws BrokenBusyboxException, TimeoutException, IOException { - SimpleCommand idCommand = new SimpleCommand("id"); - shell.add(idCommand).waitForFinish(); - - if (idCommand.getOutput().contains("uid=0")) { - return true; - } else { - return false; - } - } - - /** - * This command class gets all pids to a given process name - */ - private class PsCommand extends Command { - private String processName; - private ArrayList pids; - private String psRegex; - private Pattern psPattern; - - public PsCommand(String processName) { - super("ps"); - this.processName = processName; - pids = new ArrayList(); - - /** - * regex to get pid out of ps line, example: - * - *

-             *  root    24736    1   12140  584   ffffffff 40010d14 S /data/data/org.adaway/files/blank_webserver
-             * ^\\S \\s ([0-9]+)                          .*                                      processName    $
-             * 
- */ - psRegex = "^\\S+\\s+([0-9]+).*" + Pattern.quote(processName) + "$"; - psPattern = Pattern.compile(psRegex); - } - - public ArrayList getPids() { - return pids; - } - - public String getPidsString() { - StringBuilder sb = new StringBuilder(); - for (String s : pids) { - sb.append(s); - sb.append(" "); - } - - return sb.toString(); - } - - @Override - public void output(int id, String line) { - // general check if line contains processName - if (line.contains(processName)) { - Matcher psMatcher = psPattern.matcher(line); - - // try to match line exactly - try { - if (psMatcher.find()) { - String pid = psMatcher.group(1); - // add to pids list - pids.add(pid); - Log.d(RootCommands.TAG, "Found pid: " + pid); - } else { - Log.d(RootCommands.TAG, "Matching in ps command failed!"); - } - } catch (Exception e) { - Log.e(RootCommands.TAG, "Error with regex!", e); - } - } - } - - @Override - public void afterExecution(int id, int exitCode) { - } - - } - - /** - * This method can be used to kill a running process - * - * (commands: ps, kill) - * - * @param processName - * name of process to kill - * @return true if process was found and killed successfully - * @throws java.io.IOException - * @throws java.util.concurrent.TimeoutException - * @throws BrokenBusyboxException - */ - public boolean killAll(String processName) throws BrokenBusyboxException, TimeoutException, - IOException { - Log.d(RootCommands.TAG, "Killing process " + processName); - - PsCommand psCommand = new PsCommand(processName); - shell.add(psCommand).waitForFinish(); - - // kill processes - if (!psCommand.getPids().isEmpty()) { - // example: kill -9 1234 1222 5343 - SimpleCommand killCommand = new SimpleCommand("kill -9 " - + psCommand.getPidsString()); - shell.add(killCommand).waitForFinish(); - - if (killCommand.getExitCode() == 0) { - return true; - } else { - return false; - } - } else { - Log.d(RootCommands.TAG, "No pid found! Nothing was killed!"); - return false; - } - } - - /** - * Kill a running executable - * - * See README for more information how to use your own executables! - * - * @param executableName - * @return - * @throws BrokenBusyboxException - * @throws java.util.concurrent.TimeoutException - * @throws java.io.IOException - */ - public boolean killAllExecutable(String executableName) throws BrokenBusyboxException, - TimeoutException, IOException { - return killAll(ExecutableCommand.EXECUTABLE_PREFIX + executableName + ExecutableCommand.EXECUTABLE_SUFFIX); - } - - /** - * This method can be used to to check if a process is running - * - * @param processName - * name of process to check - * @return true if process was found - * @throws java.io.IOException - * @throws BrokenBusyboxException - * @throws java.util.concurrent.TimeoutException - * (Could not determine if the process is running) - */ - public boolean isProcessRunning(String processName) throws BrokenBusyboxException, - TimeoutException, IOException { - PsCommand psCommand = new PsCommand(processName); - shell.add(psCommand).waitForFinish(); - - // if pids are available process is running! - if (!psCommand.getPids().isEmpty()) { - return true; - } else { - return false; - } - } - - /** - * Checks if binary is running - * - * @param binaryName - * @return - * @throws BrokenBusyboxException - * @throws java.util.concurrent.TimeoutException - * @throws java.io.IOException - */ - public boolean isBinaryRunning(String binaryName) throws BrokenBusyboxException, - TimeoutException, IOException { - return isProcessRunning(ExecutableCommand.EXECUTABLE_PREFIX + binaryName - + ExecutableCommand.EXECUTABLE_SUFFIX); - } - - /** - * Ls command to get permissions or symlinks - */ - private class LsCommand extends Command { - private String fileName; - private String permissionRegex; - private Pattern permissionPattern; - private String symlinkRegex; - private Pattern symlinkPattern; - - private String symlink; - private String permissions; - - public String getSymlink() { - return symlink; - } - - public String getPermissions() { - return permissions; - } - - public LsCommand(String file) { - super("ls -l " + file); - - // get only filename: - this.fileName = (new File(file)).getName(); - Log.d(RootCommands.TAG, "fileName: " + fileName); - - /** - * regex to get pid out of ps line, example: - * - *
-             * with busybox:
-             *     lrwxrwxrwx     1 root root            15 Aug 13 12:14 dev/stdin -> /proc/self/fd/0
-             *     
-             * with toolbox:
-             *     lrwxrwxrwx root root            15 Aug 13 12:14 stdin -> /proc/self/fd/0
-             * 
-             * Regex:
-             * ^.*?(\\S{10})                     .*                                                  $
-             * 
- */ - permissionRegex = "^.*?(\\S{10}).*$"; - permissionPattern = Pattern.compile(permissionRegex); - - /** - * regex to get symlink - * - *
-             *     ->           /proc/self/fd/0
-             * ^.*?\\-\\> \\s+  (.*)           $
-             * 
- */ - symlinkRegex = "^.*?\\-\\>\\s+(.*)$"; - symlinkPattern = Pattern.compile(symlinkRegex); - } - - /** - * Converts permission string from ls command to numerical value. Example: -rwxrwxrwx gets - * to 777 - * - * @param permissions - * @return - */ - private String convertPermissions(String permissions) { - int owner = getGroupPermission(permissions.substring(1, 4)); - int group = getGroupPermission(permissions.substring(4, 7)); - int world = getGroupPermission(permissions.substring(7, 10)); - - return "" + owner + group + world; - } - - /** - * Calculates permission for one group - * - * @param permission - * @return value of permission string - */ - private int getGroupPermission(String permission) { - int value = 0; - - if (permission.charAt(0) == 'r') { - value += 4; - } - if (permission.charAt(1) == 'w') { - value += 2; - } - if (permission.charAt(2) == 'x') { - value += 1; - } - - return value; - } - - @Override - public void output(int id, String line) { - // general check if line contains file - if (line.contains(fileName)) { - - // try to match line exactly - try { - Matcher permissionMatcher = permissionPattern.matcher(line); - if (permissionMatcher.find()) { - permissions = convertPermissions(permissionMatcher.group(1)); - - Log.d(RootCommands.TAG, "Found permissions: " + permissions); - } else { - Log.d(RootCommands.TAG, "Permissions were not found in ls command!"); - } - - // try to parse for symlink - Matcher symlinkMatcher = symlinkPattern.matcher(line); - if (symlinkMatcher.find()) { - /* - * TODO: If symlink points to a file in the same directory the path is not - * absolute!!! - */ - symlink = symlinkMatcher.group(1); - Log.d(RootCommands.TAG, "Symlink found: " + symlink); - } else { - Log.d(RootCommands.TAG, "No symlink found!"); - } - } catch (Exception e) { - Log.e(RootCommands.TAG, "Error with regex!", e); - } - } - } - - @Override - public void afterExecution(int id, int exitCode) { - } - - } - - /** - * @param file - * String that represent the file, including the full path to the file and its name. - * @param followSymlinks - * @return File permissions as String, for example: 777, returns null on error - * @throws java.io.IOException - * @throws java.util.concurrent.TimeoutException - * @throws BrokenBusyboxException - * - */ - public String getFilePermissions(String file) throws BrokenBusyboxException, TimeoutException, - IOException { - Log.d(RootCommands.TAG, "Checking permissions for " + file); - - String permissions = null; - - if (fileExists(file)) { - Log.d(RootCommands.TAG, file + " was found."); - - LsCommand lsCommand = new LsCommand(file); - shell.add(lsCommand).waitForFinish(); - - permissions = lsCommand.getPermissions(); - } - - return permissions; - } - - /** - * Sets permission of file - * - * @param file - * absolute path to file - * @param permissions - * String like 777 - * @return true if command worked - * @throws BrokenBusyboxException - * @throws java.util.concurrent.TimeoutException - * @throws java.io.IOException - */ - public boolean setFilePermissions(String file, String permissions) - throws BrokenBusyboxException, TimeoutException, IOException { - Log.d(RootCommands.TAG, "Set permissions of " + file + " to " + permissions); - - SimpleCommand chmodCommand = new SimpleCommand("chmod " + permissions + " " + file); - shell.add(chmodCommand).waitForFinish(); - - if (chmodCommand.getExitCode() == 0) { - return true; - } else { - return false; - } - } - - /** - * This will return a String that represent the symlink for a specified file. - * - * @param file - * The path to the file to get the Symlink for. (must have absolute path) - * - * @return A String that represent the symlink for a specified file or null if no symlink - * exists. - * @throws java.io.IOException - * @throws java.util.concurrent.TimeoutException - * @throws BrokenBusyboxException - */ - public String getSymlink(String file) throws BrokenBusyboxException, TimeoutException, - IOException { - Log.d(RootCommands.TAG, "Find symlink for " + file); - - String symlink = null; - - LsCommand lsCommand = new LsCommand(file); - shell.add(lsCommand).waitForFinish(); - - symlink = lsCommand.getSymlink(); - - return symlink; - } - - /** - * Copys a file to a destination. Because cp is not available on all android devices, we use dd - * or cat. - * - * @param source - * example: /data/data/org.adaway/files/hosts - * @param destination - * example: /system/etc/hosts - * @param remountAsRw - * remounts the destination as read/write before writing to it - * @param preserveFileAttributes - * tries to copy file attributes from source to destination, if only cat is available - * only permissions are preserved - * @return true if it was successfully copied - * @throws BrokenBusyboxException - * @throws java.io.IOException - * @throws java.util.concurrent.TimeoutException - */ - public boolean copyFile(String source, String destination, boolean remountAsRw, - boolean preservePermissions) throws BrokenBusyboxException, IOException, - TimeoutException { - - /* - * dd can only copy files, but we can not check if the source is a file without invoking - * shell commands, because from Java we probably have no read access, thus we only check if - * they are ending with trailing slashes - */ - if (source.endsWith("/") || destination.endsWith("/")) { - throw new FileNotFoundException("dd can only copy files!"); - } - - // remount destination as read/write before copying to it - if (remountAsRw) { - if (!remount(destination, "RW")) { - Log.d(RootCommands.TAG, - "Remounting failed! There is probably no need to remount this partition!"); - } - } - - // get permissions of source before overwriting - String permissions = null; - if (preservePermissions) { - permissions = getFilePermissions(source); - } - - boolean commandSuccess = false; - - SimpleCommand ddCommand = new SimpleCommand("dd if=" + source + " of=" - + destination); - shell.add(ddCommand).waitForFinish(); - - if (ddCommand.getExitCode() == 0) { - commandSuccess = true; - } else { - // try cat if dd fails - SimpleCommand catCommand = new SimpleCommand("cat " + source + " > " - + destination); - shell.add(catCommand).waitForFinish(); - - if (catCommand.getExitCode() == 0) { - commandSuccess = true; - } - } - - // set back permissions from source to destination - if (preservePermissions) { - setFilePermissions(destination, permissions); - } - - // remount destination back to read only - if (remountAsRw) { - if (!remount(destination, "RO")) { - Log.d(RootCommands.TAG, - "Remounting failed! There is probably no need to remount this partition!"); - } - } - - return commandSuccess; - } - - public static final int REBOOT_HOTREBOOT = 1; - public static final int REBOOT_REBOOT = 2; - public static final int REBOOT_SHUTDOWN = 3; - public static final int REBOOT_RECOVERY = 4; - - /** - * Shutdown or reboot device. Possible actions are REBOOT_HOTREBOOT, REBOOT_REBOOT, - * REBOOT_SHUTDOWN, REBOOT_RECOVERY - * - * @param action - * @throws java.io.IOException - * @throws java.util.concurrent.TimeoutException - * @throws BrokenBusyboxException - */ - public void reboot(int action) throws BrokenBusyboxException, TimeoutException, IOException { - if (action == REBOOT_HOTREBOOT) { - killAll("system_server"); - // or: killAll("zygote"); - } else { - String command; - switch (action) { - case REBOOT_REBOOT: - command = "reboot"; - break; - case REBOOT_SHUTDOWN: - command = "reboot -p"; - break; - case REBOOT_RECOVERY: - command = "reboot recovery"; - break; - default: - command = "reboot"; - break; - } - - SimpleCommand rebootCommand = new SimpleCommand(command); - shell.add(rebootCommand).waitForFinish(); - - if (rebootCommand.getExitCode() == -1) { - Log.e(RootCommands.TAG, "Reboot failed!"); - } - } - } - - /** - * This command checks if a file exists - */ - private class FileExistsCommand extends Command { - private String file; - private boolean fileExists = false; - - public FileExistsCommand(String file) { - super("ls " + file); - this.file = file; - } - - public boolean isFileExists() { - return fileExists; - } - - @Override - public void output(int id, String line) { - if (line.trim().equals(file)) { - fileExists = true; - } - } - - @Override - public void afterExecution(int id, int exitCode) { - } - - } - - /** - * Use this to check whether or not a file exists on the filesystem. - * - * @param file - * String that represent the file, including the full path to the file and its name. - * - * @return a boolean that will indicate whether or not the file exists. - * @throws java.io.IOException - * @throws java.util.concurrent.TimeoutException - * @throws BrokenBusyboxException - * - */ - public boolean fileExists(String file) throws BrokenBusyboxException, TimeoutException, - IOException { - FileExistsCommand fileExistsCommand = new FileExistsCommand(file); - shell.add(fileExistsCommand).waitForFinish(); - - if (fileExistsCommand.isFileExists()) { - return true; - } else { - return false; - } - } - - public abstract class WithPermissions { - abstract void whileHavingPermissions(); - } - - /** - * Execute user defined Java code while having temporary permissions on a file - * - * @param file - * @param withPermissions - * @throws BrokenBusyboxException - * @throws java.util.concurrent.TimeoutException - * @throws java.io.IOException - */ - public void withPermission(String file, String permission, WithPermissions withPermissions) - throws BrokenBusyboxException, TimeoutException, IOException { - String oldPermissions = getFilePermissions(file); - - // set permissions (If set to 666, then Dalvik VM can also write to that file!) - setFilePermissions(file, permission); - - // execute user defined code - withPermissions.whileHavingPermissions(); - - // set back to old permissions - setFilePermissions(file, oldPermissions); - } - - /** - * Execute user defined Java code while having temporary write permissions on a file using chmod - * 666 - * - * @param file - * @param withWritePermissions - * @throws BrokenBusyboxException - * @throws java.util.concurrent.TimeoutException - * @throws java.io.IOException - */ - public void withWritePermissions(String file, WithPermissions withWritePermissions) - throws BrokenBusyboxException, TimeoutException, IOException { - withPermission(file, "666", withWritePermissions); - } - - /** - * Sets system clock using /dev/alarm - * - * @param millis - * @throws BrokenBusyboxException - * @throws java.util.concurrent.TimeoutException - * @throws java.io.IOException - */ - public void setSystemClock(final long millis) throws BrokenBusyboxException, TimeoutException, - IOException { - withWritePermissions("/dev/alarm", new WithPermissions() { - - @Override - void whileHavingPermissions() { - SystemClock.setCurrentTimeMillis(millis); - } - }); - } - - /** - * Adjust system clock by offset using /dev/alarm - * - * @param offset - * @throws BrokenBusyboxException - * @throws java.util.concurrent.TimeoutException - * @throws java.io.IOException - */ - public void adjustSystemClock(final long offset) throws BrokenBusyboxException, - TimeoutException, IOException { - withWritePermissions("/dev/alarm", new WithPermissions() { - - @Override - void whileHavingPermissions() { - SystemClock.setCurrentTimeMillis(System.currentTimeMillis() + offset); - } - }); - } - - /** - * This will take a path, which can contain the file name as well, and attempt to remount the - * underlying partition. - * - * For example, passing in the following string: - * "/system/bin/some/directory/that/really/would/never/exist" will result in /system ultimately - * being remounted. However, keep in mind that the longer the path you supply, the more work - * this has to do, and the slower it will run. - * - * @param file - * file path - * @param mountType - * mount type: pass in RO (Read only) or RW (Read Write) - * @return a boolean which indicates whether or not the partition has been - * remounted as specified. - */ - public boolean remount(String file, String mountType) { - // Recieved a request, get an instance of Remounter - Remounter remounter = new Remounter(shell); - // send the request - return (remounter.remount(file, mountType)); - } - - /** - * This will tell you how the specified mount is mounted. rw, ro, etc... - * - * @param The - * mount you want to check - * - * @return String What the mount is mounted as. - * @throws Exception - * if we cannot determine how the mount is mounted. - */ - public String getMountedAs(String path) throws Exception { - ArrayList mounts = Remounter.getMounts(); - if (mounts != null) { - for (Mount mount : mounts) { - if (path.contains(mount.getMountPoint().getAbsolutePath())) { - Log.d(RootCommands.TAG, (String) mount.getFlags().toArray()[0]); - return (String) mount.getFlags().toArray()[0]; - } - } - - throw new Exception(); - } else { - throw new Exception(); - } - } - - /** - * Check if there is enough space on partition where target is located - * - * @param size - * size of file to put on partition - * @param target - * path where to put the file - * - * @return true if it will fit on partition of target, false if it will not fit. - */ - public boolean hasEnoughSpaceOnPartition(String target, long size) { - try { - // new File(target).getFreeSpace() (API 9) is not working on data partition - - // get directory without file - String directory = new File(target).getParent().toString(); - - StatFs stat = new StatFs(directory); - long blockSize = stat.getBlockSize(); - long availableBlocks = stat.getAvailableBlocks(); - long availableSpace = availableBlocks * blockSize; - - Log.i(RootCommands.TAG, "Checking for enough space: Target: " + target - + ", directory: " + directory + " size: " + size + ", availableSpace: " - + availableSpace); - - if (size < availableSpace) { - return true; - } else { - Log.e(RootCommands.TAG, "Not enough space on partition!"); - return false; - } - } catch (Exception e) { - // if new StatFs(directory) fails catch IllegalArgumentException and just return true as - // workaround - Log.e(RootCommands.TAG, "Problem while getting available space on partition!", e); - return true; - } - } - - /** - * TODO: Not tested! - * - * @param toggle - * @throws java.io.IOException - * @throws java.util.concurrent.TimeoutException - * @throws BrokenBusyboxException - */ - public void toggleAdbDaemon(boolean toggle) throws BrokenBusyboxException, TimeoutException, - IOException { - SimpleCommand disableAdb = new SimpleCommand("setprop persist.service.adb.enable 0", - "stop adbd"); - SimpleCommand enableAdb = new SimpleCommand("setprop persist.service.adb.enable 1", - "stop adbd", "sleep 1", "start adbd"); - - if (toggle) { - shell.add(enableAdb).waitForFinish(); - } else { - shell.add(disableAdb).waitForFinish(); - } - } - -} + + +package org.sufficientlysecure.rootcommands; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.concurrent.TimeoutException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.sufficientlysecure.rootcommands.command.ExecutableCommand; +import org.sufficientlysecure.rootcommands.command.Command; +import org.sufficientlysecure.rootcommands.command.SimpleCommand; +import org.sufficientlysecure.rootcommands.util.BrokenBusyboxException; +import org.sufficientlysecure.rootcommands.util.Log; + +import android.os.StatFs; +import android.os.SystemClock; + +/** + * All methods in this class are working with Androids toolbox. Toolbox is similar to busybox, but + * normally shipped on every Android OS. You can find toolbox commands on + * https://github.com/CyanogenMod/android_system_core/tree/ics/toolbox + * + * This means that these commands are designed to work on every Android OS, with a _working_ toolbox + * binary on it. They don't require busybox! + * + */ +public class Toolbox { + private Shell shell; + + /** + * All methods in this class are working with Androids toolbox. Toolbox is similar to busybox, + * but normally shipped on every Android OS. + * + * @param shell + * where to execute commands on + */ + public Toolbox(Shell shell) { + super(); + this.shell = shell; + } + + /** + * Checks if user accepted root access + * + * (commands: id) + * + * @return true if user has given root access + * @throws java.io.IOException + * @throws java.util.concurrent.TimeoutException + * @throws BrokenBusyboxException + */ + public boolean isRootAccessGiven() throws BrokenBusyboxException, TimeoutException, IOException { + SimpleCommand idCommand = new SimpleCommand("id"); + shell.add(idCommand).waitForFinish(); + + if (idCommand.getOutput().contains("uid=0")) { + return true; + } else { + return false; + } + } + + /** + * This command class gets all pids to a given process name + */ + private class PsCommand extends Command { + private String processName; + private ArrayList pids; + private String psRegex; + private Pattern psPattern; + + public PsCommand(String processName) { + super("ps"); + this.processName = processName; + pids = new ArrayList(); + + /** + * regex to get pid out of ps line, example: + * + *
+             *  root    24736    1   12140  584   ffffffff 40010d14 S /data/data/org.adaway/files/blank_webserver
+             * ^\\S \\s ([0-9]+)                          .*                                      processName    $
+             * 
+ */ + psRegex = "^\\S+\\s+([0-9]+).*" + Pattern.quote(processName) + "$"; + psPattern = Pattern.compile(psRegex); + } + + public ArrayList getPids() { + return pids; + } + + public String getPidsString() { + StringBuilder sb = new StringBuilder(); + for (String s : pids) { + sb.append(s); + sb.append(" "); + } + + return sb.toString(); + } + + @Override + public void output(int id, String line) { + // general check if line contains processName + if (line.contains(processName)) { + Matcher psMatcher = psPattern.matcher(line); + + // try to match line exactly + try { + if (psMatcher.find()) { + String pid = psMatcher.group(1); + // add to pids list + pids.add(pid); + Log.d(RootCommands.TAG, "Found pid: " + pid); + } else { + Log.d(RootCommands.TAG, "Matching in ps command failed!"); + } + } catch (Exception e) { + Log.e(RootCommands.TAG, "Error with regex!", e); + } + } + } + + @Override + public void afterExecution(int id, int exitCode) { + } + + } + + /** + * This method can be used to kill a running process + * + * (commands: ps, kill) + * + * @param processName + * name of process to kill + * @return true if process was found and killed successfully + * @throws java.io.IOException + * @throws java.util.concurrent.TimeoutException + * @throws BrokenBusyboxException + */ + public boolean killAll(String processName) throws BrokenBusyboxException, TimeoutException, + IOException { + Log.d(RootCommands.TAG, "Killing process " + processName); + + PsCommand psCommand = new PsCommand(processName); + shell.add(psCommand).waitForFinish(); + + // kill processes + if (!psCommand.getPids().isEmpty()) { + // example: kill -9 1234 1222 5343 + SimpleCommand killCommand = new SimpleCommand("kill -9 " + + psCommand.getPidsString()); + shell.add(killCommand).waitForFinish(); + + if (killCommand.getExitCode() == 0) { + return true; + } else { + return false; + } + } else { + Log.d(RootCommands.TAG, "No pid found! Nothing was killed!"); + return false; + } + } + + /** + * Kill a running executable + * + * See README for more information how to use your own executables! + * + * @param executableName + * @return + * @throws BrokenBusyboxException + * @throws java.util.concurrent.TimeoutException + * @throws java.io.IOException + */ + public boolean killAllExecutable(String executableName) throws BrokenBusyboxException, + TimeoutException, IOException { + return killAll(ExecutableCommand.EXECUTABLE_PREFIX + executableName + ExecutableCommand.EXECUTABLE_SUFFIX); + } + + /** + * This method can be used to to check if a process is running + * + * @param processName + * name of process to check + * @return true if process was found + * @throws java.io.IOException + * @throws BrokenBusyboxException + * @throws java.util.concurrent.TimeoutException + * (Could not determine if the process is running) + */ + public boolean isProcessRunning(String processName) throws BrokenBusyboxException, + TimeoutException, IOException { + PsCommand psCommand = new PsCommand(processName); + shell.add(psCommand).waitForFinish(); + + // if pids are available process is running! + if (!psCommand.getPids().isEmpty()) { + return true; + } else { + return false; + } + } + + /** + * Checks if binary is running + * + * @param binaryName + * @return + * @throws BrokenBusyboxException + * @throws java.util.concurrent.TimeoutException + * @throws java.io.IOException + */ + public boolean isBinaryRunning(String binaryName) throws BrokenBusyboxException, + TimeoutException, IOException { + return isProcessRunning(ExecutableCommand.EXECUTABLE_PREFIX + binaryName + + ExecutableCommand.EXECUTABLE_SUFFIX); + } + + /** + * Ls command to get permissions or symlinks + */ + private class LsCommand extends Command { + private String fileName; + private String permissionRegex; + private Pattern permissionPattern; + private String symlinkRegex; + private Pattern symlinkPattern; + + private String symlink; + private String permissions; + + public String getSymlink() { + return symlink; + } + + public String getPermissions() { + return permissions; + } + + public LsCommand(String file) { + super("ls -l " + file); + + // get only filename: + this.fileName = (new File(file)).getName(); + Log.d(RootCommands.TAG, "fileName: " + fileName); + + /** + * regex to get pid out of ps line, example: + * + *
+             * with busybox:
+             *     lrwxrwxrwx     1 root root            15 Aug 13 12:14 dev/stdin -> /proc/self/fd/0
+             *     
+             * with toolbox:
+             *     lrwxrwxrwx root root            15 Aug 13 12:14 stdin -> /proc/self/fd/0
+             * 
+             * Regex:
+             * ^.*?(\\S{10})                     .*                                                  $
+             * 
+ */ + permissionRegex = "^.*?(\\S{10}).*$"; + permissionPattern = Pattern.compile(permissionRegex); + + /** + * regex to get symlink + * + *
+             *     ->           /proc/self/fd/0
+             * ^.*?\\-\\> \\s+  (.*)           $
+             * 
+ */ + symlinkRegex = "^.*?\\-\\>\\s+(.*)$"; + symlinkPattern = Pattern.compile(symlinkRegex); + } + + /** + * Converts permission string from ls command to numerical value. Example: -rwxrwxrwx gets + * to 777 + * + * @param permissions + * @return + */ + private String convertPermissions(String permissions) { + int owner = getGroupPermission(permissions.substring(1, 4)); + int group = getGroupPermission(permissions.substring(4, 7)); + int world = getGroupPermission(permissions.substring(7, 10)); + + return "" + owner + group + world; + } + + /** + * Calculates permission for one group + * + * @param permission + * @return value of permission string + */ + private int getGroupPermission(String permission) { + int value = 0; + + if (permission.charAt(0) == 'r') { + value += 4; + } + if (permission.charAt(1) == 'w') { + value += 2; + } + if (permission.charAt(2) == 'x') { + value += 1; + } + + return value; + } + + @Override + public void output(int id, String line) { + // general check if line contains file + if (line.contains(fileName)) { + + // try to match line exactly + try { + Matcher permissionMatcher = permissionPattern.matcher(line); + if (permissionMatcher.find()) { + permissions = convertPermissions(permissionMatcher.group(1)); + + Log.d(RootCommands.TAG, "Found permissions: " + permissions); + } else { + Log.d(RootCommands.TAG, "Permissions were not found in ls command!"); + } + + // try to parse for symlink + Matcher symlinkMatcher = symlinkPattern.matcher(line); + if (symlinkMatcher.find()) { + /* + * TODO: If symlink points to a file in the same directory the path is not + * absolute!!! + */ + symlink = symlinkMatcher.group(1); + Log.d(RootCommands.TAG, "Symlink found: " + symlink); + } else { + Log.d(RootCommands.TAG, "No symlink found!"); + } + } catch (Exception e) { + Log.e(RootCommands.TAG, "Error with regex!", e); + } + } + } + + @Override + public void afterExecution(int id, int exitCode) { + } + + } + + /** + * @param file + * String that represent the file, including the full path to the file and its name. + * @param followSymlinks + * @return File permissions as String, for example: 777, returns null on error + * @throws java.io.IOException + * @throws java.util.concurrent.TimeoutException + * @throws BrokenBusyboxException + * + */ + public String getFilePermissions(String file) throws BrokenBusyboxException, TimeoutException, + IOException { + Log.d(RootCommands.TAG, "Checking permissions for " + file); + + String permissions = null; + + if (fileExists(file)) { + Log.d(RootCommands.TAG, file + " was found."); + + LsCommand lsCommand = new LsCommand(file); + shell.add(lsCommand).waitForFinish(); + + permissions = lsCommand.getPermissions(); + } + + return permissions; + } + + /** + * Sets permission of file + * + * @param file + * absolute path to file + * @param permissions + * String like 777 + * @return true if command worked + * @throws BrokenBusyboxException + * @throws java.util.concurrent.TimeoutException + * @throws java.io.IOException + */ + public boolean setFilePermissions(String file, String permissions) + throws BrokenBusyboxException, TimeoutException, IOException { + Log.d(RootCommands.TAG, "Set permissions of " + file + " to " + permissions); + + SimpleCommand chmodCommand = new SimpleCommand("chmod " + permissions + " " + file); + shell.add(chmodCommand).waitForFinish(); + + if (chmodCommand.getExitCode() == 0) { + return true; + } else { + return false; + } + } + + /** + * This will return a String that represent the symlink for a specified file. + * + * @param file + * The path to the file to get the Symlink for. (must have absolute path) + * + * @return A String that represent the symlink for a specified file or null if no symlink + * exists. + * @throws java.io.IOException + * @throws java.util.concurrent.TimeoutException + * @throws BrokenBusyboxException + */ + public String getSymlink(String file) throws BrokenBusyboxException, TimeoutException, + IOException { + Log.d(RootCommands.TAG, "Find symlink for " + file); + + String symlink = null; + + LsCommand lsCommand = new LsCommand(file); + shell.add(lsCommand).waitForFinish(); + + symlink = lsCommand.getSymlink(); + + return symlink; + } + + /** + * Copys a file to a destination. Because cp is not available on all android devices, we use dd + * or cat. + * + * @param source + * example: /data/data/org.adaway/files/hosts + * @param destination + * example: /system/etc/hosts + * @param remountAsRw + * remounts the destination as read/write before writing to it + * @param preserveFileAttributes + * tries to copy file attributes from source to destination, if only cat is available + * only permissions are preserved + * @return true if it was successfully copied + * @throws BrokenBusyboxException + * @throws java.io.IOException + * @throws java.util.concurrent.TimeoutException + */ + public boolean copyFile(String source, String destination, boolean remountAsRw, + boolean preservePermissions) throws BrokenBusyboxException, IOException, + TimeoutException { + + /* + * dd can only copy files, but we can not check if the source is a file without invoking + * shell commands, because from Java we probably have no read access, thus we only check if + * they are ending with trailing slashes + */ + if (source.endsWith("/") || destination.endsWith("/")) { + throw new FileNotFoundException("dd can only copy files!"); + } + + // remount destination as read/write before copying to it + if (remountAsRw) { + if (!remount(destination, "RW")) { + Log.d(RootCommands.TAG, + "Remounting failed! There is probably no need to remount this partition!"); + } + } + + // get permissions of source before overwriting + String permissions = null; + if (preservePermissions) { + permissions = getFilePermissions(source); + } + + boolean commandSuccess = false; + + SimpleCommand ddCommand = new SimpleCommand("dd if=" + source + " of=" + + destination); + shell.add(ddCommand).waitForFinish(); + + if (ddCommand.getExitCode() == 0) { + commandSuccess = true; + } else { + // try cat if dd fails + SimpleCommand catCommand = new SimpleCommand("cat " + source + " > " + + destination); + shell.add(catCommand).waitForFinish(); + + if (catCommand.getExitCode() == 0) { + commandSuccess = true; + } + } + + // set back permissions from source to destination + if (preservePermissions) { + setFilePermissions(destination, permissions); + } + + // remount destination back to read only + if (remountAsRw) { + if (!remount(destination, "RO")) { + Log.d(RootCommands.TAG, + "Remounting failed! There is probably no need to remount this partition!"); + } + } + + return commandSuccess; + } + + public static final int REBOOT_HOTREBOOT = 1; + public static final int REBOOT_REBOOT = 2; + public static final int REBOOT_SHUTDOWN = 3; + public static final int REBOOT_RECOVERY = 4; + + /** + * Shutdown or reboot device. Possible actions are REBOOT_HOTREBOOT, REBOOT_REBOOT, + * REBOOT_SHUTDOWN, REBOOT_RECOVERY + * + * @param action + * @throws java.io.IOException + * @throws java.util.concurrent.TimeoutException + * @throws BrokenBusyboxException + */ + public void reboot(int action) throws BrokenBusyboxException, TimeoutException, IOException { + if (action == REBOOT_HOTREBOOT) { + killAll("system_server"); + // or: killAll("zygote"); + } else { + String command; + switch (action) { + case REBOOT_REBOOT: + command = "reboot"; + break; + case REBOOT_SHUTDOWN: + command = "reboot -p"; + break; + case REBOOT_RECOVERY: + command = "reboot recovery"; + break; + default: + command = "reboot"; + break; + } + + SimpleCommand rebootCommand = new SimpleCommand(command); + shell.add(rebootCommand).waitForFinish(); + + if (rebootCommand.getExitCode() == -1) { + Log.e(RootCommands.TAG, "Reboot failed!"); + } + } + } + + /** + * This command checks if a file exists + */ + private class FileExistsCommand extends Command { + private String file; + private boolean fileExists = false; + + public FileExistsCommand(String file) { + super("ls " + file); + this.file = file; + } + + public boolean isFileExists() { + return fileExists; + } + + @Override + public void output(int id, String line) { + if (line.trim().equals(file)) { + fileExists = true; + } + } + + @Override + public void afterExecution(int id, int exitCode) { + } + + } + + /** + * Use this to check whether or not a file exists on the filesystem. + * + * @param file + * String that represent the file, including the full path to the file and its name. + * + * @return a boolean that will indicate whether or not the file exists. + * @throws java.io.IOException + * @throws java.util.concurrent.TimeoutException + * @throws BrokenBusyboxException + * + */ + public boolean fileExists(String file) throws BrokenBusyboxException, TimeoutException, + IOException { + FileExistsCommand fileExistsCommand = new FileExistsCommand(file); + shell.add(fileExistsCommand).waitForFinish(); + + if (fileExistsCommand.isFileExists()) { + return true; + } else { + return false; + } + } + + public abstract class WithPermissions { + abstract void whileHavingPermissions(); + } + + /** + * Execute user defined Java code while having temporary permissions on a file + * + * @param file + * @param withPermissions + * @throws BrokenBusyboxException + * @throws java.util.concurrent.TimeoutException + * @throws java.io.IOException + */ + public void withPermission(String file, String permission, WithPermissions withPermissions) + throws BrokenBusyboxException, TimeoutException, IOException { + String oldPermissions = getFilePermissions(file); + + // set permissions (If set to 666, then Dalvik VM can also write to that file!) + setFilePermissions(file, permission); + + // execute user defined code + withPermissions.whileHavingPermissions(); + + // set back to old permissions + setFilePermissions(file, oldPermissions); + } + + /** + * Execute user defined Java code while having temporary write permissions on a file using chmod + * 666 + * + * @param file + * @param withWritePermissions + * @throws BrokenBusyboxException + * @throws java.util.concurrent.TimeoutException + * @throws java.io.IOException + */ + public void withWritePermissions(String file, WithPermissions withWritePermissions) + throws BrokenBusyboxException, TimeoutException, IOException { + withPermission(file, "666", withWritePermissions); + } + + /** + * Sets system clock using /dev/alarm + * + * @param millis + * @throws BrokenBusyboxException + * @throws java.util.concurrent.TimeoutException + * @throws java.io.IOException + */ + public void setSystemClock(final long millis) throws BrokenBusyboxException, TimeoutException, + IOException { + withWritePermissions("/dev/alarm", new WithPermissions() { + + @Override + void whileHavingPermissions() { + SystemClock.setCurrentTimeMillis(millis); + } + }); + } + + /** + * Adjust system clock by offset using /dev/alarm + * + * @param offset + * @throws BrokenBusyboxException + * @throws java.util.concurrent.TimeoutException + * @throws java.io.IOException + */ + public void adjustSystemClock(final long offset) throws BrokenBusyboxException, + TimeoutException, IOException { + withWritePermissions("/dev/alarm", new WithPermissions() { + + @Override + void whileHavingPermissions() { + SystemClock.setCurrentTimeMillis(System.currentTimeMillis() + offset); + } + }); + } + + /** + * This will take a path, which can contain the file name as well, and attempt to remount the + * underlying partition. + * + * For example, passing in the following string: + * "/system/bin/some/directory/that/really/would/never/exist" will result in /system ultimately + * being remounted. However, keep in mind that the longer the path you supply, the more work + * this has to do, and the slower it will run. + * + * @param file + * file path + * @param mountType + * mount type: pass in RO (Read only) or RW (Read Write) + * @return a boolean which indicates whether or not the partition has been + * remounted as specified. + */ + public boolean remount(String file, String mountType) { + // Recieved a request, get an instance of Remounter + Remounter remounter = new Remounter(shell); + // send the request + return (remounter.remount(file, mountType)); + } + + /** + * This will tell you how the specified mount is mounted. rw, ro, etc... + * + * @param The + * mount you want to check + * + * @return String What the mount is mounted as. + * @throws Exception + * if we cannot determine how the mount is mounted. + */ + public String getMountedAs(String path) throws Exception { + ArrayList mounts = Remounter.getMounts(); + if (mounts != null) { + for (Mount mount : mounts) { + if (path.contains(mount.getMountPoint().getAbsolutePath())) { + Log.d(RootCommands.TAG, (String) mount.getFlags().toArray()[0]); + return (String) mount.getFlags().toArray()[0]; + } + } + + throw new Exception(); + } else { + throw new Exception(); + } + } + + /** + * Check if there is enough space on partition where target is located + * + * @param size + * size of file to put on partition + * @param target + * path where to put the file + * + * @return true if it will fit on partition of target, false if it will not fit. + */ + public boolean hasEnoughSpaceOnPartition(String target, long size) { + try { + // new File(target).getFreeSpace() (API 9) is not working on data partition + + // get directory without file + String directory = new File(target).getParent().toString(); + + StatFs stat = new StatFs(directory); + long blockSize = stat.getBlockSize(); + long availableBlocks = stat.getAvailableBlocks(); + long availableSpace = availableBlocks * blockSize; + + Log.i(RootCommands.TAG, "Checking for enough space: Target: " + target + + ", directory: " + directory + " size: " + size + ", availableSpace: " + + availableSpace); + + if (size < availableSpace) { + return true; + } else { + Log.e(RootCommands.TAG, "Not enough space on partition!"); + return false; + } + } catch (Exception e) { + // if new StatFs(directory) fails catch IllegalArgumentException and just return true as + // workaround + Log.e(RootCommands.TAG, "Problem while getting available space on partition!", e); + return true; + } + } + + /** + * TODO: Not tested! + * + * @param toggle + * @throws java.io.IOException + * @throws java.util.concurrent.TimeoutException + * @throws BrokenBusyboxException + */ + public void toggleAdbDaemon(boolean toggle) throws BrokenBusyboxException, TimeoutException, + IOException { + SimpleCommand disableAdb = new SimpleCommand("setprop persist.service.adb.enable 0", + "stop adbd"); + SimpleCommand enableAdb = new SimpleCommand("setprop persist.service.adb.enable 1", + "stop adbd", "sleep 1", "start adbd"); + + if (toggle) { + shell.add(enableAdb).waitForFinish(); + } else { + shell.add(disableAdb).waitForFinish(); + } + } + +} diff --git a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/Command.java b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/Command.java index 9887e57..488cbae 100644 --- a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/Command.java +++ b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/Command.java @@ -1,155 +1,155 @@ - - -package org.sufficientlysecure.rootcommands.command; - -import java.io.IOException; -import java.io.OutputStream; -import java.util.concurrent.TimeoutException; - -import org.sufficientlysecure.rootcommands.RootCommands; -import org.sufficientlysecure.rootcommands.Shell; -import org.sufficientlysecure.rootcommands.util.BrokenBusyboxException; -import org.sufficientlysecure.rootcommands.util.Log; - -public abstract class Command { - final String command[]; - boolean finished = false; - boolean brokenBusyboxDetected = false; - int exitCode; - int id; - int timeout = RootCommands.DEFAULT_TIMEOUT; - Shell shell = null; - - public Command(String... command) { - this.command = command; - } - - public Command(int timeout, String... command) { - this.command = command; - this.timeout = timeout; - } - - /** - * This is called from Shell after adding it - * - * @param shell - * @param id - */ - public void addedToShell(Shell shell, int id) { - this.shell = shell; - this.id = id; - } - - /** - * Gets command string executed on the shell - * - * @return - */ - public String getCommand() { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < command.length; i++) { - // redirect stderr to stdout - sb.append(command[i] + " 2>&1"); - sb.append('\n'); - } - Log.d(RootCommands.TAG, "Sending command(s): " + sb.toString()); - return sb.toString(); - } - - public void writeCommand(OutputStream out) throws IOException { - out.write(getCommand().getBytes()); - } - - public void processOutput(String line) { - Log.d(RootCommands.TAG, "ID: " + id + ", Output: " + line); - - /* - * Try to detect broken toolbox/busybox binaries (see - * https://code.google.com/p/busybox-android/issues/detail?id=1) - * - * It is giving "Value too large for defined data type" on certain file operations (e.g. ls - * and chown) in certain directories (e.g. /data/data) - */ - if (line.contains("Value too large for defined data type")) { - Log.e(RootCommands.TAG, "Busybox is broken with high probability due to line: " + line); - brokenBusyboxDetected = true; - } - - // now execute specific output parsing - output(id, line); - } - - public abstract void output(int id, String line); - - public void processAfterExecution(int exitCode) { - Log.d(RootCommands.TAG, "ID: " + id + ", ExitCode: " + exitCode); - - afterExecution(id, exitCode); - } - - public abstract void afterExecution(int id, int exitCode); - - public void commandFinished(int id) { - Log.d(RootCommands.TAG, "Command " + id + " finished."); - } - - public void setExitCode(int code) { - synchronized (this) { - exitCode = code; - finished = true; - commandFinished(id); - this.notifyAll(); - } - } - - /** - * Close the shell - * - * @param reason - */ - public void terminate(String reason) { - try { - shell.close(); - Log.d(RootCommands.TAG, "Terminating the shell."); - terminated(reason); - } catch (IOException e) { - } - } - - public void terminated(String reason) { - setExitCode(-1); - Log.d(RootCommands.TAG, "Command " + id + " did not finish, because of " + reason); - } - - /** - * Waits for this command to finish and forwards exitCode into afterExecution method - * - * @param timeout - * @throws java.util.concurrent.TimeoutException - * @throws BrokenBusyboxException - */ - public void waitForFinish() throws TimeoutException, BrokenBusyboxException { - synchronized (this) { - while (!finished) { - try { - this.wait(timeout); - } catch (InterruptedException e) { - Log.e(RootCommands.TAG, "InterruptedException in waitForFinish()", e); - } - - if (!finished) { - finished = true; - terminate("Timeout"); - throw new TimeoutException("Timeout has occurred."); - } - } - - if (brokenBusyboxDetected) { - throw new BrokenBusyboxException(); - } - - processAfterExecution(exitCode); - } - } - + + +package org.sufficientlysecure.rootcommands.command; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.concurrent.TimeoutException; + +import org.sufficientlysecure.rootcommands.RootCommands; +import org.sufficientlysecure.rootcommands.Shell; +import org.sufficientlysecure.rootcommands.util.BrokenBusyboxException; +import org.sufficientlysecure.rootcommands.util.Log; + +public abstract class Command { + final String command[]; + boolean finished = false; + boolean brokenBusyboxDetected = false; + int exitCode; + int id; + int timeout = RootCommands.DEFAULT_TIMEOUT; + Shell shell = null; + + public Command(String... command) { + this.command = command; + } + + public Command(int timeout, String... command) { + this.command = command; + this.timeout = timeout; + } + + /** + * This is called from Shell after adding it + * + * @param shell + * @param id + */ + public void addedToShell(Shell shell, int id) { + this.shell = shell; + this.id = id; + } + + /** + * Gets command string executed on the shell + * + * @return + */ + public String getCommand() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < command.length; i++) { + // redirect stderr to stdout + sb.append(command[i] + " 2>&1"); + sb.append('\n'); + } + Log.d(RootCommands.TAG, "Sending command(s): " + sb.toString()); + return sb.toString(); + } + + public void writeCommand(OutputStream out) throws IOException { + out.write(getCommand().getBytes()); + } + + public void processOutput(String line) { + Log.d(RootCommands.TAG, "ID: " + id + ", Output: " + line); + + /* + * Try to detect broken toolbox/busybox binaries (see + * https://code.google.com/p/busybox-android/issues/detail?id=1) + * + * It is giving "Value too large for defined data type" on certain file operations (e.g. ls + * and chown) in certain directories (e.g. /data/data) + */ + if (line.contains("Value too large for defined data type")) { + Log.e(RootCommands.TAG, "Busybox is broken with high probability due to line: " + line); + brokenBusyboxDetected = true; + } + + // now execute specific output parsing + output(id, line); + } + + public abstract void output(int id, String line); + + public void processAfterExecution(int exitCode) { + Log.d(RootCommands.TAG, "ID: " + id + ", ExitCode: " + exitCode); + + afterExecution(id, exitCode); + } + + public abstract void afterExecution(int id, int exitCode); + + public void commandFinished(int id) { + Log.d(RootCommands.TAG, "Command " + id + " finished."); + } + + public void setExitCode(int code) { + synchronized (this) { + exitCode = code; + finished = true; + commandFinished(id); + this.notifyAll(); + } + } + + /** + * Close the shell + * + * @param reason + */ + public void terminate(String reason) { + try { + shell.close(); + Log.d(RootCommands.TAG, "Terminating the shell."); + terminated(reason); + } catch (IOException e) { + } + } + + public void terminated(String reason) { + setExitCode(-1); + Log.d(RootCommands.TAG, "Command " + id + " did not finish, because of " + reason); + } + + /** + * Waits for this command to finish and forwards exitCode into afterExecution method + * + * @param timeout + * @throws java.util.concurrent.TimeoutException + * @throws BrokenBusyboxException + */ + public void waitForFinish() throws TimeoutException, BrokenBusyboxException { + synchronized (this) { + while (!finished) { + try { + this.wait(timeout); + } catch (InterruptedException e) { + Log.e(RootCommands.TAG, "InterruptedException in waitForFinish()", e); + } + + if (!finished) { + finished = true; + terminate("Timeout"); + throw new TimeoutException("Timeout has occurred."); + } + } + + if (brokenBusyboxDetected) { + throw new BrokenBusyboxException(); + } + + processAfterExecution(exitCode); + } + } + } \ No newline at end of file diff --git a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/ExecutableCommand.java b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/ExecutableCommand.java index 1cd2a95..0bd92d7 100644 --- a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/ExecutableCommand.java +++ b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/ExecutableCommand.java @@ -1,51 +1,51 @@ - - -package org.sufficientlysecure.rootcommands.command; - -import java.io.File; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.os.Build; - -public abstract class ExecutableCommand extends Command { - public static final String EXECUTABLE_PREFIX = "lib"; - public static final String EXECUTABLE_SUFFIX = "_exec.so"; - - /** - * This class provides a way to use your own binaries! - * - * Include your own executables, renamed from * to lib*_exec.so, in your libs folder under the - * architecture directories. Now they will be deployed by Android the same way libraries are - * deployed! - * - * See README for more information how to use your own executables! - * - * @param context - * @param executableName - * @param parameters - */ - public ExecutableCommand(Context context, String executableName, String parameters) { - super(getLibDirectory(context) + File.separator + EXECUTABLE_PREFIX + executableName - + EXECUTABLE_SUFFIX + " " + parameters); - } - - /** - * Get full path to lib directory of app - * - * @return dir as String - */ - @SuppressLint("NewApi") - private static String getLibDirectory(Context context) { - if (Build.VERSION.SDK_INT >= 9) { - return context.getApplicationInfo().nativeLibraryDir; - } else { - return context.getApplicationInfo().dataDir + File.separator + "lib"; - } - } - - public abstract void output(int id, String line); - - public abstract void afterExecution(int id, int exitCode); - + + +package org.sufficientlysecure.rootcommands.command; + +import java.io.File; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.os.Build; + +public abstract class ExecutableCommand extends Command { + public static final String EXECUTABLE_PREFIX = "lib"; + public static final String EXECUTABLE_SUFFIX = "_exec.so"; + + /** + * This class provides a way to use your own binaries! + * + * Include your own executables, renamed from * to lib*_exec.so, in your libs folder under the + * architecture directories. Now they will be deployed by Android the same way libraries are + * deployed! + * + * See README for more information how to use your own executables! + * + * @param context + * @param executableName + * @param parameters + */ + public ExecutableCommand(Context context, String executableName, String parameters) { + super(getLibDirectory(context) + File.separator + EXECUTABLE_PREFIX + executableName + + EXECUTABLE_SUFFIX + " " + parameters); + } + + /** + * Get full path to lib directory of app + * + * @return dir as String + */ + @SuppressLint("NewApi") + private static String getLibDirectory(Context context) { + if (Build.VERSION.SDK_INT >= 9) { + return context.getApplicationInfo().nativeLibraryDir; + } else { + return context.getApplicationInfo().dataDir + File.separator + "lib"; + } + } + + public abstract void output(int id, String line); + + public abstract void afterExecution(int id, int exitCode); + } \ No newline at end of file diff --git a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/SimpleCommand.java b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/SimpleCommand.java index 8f11c05..b2091c8 100644 --- a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/SimpleCommand.java +++ b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/SimpleCommand.java @@ -1,29 +1,29 @@ - - -package org.sufficientlysecure.rootcommands.command; - -public class SimpleCommand extends Command { - private StringBuilder sb = new StringBuilder(); - - public SimpleCommand(String... command) { - super(command); - } - - @Override - public void output(int id, String line) { - sb.append(line).append('\n'); - } - - @Override - public void afterExecution(int id, int exitCode) { - } - - public String getOutput() { - return sb.toString(); - } - - public int getExitCode() { - return exitCode; - } - + + +package org.sufficientlysecure.rootcommands.command; + +public class SimpleCommand extends Command { + private StringBuilder sb = new StringBuilder(); + + public SimpleCommand(String... command) { + super(command); + } + + @Override + public void output(int id, String line) { + sb.append(line).append('\n'); + } + + @Override + public void afterExecution(int id, int exitCode) { + } + + public String getOutput() { + return sb.toString(); + } + + public int getExitCode() { + return exitCode; + } + } \ No newline at end of file diff --git a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/SimpleExecutableCommand.java b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/SimpleExecutableCommand.java index 1ead418..78fd2be 100644 --- a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/SimpleExecutableCommand.java +++ b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/command/SimpleExecutableCommand.java @@ -1,31 +1,31 @@ - - -package org.sufficientlysecure.rootcommands.command; - -import android.content.Context; - -public class SimpleExecutableCommand extends ExecutableCommand { - private StringBuilder sb = new StringBuilder(); - - public SimpleExecutableCommand(Context context, String executableName, String parameters) { - super(context, executableName, parameters); - } - - @Override - public void output(int id, String line) { - sb.append(line).append('\n'); - } - - @Override - public void afterExecution(int id, int exitCode) { - } - - public String getOutput() { - return sb.toString(); - } - - public int getExitCode() { - return exitCode; - } - + + +package org.sufficientlysecure.rootcommands.command; + +import android.content.Context; + +public class SimpleExecutableCommand extends ExecutableCommand { + private StringBuilder sb = new StringBuilder(); + + public SimpleExecutableCommand(Context context, String executableName, String parameters) { + super(context, executableName, parameters); + } + + @Override + public void output(int id, String line) { + sb.append(line).append('\n'); + } + + @Override + public void afterExecution(int id, int exitCode) { + } + + public String getOutput() { + return sb.toString(); + } + + public int getExitCode() { + return exitCode; + } + } \ No newline at end of file diff --git a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/util/BrokenBusyboxException.java b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/util/BrokenBusyboxException.java index 579518d..bf40bf4 100644 --- a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/util/BrokenBusyboxException.java +++ b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/util/BrokenBusyboxException.java @@ -1,18 +1,18 @@ - - -package org.sufficientlysecure.rootcommands.util; - -import java.io.IOException; - -public class BrokenBusyboxException extends IOException { - private static final long serialVersionUID = 8337358201589488409L; - - public BrokenBusyboxException() { - super(); - } - - public BrokenBusyboxException(String detailMessage) { - super(detailMessage); - } - -} + + +package org.sufficientlysecure.rootcommands.util; + +import java.io.IOException; + +public class BrokenBusyboxException extends IOException { + private static final long serialVersionUID = 8337358201589488409L; + + public BrokenBusyboxException() { + super(); + } + + public BrokenBusyboxException(String detailMessage) { + super(detailMessage); + } + +} diff --git a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/util/Log.java b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/util/Log.java index a9a9cb7..e55da98 100644 --- a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/util/Log.java +++ b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/util/Log.java @@ -1,69 +1,69 @@ - - -package org.sufficientlysecure.rootcommands.util; - -import org.sufficientlysecure.rootcommands.RootCommands; - -/** - * Wraps Android Logging to enable or disable debug output using Constants - * - */ -public final class Log { - - public static void v(String tag, String msg) { - if (RootCommands.DEBUG) { - android.util.Log.v(tag, msg); - } - } - - public static void v(String tag, String msg, Throwable tr) { - if (RootCommands.DEBUG) { - android.util.Log.v(tag, msg, tr); - } - } - - public static void d(String tag, String msg) { - if (RootCommands.DEBUG) { - android.util.Log.d(tag, msg); - } - } - - public static void d(String tag, String msg, Throwable tr) { - if (RootCommands.DEBUG) { - android.util.Log.d(tag, msg, tr); - } - } - - public static void i(String tag, String msg) { - if (RootCommands.DEBUG) { - android.util.Log.i(tag, msg); - } - } - - public static void i(String tag, String msg, Throwable tr) { - if (RootCommands.DEBUG) { - android.util.Log.i(tag, msg, tr); - } - } - - public static void w(String tag, String msg) { - android.util.Log.w(tag, msg); - } - - public static void w(String tag, String msg, Throwable tr) { - android.util.Log.w(tag, msg, tr); - } - - public static void w(String tag, Throwable tr) { - android.util.Log.w(tag, tr); - } - - public static void e(String tag, String msg) { - android.util.Log.e(tag, msg); - } - - public static void e(String tag, String msg, Throwable tr) { - android.util.Log.e(tag, msg, tr); - } - -} + + +package org.sufficientlysecure.rootcommands.util; + +import org.sufficientlysecure.rootcommands.RootCommands; + +/** + * Wraps Android Logging to enable or disable debug output using Constants + * + */ +public final class Log { + + public static void v(String tag, String msg) { + if (RootCommands.DEBUG) { + android.util.Log.v(tag, msg); + } + } + + public static void v(String tag, String msg, Throwable tr) { + if (RootCommands.DEBUG) { + android.util.Log.v(tag, msg, tr); + } + } + + public static void d(String tag, String msg) { + if (RootCommands.DEBUG) { + android.util.Log.d(tag, msg); + } + } + + public static void d(String tag, String msg, Throwable tr) { + if (RootCommands.DEBUG) { + android.util.Log.d(tag, msg, tr); + } + } + + public static void i(String tag, String msg) { + if (RootCommands.DEBUG) { + android.util.Log.i(tag, msg); + } + } + + public static void i(String tag, String msg, Throwable tr) { + if (RootCommands.DEBUG) { + android.util.Log.i(tag, msg, tr); + } + } + + public static void w(String tag, String msg) { + android.util.Log.w(tag, msg); + } + + public static void w(String tag, String msg, Throwable tr) { + android.util.Log.w(tag, msg, tr); + } + + public static void w(String tag, Throwable tr) { + android.util.Log.w(tag, tr); + } + + public static void e(String tag, String msg) { + android.util.Log.e(tag, msg); + } + + public static void e(String tag, String msg, Throwable tr) { + android.util.Log.e(tag, msg, tr); + } + +} diff --git a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/util/RootAccessDeniedException.java b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/util/RootAccessDeniedException.java index 1520a5b..650f1ed 100644 --- a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/util/RootAccessDeniedException.java +++ b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/util/RootAccessDeniedException.java @@ -1,18 +1,18 @@ - - -package org.sufficientlysecure.rootcommands.util; - -import java.io.IOException; - -public class RootAccessDeniedException extends IOException { - private static final long serialVersionUID = 9088998884166225540L; - - public RootAccessDeniedException() { - super(); - } - - public RootAccessDeniedException(String detailMessage) { - super(detailMessage); - } - -} + + +package org.sufficientlysecure.rootcommands.util; + +import java.io.IOException; + +public class RootAccessDeniedException extends IOException { + private static final long serialVersionUID = 9088998884166225540L; + + public RootAccessDeniedException() { + super(); + } + + public RootAccessDeniedException(String detailMessage) { + super(detailMessage); + } + +} diff --git a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/util/UnsupportedArchitectureException.java b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/util/UnsupportedArchitectureException.java index 4124d9b..e8535d2 100644 --- a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/util/UnsupportedArchitectureException.java +++ b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/util/UnsupportedArchitectureException.java @@ -1,16 +1,16 @@ - - -package org.sufficientlysecure.rootcommands.util; - -public class UnsupportedArchitectureException extends Exception { - private static final long serialVersionUID = 7826528799780001655L; - - public UnsupportedArchitectureException() { - super(); - } - - public UnsupportedArchitectureException(String detailMessage) { - super(detailMessage); - } - -} + + +package org.sufficientlysecure.rootcommands.util; + +public class UnsupportedArchitectureException extends Exception { + private static final long serialVersionUID = 7826528799780001655L; + + public UnsupportedArchitectureException() { + super(); + } + + public UnsupportedArchitectureException(String detailMessage) { + super(detailMessage); + } + +} diff --git a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/util/Utils.java b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/util/Utils.java index 98d90d1..e9a382f 100644 --- a/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/util/Utils.java +++ b/libraries/RootCommands/src/main/java/org/sufficientlysecure/rootcommands/util/Utils.java @@ -1,89 +1,89 @@ - - -package org.sufficientlysecure.rootcommands.util; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Map; - -import org.sufficientlysecure.rootcommands.RootCommands; - -public class Utils { - /* - * The emulator and ADP1 device both have a su binary in /system/xbin/su, but it doesn't allow - * apps to use it (su app_29 $ su su: uid 10029 not allowed to su). - * - * Cyanogen used to have su in /system/bin/su, in newer versions it's a symlink to - * /system/xbin/su. - * - * The Archos tablet has it in /data/bin/su, since they don't have write access to /system yet. - */ - static final String[] BinaryPlaces = { "/data/bin/", "/system/bin/", "/system/xbin/", "/sbin/", - "/data/local/xbin/", "/data/local/bin/", "/system/sd/xbin/", "/system/bin/failsafe/", - "/data/local/" }; - - /** - * Determine the path of the su executable. - * - * Code from https://github.com/miracle2k/android-autostarts, use under Apache License was - * agreed by Michael Elsdörfer - */ - public static String getSuPath() { - for (String p : BinaryPlaces) { - File su = new File(p + "su"); - if (su.exists()) { - Log.d(RootCommands.TAG, "su found at: " + p); - return su.getAbsolutePath(); - } else { - Log.v(RootCommands.TAG, "No su in: " + p); - } - } - Log.d(RootCommands.TAG, "No su found in a well-known location, " + "will just use \"su\"."); - return "su"; - } - - /** - * This code is adapted from java.lang.ProcessBuilder.start(). - * - * The problem is that Android doesn't allow us to modify the map returned by - * ProcessBuilder.environment(), even though the docstring indicates that it should. This is - * because it simply returns the SystemEnvironment object that System.getenv() gives us. The - * relevant portion in the source code is marked as "// android changed", so presumably it's not - * the case in the original version of the Apache Harmony project. - * - * Note that simply passing the environment variables we want to Process.exec won't be good - * enough, since that would override the environment we inherited completely. - * - * We needed to be able to set a CLASSPATH environment variable for our new process in order to - * use the "app_process" command directly. Note: "app_process" takes arguments passed on to the - * Dalvik VM as well; this might be an alternative way to set the class path. - * - * Code from https://github.com/miracle2k/android-autostarts, use under Apache License was - * agreed by Michael Elsdörfer - */ - public static Process runWithEnv(String command, ArrayList customAddedEnv, - String baseDirectory) throws IOException { - - Map environment = System.getenv(); - String[] envArray = new String[environment.size() - + (customAddedEnv != null ? customAddedEnv.size() : 0)]; - int i = 0; - for (Map.Entry entry : environment.entrySet()) { - envArray[i++] = entry.getKey() + "=" + entry.getValue(); - } - if (customAddedEnv != null) { - for (String entry : customAddedEnv) { - envArray[i++] = entry; - } - } - - Process process; - if (baseDirectory == null) { - process = Runtime.getRuntime().exec(command, envArray, null); - } else { - process = Runtime.getRuntime().exec(command, envArray, new File(baseDirectory)); - } - return process; - } -} + + +package org.sufficientlysecure.rootcommands.util; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Map; + +import org.sufficientlysecure.rootcommands.RootCommands; + +public class Utils { + /* + * The emulator and ADP1 device both have a su binary in /system/xbin/su, but it doesn't allow + * apps to use it (su app_29 $ su su: uid 10029 not allowed to su). + * + * Cyanogen used to have su in /system/bin/su, in newer versions it's a symlink to + * /system/xbin/su. + * + * The Archos tablet has it in /data/bin/su, since they don't have write access to /system yet. + */ + static final String[] BinaryPlaces = { "/data/bin/", "/system/bin/", "/system/xbin/", "/sbin/", + "/data/local/xbin/", "/data/local/bin/", "/system/sd/xbin/", "/system/bin/failsafe/", + "/data/local/" }; + + /** + * Determine the path of the su executable. + * + * Code from https://github.com/miracle2k/android-autostarts, use under Apache License was + * agreed by Michael Elsdörfer + */ + public static String getSuPath() { + for (String p : BinaryPlaces) { + File su = new File(p + "su"); + if (su.exists()) { + Log.d(RootCommands.TAG, "su found at: " + p); + return su.getAbsolutePath(); + } else { + Log.v(RootCommands.TAG, "No su in: " + p); + } + } + Log.d(RootCommands.TAG, "No su found in a well-known location, " + "will just use \"su\"."); + return "su"; + } + + /** + * This code is adapted from java.lang.ProcessBuilder.start(). + * + * The problem is that Android doesn't allow us to modify the map returned by + * ProcessBuilder.environment(), even though the docstring indicates that it should. This is + * because it simply returns the SystemEnvironment object that System.getenv() gives us. The + * relevant portion in the source code is marked as "// android changed", so presumably it's not + * the case in the original version of the Apache Harmony project. + * + * Note that simply passing the environment variables we want to Process.exec won't be good + * enough, since that would override the environment we inherited completely. + * + * We needed to be able to set a CLASSPATH environment variable for our new process in order to + * use the "app_process" command directly. Note: "app_process" takes arguments passed on to the + * Dalvik VM as well; this might be an alternative way to set the class path. + * + * Code from https://github.com/miracle2k/android-autostarts, use under Apache License was + * agreed by Michael Elsdörfer + */ + public static Process runWithEnv(String command, ArrayList customAddedEnv, + String baseDirectory) throws IOException { + + Map environment = System.getenv(); + String[] envArray = new String[environment.size() + + (customAddedEnv != null ? customAddedEnv.size() : 0)]; + int i = 0; + for (Map.Entry entry : environment.entrySet()) { + envArray[i++] = entry.getKey() + "=" + entry.getValue(); + } + if (customAddedEnv != null) { + for (String entry : customAddedEnv) { + envArray[i++] = entry; + } + } + + Process process; + if (baseDirectory == null) { + process = Runtime.getRuntime().exec(command, envArray, null); + } else { + process = Runtime.getRuntime().exec(command, envArray, new File(baseDirectory)); + } + return process; + } +} diff --git a/libraries/sharedCode/.gitignore b/libraries/sharedCode/.gitignore index 796b96d..3543521 100644 --- a/libraries/sharedCode/.gitignore +++ b/libraries/sharedCode/.gitignore @@ -1 +1 @@ -/build +/build diff --git a/libraries/sharedCode/build.gradle b/libraries/sharedCode/build.gradle index e15a501..d87b778 100644 --- a/libraries/sharedCode/build.gradle +++ b/libraries/sharedCode/build.gradle @@ -1,78 +1,77 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 . - */ - - - -apply plugin: 'com.android.library' - -android { - lintOptions { - disable 'MissingTranslation', 'ExtraTranslation' - } - - compileSdkVersion 21 - buildToolsVersion '21.0.2' - - defaultConfig { - applicationId "sharedcode.turboeditor" - minSdkVersion 11 - targetSdkVersion 21 - versionCode 1 - versionName "1.0" - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_7 - targetCompatibility JavaVersion.VERSION_1_7 - } - buildTypes { - release { - runProguard false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' - } - } - - lintOptions { - abortOnError false - } - - packagingOptions { - exclude 'META-INF/LICENSE.txt' - exclude 'META-INF/NOTICE.txt' - } -} - -dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - compile project(':libraries:RootCommands') - compile project(':libraries:FloatingActionButton') - //compile('de.greenrobot:eventbus:2.2.1') { - // exclude module: 'support-v4' - // } - // compile 'com.googlecode.juniversalchardet:juniversalchardet:1.0.3' - compile 'org.apache.commons:commons-lang3:3.1' - compile files('libs/juniversalchardet-1.0.3.jar') - compile ("com.android.support:appcompat-v7:21.+") { - exclude group: 'com.android.support', module: 'support-v4' - } - compile 'com.android.support:support-v4:21.+' - compile 'com.github.gabrielemariotti.changeloglib:library:1.5.1' - compile 'commons-io:commons-io:2.4' - compile 'com.android.support:support-annotations:20.0.0' -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 . + */ + + + +apply plugin: 'com.android.library' + +android { + lintOptions { + disable 'MissingTranslation', 'ExtraTranslation' + } + + compileSdkVersion 21 + buildToolsVersion '21.0.2' + + defaultConfig { + applicationId "sharedcode.turboeditor" + minSdkVersion 11 + targetSdkVersion 21 + versionCode 1 + versionName "1.0" + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 + } + buildTypes { + release { + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + lintOptions { + abortOnError false + } + + packagingOptions { + exclude 'META-INF/LICENSE.txt' + exclude 'META-INF/NOTICE.txt' + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile project(':libraries:RootCommands') + compile project(':libraries:FloatingActionButton') + //compile('de.greenrobot:eventbus:2.2.1') { + // exclude module: 'support-v4' + // } + // compile 'com.googlecode.juniversalchardet:juniversalchardet:1.0.3' + compile 'org.apache.commons:commons-lang3:3.1' + compile files('libs/juniversalchardet-1.0.3.jar') + compile ("com.android.support:appcompat-v7:21.+") { + exclude group: 'com.android.support', module: 'support-v4' + } + compile 'com.android.support:support-v4:21.+' + compile 'com.github.gabrielemariotti.changeloglib:library:1.5.1' + compile 'commons-io:commons-io:2.4' + compile 'com.android.support:support-annotations:20.0.0' +} diff --git a/libraries/sharedCode/proguard-rules.pro b/libraries/sharedCode/proguard-rules.pro index 9f604d1..a946ab9 100644 --- a/libraries/sharedCode/proguard-rules.pro +++ b/libraries/sharedCode/proguard-rules.pro @@ -1,17 +1,17 @@ -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in C:/Users/Vlad/AppData/Local/Android/android-sdk/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the proguardFiles -# directive in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in C:/Users/Vlad/AppData/Local/Android/android-sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/libraries/sharedCode/src/androidTest/java/sharedcode/turboeditor/ApplicationTest.java b/libraries/sharedCode/src/androidTest/java/sharedcode/turboeditor/ApplicationTest.java index 8a646ed..a195b1c 100644 --- a/libraries/sharedCode/src/androidTest/java/sharedcode/turboeditor/ApplicationTest.java +++ b/libraries/sharedCode/src/androidTest/java/sharedcode/turboeditor/ApplicationTest.java @@ -1,32 +1,32 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor; - -import android.app.Application; -import android.test.ApplicationTestCase; - -/** - * Testing Fundamentals - */ -public class ApplicationTest extends ApplicationTestCase { - public ApplicationTest() { - super(Application.class); - } +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } } \ No newline at end of file diff --git a/libraries/sharedCode/src/main/AndroidManifest.xml b/libraries/sharedCode/src/main/AndroidManifest.xml index ee4199c..da3a89d 100644 --- a/libraries/sharedCode/src/main/AndroidManifest.xml +++ b/libraries/sharedCode/src/main/AndroidManifest.xml @@ -1,23 +1,23 @@ - - - - - + + + + + diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/activity/MainActivity.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/activity/MainActivity.java index 132b5e5..a4dbfd0 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/activity/MainActivity.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/activity/MainActivity.java @@ -1,2124 +1,2120 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.activity; - -import android.app.ProgressDialog; -import android.content.ActivityNotFoundException; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.res.Configuration; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Typeface; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.preference.PreferenceManager; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.view.MenuItemCompat; -import android.support.v7.app.ActionBarActivity; -import android.support.v7.app.ActionBarDrawerToggle; -import android.support.v7.widget.ShareActionProvider; -import android.support.v7.widget.Toolbar; -import android.text.Editable; -import android.text.InputType; -import android.text.Selection; -import android.text.Spannable; -import android.text.TextPaint; -import android.text.TextUtils; -import android.text.TextWatcher; -import android.text.method.KeyListener; -import android.text.style.ForegroundColorSpan; -import android.text.style.UnderlineSpan; -import android.util.AttributeSet; -import android.view.Gravity; -import android.view.KeyEvent; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.WindowManager; -import android.view.inputmethod.InputMethodManager; -import android.widget.AdapterView; -import android.widget.EditText; -import android.widget.HorizontalScrollView; -import android.widget.ListView; -import android.widget.Toast; - -import com.faizmalkani.floatingactionbutton.FloatingActionButton; - -import org.apache.commons.io.FilenameUtils; -import org.apache.commons.lang3.ArrayUtils; -import org.sufficientlysecure.rootcommands.Shell; -import org.sufficientlysecure.rootcommands.Toolbox; - -import java.io.File; -import java.io.UnsupportedEncodingException; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import sharedcode.turboeditor.R; -import sharedcode.turboeditor.adapter.AdapterDrawer; -import sharedcode.turboeditor.dialogfragment.ChangelogDialog; -import sharedcode.turboeditor.dialogfragment.FileInfoDialog; -import sharedcode.turboeditor.dialogfragment.FindTextDialog; -import sharedcode.turboeditor.dialogfragment.NewFileDetailsDialog; -import sharedcode.turboeditor.dialogfragment.NumberPickerDialog; -import sharedcode.turboeditor.dialogfragment.SaveFileDialog; -import sharedcode.turboeditor.preferences.PreferenceHelper; -import sharedcode.turboeditor.task.SaveFileTask; -import sharedcode.turboeditor.texteditor.EditTextPadding; -import sharedcode.turboeditor.texteditor.FileUtils; -import sharedcode.turboeditor.texteditor.LineUtils; -import sharedcode.turboeditor.texteditor.PageSystem; -import sharedcode.turboeditor.texteditor.PageSystemButtons; -import sharedcode.turboeditor.texteditor.Patterns; -import sharedcode.turboeditor.texteditor.SearchResult; -import sharedcode.turboeditor.util.AccessStorageApi; -import sharedcode.turboeditor.util.AnimationUtils; -import sharedcode.turboeditor.util.AppInfoHelper; -import sharedcode.turboeditor.util.EventBusEvents; -import sharedcode.turboeditor.util.MimeTypes; -import sharedcode.turboeditor.util.ProCheckUtils; -import sharedcode.turboeditor.util.ThemeUtils; -import sharedcode.turboeditor.views.CustomDrawerLayout; -import sharedcode.turboeditor.views.DialogHelper; -import sharedcode.turboeditor.views.GoodScrollView; - -import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.ENCODING; -import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.FONT_SIZE; -import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.LINE_NUMERS; -import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.MONOSPACE; -import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.READ_ONLY; -import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.SYNTAX; -import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.TEXT_SUGGESTIONS; -import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.THEME_CHANGE; -import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.WRAP_CONTENT; - -public abstract class MainActivity extends ActionBarActivity implements FindTextDialog - .SearchDialogInterface, GoodScrollView.ScrollInterface, PageSystem.PageSystemInterface, - PageSystemButtons.PageButtonsInterface, NumberPickerDialog.INumberPickerDialog, SaveFileDialog.ISaveDialog, - AdapterView.OnItemClickListener, AdapterDrawer.Callbacks{ - - //region VARIABLES - private static final int - ID_SELECT_ALL = android.R.id.selectAll; - private static final int ID_CUT = android.R.id.cut; - private static final int ID_COPY = android.R.id.copy; - private static final int ID_PASTE = android.R.id.paste; - private static final int SELECT_FILE_CODE = 121; - private static final int KITKAT_OPEN_REQUEST_CODE = 41; - private static final int SYNTAX_DELAY_MILLIS_SHORT = 250; - private static final int SYNTAX_DELAY_MILLIS_LONG = 1500; - private static final int ID_UNDO = R.id.im_undo; - private static final int ID_REDO = R.id.im_redo; - private static final int CHARS_TO_COLOR = 2500; - private static final Handler updateHandler = new Handler(); - private static final Runnable colorRunnable_duringEditing = - new Runnable() { - @Override - public void run() { - mEditor.replaceTextKeepCursor(null, true); - } - }; - private static final Runnable colorRunnable_duringScroll = - new Runnable() { - @Override - public void run() { - mEditor.replaceTextKeepCursor(null, false); - } - }; - private static boolean fileOpened = false; - private static String fileExtension; - /* - * This class provides a handy way to tie together the functionality of - * {@link DrawerLayout} and the framework ActionBar to implement the recommended - * design for navigation drawers. - */ - private static ActionBarDrawerToggle mDrawerToggle; - /* - * The Drawer Layout - */ - private static CustomDrawerLayout mDrawerLayout; - private static GoodScrollView verticalScroll; - private static String sFilePath = ""; - private static Editor mEditor; - private static HorizontalScrollView horizontalScroll; - private boolean searchingText; - private static SearchResult searchResult; - private static PageSystem pageSystem; - private static PageSystemButtons pageSystemButtons; - private static String currentEncoding = "UTF-8"; - private static Toolbar toolbar; - - /* - Navigation Drawer - */ - private static AdapterDrawer arrayAdapter; - private static LinkedList files; - //endregion - - //region Activity facts - - @Override - protected void onCreate(Bundle savedInstanceState) { - // set the windows background - ThemeUtils.setWindowsBackground(this); - // super!! - super.onCreate(savedInstanceState); - // setup the layout - setContentView(R.layout.activity_home); - toolbar = (Toolbar) findViewById(R.id.my_awesome_toolbar); - setSupportActionBar(toolbar); - // setup the navigation drawer - setupNavigationDrawer(); - // reset text editor - setupTextEditor(); - hideTextEditor(); - /* First Time we open this activity */ - if (savedInstanceState == null) { - // Open - mDrawerLayout.openDrawer(Gravity.START); - // Set the default title - getSupportActionBar().setTitle(getString(R.string.nome_app_turbo_editor)); - } - // parse the intent - parseIntent(getIntent()); - // show a dialog with the changelog - showChangeLog(); - } - - - @Override - protected final void onPostCreate(Bundle savedInstanceState) { - super.onPostCreate(savedInstanceState); - mDrawerToggle.syncState(); - } - - @Override - public void onResume() { - super.onResume(); - // Refresh the list view - refreshList(); - } - - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - parseIntent(intent); - } - - @Override - public void onPause() { - super.onPause(); - - if (PreferenceHelper.getAutoSave(getBaseContext()) && mEditor.canSaveFile()) { - saveTheFile(); - mEditor.fileSaved(); // so it doesn't ask to save in onDetach - } - } - - @Override - protected void onDestroy() { - try { - closeKeyBoard(); - } catch (NullPointerException e) { - e.printStackTrace(); - } - super.onDestroy(); - } - - @Override - public final void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - mDrawerToggle.onConfigurationChanged(newConfig); - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - - if (keyCode == KeyEvent.KEYCODE_BACK) { - onBackPressed(); - return true; - } else if (keyCode == KeyEvent.KEYCODE_MENU) { - return false; - } else { - if (mEditor == null) - mEditor = (Editor) findViewById(R.id.editor); - - // this will happen on first key pressed on hard-keyboard only. Once myInputField - // gets the focus again, it will automatically receive further key presses. - - try { - if (fileOpened && mEditor != null && !mEditor.hasFocus()) { - mEditor.requestFocus(); - mEditor.onKeyDown(keyCode, event); - return true; - } - } catch (NullPointerException ex) { - - } - } - - - return false; - } - - @Override - public void onBackPressed() { - - try { - // if we should ignore the back button - if (PreferenceHelper.getIgnoreBackButton(this)) - return; - - if (mDrawerLayout.isDrawerOpen(Gravity.START) && fileOpened) { - mDrawerLayout.closeDrawer(Gravity.START); - } else if (mDrawerLayout.isDrawerOpen(Gravity.END) && fileOpened) { - mDrawerLayout.closeDrawer(Gravity.END); - } else if (fileOpened && mEditor.canSaveFile()) { - SaveFileDialog.newInstance(sFilePath, pageSystem.getAllText(mEditor - .getText().toString()), currentEncoding).show(getFragmentManager(), - "dialog"); - } else if (fileOpened) { - - // remove editor fragment - hideTextEditor(); - - // Set the default title - getSupportActionBar().setTitle(getString(R.string.nome_app_turbo_editor)); - - onEvent(new EventBusEvents.ClosedAFile()); - - mDrawerLayout.openDrawer(Gravity.START); - mDrawerLayout.closeDrawer(Gravity.END); - } else { - displayInterstitial(); - super.onBackPressed(); - } - } catch (Exception e) { - // maybe something is null, who knows - } - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (resultCode == RESULT_OK) { - String path = ""; - if (requestCode == SELECT_FILE_CODE) { - path = data.getStringExtra("path"); - if(TextUtils.isEmpty(path)) - path = AccessStorageApi.getPath(getBaseContext(), data.getData()); - } - - if (requestCode == KITKAT_OPEN_REQUEST_CODE) { - path = AccessStorageApi.getPath(getBaseContext(), data.getData()); - } - - if (!TextUtils.isEmpty(path)) { - File file = new File(path); - if (file.isFile() && file.exists()) { - onEvent(new EventBusEvents.NewFileToOpen(new File - (path))); - } - } - - } - } - - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - // Path of the file selected - String filePath = files.get(position).getAbsolutePath(); - // Send the event that a file was selected - onEvent(new EventBusEvents.NewFileToOpen(new File(filePath))); - } - - //endregion - - //region MENU - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - if (fileOpened && searchingText) - getMenuInflater().inflate(R.menu.fragment_editor_search, menu); - else if (fileOpened) - getMenuInflater().inflate(R.menu.fragment_editor, menu); - return super.onCreateOptionsMenu(menu); - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - - if (fileOpened && searchingText) { - MenuItem imReplace = menu.findItem(R.id.im_replace); - MenuItem imPrev = menu.findItem(R.id.im_previous_item); - MenuItem imNext = menu.findItem(R.id.im_next_item); - - if (imReplace != null) - imReplace.setVisible(searchResult.canReplaceSomething()); - - if (imPrev != null) - imPrev.setVisible(searchResult.hasPrevious()); - - if (imNext != null) - imNext.setVisible(searchResult.hasNext()); - - - } else if (fileOpened) { - MenuItem imSave = menu.findItem(R.id.im_save); - MenuItem imUndo = menu.findItem(R.id.im_undo); - MenuItem imRedo = menu.findItem(R.id.im_redo); - if (mEditor != null) { - if (imSave != null) - imSave.setVisible(mEditor.canSaveFile()); - if (imUndo != null) - imUndo.setVisible(mEditor.getCanUndo()); - if (imRedo != null) - imRedo.setVisible(mEditor.getCanRedo()); - } else { - imSave.setVisible(false); - imUndo.setVisible(false); - imRedo.setVisible(false); - } - - MenuItem item = menu.findItem(R.id.im_share); - ShareActionProvider shareAction = (ShareActionProvider) MenuItemCompat - .getActionProvider(item); - File f = new File(sFilePath); - Intent shareIntent = new Intent(); - shareIntent.setAction(Intent.ACTION_SEND); - shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(f)); - shareIntent.setType("text/plain"); - shareAction.setShareIntent(shareIntent); - } - - MenuItem imDonate = menu.findItem(R.id.im_donate); - if (imDonate != null) - if (ProCheckUtils.isPro(this, false)) - imDonate.setVisible(false); - - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - int i = item.getItemId(); - if (mDrawerToggle.onOptionsItemSelected(item)) { - Toast.makeText(getBaseContext(), "drawer click", Toast.LENGTH_SHORT).show(); - mDrawerLayout.closeDrawer(Gravity.END); - return true; - } else if (i == R.id.im_save) { - saveTheFile(); - - } else if (i == R.id.im_undo) { - mEditor.onTextContextMenuItem(ID_UNDO); - - } else if (i == R.id.im_redo) { - mEditor.onTextContextMenuItem(ID_REDO); - - } else if (i == R.id.im_search) { - FindTextDialog.newInstance(mEditor.getText().toString()).show(getFragmentManager() - .beginTransaction(), "dialog"); - } else if (i == R.id.im_cancel) { - searchingText = false; - invalidateOptionsMenu(); - - } else if (i == R.id.im_replace) { - replaceText(); - - } else if (i == R.id.im_next_item) { - nextResult(); - - } else if (i == R.id.im_previous_item) { - previousResult(); - - } else if (i == R.id.im_goto_line) { - int min = mEditor.getLineUtils().firstReadLine(); - int max = mEditor.getLineUtils().lastReadLine(); - NumberPickerDialog.newInstance - (NumberPickerDialog.Actions.GoToLine, min, min, max).show(getFragmentManager().beginTransaction(), "dialog"); - } else if (i == R.id.im_view_it_on_browser) { - Intent browserIntent; - try { - browserIntent = new Intent(Intent.ACTION_VIEW); - browserIntent.setDataAndType(Uri.fromFile(new File(sFilePath)), "text/*"); - startActivity(browserIntent); - } catch (ActivityNotFoundException ex2) { - // - } - - } else if (i == R.id.im_info) { - FileInfoDialog.newInstance(sFilePath).show(getFragmentManager().beginTransaction(), "dialog"); - } - - else if (i == R.id.im_donate) { - DialogHelper.showDonateDialog(this); - } - return super.onOptionsItemSelected(item); - } - //endregion - - // region OTHER THINGS - void replaceText() { - int start = searchResult.foundIndex.get(searchResult.index); - int end = start + searchResult.textLength; - mEditor.setText(mEditor.getText().replace(start, end, searchResult.textToReplace)); - searchResult.doneReplace(); - - invalidateOptionsMenu(); - - if (searchResult.hasNext()) - nextResult(); - else if (searchResult.hasPrevious()) - previousResult(); - } - - void nextResult() { - if (searchResult.index == mEditor.getLineCount() - 1) // last result of page - { - return; - } - - - if (searchResult.index < searchResult.numberOfResults() - 1) { // equal zero is not good - searchResult.index++; - final int line = mEditor.getLineUtils().getLineFromIndex(searchResult.foundIndex.get - (searchResult.index), mEditor.getLineCount(), mEditor.getLayout()); - - - verticalScroll.post(new Runnable() { - @Override - public void run() { - int y = mEditor.getLayout().getLineTop(line); - if (y > 100) - y -= 100; - else - y = 0; - - verticalScroll.scrollTo(0, y); - } - }); - - mEditor.setFocusable(true); - mEditor.requestFocus(); - mEditor.setSelection(searchResult.foundIndex.get(searchResult.index), - searchResult.foundIndex.get(searchResult.index) + searchResult.textLength); - } - - invalidateOptionsMenu(); - } - - void previousResult() { - if (searchResult.index == 0) - return; - if (searchResult.index > 0) { - searchResult.index--; - final int line = LineUtils.getLineFromIndex(searchResult.foundIndex.get - (searchResult.index), mEditor.getLineCount(), mEditor.getLayout()); - verticalScroll.post(new Runnable() { - @Override - public void run() { - int y = mEditor.getLayout().getLineTop(line); - if (y > 100) - y -= 100; - else - y = 0; - verticalScroll.scrollTo(0, y); - } - }); - - mEditor.setFocusable(true); - mEditor.requestFocus(); - mEditor.setSelection(searchResult.foundIndex.get(searchResult.index), - searchResult.foundIndex.get(searchResult.index) + searchResult.textLength); - } - - invalidateOptionsMenu(); - } - - public abstract void displayInterstitial(); - - private void saveTheFile() { - File file = new File(sFilePath); - if (!file.getName().isEmpty()) - new SaveFileTask(this, sFilePath, pageSystem.getAllText(mEditor.getText() - .toString()), currentEncoding).execute(); - else { - NewFileDetailsDialog.newInstance - (pageSystem.getAllText(mEditor.getText().toString()), currentEncoding).show(getFragmentManager().beginTransaction(), "dialog"); - } - } - - /** - * Setup the navigation drawer - */ - private void setupNavigationDrawer() { - mDrawerLayout = (CustomDrawerLayout) findViewById(R.id.drawer_layout); - /* Action Bar - final ActionBar ab = toolbar; - ab.setDisplayHomeAsUpEnabled(true); - ab.setHomeButtonEnabled(true);*/ - /* Navigation drawer */ - mDrawerToggle = - new ActionBarDrawerToggle( - this, - mDrawerLayout, - toolbar, - R.string.nome_app_turbo_editor, - R.string.nome_app_turbo_editor) { - - @Override - public void onDrawerOpened(View drawerView) { - supportInvalidateOptionsMenu(); - try { - closeKeyBoard(); - } catch (NullPointerException e) { - e.printStackTrace(); - } - } - - @Override - public void onDrawerClosed(View view) { - supportInvalidateOptionsMenu(); - } - }; - /* link the mDrawerToggle to the Drawer Layout */ - mDrawerLayout.setDrawerListener(mDrawerToggle); -//mDrawerLayout.setFocusableInTouchMode(false); - - ListView listView = (ListView) findViewById(android.R.id.list); - listView.setEmptyView(findViewById(android.R.id.empty)); - files = new LinkedList<>(); - arrayAdapter = new AdapterDrawer(this, files, this); - listView.setAdapter(arrayAdapter); - listView.setOnItemClickListener(this); - } - - private void setupTextEditor() { - - verticalScroll = (GoodScrollView) findViewById(R.id.vertical_scroll); - horizontalScroll = (HorizontalScrollView) findViewById(R.id.horizontal_scroll); - mEditor = (Editor) findViewById(R.id.editor); - - //mEditor.setLayerType(View.LAYER_TYPE_NONE, null); - - if (PreferenceHelper.getWrapContent(this)) { - horizontalScroll.removeView(mEditor); - verticalScroll.removeView(horizontalScroll); - verticalScroll.addView(mEditor); - } - - if (PreferenceHelper.getReadOnly(this)) { - getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); - } else { - getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED); - } - - verticalScroll.setScrollInterface(this); - - pageSystem = new PageSystem(this, this, "", null); - - pageSystemButtons = new PageSystemButtons(this, this, - (FloatingActionButton) findViewById(R.id.fabPrev), - (FloatingActionButton) findViewById(R.id.fabNext)); - } - - private void showTextEditor() { - - fileOpened = true; - - findViewById(R.id.text_editor).setVisibility(View.VISIBLE); - findViewById(R.id.no_file_opened_messagge).setVisibility(View.GONE); - - mEditor.resetVariables(); - searchResult = null; - searchingText = false; - - invalidateOptionsMenu(); - - mEditor.disableTextChangedListener(); - mEditor.replaceTextKeepCursor(pageSystem.getCurrentPageText(), false); - mEditor.enableTextChangedListener(); - } - - private void hideTextEditor() { - - fileOpened = false; - - try { - findViewById(R.id.text_editor).setVisibility(View.GONE); - findViewById(R.id.no_file_opened_messagge).setVisibility(View.VISIBLE); - - mEditor.disableTextChangedListener(); - mEditor.replaceTextKeepCursor("", false); - mEditor.enableTextChangedListener(); - } catch (Exception e) { - // lol - } - } - - /** - * Parses the intent - */ - private void parseIntent(Intent intent) { - final String action = intent.getAction(); - final String type = intent.getType(); - - if (Intent.ACTION_VIEW.equals(action) - || Intent.ACTION_EDIT.equals(action) - || Intent.ACTION_PICK.equals(action) - && type != null) { - // Post event - onEvent(new EventBusEvents.NewFileToOpen(new File(intent - .getData().getPath()))); - } else if (Intent.ACTION_SEND.equals(action) && type != null) { - if ("text/plain".equals(type)) { - onEvent(new EventBusEvents.NewFileToOpen(intent.getStringExtra(Intent.EXTRA_TEXT))); - } - } - } - - /** - * Show a dialog with the changelog - */ - private void showChangeLog() { - final String currentVersion = AppInfoHelper.getCurrentVersion(this); - final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); - final String lastVersion = preferences.getString("last_version", currentVersion); - preferences.edit().putString("last_version", currentVersion).apply(); - if (!lastVersion.equals(currentVersion)) { - ChangelogDialog.showChangeLogDialog(getFragmentManager()); - } - } - - // closes the soft keyboard - private void closeKeyBoard() throws NullPointerException { - // Central system API to the overall input method framework (IMF) architecture - InputMethodManager inputManager = - (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - - // Base interface for a remotable object - IBinder windowToken = getCurrentFocus().getWindowToken(); - - // Hide type - int hideType = InputMethodManager.HIDE_NOT_ALWAYS; - - // Hide the KeyBoard - inputManager.hideSoftInputFromWindow(windowToken, hideType); - } - - void updateTextSyntax() { - if (!PreferenceHelper.getSyntaxHighlight(this) || mEditor.hasSelection() || - updateHandler == null || colorRunnable_duringEditing == null) - return; - - updateHandler.removeCallbacks(colorRunnable_duringEditing); - updateHandler.removeCallbacks(colorRunnable_duringScroll); - updateHandler.postDelayed(colorRunnable_duringEditing, SYNTAX_DELAY_MILLIS_LONG); - } - - private void refreshList(){ - refreshList(null, false, false); - } - - private void refreshList(@Nullable String path, boolean add, boolean delete) { - int max_recent_files = 15; - if(add) - max_recent_files--; - - // File paths saved in preferences - String[] savedPaths = PreferenceHelper.getSavedPaths(this); - int first_index_of_array = savedPaths.length > max_recent_files ? savedPaths.length - max_recent_files : 0; - savedPaths = ArrayUtils.subarray(savedPaths, first_index_of_array, savedPaths.length); - // File names for the list - files.clear(); - // StringBuilder that will contain the file paths - StringBuilder sb = new StringBuilder(); - // for cycle to convert paths to names - - for(int i = 0; i < savedPaths.length; i++){ - String savedPath = savedPaths[i]; - File file = new File(savedPath); - // Check that the file exist - if (file.exists()) { - if(path != null && path.equals(savedPath) && delete) - continue; - else { - files.addFirst(file); - sb.append(savedPath).append(","); - } - } - } - if(path != null && !path.isEmpty() && add && !ArrayUtils.contains(savedPaths, path)) { - sb.append(path).append(","); - files.addFirst(new File(path)); - } - // save list without empty or non existed files - PreferenceHelper.setSavedPaths(this, sb); - // Set adapter - arrayAdapter.notifyDataSetChanged(); - } - //endregion - - //region EVENTBUS - void onEvent(final EventBusEvents.NewFileToOpen event) { - - if (fileOpened && mEditor.canSaveFile()) { - SaveFileDialog.newInstance(sFilePath, pageSystem.getAllText(mEditor - .getText().toString()), currentEncoding, true, event.getFile().getAbsolutePath()).show(getFragmentManager(), - "dialog"); - return; - } - - new AsyncTask() { - - File file; - String message = ""; - String fileText; - String encoding; - ProgressDialog progressDialog; - - @Override - protected void onPreExecute() { - super.onPreExecute(); - // Close the drawer - mDrawerLayout.closeDrawer(Gravity.START); - progressDialog = new ProgressDialog(MainActivity.this); - progressDialog.setMessage(getString(R.string.please_wait)); - progressDialog.show(); - - } - - @Override - protected Void doInBackground(Void... params) { - file = event.getFile(); - try { - if (!file.exists() || !file.isFile()) { - fileText = event.getFileText(); - sFilePath = file.getAbsolutePath(); - fileExtension = "txt"; - return null; - } - - file = file.getCanonicalFile(); - sFilePath = file.getAbsolutePath(); - fileExtension = FilenameUtils.getExtension(sFilePath).toLowerCase(); - - boolean isRoot; - - if (!file.canRead()) { - Shell shell; - shell = Shell.startRootShell(); - Toolbox tb = new Toolbox(shell); - isRoot = tb.isRootAccessGiven(); - - if (isRoot) { - File tempFile = new File(getFilesDir(), "temp.root.file"); - if (!tempFile.exists()) - tempFile.createNewFile(); - tb.copyFile(event.getFile().getAbsolutePath(), - tempFile.getAbsolutePath(), false, false); - file = new File(tempFile.getAbsolutePath()); - } - } - - boolean autoencoding = PreferenceHelper.getAutoEncoding(MainActivity.this); - if (autoencoding) { - - encoding = FileUtils.getDetectedEncoding(file); - if (encoding.isEmpty()) { - encoding = PreferenceHelper.getEncoding(MainActivity.this); - } - } else { - encoding = PreferenceHelper.getEncoding(MainActivity.this); - } - - fileText = org.apache.commons.io.FileUtils.readFileToString(file, encoding); - } catch (Exception e) { - message = e.getMessage(); - fileText = ""; - } - - while (mDrawerLayout.isDrawerOpen(Gravity.START)) { - try { - Thread.sleep(50); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - return null; - } - - @Override - protected void onPostExecute(Void result) { - super.onPostExecute(result); - progressDialog.hide(); - - if (!message.isEmpty()) { - Toast.makeText(MainActivity.this, message, Toast.LENGTH_LONG).show(); - onEvent(new EventBusEvents.CannotOpenAFile()); - } else { - - pageSystem = new PageSystem(MainActivity.this, MainActivity.this, fileText, new File(sFilePath)); - currentEncoding = encoding; - - onEvent(new EventBusEvents.AFileIsSelected(sFilePath)); - - showTextEditor(); - - String name = FilenameUtils.getName(sFilePath); - if (name.isEmpty()) - getSupportActionBar().setTitle(R.string.new_file); - else - getSupportActionBar().setTitle(name); - - if(!name.isEmpty()) { - refreshList(sFilePath, true, false); - } - } - - } - }.execute(); - } - - public void onEvent(EventBusEvents.SavedAFile event) { - - sFilePath = event.getPath(); - fileExtension = FilenameUtils.getExtension(sFilePath).toLowerCase(); - - mEditor.clearHistory(); - mEditor.fileSaved(); - invalidateOptionsMenu(); - - try { - closeKeyBoard(); - } catch (NullPointerException e) { - e.printStackTrace(); - } - - refreshList(event.getPath(), true, false); - arrayAdapter.selectView(event.getPath()); - - displayInterstitial(); - } - - /** - * When a file can't be opened - * Invoked by the EditorFragment - * - * @param event The event called - */ - void onEvent(EventBusEvents.CannotOpenAFile event) { - // - mDrawerLayout.openDrawer(Gravity.LEFT); - // - getSupportActionBar().setTitle(getString(R.string.nome_app_turbo_editor)); - // - supportInvalidateOptionsMenu(); - // Replace fragment - hideTextEditor(); - } - - public void onEvent(EventBusEvents.APreferenceValueWasChanged event) { - - if (event.hasType(EventBusEvents.APreferenceValueWasChanged.Type.THEME_CHANGE)) { - ThemeUtils.setWindowsBackground(this); - } - - if (event.hasType(WRAP_CONTENT)) { - if (PreferenceHelper.getWrapContent(this)) { - horizontalScroll.removeView(mEditor); - verticalScroll.removeView(horizontalScroll); - verticalScroll.addView(mEditor); - } else { - verticalScroll.removeView(mEditor); - verticalScroll.addView(horizontalScroll); - horizontalScroll.addView(mEditor); - } - } else if (event.hasType(LINE_NUMERS)) { - mEditor.disableTextChangedListener(); - mEditor.replaceTextKeepCursor(null, true); - mEditor.enableTextChangedListener(); - if (PreferenceHelper.getLineNumbers(this)) { - mEditor.setPadding(EditTextPadding.getPaddingWithLineNumbers(this, - PreferenceHelper.getFontSize(this)), - EditTextPadding.getPaddingTop(this), 0, 0); - } else { - mEditor.setPadding(EditTextPadding.getPaddingWithoutLineNumbers(this) - , EditTextPadding.getPaddingTop(this), 0, 0); - } - } else if (event.hasType(SYNTAX)) { - mEditor.disableTextChangedListener(); - mEditor.replaceTextKeepCursor(null, true); - mEditor.enableTextChangedListener(); - } else if (event.hasType(MONOSPACE)) { - if (PreferenceHelper.getUseMonospace(this)) - mEditor.setTypeface(Typeface.MONOSPACE); - else - mEditor.setTypeface(Typeface.DEFAULT); - } else if (event.hasType(THEME_CHANGE)) { - if (PreferenceHelper.getLightTheme(this)) { - mEditor.setTextColor(getResources().getColor(R.color.textColorInverted)); - } else { - mEditor.setTextColor(getResources().getColor(R.color.textColor)); - } - } else if (event.hasType(TEXT_SUGGESTIONS) || event.hasType(READ_ONLY)) { - if (PreferenceHelper.getReadOnly(this)) { - getWindow().setSoftInputMode(WindowManager.LayoutParams - .SOFT_INPUT_STATE_ALWAYS_HIDDEN); - mEditor.setReadOnly(true); - } else { - getWindow().setSoftInputMode(WindowManager.LayoutParams - .SOFT_INPUT_STATE_UNSPECIFIED); - mEditor.setReadOnly(false); - if (PreferenceHelper.getSuggestionActive(this)) { - mEditor.setInputType(InputType.TYPE_CLASS_TEXT | InputType - .TYPE_TEXT_FLAG_MULTI_LINE | InputType.TYPE_TEXT_FLAG_IME_MULTI_LINE); - } else { - mEditor.setInputType(InputType.TYPE_CLASS_TEXT | InputType - .TYPE_TEXT_FLAG_MULTI_LINE | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS - | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD | InputType - .TYPE_TEXT_FLAG_IME_MULTI_LINE); - } - } - // sometimes it becomes monospace after setting the input type - if (PreferenceHelper.getUseMonospace(this)) - mEditor.setTypeface(Typeface.MONOSPACE); - else - mEditor.setTypeface(Typeface.DEFAULT); - } else if (event.hasType(FONT_SIZE)) { - if (PreferenceHelper.getLineNumbers(this)) { - mEditor.setPadding(EditTextPadding.getPaddingWithLineNumbers(this, - PreferenceHelper.getFontSize(this)), - EditTextPadding.getPaddingTop(this), 0, 0); - } else { - mEditor.setPadding(EditTextPadding.getPaddingWithoutLineNumbers(this) - , EditTextPadding.getPaddingTop(this), 0, 0); - } - mEditor.setTextSize(PreferenceHelper.getFontSize(this)); - } else if (event.hasType(ENCODING)) { - String oldEncoding, newEncoding; - oldEncoding = currentEncoding; - newEncoding = PreferenceHelper.getEncoding(this); - try { - final byte[] oldText = mEditor.getText().toString().getBytes(oldEncoding); - mEditor.disableTextChangedListener(); - mEditor.replaceTextKeepCursor(new String(oldText, newEncoding), true); - mEditor.enableTextChangedListener(); - currentEncoding = newEncoding; - } catch (UnsupportedEncodingException ignored) { - try { - final byte[] oldText = mEditor.getText().toString().getBytes(oldEncoding); - mEditor.disableTextChangedListener(); - mEditor.replaceTextKeepCursor(new String(oldText, "UTF-8"), true); - mEditor.enableTextChangedListener(); - } catch (UnsupportedEncodingException ignored2) { - } - } - } - } - - void onEvent(EventBusEvents.AFileIsSelected event) { - arrayAdapter.selectView(event.getPath()); - } - - void onEvent(EventBusEvents.ClosedAFile event) { - arrayAdapter.selectView(""); - } - //endregion - - //region Calls from the layout - public void OpenFile(View view) { - Intent subActivity = new Intent(MainActivity.this, SelectFileActivity.class); - subActivity.putExtra("action", SelectFileActivity.Actions.SelectFile); - AnimationUtils.startActivityWithScale(this, subActivity, true, SELECT_FILE_CODE, view); - } - - public void CreateFile(View view) { - onEvent(new EventBusEvents.NewFileToOpen("")); // do not send the event to others - } - - public void OpenInfo(View view) { - DialogHelper.showAboutDialog(this); - } - - public void OpenSettings(View view) { - mDrawerLayout.closeDrawer(Gravity.START); - mDrawerLayout.openDrawer(Gravity.END); - } - //endregion - - //region Ovverideses - @Override - public void nextPageClicked() { - pageSystem.savePage(mEditor.getText().toString()); - pageSystem.nextPage(); - mEditor.disableTextChangedListener(); - mEditor.replaceTextKeepCursor(pageSystem.getCurrentPageText(), false); - mEditor.enableTextChangedListener(); - - verticalScroll.postDelayed(new Runnable() { - @Override - public void run() { - verticalScroll.smoothScrollTo(0, 0); - } - }, 200); - - if (!PreferenceHelper.getPageSystemButtonsPopupShown(this)) { - PreferenceHelper.setPageSystemButtonsPopupShown(this, true); - Toast.makeText(this, getString(R.string.long_click_for_more_options), - Toast.LENGTH_LONG).show(); - } - } - - @Override - public void prevPageClicked() { - pageSystem.savePage(mEditor.getText().toString()); - pageSystem.prevPage(); - mEditor.disableTextChangedListener(); - mEditor.replaceTextKeepCursor(pageSystem.getCurrentPageText(), false); - mEditor.enableTextChangedListener(); - - verticalScroll.postDelayed(new Runnable() { - @Override - public void run() { - verticalScroll.smoothScrollTo(0, 0); - } - }, 200); - - if (!PreferenceHelper.getPageSystemButtonsPopupShown(this)) { - PreferenceHelper.setPageSystemButtonsPopupShown(this, true); - Toast.makeText(this, getString(R.string.long_click_for_more_options), - Toast.LENGTH_LONG).show(); - } - } - - @Override - public void pageSystemButtonLongClicked() { - int maxPages = pageSystem.getMaxPage(); - int currentPage = pageSystem.getCurrentPage(); - NumberPickerDialog.newInstance - (NumberPickerDialog.Actions.SelectPage, 0, currentPage, maxPages).show(getFragmentManager().beginTransaction(), "dialog"); - } - - @Override - public boolean canReadNextPage() { - return pageSystem.canReadNextPage(); - } - - @Override - public boolean canReadPrevPage() { - return pageSystem.canReadPrevPage(); - } - - @Override - public void onSearchDone(SearchResult searchResult) { - MainActivity.searchResult = searchResult; - searchingText = true; - invalidateOptionsMenu(); - - final int line = LineUtils.getLineFromIndex(searchResult.foundIndex.getFirst - (), mEditor.getLineCount(), mEditor.getLayout()); - verticalScroll.post(new Runnable() { - @Override - public void run() { - int y = mEditor.getLayout().getLineTop(line); - if (y > 100) - y -= 100; - else - y = 0; - - verticalScroll.scrollTo(0, y); - } - }); - - mEditor.setFocusable(true); - mEditor.requestFocus(); - mEditor.setSelection(searchResult.foundIndex.getFirst(), searchResult.foundIndex.getFirst - () + searchResult.textLength); - - } - - @Override - public void onPageChanged(int page) { - pageSystemButtons.updateVisibility(false); - searchingText = false; - mEditor.clearHistory(); - invalidateOptionsMenu(); - } - - @Override - public void onScrollChanged(int l, int t, int oldl, int oldt) { - pageSystemButtons.updateVisibility(Math.abs(t) > 10); - - if (!PreferenceHelper.getSyntaxHighlight(this) || (mEditor.hasSelection() && - !searchingText) || updateHandler == null || colorRunnable_duringScroll == null) - return; - - updateHandler.removeCallbacks(colorRunnable_duringEditing); - updateHandler.removeCallbacks(colorRunnable_duringScroll); - updateHandler.postDelayed(colorRunnable_duringScroll, SYNTAX_DELAY_MILLIS_SHORT); - } - - @Override - public void onNumberPickerDialogDismissed(NumberPickerDialog.Actions action, int value) { - if (action == NumberPickerDialog.Actions.SelectPage) { - pageSystem.savePage(mEditor.getText().toString()); - pageSystem.goToPage(value); - mEditor.disableTextChangedListener(); - mEditor.replaceTextKeepCursor(pageSystem.getCurrentPageText(), true); - mEditor.enableTextChangedListener(); - - verticalScroll.postDelayed(new Runnable() { - @Override - public void run() { - verticalScroll.smoothScrollTo(0, 0); - } - }, 200); - - } else if (action == NumberPickerDialog.Actions.GoToLine) { - - int fakeLine = mEditor.getLineUtils().fakeLineFromRealLine(value); - final int y = mEditor.getLineUtils().getYAtLine(verticalScroll, - mEditor.getLineCount(), fakeLine); - - verticalScroll.postDelayed(new Runnable() { - @Override - public void run() { - verticalScroll.smoothScrollTo(0, y); - } - }, 200); - } - - } - - @Override - public void userDoesntWantToSave(boolean openNewFile, String pathOfNewFile) { - Editor.canSaveFile = false; - if(openNewFile) - onEvent(new EventBusEvents.NewFileToOpen(new File(pathOfNewFile))); - else - onEvent(new EventBusEvents.CannotOpenAFile()); - } - - @Override - public void CancelItem(int position, boolean andCloseOpenedFile) { - refreshList(files.get(position).getAbsolutePath(), false, true); - if (andCloseOpenedFile) - onEvent(new EventBusEvents.CannotOpenAFile()); - } - //endregion - - public static class Editor extends EditText { - - //region VARIABLES - private static final TextPaint mPaintNumbers = new TextPaint(); - /** - * The edit history. - */ - private final EditHistory mEditHistory; - /** - * The change listener. - */ - private final EditTextChangeListener - mChangeListener; - /** - * Disconnect this undo/redo from the text - * view. - */ - private static boolean enabledChangeListener; - private static int paddingTop; - private static int numbersWidth; - private static int lineHeight; - - private static int lineCount, realLine, startingLine; - private static LineUtils lineUtils; - /** - * Is undo/redo being performed? This member - * signals if an undo/redo operation is - * currently being performed. Changes in the - * text during undo/redo are not recorded - * because it would mess up the undo history. - */ - private static boolean mIsUndoOrRedo; - private static Matcher m; - private static boolean mShowUndo, mShowRedo; - private static boolean canSaveFile; - private static KeyListener keyListener; - private static int firstVisibleIndex, firstColoredIndex; - private static int deviceHeight; - private static int editorHeight; - private static boolean[] hasNewLineArray; - private static int[] realLines; - private static boolean wrapContent; - private static int lastLine; - private static int firstLine; - private static CharSequence textToHighlight; - private static int lastVisibleIndex; - private static int i; - //endregion - - //region CONSTRUCTOR - public Editor(final Context context, AttributeSet attrs) { - super(context, attrs); - - //setLayerType(View.LAYER_TYPE_NONE, null); - - mEditHistory = new EditHistory(); - mChangeListener = new EditTextChangeListener(); - lineUtils = new LineUtils(); - - deviceHeight = getResources().getDisplayMetrics().heightPixels; - - paddingTop = EditTextPadding.getPaddingTop(getContext()); - - mPaintNumbers.setAntiAlias(true); - mPaintNumbers.setDither(false); - mPaintNumbers.setTextAlign(Paint.Align.RIGHT); - mPaintNumbers.setColor(getResources().getColor(R.color.file_text)); - - if (PreferenceHelper.getLightTheme(getContext())) { - setTextColor(getResources().getColor(R.color.textColorInverted)); - } else { - setTextColor(getResources().getColor(R.color.textColor)); - } - if (PreferenceHelper.getLineNumbers(getContext())) { - setPadding(EditTextPadding.getPaddingWithLineNumbers(getContext(), - PreferenceHelper.getFontSize(getContext())), - EditTextPadding.getPaddingTop(getContext()), 0, 0); - } else { - setPadding(EditTextPadding.getPaddingWithoutLineNumbers(getContext()), - EditTextPadding.getPaddingTop(getContext()), 0, 0); - } - - if (PreferenceHelper.getReadOnly(getContext())) { - setReadOnly(true); - } else { - setReadOnly(false); - if (PreferenceHelper.getSuggestionActive(getContext())) { - setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE - | InputType.TYPE_TEXT_FLAG_IME_MULTI_LINE); - } else { - setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE - | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType - .TYPE_TEXT_VARIATION_VISIBLE_PASSWORD | InputType - .TYPE_TEXT_FLAG_IME_MULTI_LINE); - } - } - - if (PreferenceHelper.getUseMonospace(getContext())) { - setTypeface(Typeface.MONOSPACE); - } else { - setTypeface(Typeface.DEFAULT); - } - setTextSize(PreferenceHelper.getFontSize(getContext())); - - setFocusable(true); - setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (!PreferenceHelper.getReadOnly(getContext())) { - verticalScroll.tempDisableListener(1000); - ((InputMethodManager) getContext().getSystemService(Context - .INPUT_METHOD_SERVICE)) - .showSoftInput(Editor.this, InputMethodManager.SHOW_IMPLICIT); - } - - } - }); - setOnFocusChangeListener(new View.OnFocusChangeListener() { - @Override - public void onFocusChange(View v, boolean hasFocus) { - if (hasFocus && !PreferenceHelper.getReadOnly(getContext())) { - verticalScroll.tempDisableListener(1000); - ((InputMethodManager) getContext().getSystemService(Context - .INPUT_METHOD_SERVICE)) - .showSoftInput(Editor.this, InputMethodManager.SHOW_IMPLICIT); - } - } - }); - - setMaxHistorySize(30); - - resetVariables(); - } - - public void setReadOnly(boolean value) { - if (value) { - keyListener = getKeyListener(); - setKeyListener(null); - } else { - if (keyListener != null) - setKeyListener(keyListener); - } - } - - //region OVERRIDES - @Override - public void setTextSize(float size) { - super.setTextSize(size); - final float scale = getContext().getResources().getDisplayMetrics().density; - mPaintNumbers.setTextSize((int) (size * scale * 0.65f)); - numbersWidth = (int) (EditTextPadding.getPaddingWithLineNumbers(getContext(), - PreferenceHelper.getFontSize(getContext())) * 0.8); - lineHeight = getLineHeight(); - } - - - @Override - public void onDraw(@NonNull final Canvas canvas) { - - if (lineCount != getLineCount() || startingLine != pageSystem.getStartingLine()) { - startingLine = pageSystem.getStartingLine(); - lineCount = getLineCount(); - lineUtils.updateHasNewLineArray(pageSystem - .getStartingLine(), lineCount, getLayout(), getText().toString()); - - hasNewLineArray = lineUtils.getToCountLinesArray(); - realLines = lineUtils.getRealLines(); - - } - - editorHeight = getHeight(); - firstLine = lineUtils.getFirstVisibleLine(verticalScroll, editorHeight, lineCount); - lastLine = lineUtils.getLastVisibleLine(verticalScroll, editorHeight, lineCount, deviceHeight); - - if (PreferenceHelper.getLineNumbers(getContext())) { - wrapContent = PreferenceHelper.getWrapContent(getContext()); - i = firstLine; - - while (i < lastLine) { - // if last line we count it anyway - if (!wrapContent - || hasNewLineArray[i] - || i == lastLine - 1) { - if (i == lastLine - 1) - realLine = realLines[i] + 1; - else - realLine = realLines[i]; - - canvas.drawText(String.valueOf(realLine), - numbersWidth, // they are all center aligned - paddingTop + lineHeight * (i + 1), - mPaintNumbers); - } - i++; - } - } - - super.onDraw(canvas); - } - - - //endregion - - //region Other - - @Override - public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) { - - if (event.isCtrlPressed()) { - switch (keyCode) { - case KeyEvent.KEYCODE_A: - return onTextContextMenuItem(ID_SELECT_ALL); - case KeyEvent.KEYCODE_X: - return onTextContextMenuItem(ID_CUT); - case KeyEvent.KEYCODE_C: - return onTextContextMenuItem(ID_COPY); - case KeyEvent.KEYCODE_V: - return onTextContextMenuItem(ID_PASTE); - case KeyEvent.KEYCODE_Z: - if (getCanUndo()) { - return onTextContextMenuItem(ID_UNDO); - } - case KeyEvent.KEYCODE_Y: - if (getCanRedo()) { - return onTextContextMenuItem(ID_REDO); - } - case KeyEvent.KEYCODE_S: - ((MainActivity) getContext()).saveTheFile(); - return true; - default: - return super.onKeyDown(keyCode, event); - } - } else { - switch (keyCode) { - case KeyEvent.KEYCODE_TAB: - String textToInsert = " "; - int start, end; - start = Math.max(getSelectionStart(), 0); - end = Math.max(getSelectionEnd(), 0); - getText().replace(Math.min(start, end), Math.max(start, end), - textToInsert, 0, textToInsert.length()); - return true; - default: - return super.onKeyDown(keyCode, event); - } - } - } - - @Override - public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) { - if (event.isCtrlPressed()) { - switch (keyCode) { - case KeyEvent.KEYCODE_A: - case KeyEvent.KEYCODE_X: - case KeyEvent.KEYCODE_C: - case KeyEvent.KEYCODE_V: - case KeyEvent.KEYCODE_Z: - case KeyEvent.KEYCODE_Y: - case KeyEvent.KEYCODE_S: - return true; - default: - return false; - } - } else { - switch (keyCode) { - case KeyEvent.KEYCODE_TAB: - return true; - default: - return false; - } - } - } - - /** - * {@inheritDoc} - */ - @Override - public boolean onTextContextMenuItem( - final int id) { - if (id == ID_UNDO) { - undo(); - return true; - } else if (id == ID_REDO) { - redo(); - return true; - } else { - return super.onTextContextMenuItem(id); - } - } - - /** - * Can undo be performed? - */ - public boolean getCanUndo() { - return (mEditHistory.mmPosition > 0); - } - - /** - * Can redo be performed? - */ - public boolean getCanRedo() { - return (mEditHistory.mmPosition - < mEditHistory.mmHistory.size()); - } - - /** - * Perform undo. - */ - public void undo() { - EditItem edit = mEditHistory.getPrevious(); - if (edit == null) { - return; - } - - Editable text = getEditableText(); - int start = edit.mmStart; - int end = start + (edit.mmAfter != null - ? edit.mmAfter.length() : 0); - - mIsUndoOrRedo = true; - text.replace(start, end, edit.mmBefore); - mIsUndoOrRedo = false; - - // This will get rid of underlines inserted when editor tries to come - // up with a suggestion. - for (Object o : text.getSpans(0, - text.length(), UnderlineSpan.class)) { - text.removeSpan(o); - } - - Selection.setSelection(text, - edit.mmBefore == null ? start - : (start + edit.mmBefore.length())); - } - - /** - * Perform redo. - */ - public void redo() { - EditItem edit = mEditHistory.getNext(); - if (edit == null) { - return; - } - - Editable text = getEditableText(); - int start = edit.mmStart; - int end = start + (edit.mmBefore != null - ? edit.mmBefore.length() : 0); - - mIsUndoOrRedo = true; - text.replace(start, end, edit.mmAfter); - mIsUndoOrRedo = false; - - // This will get rid of underlines inserted when editor tries to come - // up with a suggestion. - for (Object o : text.getSpans(0, - text.length(), UnderlineSpan.class)) { - text.removeSpan(o); - } - - Selection.setSelection(text, - edit.mmAfter == null ? start - : (start + edit.mmAfter.length())); - } - - /** - * Set the maximum history size. If size is - * negative, then history size is only limited - * by the device memory. - */ - public void setMaxHistorySize( - int maxHistorySize) { - mEditHistory.setMaxHistorySize( - maxHistorySize); - } - - public void resetVariables() { - mEditHistory.clear(); - enabledChangeListener = false; - lineCount = 0; - realLine = 0; - startingLine = 0; - mIsUndoOrRedo = false; - mShowUndo = false; - mShowRedo = false; - canSaveFile = false; - firstVisibleIndex = 0; - firstColoredIndex = 0; - } - - public boolean canSaveFile() { - return canSaveFile; - } - - public void fileSaved() { - canSaveFile = false; - } - - public void replaceTextKeepCursor(String textToUpdate, boolean mantainCursorPos) { - - int cursorPos; - int cursorPosEnd; - if (textToUpdate != null) { - cursorPos = 0; - cursorPosEnd = 0; - } else { - cursorPos = getSelectionStart(); - cursorPosEnd = getSelectionEnd(); - } - disableTextChangedListener(); - - if (PreferenceHelper.getSyntaxHighlight(getContext())) - setText(highlight(textToUpdate == null ? getEditableText() : Editable.Factory - .getInstance().newEditable(textToUpdate), textToUpdate != null)); - else - setText(textToUpdate == null ? getText().toString() : textToUpdate); - - enableTextChangedListener(); - - if (mantainCursorPos) - firstVisibleIndex = cursorPos; - - if (firstVisibleIndex > -1) { - if (cursorPosEnd != cursorPos) - setSelection(cursorPos, cursorPosEnd); - else - setSelection(firstVisibleIndex); - } - } - //endregion - - //region UNDO REDO - - public void disableTextChangedListener() { - enabledChangeListener = false; - removeTextChangedListener(mChangeListener); - } - - public CharSequence highlight(Editable editable, boolean newText) { - editable.clearSpans(); - - if (editable.length() == 0) { - return editable; - } - - editorHeight = getHeight(); - - if (!newText && editorHeight > 0) { - firstLine = lineUtils.getFirstVisibleLine(verticalScroll, editorHeight, lineCount); - lastLine = lineUtils.getLastVisibleLine(verticalScroll, editorHeight, lineCount, deviceHeight); - firstVisibleIndex = getLayout().getLineStart(firstLine); - lastVisibleIndex = getLayout().getLineStart(lastLine); - } else { - firstVisibleIndex = 0; - lastVisibleIndex = CHARS_TO_COLOR; - } - - firstColoredIndex = firstVisibleIndex - (CHARS_TO_COLOR / 5); - - // normalize - if (firstColoredIndex < 0) - firstColoredIndex = 0; - if (lastVisibleIndex > editable.length()) - lastVisibleIndex = editable.length(); - if (firstColoredIndex > lastVisibleIndex) - firstColoredIndex = lastVisibleIndex; - - - textToHighlight = editable.subSequence(firstColoredIndex, lastVisibleIndex); - - if (fileExtension.contains("htm") - || fileExtension.contains("xml")) { - color(Patterns.HTML_OPEN_TAGS, editable, textToHighlight, firstColoredIndex); - color(Patterns.HTML_CLOSE_TAGS, editable, textToHighlight, firstColoredIndex); - color(Patterns.HTML_ATTRS, editable, textToHighlight, firstColoredIndex); - color(Patterns.GENERAL_STRINGS, editable, textToHighlight, firstColoredIndex); - color(Patterns.XML_COMMENTS, editable, textToHighlight, firstColoredIndex); - } else if (fileExtension.equals("css")) { - //color(CSS_STYLE_NAME, editable); - color(Patterns.CSS_ATTRS, editable, textToHighlight, firstColoredIndex); - color(Patterns.CSS_ATTR_VALUE, editable, textToHighlight, firstColoredIndex); - color(Patterns.SYMBOLS, editable, textToHighlight, firstColoredIndex); - color(Patterns.GENERAL_COMMENTS, editable, textToHighlight, firstColoredIndex); - } else if (Arrays.asList(MimeTypes.MIME_CODE).contains(fileExtension)) { - switch (fileExtension) { - case "lua": - color(Patterns.LUA_KEYWORDS, editable, textToHighlight, firstColoredIndex); - break; - case "py": - color(Patterns.PY_KEYWORDS, editable, textToHighlight, firstColoredIndex); - break; - default: - color(Patterns.GENERAL_KEYWORDS, editable, textToHighlight, firstColoredIndex); - break; - } - color(Patterns.NUMBERS_OR_SYMBOLS, editable, textToHighlight, firstColoredIndex); - color(Patterns.GENERAL_STRINGS, editable, textToHighlight, firstColoredIndex); - color(Patterns.GENERAL_COMMENTS, editable, textToHighlight, firstColoredIndex); - if (fileExtension.equals("php")) - color(Patterns.PHP_VARIABLES, editable, textToHighlight, firstColoredIndex); - } else if (Arrays.asList(MimeTypes.MIME_SQL).contains(fileExtension)) { - color(Patterns.SYMBOLS, editable, textToHighlight, firstColoredIndex); - color(Patterns.GENERAL_STRINGS, editable, textToHighlight, firstColoredIndex); - color(Patterns.SQL_KEYWORDS, editable, textToHighlight, firstColoredIndex); - } else { - if (!(Arrays.asList(MimeTypes.MIME_MARKDOWN).contains(fileExtension))) - color(Patterns.GENERAL_KEYWORDS, editable, textToHighlight, firstColoredIndex); - color(Patterns.NUMBERS_OR_SYMBOLS, editable, textToHighlight, firstColoredIndex); - color(Patterns.GENERAL_STRINGS, editable, textToHighlight, firstColoredIndex); - if (fileExtension.equals("prop") || fileExtension.contains("conf") || - (Arrays.asList(MimeTypes.MIME_MARKDOWN).contains(fileExtension))) - color(Patterns.GENERAL_COMMENTS_NO_SLASH, editable, textToHighlight, - firstColoredIndex); - else - color(Patterns.GENERAL_COMMENTS, editable, textToHighlight, firstColoredIndex); - - if ((Arrays.asList(MimeTypes.MIME_MARKDOWN).contains(fileExtension))) - color(Patterns.LINK, editable, textToHighlight, firstColoredIndex); - } - - return editable; - } - - public void enableTextChangedListener() { - if (!enabledChangeListener) { - addTextChangedListener(mChangeListener); - enabledChangeListener = true; - } - } - - public LineUtils getLineUtils() { - return lineUtils; - } - - private void color(Pattern pattern, - Editable allText, - CharSequence textToHighlight, - int start) { - int color = 0; - if (pattern.equals(Patterns.HTML_OPEN_TAGS) - || pattern.equals(Patterns.HTML_CLOSE_TAGS) - || pattern.equals(Patterns.GENERAL_KEYWORDS) - || pattern.equals(Patterns.SQL_KEYWORDS) - || pattern.equals(Patterns.PY_KEYWORDS) - || pattern.equals(Patterns.LUA_KEYWORDS) - - ) { - color = getResources().getColor(R.color.syntax_keyword); - } else if (pattern.equals(Patterns.HTML_ATTRS) - || pattern.equals(Patterns.CSS_ATTRS) - || pattern.equals(Patterns.LINK)) { - color = getResources().getColor(R.color.syntax_attr); - } else if (pattern.equals(Patterns.CSS_ATTR_VALUE)) { - color = getResources().getColor(R.color.syntax_attr_value); - } else if (pattern.equals(Patterns.XML_COMMENTS) - || pattern.equals(Patterns.GENERAL_COMMENTS) - || pattern.equals(Patterns.GENERAL_COMMENTS_NO_SLASH)) { - color = getResources().getColor(R.color.syntax_comment); - } else if (pattern.equals(Patterns.GENERAL_STRINGS)) { - color = getResources().getColor(R.color.syntax_string); - } else if (pattern.equals(Patterns.NUMBERS) || pattern.equals(Patterns.SYMBOLS) || pattern.equals(Patterns.NUMBERS_OR_SYMBOLS)) { - color = getResources().getColor(R.color.syntax_number); - } else if (pattern.equals(Patterns.PHP_VARIABLES)) { - color = getResources().getColor(R.color.syntax_variable); - } - - m = pattern.matcher(textToHighlight); - - while (m.find()) { - allText.setSpan( - new ForegroundColorSpan(color), - start + m.start(), - start + m.end(), - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - } - } - - /** - * Clear history. - */ - public void clearHistory() { - mEditHistory.clear(); - mShowUndo = getCanUndo(); - mShowRedo = getCanRedo(); - } - - /** - * Store preferences. - */ - public void storePersistentState( - SharedPreferences.Editor editor, - String prefix) { - // Store hash code of text in the editor so that we can check if the - // editor contents has changed. - editor.putString(prefix + ".hash", - String.valueOf( - getText().toString().hashCode())); - editor.putInt(prefix + ".maxSize", - mEditHistory.mmMaxHistorySize); - editor.putInt(prefix + ".position", - mEditHistory.mmPosition); - editor.putInt(prefix + ".size", - mEditHistory.mmHistory.size()); - - int i = 0; - for (EditItem ei : mEditHistory.mmHistory) { - String pre = prefix + "." + i; - - editor.putInt(pre + ".start", ei.mmStart); - editor.putString(pre + ".before", - ei.mmBefore.toString()); - editor.putString(pre + ".after", - ei.mmAfter.toString()); - - i++; - } - } - - /** - * Restore preferences. - * - * @param prefix The preference key prefix - * used when state was stored. - * @return did restore succeed? If this is - * false, the undo history will be empty. - */ - public boolean restorePersistentState( - SharedPreferences sp, String prefix) - throws IllegalStateException { - - boolean ok = - doRestorePersistentState(sp, prefix); - if (!ok) { - mEditHistory.clear(); - } - - return ok; - } - - private boolean doRestorePersistentState( - SharedPreferences sp, String prefix) { - - String hash = - sp.getString(prefix + ".hash", null); - if (hash == null) { - // No state to be restored. - return true; - } - - if (Integer.valueOf(hash) - != getText().toString().hashCode()) { - return false; - } - - mEditHistory.clear(); - mEditHistory.mmMaxHistorySize = - sp.getInt(prefix + ".maxSize", -1); - - int count = sp.getInt(prefix + ".size", -1); - if (count == -1) { - return false; - } - - for (int i = 0; i < count; i++) { - String pre = prefix + "." + i; - - int start = sp.getInt(pre + ".start", -1); - String before = - sp.getString(pre + ".before", null); - String after = - sp.getString(pre + ".after", null); - - if (start == -1 - || before == null - || after == null) { - return false; - } - mEditHistory.add( - new EditItem(start, before, after)); - } - - mEditHistory.mmPosition = - sp.getInt(prefix + ".position", -1); - return mEditHistory.mmPosition != -1; - - } - - /** - * Class that listens to changes in the text. - */ - private final class EditTextChangeListener - implements TextWatcher { - - /** - * The text that will be removed by the - * change event. - */ - private CharSequence mBeforeChange; - - /** - * The text that was inserted by the change - * event. - */ - private CharSequence mAfterChange; - - public void beforeTextChanged( - CharSequence s, int start, int count, - int after) { - if (mIsUndoOrRedo) { - return; - } - - mBeforeChange = - s.subSequence(start, start + count); - } - - public void onTextChanged(CharSequence s, - int start, int before, - int count) { - if (mIsUndoOrRedo) { - return; - } - - mAfterChange = - s.subSequence(start, start + count); - mEditHistory.add( - new EditItem(start, mBeforeChange, - mAfterChange)); - } - - public void afterTextChanged(Editable s) { - boolean showUndo = getCanUndo(); - boolean showRedo = getCanRedo(); - if (!canSaveFile) - canSaveFile = getCanUndo(); - if (showUndo != mShowUndo || showRedo != mShowRedo) { - mShowUndo = showUndo; - mShowRedo = showRedo; - ((MainActivity) getContext()).invalidateOptionsMenu(); - } - - ((MainActivity) getContext()).updateTextSyntax(); - } - } - - //endregion - - //region EDIT HISTORY - - /** - * Keeps track of all the edit history of a - * text. - */ - private final class EditHistory { - - /** - * The list of edits in chronological - * order. - */ - private final LinkedList - mmHistory = new LinkedList<>(); - /** - * The position from which an EditItem will - * be retrieved when getNext() is called. If - * getPrevious() has not been called, this - * has the same value as mmHistory.size(). - */ - private int mmPosition = 0; - /** - * Maximum undo history size. - */ - private int mmMaxHistorySize = -1; - - private int size() { - return mmHistory.size(); - } - - /** - * Clear history. - */ - private void clear() { - mmPosition = 0; - mmHistory.clear(); - } - - /** - * Adds a new edit operation to the history - * at the current position. If executed - * after a call to getPrevious() removes all - * the future history (elements with - * positions >= current history position). - */ - private void add(EditItem item) { - while (mmHistory.size() > mmPosition) { - mmHistory.removeLast(); - } - mmHistory.add(item); - mmPosition++; - - if (mmMaxHistorySize >= 0) { - trimHistory(); - } - } - - /** - * Trim history when it exceeds max history - * size. - */ - private void trimHistory() { - while (mmHistory.size() - > mmMaxHistorySize) { - mmHistory.removeFirst(); - mmPosition--; - } - - if (mmPosition < 0) { - mmPosition = 0; - } - } - - /** - * Set the maximum history size. If size is - * negative, then history size is only - * limited by the device memory. - */ - private void setMaxHistorySize( - int maxHistorySize) { - mmMaxHistorySize = maxHistorySize; - if (mmMaxHistorySize >= 0) { - trimHistory(); - } - } - - /** - * Traverses the history backward by one - * position, returns and item at that - * position. - */ - private EditItem getPrevious() { - if (mmPosition == 0) { - return null; - } - mmPosition--; - return mmHistory.get(mmPosition); - } - - /** - * Traverses the history forward by one - * position, returns and item at that - * position. - */ - private EditItem getNext() { - if (mmPosition >= mmHistory.size()) { - return null; - } - - EditItem item = mmHistory.get(mmPosition); - mmPosition++; - return item; - } - } - - /** - * Represents the changes performed by a - * single edit operation. - */ - private final class EditItem { - private final int mmStart; - private final CharSequence mmBefore; - private final CharSequence mmAfter; - - /** - * Constructs EditItem of a modification - * that was applied at position start and - * replaced CharSequence before with - * CharSequence after. - */ - public EditItem(int start, - CharSequence before, CharSequence after) { - mmStart = start; - mmBefore = before; - mmAfter = after; - } - } - //endregion - - - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.activity; + +import android.app.ProgressDialog; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.res.Configuration; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Typeface; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.preference.PreferenceManager; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.view.MenuItemCompat; +import android.support.v7.app.ActionBarActivity; +import android.support.v7.app.ActionBarDrawerToggle; +import android.support.v7.widget.ShareActionProvider; +import android.support.v7.widget.Toolbar; +import android.text.Editable; +import android.text.InputType; +import android.text.Selection; +import android.text.Spannable; +import android.text.TextPaint; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.text.method.KeyListener; +import android.text.style.ForegroundColorSpan; +import android.text.style.UnderlineSpan; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; +import android.widget.AdapterView; +import android.widget.EditText; +import android.widget.HorizontalScrollView; +import android.widget.ListView; +import android.widget.Toast; + +import com.faizmalkani.floatingactionbutton.FloatingActionButton; + +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.ArrayUtils; +import org.sufficientlysecure.rootcommands.Shell; +import org.sufficientlysecure.rootcommands.Toolbox; + +import java.io.File; +import java.io.UnsupportedEncodingException; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import sharedcode.turboeditor.R; +import sharedcode.turboeditor.adapter.AdapterDrawer; +import sharedcode.turboeditor.dialogfragment.ChangelogDialog; +import sharedcode.turboeditor.dialogfragment.FileInfoDialog; +import sharedcode.turboeditor.dialogfragment.FindTextDialog; +import sharedcode.turboeditor.dialogfragment.NewFileDetailsDialog; +import sharedcode.turboeditor.dialogfragment.NumberPickerDialog; +import sharedcode.turboeditor.dialogfragment.SaveFileDialog; +import sharedcode.turboeditor.preferences.PreferenceHelper; +import sharedcode.turboeditor.task.SaveFileTask; +import sharedcode.turboeditor.texteditor.EditTextPadding; +import sharedcode.turboeditor.texteditor.FileUtils; +import sharedcode.turboeditor.texteditor.LineUtils; +import sharedcode.turboeditor.texteditor.PageSystem; +import sharedcode.turboeditor.texteditor.PageSystemButtons; +import sharedcode.turboeditor.texteditor.Patterns; +import sharedcode.turboeditor.texteditor.SearchResult; +import sharedcode.turboeditor.util.AccessStorageApi; +import sharedcode.turboeditor.util.AnimationUtils; +import sharedcode.turboeditor.util.AppInfoHelper; +import sharedcode.turboeditor.util.EventBusEvents; +import sharedcode.turboeditor.util.IHomeActivity; +import sharedcode.turboeditor.util.MimeTypes; +import sharedcode.turboeditor.util.ProCheckUtils; +import sharedcode.turboeditor.util.ThemeUtils; +import sharedcode.turboeditor.views.CustomDrawerLayout; +import sharedcode.turboeditor.views.DialogHelper; +import sharedcode.turboeditor.views.GoodScrollView; + +import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.ENCODING; +import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.FONT_SIZE; +import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.LINE_NUMERS; +import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.MONOSPACE; +import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.READ_ONLY; +import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.SYNTAX; +import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.TEXT_SUGGESTIONS; +import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.THEME_CHANGE; +import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.WRAP_CONTENT; + +public abstract class MainActivity extends ActionBarActivity implements IHomeActivity, FindTextDialog + .SearchDialogInterface, GoodScrollView.ScrollInterface, PageSystem.PageSystemInterface, + PageSystemButtons.PageButtonsInterface, NumberPickerDialog.INumberPickerDialog, SaveFileDialog.ISaveDialog, + AdapterView.OnItemClickListener, AdapterDrawer.Callbacks{ + + //region VARIABLES + private static final int + ID_SELECT_ALL = android.R.id.selectAll; + private static final int ID_CUT = android.R.id.cut; + private static final int ID_COPY = android.R.id.copy; + private static final int ID_PASTE = android.R.id.paste; + private static final int SELECT_FILE_CODE = 121; + private static final int KITKAT_OPEN_REQUEST_CODE = 41; + private static final int SYNTAX_DELAY_MILLIS_SHORT = 250; + private static final int SYNTAX_DELAY_MILLIS_LONG = 1500; + private static final int ID_UNDO = R.id.im_undo; + private static final int ID_REDO = R.id.im_redo; + private static final int CHARS_TO_COLOR = 2500; + private static final Handler updateHandler = new Handler(); + private static final Runnable colorRunnable_duringEditing = + new Runnable() { + @Override + public void run() { + mEditor.replaceTextKeepCursor(null, true); + } + }; + private static final Runnable colorRunnable_duringScroll = + new Runnable() { + @Override + public void run() { + mEditor.replaceTextKeepCursor(null, false); + } + }; + private static boolean fileOpened = false; + private static String fileExtension; + /* + * This class provides a handy way to tie together the functionality of + * {@link DrawerLayout} and the framework ActionBar to implement the recommended + * design for navigation drawers. + */ + private static ActionBarDrawerToggle mDrawerToggle; + /* + * The Drawer Layout + */ + private static CustomDrawerLayout mDrawerLayout; + private static GoodScrollView verticalScroll; + private static String sFilePath = ""; + private static Editor mEditor; + private static HorizontalScrollView horizontalScroll; + private boolean searchingText; + private static SearchResult searchResult; + private static PageSystem pageSystem; + private static PageSystemButtons pageSystemButtons; + private static String currentEncoding = "UTF-8"; + private static Toolbar toolbar; + + /* + Navigation Drawer + */ + private static AdapterDrawer arrayAdapter; + private static LinkedList files; + //endregion + + //region Activity facts + + @Override + protected void onCreate(Bundle savedInstanceState) { + // set the windows background + ThemeUtils.setWindowsBackground(this); + // super!! + super.onCreate(savedInstanceState); + // setup the layout + setContentView(R.layout.activity_home); + toolbar = (Toolbar) findViewById(R.id.my_awesome_toolbar); + setSupportActionBar(toolbar); + // setup the navigation drawer + setupNavigationDrawer(); + // reset text editor + setupTextEditor(); + hideTextEditor(); + /* First Time we open this activity */ + if (savedInstanceState == null) { + // Open + mDrawerLayout.openDrawer(Gravity.START); + // Set the default title + getSupportActionBar().setTitle(getString(R.string.nome_app_turbo_editor)); + } + // parse the intent + parseIntent(getIntent()); + // show a dialog with the changelog + showChangeLog(); + } + + + @Override + protected final void onPostCreate(Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + mDrawerToggle.syncState(); + } + + @Override + public void onResume() { + super.onResume(); + // Refresh the list view + refreshList(); + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + parseIntent(intent); + } + + @Override + public void onPause() { + super.onPause(); + + if (PreferenceHelper.getAutoSave(getBaseContext()) && mEditor.canSaveFile()) { + saveTheFile(); + mEditor.fileSaved(); // so it doesn't ask to save in onDetach + } + } + + @Override + protected void onDestroy() { + try { + closeKeyBoard(); + } catch (NullPointerException e) { + e.printStackTrace(); + } + super.onDestroy(); + } + + @Override + public final void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + mDrawerToggle.onConfigurationChanged(newConfig); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + + if (keyCode == KeyEvent.KEYCODE_BACK) { + onBackPressed(); + return true; + } else if (keyCode == KeyEvent.KEYCODE_MENU) { + return false; + } else { + if (mEditor == null) + mEditor = (Editor) findViewById(R.id.editor); + + // this will happen on first key pressed on hard-keyboard only. Once myInputField + // gets the focus again, it will automatically receive further key presses. + + try { + if (fileOpened && mEditor != null && !mEditor.hasFocus()) { + mEditor.requestFocus(); + mEditor.onKeyDown(keyCode, event); + return true; + } + } catch (NullPointerException ex) { + + } + } + + + return false; + } + + @Override + public void onBackPressed() { + + try { + // if we should ignore the back button + if (PreferenceHelper.getIgnoreBackButton(this)) + return; + + if (mDrawerLayout.isDrawerOpen(Gravity.START) && fileOpened) { + mDrawerLayout.closeDrawer(Gravity.START); + } else if (mDrawerLayout.isDrawerOpen(Gravity.END) && fileOpened) { + mDrawerLayout.closeDrawer(Gravity.END); + } else if (fileOpened && mEditor.canSaveFile()) { + SaveFileDialog.newInstance(sFilePath, pageSystem.getAllText(mEditor + .getText().toString()), currentEncoding).show(getFragmentManager(), + "dialog"); + } else if (fileOpened) { + + // remove editor fragment + hideTextEditor(); + + // Set the default title + getSupportActionBar().setTitle(getString(R.string.nome_app_turbo_editor)); + + onEvent(new EventBusEvents.ClosedAFile()); + + mDrawerLayout.openDrawer(Gravity.START); + mDrawerLayout.closeDrawer(Gravity.END); + } else { + showInterstitial(); + super.onBackPressed(); + } + } catch (Exception e) { + // maybe something is null, who knows + } + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (resultCode == RESULT_OK) { + String path = ""; + if (requestCode == SELECT_FILE_CODE) { + path = data.getStringExtra("path"); + if(TextUtils.isEmpty(path)) + path = AccessStorageApi.getPath(getBaseContext(), data.getData()); + } + + if (requestCode == KITKAT_OPEN_REQUEST_CODE) { + path = AccessStorageApi.getPath(getBaseContext(), data.getData()); + } + + if (!TextUtils.isEmpty(path)) { + File file = new File(path); + if (file.isFile() && file.exists()) { + onEvent(new EventBusEvents.NewFileToOpen(new File + (path))); + } + } + + } + } + + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + // Path of the file selected + String filePath = files.get(position).getAbsolutePath(); + // Send the event that a file was selected + onEvent(new EventBusEvents.NewFileToOpen(new File(filePath))); + } + + //endregion + + //region MENU + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + if (fileOpened && searchingText) + getMenuInflater().inflate(R.menu.fragment_editor_search, menu); + else if (fileOpened) + getMenuInflater().inflate(R.menu.fragment_editor, menu); + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + + if (fileOpened && searchingText) { + MenuItem imReplace = menu.findItem(R.id.im_replace); + MenuItem imPrev = menu.findItem(R.id.im_previous_item); + MenuItem imNext = menu.findItem(R.id.im_next_item); + + if (imReplace != null) + imReplace.setVisible(searchResult.canReplaceSomething()); + + if (imPrev != null) + imPrev.setVisible(searchResult.hasPrevious()); + + if (imNext != null) + imNext.setVisible(searchResult.hasNext()); + + + } else if (fileOpened) { + MenuItem imSave = menu.findItem(R.id.im_save); + MenuItem imUndo = menu.findItem(R.id.im_undo); + MenuItem imRedo = menu.findItem(R.id.im_redo); + if (mEditor != null) { + if (imSave != null) + imSave.setVisible(mEditor.canSaveFile()); + if (imUndo != null) + imUndo.setVisible(mEditor.getCanUndo()); + if (imRedo != null) + imRedo.setVisible(mEditor.getCanRedo()); + } else { + imSave.setVisible(false); + imUndo.setVisible(false); + imRedo.setVisible(false); + } + + MenuItem item = menu.findItem(R.id.im_share); + ShareActionProvider shareAction = (ShareActionProvider) MenuItemCompat + .getActionProvider(item); + File f = new File(sFilePath); + Intent shareIntent = new Intent(); + shareIntent.setAction(Intent.ACTION_SEND); + shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(f)); + shareIntent.setType("text/plain"); + shareAction.setShareIntent(shareIntent); + } + + MenuItem imDonate = menu.findItem(R.id.im_donate); + if (imDonate != null) + if (ProCheckUtils.isPro(this, false)) + imDonate.setVisible(false); + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int i = item.getItemId(); + if (mDrawerToggle.onOptionsItemSelected(item)) { + Toast.makeText(getBaseContext(), "drawer click", Toast.LENGTH_SHORT).show(); + mDrawerLayout.closeDrawer(Gravity.END); + return true; + } else if (i == R.id.im_save) { + saveTheFile(); + + } else if (i == R.id.im_undo) { + mEditor.onTextContextMenuItem(ID_UNDO); + + } else if (i == R.id.im_redo) { + mEditor.onTextContextMenuItem(ID_REDO); + + } else if (i == R.id.im_search) { + FindTextDialog.newInstance(mEditor.getText().toString()).show(getFragmentManager() + .beginTransaction(), "dialog"); + } else if (i == R.id.im_cancel) { + searchingText = false; + invalidateOptionsMenu(); + + } else if (i == R.id.im_replace) { + replaceText(); + + } else if (i == R.id.im_next_item) { + nextResult(); + + } else if (i == R.id.im_previous_item) { + previousResult(); + + } else if (i == R.id.im_goto_line) { + int min = mEditor.getLineUtils().firstReadLine(); + int max = mEditor.getLineUtils().lastReadLine(); + NumberPickerDialog.newInstance + (NumberPickerDialog.Actions.GoToLine, min, min, max).show(getFragmentManager().beginTransaction(), "dialog"); + } else if (i == R.id.im_view_it_on_browser) { + Intent browserIntent; + try { + browserIntent = new Intent(Intent.ACTION_VIEW); + browserIntent.setDataAndType(Uri.fromFile(new File(sFilePath)), "text/*"); + startActivity(browserIntent); + } catch (ActivityNotFoundException ex2) { + // + } + + } else if (i == R.id.im_info) { + FileInfoDialog.newInstance(sFilePath).show(getFragmentManager().beginTransaction(), "dialog"); + } + + else if (i == R.id.im_donate) { + DialogHelper.showDonateDialog(this); + } + return super.onOptionsItemSelected(item); + } + //endregion + + // region OTHER THINGS + void replaceText() { + int start = searchResult.foundIndex.get(searchResult.index); + int end = start + searchResult.textLength; + mEditor.setText(mEditor.getText().replace(start, end, searchResult.textToReplace)); + searchResult.doneReplace(); + + invalidateOptionsMenu(); + + if (searchResult.hasNext()) + nextResult(); + else if (searchResult.hasPrevious()) + previousResult(); + } + + void nextResult() { + if (searchResult.index == mEditor.getLineCount() - 1) // last result of page + { + return; + } + + + if (searchResult.index < searchResult.numberOfResults() - 1) { // equal zero is not good + searchResult.index++; + final int line = mEditor.getLineUtils().getLineFromIndex(searchResult.foundIndex.get + (searchResult.index), mEditor.getLineCount(), mEditor.getLayout()); + + + verticalScroll.post(new Runnable() { + @Override + public void run() { + int y = mEditor.getLayout().getLineTop(line); + if (y > 100) + y -= 100; + else + y = 0; + + verticalScroll.scrollTo(0, y); + } + }); + + mEditor.setFocusable(true); + mEditor.requestFocus(); + mEditor.setSelection(searchResult.foundIndex.get(searchResult.index), + searchResult.foundIndex.get(searchResult.index) + searchResult.textLength); + } + + invalidateOptionsMenu(); + } + + void previousResult() { + if (searchResult.index == 0) + return; + if (searchResult.index > 0) { + searchResult.index--; + final int line = LineUtils.getLineFromIndex(searchResult.foundIndex.get + (searchResult.index), mEditor.getLineCount(), mEditor.getLayout()); + verticalScroll.post(new Runnable() { + @Override + public void run() { + int y = mEditor.getLayout().getLineTop(line); + if (y > 100) + y -= 100; + else + y = 0; + verticalScroll.scrollTo(0, y); + } + }); + + mEditor.setFocusable(true); + mEditor.requestFocus(); + mEditor.setSelection(searchResult.foundIndex.get(searchResult.index), + searchResult.foundIndex.get(searchResult.index) + searchResult.textLength); + } + + invalidateOptionsMenu(); + } + + private void saveTheFile() { + File file = new File(sFilePath); + if (!file.getName().isEmpty()) + new SaveFileTask(this, sFilePath, pageSystem.getAllText(mEditor.getText() + .toString()), currentEncoding).execute(); + else { + NewFileDetailsDialog.newInstance + (pageSystem.getAllText(mEditor.getText().toString()), currentEncoding).show(getFragmentManager().beginTransaction(), "dialog"); + } + } + + /** + * Setup the navigation drawer + */ + private void setupNavigationDrawer() { + mDrawerLayout = (CustomDrawerLayout) findViewById(R.id.drawer_layout); + /* Action Bar + final ActionBar ab = toolbar; + ab.setDisplayHomeAsUpEnabled(true); + ab.setHomeButtonEnabled(true);*/ + /* Navigation drawer */ + mDrawerToggle = + new ActionBarDrawerToggle( + this, + mDrawerLayout, + toolbar, + R.string.nome_app_turbo_editor, + R.string.nome_app_turbo_editor) { + + @Override + public void onDrawerOpened(View drawerView) { + supportInvalidateOptionsMenu(); + try { + closeKeyBoard(); + } catch (NullPointerException e) { + e.printStackTrace(); + } + } + + @Override + public void onDrawerClosed(View view) { + supportInvalidateOptionsMenu(); + } + }; + /* link the mDrawerToggle to the Drawer Layout */ + mDrawerLayout.setDrawerListener(mDrawerToggle); +//mDrawerLayout.setFocusableInTouchMode(false); + + ListView listView = (ListView) findViewById(android.R.id.list); + listView.setEmptyView(findViewById(android.R.id.empty)); + files = new LinkedList<>(); + arrayAdapter = new AdapterDrawer(this, files, this); + listView.setAdapter(arrayAdapter); + listView.setOnItemClickListener(this); + } + + private void setupTextEditor() { + + verticalScroll = (GoodScrollView) findViewById(R.id.vertical_scroll); + horizontalScroll = (HorizontalScrollView) findViewById(R.id.horizontal_scroll); + mEditor = (Editor) findViewById(R.id.editor); + + //mEditor.setLayerType(View.LAYER_TYPE_NONE, null); + + if (PreferenceHelper.getWrapContent(this)) { + horizontalScroll.removeView(mEditor); + verticalScroll.removeView(horizontalScroll); + verticalScroll.addView(mEditor); + } + + if (PreferenceHelper.getReadOnly(this)) { + getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); + } else { + getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED); + } + + verticalScroll.setScrollInterface(this); + + pageSystem = new PageSystem(this, this, "", null); + + pageSystemButtons = new PageSystemButtons(this, this, + (FloatingActionButton) findViewById(R.id.fabPrev), + (FloatingActionButton) findViewById(R.id.fabNext)); + } + + private void showTextEditor() { + + fileOpened = true; + + findViewById(R.id.text_editor).setVisibility(View.VISIBLE); + findViewById(R.id.no_file_opened_messagge).setVisibility(View.GONE); + + mEditor.resetVariables(); + searchResult = null; + searchingText = false; + + invalidateOptionsMenu(); + + mEditor.disableTextChangedListener(); + mEditor.replaceTextKeepCursor(pageSystem.getCurrentPageText(), false); + mEditor.enableTextChangedListener(); + } + + private void hideTextEditor() { + + fileOpened = false; + + try { + findViewById(R.id.text_editor).setVisibility(View.GONE); + findViewById(R.id.no_file_opened_messagge).setVisibility(View.VISIBLE); + + mEditor.disableTextChangedListener(); + mEditor.replaceTextKeepCursor("", false); + mEditor.enableTextChangedListener(); + } catch (Exception e) { + // lol + } + } + + /** + * Parses the intent + */ + private void parseIntent(Intent intent) { + final String action = intent.getAction(); + final String type = intent.getType(); + + if (Intent.ACTION_VIEW.equals(action) + || Intent.ACTION_EDIT.equals(action) + || Intent.ACTION_PICK.equals(action) + && type != null) { + // Post event + onEvent(new EventBusEvents.NewFileToOpen(new File(intent + .getData().getPath()))); + } else if (Intent.ACTION_SEND.equals(action) && type != null) { + if ("text/plain".equals(type)) { + onEvent(new EventBusEvents.NewFileToOpen(intent.getStringExtra(Intent.EXTRA_TEXT))); + } + } + } + + /** + * Show a dialog with the changelog + */ + private void showChangeLog() { + final String currentVersion = AppInfoHelper.getCurrentVersion(this); + final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); + final String lastVersion = preferences.getString("last_version", currentVersion); + preferences.edit().putString("last_version", currentVersion).apply(); + if (!lastVersion.equals(currentVersion)) { + ChangelogDialog.showChangeLogDialog(getFragmentManager()); + } + } + + // closes the soft keyboard + private void closeKeyBoard() throws NullPointerException { + // Central system API to the overall input method framework (IMF) architecture + InputMethodManager inputManager = + (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + + // Base interface for a remotable object + IBinder windowToken = getCurrentFocus().getWindowToken(); + + // Hide type + int hideType = InputMethodManager.HIDE_NOT_ALWAYS; + + // Hide the KeyBoard + inputManager.hideSoftInputFromWindow(windowToken, hideType); + } + + void updateTextSyntax() { + if (!PreferenceHelper.getSyntaxHighlight(this) || mEditor.hasSelection() || + updateHandler == null || colorRunnable_duringEditing == null) + return; + + updateHandler.removeCallbacks(colorRunnable_duringEditing); + updateHandler.removeCallbacks(colorRunnable_duringScroll); + updateHandler.postDelayed(colorRunnable_duringEditing, SYNTAX_DELAY_MILLIS_LONG); + } + + private void refreshList(){ + refreshList(null, false, false); + } + + private void refreshList(@Nullable String path, boolean add, boolean delete) { + int max_recent_files = 15; + if(add) + max_recent_files--; + + // File paths saved in preferences + String[] savedPaths = PreferenceHelper.getSavedPaths(this); + int first_index_of_array = savedPaths.length > max_recent_files ? savedPaths.length - max_recent_files : 0; + savedPaths = ArrayUtils.subarray(savedPaths, first_index_of_array, savedPaths.length); + // File names for the list + files.clear(); + // StringBuilder that will contain the file paths + StringBuilder sb = new StringBuilder(); + // for cycle to convert paths to names + + for(int i = 0; i < savedPaths.length; i++){ + String savedPath = savedPaths[i]; + File file = new File(savedPath); + // Check that the file exist + if (file.exists()) { + if(path != null && path.equals(savedPath) && delete) + continue; + else { + files.addFirst(file); + sb.append(savedPath).append(","); + } + } + } + if(path != null && !path.isEmpty() && add && !ArrayUtils.contains(savedPaths, path)) { + sb.append(path).append(","); + files.addFirst(new File(path)); + } + // save list without empty or non existed files + PreferenceHelper.setSavedPaths(this, sb); + // Set adapter + arrayAdapter.notifyDataSetChanged(); + } + //endregion + + //region EVENTBUS + void onEvent(final EventBusEvents.NewFileToOpen event) { + + if (fileOpened && mEditor.canSaveFile()) { + SaveFileDialog.newInstance(sFilePath, pageSystem.getAllText(mEditor + .getText().toString()), currentEncoding, true, event.getFile().getAbsolutePath()).show(getFragmentManager(), + "dialog"); + return; + } + + new AsyncTask() { + + File file; + String message = ""; + String fileText; + String encoding; + ProgressDialog progressDialog; + + @Override + protected void onPreExecute() { + super.onPreExecute(); + // Close the drawer + mDrawerLayout.closeDrawer(Gravity.START); + progressDialog = new ProgressDialog(MainActivity.this); + progressDialog.setMessage(getString(R.string.please_wait)); + progressDialog.show(); + + } + + @Override + protected Void doInBackground(Void... params) { + file = event.getFile(); + try { + if (!file.exists() || !file.isFile()) { + fileText = event.getFileText(); + sFilePath = file.getAbsolutePath(); + fileExtension = "txt"; + return null; + } + + file = file.getCanonicalFile(); + sFilePath = file.getAbsolutePath(); + fileExtension = FilenameUtils.getExtension(sFilePath).toLowerCase(); + + boolean isRoot; + + if (!file.canRead()) { + Shell shell; + shell = Shell.startRootShell(); + Toolbox tb = new Toolbox(shell); + isRoot = tb.isRootAccessGiven(); + + if (isRoot) { + File tempFile = new File(getFilesDir(), "temp.root.file"); + if (!tempFile.exists()) + tempFile.createNewFile(); + tb.copyFile(event.getFile().getAbsolutePath(), + tempFile.getAbsolutePath(), false, false); + file = new File(tempFile.getAbsolutePath()); + } + } + + boolean autoencoding = PreferenceHelper.getAutoEncoding(MainActivity.this); + if (autoencoding) { + + encoding = FileUtils.getDetectedEncoding(file); + if (encoding.isEmpty()) { + encoding = PreferenceHelper.getEncoding(MainActivity.this); + } + } else { + encoding = PreferenceHelper.getEncoding(MainActivity.this); + } + + fileText = org.apache.commons.io.FileUtils.readFileToString(file, encoding); + } catch (Exception e) { + message = e.getMessage(); + fileText = ""; + } + + while (mDrawerLayout.isDrawerOpen(Gravity.START)) { + try { + Thread.sleep(50); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + return null; + } + + @Override + protected void onPostExecute(Void result) { + super.onPostExecute(result); + progressDialog.hide(); + + if (!message.isEmpty()) { + Toast.makeText(MainActivity.this, message, Toast.LENGTH_LONG).show(); + onEvent(new EventBusEvents.CannotOpenAFile()); + } else { + + pageSystem = new PageSystem(MainActivity.this, MainActivity.this, fileText, new File(sFilePath)); + currentEncoding = encoding; + + onEvent(new EventBusEvents.AFileIsSelected(sFilePath)); + + showTextEditor(); + + String name = FilenameUtils.getName(sFilePath); + if (name.isEmpty()) + getSupportActionBar().setTitle(R.string.new_file); + else + getSupportActionBar().setTitle(name); + + if(!name.isEmpty()) { + refreshList(sFilePath, true, false); + } + } + + } + }.execute(); + } + + public void onEvent(EventBusEvents.SavedAFile event) { + + sFilePath = event.getPath(); + fileExtension = FilenameUtils.getExtension(sFilePath).toLowerCase(); + + mEditor.clearHistory(); + mEditor.fileSaved(); + invalidateOptionsMenu(); + + try { + closeKeyBoard(); + } catch (NullPointerException e) { + e.printStackTrace(); + } + + refreshList(event.getPath(), true, false); + arrayAdapter.selectView(event.getPath()); + + showInterstitial(); + } + + /** + * When a file can't be opened + * Invoked by the EditorFragment + * + * @param event The event called + */ + void onEvent(EventBusEvents.CannotOpenAFile event) { + // + mDrawerLayout.openDrawer(Gravity.LEFT); + // + getSupportActionBar().setTitle(getString(R.string.nome_app_turbo_editor)); + // + supportInvalidateOptionsMenu(); + // Replace fragment + hideTextEditor(); + } + + public void onEvent(EventBusEvents.APreferenceValueWasChanged event) { + + if (event.hasType(EventBusEvents.APreferenceValueWasChanged.Type.THEME_CHANGE)) { + ThemeUtils.setWindowsBackground(this); + } + + if (event.hasType(WRAP_CONTENT)) { + if (PreferenceHelper.getWrapContent(this)) { + horizontalScroll.removeView(mEditor); + verticalScroll.removeView(horizontalScroll); + verticalScroll.addView(mEditor); + } else { + verticalScroll.removeView(mEditor); + verticalScroll.addView(horizontalScroll); + horizontalScroll.addView(mEditor); + } + } else if (event.hasType(LINE_NUMERS)) { + mEditor.disableTextChangedListener(); + mEditor.replaceTextKeepCursor(null, true); + mEditor.enableTextChangedListener(); + if (PreferenceHelper.getLineNumbers(this)) { + mEditor.setPadding(EditTextPadding.getPaddingWithLineNumbers(this, + PreferenceHelper.getFontSize(this)), + EditTextPadding.getPaddingTop(this), 0, 0); + } else { + mEditor.setPadding(EditTextPadding.getPaddingWithoutLineNumbers(this) + , EditTextPadding.getPaddingTop(this), 0, 0); + } + } else if (event.hasType(SYNTAX)) { + mEditor.disableTextChangedListener(); + mEditor.replaceTextKeepCursor(null, true); + mEditor.enableTextChangedListener(); + } else if (event.hasType(MONOSPACE)) { + if (PreferenceHelper.getUseMonospace(this)) + mEditor.setTypeface(Typeface.MONOSPACE); + else + mEditor.setTypeface(Typeface.DEFAULT); + } else if (event.hasType(THEME_CHANGE)) { + if (PreferenceHelper.getLightTheme(this)) { + mEditor.setTextColor(getResources().getColor(R.color.textColorInverted)); + } else { + mEditor.setTextColor(getResources().getColor(R.color.textColor)); + } + } else if (event.hasType(TEXT_SUGGESTIONS) || event.hasType(READ_ONLY)) { + if (PreferenceHelper.getReadOnly(this)) { + getWindow().setSoftInputMode(WindowManager.LayoutParams + .SOFT_INPUT_STATE_ALWAYS_HIDDEN); + mEditor.setReadOnly(true); + } else { + getWindow().setSoftInputMode(WindowManager.LayoutParams + .SOFT_INPUT_STATE_UNSPECIFIED); + mEditor.setReadOnly(false); + if (PreferenceHelper.getSuggestionActive(this)) { + mEditor.setInputType(InputType.TYPE_CLASS_TEXT | InputType + .TYPE_TEXT_FLAG_MULTI_LINE | InputType.TYPE_TEXT_FLAG_IME_MULTI_LINE); + } else { + mEditor.setInputType(InputType.TYPE_CLASS_TEXT | InputType + .TYPE_TEXT_FLAG_MULTI_LINE | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS + | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD | InputType + .TYPE_TEXT_FLAG_IME_MULTI_LINE); + } + } + // sometimes it becomes monospace after setting the input type + if (PreferenceHelper.getUseMonospace(this)) + mEditor.setTypeface(Typeface.MONOSPACE); + else + mEditor.setTypeface(Typeface.DEFAULT); + } else if (event.hasType(FONT_SIZE)) { + if (PreferenceHelper.getLineNumbers(this)) { + mEditor.setPadding(EditTextPadding.getPaddingWithLineNumbers(this, + PreferenceHelper.getFontSize(this)), + EditTextPadding.getPaddingTop(this), 0, 0); + } else { + mEditor.setPadding(EditTextPadding.getPaddingWithoutLineNumbers(this) + , EditTextPadding.getPaddingTop(this), 0, 0); + } + mEditor.setTextSize(PreferenceHelper.getFontSize(this)); + } else if (event.hasType(ENCODING)) { + String oldEncoding, newEncoding; + oldEncoding = currentEncoding; + newEncoding = PreferenceHelper.getEncoding(this); + try { + final byte[] oldText = mEditor.getText().toString().getBytes(oldEncoding); + mEditor.disableTextChangedListener(); + mEditor.replaceTextKeepCursor(new String(oldText, newEncoding), true); + mEditor.enableTextChangedListener(); + currentEncoding = newEncoding; + } catch (UnsupportedEncodingException ignored) { + try { + final byte[] oldText = mEditor.getText().toString().getBytes(oldEncoding); + mEditor.disableTextChangedListener(); + mEditor.replaceTextKeepCursor(new String(oldText, "UTF-8"), true); + mEditor.enableTextChangedListener(); + } catch (UnsupportedEncodingException ignored2) { + } + } + } + } + + void onEvent(EventBusEvents.AFileIsSelected event) { + arrayAdapter.selectView(event.getPath()); + } + + void onEvent(EventBusEvents.ClosedAFile event) { + arrayAdapter.selectView(""); + } + //endregion + + //region Calls from the layout + public void OpenFile(View view) { + Intent subActivity = new Intent(MainActivity.this, SelectFileActivity.class); + subActivity.putExtra("action", SelectFileActivity.Actions.SelectFile); + AnimationUtils.startActivityWithScale(this, subActivity, true, SELECT_FILE_CODE, view); + } + + public void CreateFile(View view) { + onEvent(new EventBusEvents.NewFileToOpen("")); // do not send the event to others + } + + public void OpenInfo(View view) { + DialogHelper.showAboutDialog(this); + } + + public void OpenSettings(View view) { + mDrawerLayout.closeDrawer(Gravity.START); + mDrawerLayout.openDrawer(Gravity.END); + } + //endregion + + //region Ovverideses + @Override + public void nextPageClicked() { + pageSystem.savePage(mEditor.getText().toString()); + pageSystem.nextPage(); + mEditor.disableTextChangedListener(); + mEditor.replaceTextKeepCursor(pageSystem.getCurrentPageText(), false); + mEditor.enableTextChangedListener(); + + verticalScroll.postDelayed(new Runnable() { + @Override + public void run() { + verticalScroll.smoothScrollTo(0, 0); + } + }, 200); + + if (!PreferenceHelper.getPageSystemButtonsPopupShown(this)) { + PreferenceHelper.setPageSystemButtonsPopupShown(this, true); + Toast.makeText(this, getString(R.string.long_click_for_more_options), + Toast.LENGTH_LONG).show(); + } + } + + @Override + public void prevPageClicked() { + pageSystem.savePage(mEditor.getText().toString()); + pageSystem.prevPage(); + mEditor.disableTextChangedListener(); + mEditor.replaceTextKeepCursor(pageSystem.getCurrentPageText(), false); + mEditor.enableTextChangedListener(); + + verticalScroll.postDelayed(new Runnable() { + @Override + public void run() { + verticalScroll.smoothScrollTo(0, 0); + } + }, 200); + + if (!PreferenceHelper.getPageSystemButtonsPopupShown(this)) { + PreferenceHelper.setPageSystemButtonsPopupShown(this, true); + Toast.makeText(this, getString(R.string.long_click_for_more_options), + Toast.LENGTH_LONG).show(); + } + } + + @Override + public void pageSystemButtonLongClicked() { + int maxPages = pageSystem.getMaxPage(); + int currentPage = pageSystem.getCurrentPage(); + NumberPickerDialog.newInstance + (NumberPickerDialog.Actions.SelectPage, 0, currentPage, maxPages).show(getFragmentManager().beginTransaction(), "dialog"); + } + + @Override + public boolean canReadNextPage() { + return pageSystem.canReadNextPage(); + } + + @Override + public boolean canReadPrevPage() { + return pageSystem.canReadPrevPage(); + } + + @Override + public void onSearchDone(SearchResult searchResult) { + MainActivity.searchResult = searchResult; + searchingText = true; + invalidateOptionsMenu(); + + final int line = LineUtils.getLineFromIndex(searchResult.foundIndex.getFirst + (), mEditor.getLineCount(), mEditor.getLayout()); + verticalScroll.post(new Runnable() { + @Override + public void run() { + int y = mEditor.getLayout().getLineTop(line); + if (y > 100) + y -= 100; + else + y = 0; + + verticalScroll.scrollTo(0, y); + } + }); + + mEditor.setFocusable(true); + mEditor.requestFocus(); + mEditor.setSelection(searchResult.foundIndex.getFirst(), searchResult.foundIndex.getFirst + () + searchResult.textLength); + + } + + @Override + public void onPageChanged(int page) { + pageSystemButtons.updateVisibility(false); + searchingText = false; + mEditor.clearHistory(); + invalidateOptionsMenu(); + } + + @Override + public void onScrollChanged(int l, int t, int oldl, int oldt) { + pageSystemButtons.updateVisibility(Math.abs(t) > 10); + + if (!PreferenceHelper.getSyntaxHighlight(this) || (mEditor.hasSelection() && + !searchingText) || updateHandler == null || colorRunnable_duringScroll == null) + return; + + updateHandler.removeCallbacks(colorRunnable_duringEditing); + updateHandler.removeCallbacks(colorRunnable_duringScroll); + updateHandler.postDelayed(colorRunnable_duringScroll, SYNTAX_DELAY_MILLIS_SHORT); + } + + @Override + public void onNumberPickerDialogDismissed(NumberPickerDialog.Actions action, int value) { + if (action == NumberPickerDialog.Actions.SelectPage) { + pageSystem.savePage(mEditor.getText().toString()); + pageSystem.goToPage(value); + mEditor.disableTextChangedListener(); + mEditor.replaceTextKeepCursor(pageSystem.getCurrentPageText(), true); + mEditor.enableTextChangedListener(); + + verticalScroll.postDelayed(new Runnable() { + @Override + public void run() { + verticalScroll.smoothScrollTo(0, 0); + } + }, 200); + + } else if (action == NumberPickerDialog.Actions.GoToLine) { + + int fakeLine = mEditor.getLineUtils().fakeLineFromRealLine(value); + final int y = mEditor.getLineUtils().getYAtLine(verticalScroll, + mEditor.getLineCount(), fakeLine); + + verticalScroll.postDelayed(new Runnable() { + @Override + public void run() { + verticalScroll.smoothScrollTo(0, y); + } + }, 200); + } + + } + + @Override + public void userDoesntWantToSave(boolean openNewFile, String pathOfNewFile) { + Editor.canSaveFile = false; + if(openNewFile) + onEvent(new EventBusEvents.NewFileToOpen(new File(pathOfNewFile))); + else + onEvent(new EventBusEvents.CannotOpenAFile()); + } + + @Override + public void CancelItem(int position, boolean andCloseOpenedFile) { + refreshList(files.get(position).getAbsolutePath(), false, true); + if (andCloseOpenedFile) + onEvent(new EventBusEvents.CannotOpenAFile()); + } + //endregion + + public static class Editor extends EditText { + + //region VARIABLES + private static final TextPaint mPaintNumbers = new TextPaint(); + /** + * The edit history. + */ + private final EditHistory mEditHistory; + /** + * The change listener. + */ + private final EditTextChangeListener + mChangeListener; + /** + * Disconnect this undo/redo from the text + * view. + */ + private static boolean enabledChangeListener; + private static int paddingTop; + private static int numbersWidth; + private static int lineHeight; + + private static int lineCount, realLine, startingLine; + private static LineUtils lineUtils; + /** + * Is undo/redo being performed? This member + * signals if an undo/redo operation is + * currently being performed. Changes in the + * text during undo/redo are not recorded + * because it would mess up the undo history. + */ + private static boolean mIsUndoOrRedo; + private static Matcher m; + private static boolean mShowUndo, mShowRedo; + private static boolean canSaveFile; + private static KeyListener keyListener; + private static int firstVisibleIndex, firstColoredIndex; + private static int deviceHeight; + private static int editorHeight; + private static boolean[] hasNewLineArray; + private static int[] realLines; + private static boolean wrapContent; + private static int lastLine; + private static int firstLine; + private static CharSequence textToHighlight; + private static int lastVisibleIndex; + private static int i; + //endregion + + //region CONSTRUCTOR + public Editor(final Context context, AttributeSet attrs) { + super(context, attrs); + + //setLayerType(View.LAYER_TYPE_NONE, null); + + mEditHistory = new EditHistory(); + mChangeListener = new EditTextChangeListener(); + lineUtils = new LineUtils(); + + deviceHeight = getResources().getDisplayMetrics().heightPixels; + + paddingTop = EditTextPadding.getPaddingTop(getContext()); + + mPaintNumbers.setAntiAlias(true); + mPaintNumbers.setDither(false); + mPaintNumbers.setTextAlign(Paint.Align.RIGHT); + mPaintNumbers.setColor(getResources().getColor(R.color.file_text)); + + if (PreferenceHelper.getLightTheme(getContext())) { + setTextColor(getResources().getColor(R.color.textColorInverted)); + } else { + setTextColor(getResources().getColor(R.color.textColor)); + } + if (PreferenceHelper.getLineNumbers(getContext())) { + setPadding(EditTextPadding.getPaddingWithLineNumbers(getContext(), + PreferenceHelper.getFontSize(getContext())), + EditTextPadding.getPaddingTop(getContext()), 0, 0); + } else { + setPadding(EditTextPadding.getPaddingWithoutLineNumbers(getContext()), + EditTextPadding.getPaddingTop(getContext()), 0, 0); + } + + if (PreferenceHelper.getReadOnly(getContext())) { + setReadOnly(true); + } else { + setReadOnly(false); + if (PreferenceHelper.getSuggestionActive(getContext())) { + setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE + | InputType.TYPE_TEXT_FLAG_IME_MULTI_LINE); + } else { + setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE + | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType + .TYPE_TEXT_VARIATION_VISIBLE_PASSWORD | InputType + .TYPE_TEXT_FLAG_IME_MULTI_LINE); + } + } + + if (PreferenceHelper.getUseMonospace(getContext())) { + setTypeface(Typeface.MONOSPACE); + } else { + setTypeface(Typeface.DEFAULT); + } + setTextSize(PreferenceHelper.getFontSize(getContext())); + + setFocusable(true); + setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (!PreferenceHelper.getReadOnly(getContext())) { + verticalScroll.tempDisableListener(1000); + ((InputMethodManager) getContext().getSystemService(Context + .INPUT_METHOD_SERVICE)) + .showSoftInput(Editor.this, InputMethodManager.SHOW_IMPLICIT); + } + + } + }); + setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + if (hasFocus && !PreferenceHelper.getReadOnly(getContext())) { + verticalScroll.tempDisableListener(1000); + ((InputMethodManager) getContext().getSystemService(Context + .INPUT_METHOD_SERVICE)) + .showSoftInput(Editor.this, InputMethodManager.SHOW_IMPLICIT); + } + } + }); + + setMaxHistorySize(30); + + resetVariables(); + } + + public void setReadOnly(boolean value) { + if (value) { + keyListener = getKeyListener(); + setKeyListener(null); + } else { + if (keyListener != null) + setKeyListener(keyListener); + } + } + + //region OVERRIDES + @Override + public void setTextSize(float size) { + super.setTextSize(size); + final float scale = getContext().getResources().getDisplayMetrics().density; + mPaintNumbers.setTextSize((int) (size * scale * 0.65f)); + numbersWidth = (int) (EditTextPadding.getPaddingWithLineNumbers(getContext(), + PreferenceHelper.getFontSize(getContext())) * 0.8); + lineHeight = getLineHeight(); + } + + + @Override + public void onDraw(@NonNull final Canvas canvas) { + + if (lineCount != getLineCount() || startingLine != pageSystem.getStartingLine()) { + startingLine = pageSystem.getStartingLine(); + lineCount = getLineCount(); + lineUtils.updateHasNewLineArray(pageSystem + .getStartingLine(), lineCount, getLayout(), getText().toString()); + + hasNewLineArray = lineUtils.getToCountLinesArray(); + realLines = lineUtils.getRealLines(); + + } + + editorHeight = getHeight(); + firstLine = lineUtils.getFirstVisibleLine(verticalScroll, editorHeight, lineCount); + lastLine = lineUtils.getLastVisibleLine(verticalScroll, editorHeight, lineCount, deviceHeight); + + if (PreferenceHelper.getLineNumbers(getContext())) { + wrapContent = PreferenceHelper.getWrapContent(getContext()); + i = firstLine; + + while (i < lastLine) { + // if last line we count it anyway + if (!wrapContent + || hasNewLineArray[i] + || i == lastLine - 1) { + realLine = realLines[i]; + + canvas.drawText(String.valueOf(realLine), + numbersWidth, // they are all center aligned + paddingTop + lineHeight * (i + 1), + mPaintNumbers); + } + i++; + } + } + + super.onDraw(canvas); + } + + + //endregion + + //region Other + + @Override + public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) { + + if (event.isCtrlPressed()) { + switch (keyCode) { + case KeyEvent.KEYCODE_A: + return onTextContextMenuItem(ID_SELECT_ALL); + case KeyEvent.KEYCODE_X: + return onTextContextMenuItem(ID_CUT); + case KeyEvent.KEYCODE_C: + return onTextContextMenuItem(ID_COPY); + case KeyEvent.KEYCODE_V: + return onTextContextMenuItem(ID_PASTE); + case KeyEvent.KEYCODE_Z: + if (getCanUndo()) { + return onTextContextMenuItem(ID_UNDO); + } + case KeyEvent.KEYCODE_Y: + if (getCanRedo()) { + return onTextContextMenuItem(ID_REDO); + } + case KeyEvent.KEYCODE_S: + ((MainActivity) getContext()).saveTheFile(); + return true; + default: + return super.onKeyDown(keyCode, event); + } + } else { + switch (keyCode) { + case KeyEvent.KEYCODE_TAB: + String textToInsert = " "; + int start, end; + start = Math.max(getSelectionStart(), 0); + end = Math.max(getSelectionEnd(), 0); + getText().replace(Math.min(start, end), Math.max(start, end), + textToInsert, 0, textToInsert.length()); + return true; + default: + return super.onKeyDown(keyCode, event); + } + } + } + + @Override + public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) { + if (event.isCtrlPressed()) { + switch (keyCode) { + case KeyEvent.KEYCODE_A: + case KeyEvent.KEYCODE_X: + case KeyEvent.KEYCODE_C: + case KeyEvent.KEYCODE_V: + case KeyEvent.KEYCODE_Z: + case KeyEvent.KEYCODE_Y: + case KeyEvent.KEYCODE_S: + return true; + default: + return false; + } + } else { + switch (keyCode) { + case KeyEvent.KEYCODE_TAB: + return true; + default: + return false; + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean onTextContextMenuItem( + final int id) { + if (id == ID_UNDO) { + undo(); + return true; + } else if (id == ID_REDO) { + redo(); + return true; + } else { + return super.onTextContextMenuItem(id); + } + } + + /** + * Can undo be performed? + */ + public boolean getCanUndo() { + return (mEditHistory.mmPosition > 0); + } + + /** + * Can redo be performed? + */ + public boolean getCanRedo() { + return (mEditHistory.mmPosition + < mEditHistory.mmHistory.size()); + } + + /** + * Perform undo. + */ + public void undo() { + EditItem edit = mEditHistory.getPrevious(); + if (edit == null) { + return; + } + + Editable text = getEditableText(); + int start = edit.mmStart; + int end = start + (edit.mmAfter != null + ? edit.mmAfter.length() : 0); + + mIsUndoOrRedo = true; + text.replace(start, end, edit.mmBefore); + mIsUndoOrRedo = false; + + // This will get rid of underlines inserted when editor tries to come + // up with a suggestion. + for (Object o : text.getSpans(0, + text.length(), UnderlineSpan.class)) { + text.removeSpan(o); + } + + Selection.setSelection(text, + edit.mmBefore == null ? start + : (start + edit.mmBefore.length())); + } + + /** + * Perform redo. + */ + public void redo() { + EditItem edit = mEditHistory.getNext(); + if (edit == null) { + return; + } + + Editable text = getEditableText(); + int start = edit.mmStart; + int end = start + (edit.mmBefore != null + ? edit.mmBefore.length() : 0); + + mIsUndoOrRedo = true; + text.replace(start, end, edit.mmAfter); + mIsUndoOrRedo = false; + + // This will get rid of underlines inserted when editor tries to come + // up with a suggestion. + for (Object o : text.getSpans(0, + text.length(), UnderlineSpan.class)) { + text.removeSpan(o); + } + + Selection.setSelection(text, + edit.mmAfter == null ? start + : (start + edit.mmAfter.length())); + } + + /** + * Set the maximum history size. If size is + * negative, then history size is only limited + * by the device memory. + */ + public void setMaxHistorySize( + int maxHistorySize) { + mEditHistory.setMaxHistorySize( + maxHistorySize); + } + + public void resetVariables() { + mEditHistory.clear(); + enabledChangeListener = false; + lineCount = 0; + realLine = 0; + startingLine = 0; + mIsUndoOrRedo = false; + mShowUndo = false; + mShowRedo = false; + canSaveFile = false; + firstVisibleIndex = 0; + firstColoredIndex = 0; + } + + public boolean canSaveFile() { + return canSaveFile; + } + + public void fileSaved() { + canSaveFile = false; + } + + public void replaceTextKeepCursor(String textToUpdate, boolean mantainCursorPos) { + + int cursorPos; + int cursorPosEnd; + if (textToUpdate != null) { + cursorPos = 0; + cursorPosEnd = 0; + } else { + cursorPos = getSelectionStart(); + cursorPosEnd = getSelectionEnd(); + } + disableTextChangedListener(); + + if (PreferenceHelper.getSyntaxHighlight(getContext())) + setText(highlight(textToUpdate == null ? getEditableText() : Editable.Factory + .getInstance().newEditable(textToUpdate), textToUpdate != null)); + else + setText(textToUpdate == null ? getText().toString() : textToUpdate); + + enableTextChangedListener(); + + if (mantainCursorPos) + firstVisibleIndex = cursorPos; + + if (firstVisibleIndex > -1) { + if (cursorPosEnd != cursorPos) + setSelection(cursorPos, cursorPosEnd); + else + setSelection(firstVisibleIndex); + } + } + //endregion + + //region UNDO REDO + + public void disableTextChangedListener() { + enabledChangeListener = false; + removeTextChangedListener(mChangeListener); + } + + public CharSequence highlight(Editable editable, boolean newText) { + editable.clearSpans(); + + if (editable.length() == 0) { + return editable; + } + + editorHeight = getHeight(); + + if (!newText && editorHeight > 0) { + firstLine = lineUtils.getFirstVisibleLine(verticalScroll, editorHeight, lineCount); + lastLine = lineUtils.getLastVisibleLine(verticalScroll, editorHeight, lineCount, deviceHeight); + firstVisibleIndex = getLayout().getLineStart(firstLine); + lastVisibleIndex = getLayout().getLineStart(lastLine); + } else { + firstVisibleIndex = 0; + lastVisibleIndex = CHARS_TO_COLOR; + } + + firstColoredIndex = firstVisibleIndex - (CHARS_TO_COLOR / 5); + + // normalize + if (firstColoredIndex < 0) + firstColoredIndex = 0; + if (lastVisibleIndex > editable.length()) + lastVisibleIndex = editable.length(); + if (firstColoredIndex > lastVisibleIndex) + firstColoredIndex = lastVisibleIndex; + + + textToHighlight = editable.subSequence(firstColoredIndex, lastVisibleIndex); + + if (fileExtension.contains("htm") + || fileExtension.contains("xml")) { + color(Patterns.HTML_OPEN_TAGS, editable, textToHighlight, firstColoredIndex); + color(Patterns.HTML_CLOSE_TAGS, editable, textToHighlight, firstColoredIndex); + color(Patterns.HTML_ATTRS, editable, textToHighlight, firstColoredIndex); + color(Patterns.GENERAL_STRINGS, editable, textToHighlight, firstColoredIndex); + color(Patterns.XML_COMMENTS, editable, textToHighlight, firstColoredIndex); + } else if (fileExtension.equals("css")) { + //color(CSS_STYLE_NAME, editable); + color(Patterns.CSS_ATTRS, editable, textToHighlight, firstColoredIndex); + color(Patterns.CSS_ATTR_VALUE, editable, textToHighlight, firstColoredIndex); + color(Patterns.SYMBOLS, editable, textToHighlight, firstColoredIndex); + color(Patterns.GENERAL_COMMENTS, editable, textToHighlight, firstColoredIndex); + } else if (Arrays.asList(MimeTypes.MIME_CODE).contains(fileExtension)) { + switch (fileExtension) { + case "lua": + color(Patterns.LUA_KEYWORDS, editable, textToHighlight, firstColoredIndex); + break; + case "py": + color(Patterns.PY_KEYWORDS, editable, textToHighlight, firstColoredIndex); + break; + default: + color(Patterns.GENERAL_KEYWORDS, editable, textToHighlight, firstColoredIndex); + break; + } + color(Patterns.NUMBERS_OR_SYMBOLS, editable, textToHighlight, firstColoredIndex); + color(Patterns.GENERAL_STRINGS, editable, textToHighlight, firstColoredIndex); + color(Patterns.GENERAL_COMMENTS, editable, textToHighlight, firstColoredIndex); + if (fileExtension.equals("php")) + color(Patterns.PHP_VARIABLES, editable, textToHighlight, firstColoredIndex); + } else if (Arrays.asList(MimeTypes.MIME_SQL).contains(fileExtension)) { + color(Patterns.SYMBOLS, editable, textToHighlight, firstColoredIndex); + color(Patterns.GENERAL_STRINGS, editable, textToHighlight, firstColoredIndex); + color(Patterns.SQL_KEYWORDS, editable, textToHighlight, firstColoredIndex); + } else { + if (!(Arrays.asList(MimeTypes.MIME_MARKDOWN).contains(fileExtension))) + color(Patterns.GENERAL_KEYWORDS, editable, textToHighlight, firstColoredIndex); + color(Patterns.NUMBERS_OR_SYMBOLS, editable, textToHighlight, firstColoredIndex); + color(Patterns.GENERAL_STRINGS, editable, textToHighlight, firstColoredIndex); + if (fileExtension.equals("prop") || fileExtension.contains("conf") || + (Arrays.asList(MimeTypes.MIME_MARKDOWN).contains(fileExtension))) + color(Patterns.GENERAL_COMMENTS_NO_SLASH, editable, textToHighlight, + firstColoredIndex); + else + color(Patterns.GENERAL_COMMENTS, editable, textToHighlight, firstColoredIndex); + + if ((Arrays.asList(MimeTypes.MIME_MARKDOWN).contains(fileExtension))) + color(Patterns.LINK, editable, textToHighlight, firstColoredIndex); + } + + return editable; + } + + public void enableTextChangedListener() { + if (!enabledChangeListener) { + addTextChangedListener(mChangeListener); + enabledChangeListener = true; + } + } + + public LineUtils getLineUtils() { + return lineUtils; + } + + private void color(Pattern pattern, + Editable allText, + CharSequence textToHighlight, + int start) { + int color = 0; + if (pattern.equals(Patterns.HTML_OPEN_TAGS) + || pattern.equals(Patterns.HTML_CLOSE_TAGS) + || pattern.equals(Patterns.GENERAL_KEYWORDS) + || pattern.equals(Patterns.SQL_KEYWORDS) + || pattern.equals(Patterns.PY_KEYWORDS) + || pattern.equals(Patterns.LUA_KEYWORDS) + + ) { + color = getResources().getColor(R.color.syntax_keyword); + } else if (pattern.equals(Patterns.HTML_ATTRS) + || pattern.equals(Patterns.CSS_ATTRS) + || pattern.equals(Patterns.LINK)) { + color = getResources().getColor(R.color.syntax_attr); + } else if (pattern.equals(Patterns.CSS_ATTR_VALUE)) { + color = getResources().getColor(R.color.syntax_attr_value); + } else if (pattern.equals(Patterns.XML_COMMENTS) + || pattern.equals(Patterns.GENERAL_COMMENTS) + || pattern.equals(Patterns.GENERAL_COMMENTS_NO_SLASH)) { + color = getResources().getColor(R.color.syntax_comment); + } else if (pattern.equals(Patterns.GENERAL_STRINGS)) { + color = getResources().getColor(R.color.syntax_string); + } else if (pattern.equals(Patterns.NUMBERS) || pattern.equals(Patterns.SYMBOLS) || pattern.equals(Patterns.NUMBERS_OR_SYMBOLS)) { + color = getResources().getColor(R.color.syntax_number); + } else if (pattern.equals(Patterns.PHP_VARIABLES)) { + color = getResources().getColor(R.color.syntax_variable); + } + + m = pattern.matcher(textToHighlight); + + while (m.find()) { + allText.setSpan( + new ForegroundColorSpan(color), + start + m.start(), + start + m.end(), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + + /** + * Clear history. + */ + public void clearHistory() { + mEditHistory.clear(); + mShowUndo = getCanUndo(); + mShowRedo = getCanRedo(); + } + + /** + * Store preferences. + */ + public void storePersistentState( + SharedPreferences.Editor editor, + String prefix) { + // Store hash code of text in the editor so that we can check if the + // editor contents has changed. + editor.putString(prefix + ".hash", + String.valueOf( + getText().toString().hashCode())); + editor.putInt(prefix + ".maxSize", + mEditHistory.mmMaxHistorySize); + editor.putInt(prefix + ".position", + mEditHistory.mmPosition); + editor.putInt(prefix + ".size", + mEditHistory.mmHistory.size()); + + int i = 0; + for (EditItem ei : mEditHistory.mmHistory) { + String pre = prefix + "." + i; + + editor.putInt(pre + ".start", ei.mmStart); + editor.putString(pre + ".before", + ei.mmBefore.toString()); + editor.putString(pre + ".after", + ei.mmAfter.toString()); + + i++; + } + } + + /** + * Restore preferences. + * + * @param prefix The preference key prefix + * used when state was stored. + * @return did restore succeed? If this is + * false, the undo history will be empty. + */ + public boolean restorePersistentState( + SharedPreferences sp, String prefix) + throws IllegalStateException { + + boolean ok = + doRestorePersistentState(sp, prefix); + if (!ok) { + mEditHistory.clear(); + } + + return ok; + } + + private boolean doRestorePersistentState( + SharedPreferences sp, String prefix) { + + String hash = + sp.getString(prefix + ".hash", null); + if (hash == null) { + // No state to be restored. + return true; + } + + if (Integer.valueOf(hash) + != getText().toString().hashCode()) { + return false; + } + + mEditHistory.clear(); + mEditHistory.mmMaxHistorySize = + sp.getInt(prefix + ".maxSize", -1); + + int count = sp.getInt(prefix + ".size", -1); + if (count == -1) { + return false; + } + + for (int i = 0; i < count; i++) { + String pre = prefix + "." + i; + + int start = sp.getInt(pre + ".start", -1); + String before = + sp.getString(pre + ".before", null); + String after = + sp.getString(pre + ".after", null); + + if (start == -1 + || before == null + || after == null) { + return false; + } + mEditHistory.add( + new EditItem(start, before, after)); + } + + mEditHistory.mmPosition = + sp.getInt(prefix + ".position", -1); + return mEditHistory.mmPosition != -1; + + } + + /** + * Class that listens to changes in the text. + */ + private final class EditTextChangeListener + implements TextWatcher { + + /** + * The text that will be removed by the + * change event. + */ + private CharSequence mBeforeChange; + + /** + * The text that was inserted by the change + * event. + */ + private CharSequence mAfterChange; + + public void beforeTextChanged( + CharSequence s, int start, int count, + int after) { + if (mIsUndoOrRedo) { + return; + } + + mBeforeChange = + s.subSequence(start, start + count); + } + + public void onTextChanged(CharSequence s, + int start, int before, + int count) { + if (mIsUndoOrRedo) { + return; + } + + mAfterChange = + s.subSequence(start, start + count); + mEditHistory.add( + new EditItem(start, mBeforeChange, + mAfterChange)); + } + + public void afterTextChanged(Editable s) { + boolean showUndo = getCanUndo(); + boolean showRedo = getCanRedo(); + if (!canSaveFile) + canSaveFile = getCanUndo(); + if (showUndo != mShowUndo || showRedo != mShowRedo) { + mShowUndo = showUndo; + mShowRedo = showRedo; + ((MainActivity) getContext()).invalidateOptionsMenu(); + } + + ((MainActivity) getContext()).updateTextSyntax(); + } + } + + //endregion + + //region EDIT HISTORY + + /** + * Keeps track of all the edit history of a + * text. + */ + private final class EditHistory { + + /** + * The list of edits in chronological + * order. + */ + private final LinkedList + mmHistory = new LinkedList<>(); + /** + * The position from which an EditItem will + * be retrieved when getNext() is called. If + * getPrevious() has not been called, this + * has the same value as mmHistory.size(). + */ + private int mmPosition = 0; + /** + * Maximum undo history size. + */ + private int mmMaxHistorySize = -1; + + private int size() { + return mmHistory.size(); + } + + /** + * Clear history. + */ + private void clear() { + mmPosition = 0; + mmHistory.clear(); + } + + /** + * Adds a new edit operation to the history + * at the current position. If executed + * after a call to getPrevious() removes all + * the future history (elements with + * positions >= current history position). + */ + private void add(EditItem item) { + while (mmHistory.size() > mmPosition) { + mmHistory.removeLast(); + } + mmHistory.add(item); + mmPosition++; + + if (mmMaxHistorySize >= 0) { + trimHistory(); + } + } + + /** + * Trim history when it exceeds max history + * size. + */ + private void trimHistory() { + while (mmHistory.size() + > mmMaxHistorySize) { + mmHistory.removeFirst(); + mmPosition--; + } + + if (mmPosition < 0) { + mmPosition = 0; + } + } + + /** + * Set the maximum history size. If size is + * negative, then history size is only + * limited by the device memory. + */ + private void setMaxHistorySize( + int maxHistorySize) { + mmMaxHistorySize = maxHistorySize; + if (mmMaxHistorySize >= 0) { + trimHistory(); + } + } + + /** + * Traverses the history backward by one + * position, returns and item at that + * position. + */ + private EditItem getPrevious() { + if (mmPosition == 0) { + return null; + } + mmPosition--; + return mmHistory.get(mmPosition); + } + + /** + * Traverses the history forward by one + * position, returns and item at that + * position. + */ + private EditItem getNext() { + if (mmPosition >= mmHistory.size()) { + return null; + } + + EditItem item = mmHistory.get(mmPosition); + mmPosition++; + return item; + } + } + + /** + * Represents the changes performed by a + * single edit operation. + */ + private final class EditItem { + private final int mmStart; + private final CharSequence mmBefore; + private final CharSequence mmAfter; + + /** + * Constructs EditItem of a modification + * that was applied at position start and + * replaced CharSequence before with + * CharSequence after. + */ + public EditItem(int start, + CharSequence before, CharSequence after) { + mmStart = start; + mmBefore = before; + mmAfter = after; + } + } + //endregion + + + } +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/activity/SelectFileActivity.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/activity/SelectFileActivity.java index 3aeb5b5..f9679fe 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/activity/SelectFileActivity.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/activity/SelectFileActivity.java @@ -1,389 +1,389 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.activity; - -import android.content.Intent; -import android.os.AsyncTask; -import android.os.Bundle; -import android.support.v4.view.MenuItemCompat; -import android.support.v7.app.ActionBarActivity; -import android.support.v7.widget.SearchView; -import android.support.v7.widget.Toolbar; -import android.text.TextUtils; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.widget.AdapterView; -import android.widget.Filter; -import android.widget.ListView; -import android.widget.PopupMenu; -import android.widget.TextView; -import android.widget.Toast; - -import com.faizmalkani.floatingactionbutton.FloatingActionButton; - -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.FilenameUtils; -import org.sufficientlysecure.rootcommands.Shell; -import org.sufficientlysecure.rootcommands.Toolbox; - -import java.io.File; -import java.io.IOException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.LinkedList; -import java.util.concurrent.TimeoutException; - -import sharedcode.turboeditor.R; -import sharedcode.turboeditor.adapter.AdapterDetailedList; -import sharedcode.turboeditor.dialogfragment.EditTextDialog; -import sharedcode.turboeditor.preferences.PreferenceHelper; -import sharedcode.turboeditor.root.RootUtils; -import sharedcode.turboeditor.util.AlphanumComparator; -import sharedcode.turboeditor.util.Build; -import sharedcode.turboeditor.util.ThemeUtils; - -public class SelectFileActivity extends ActionBarActivity implements SearchView.OnQueryTextListener, AdapterView.OnItemClickListener, EditTextDialog.EditDialogListener { - private String currentFolder = PreferenceHelper.SD_CARD_ROOT; - private ListView listView; - private boolean wantAFile = true; - private MenuItem mSearchViewMenuItem; - private SearchView mSearchView; - private Filter filter; - - - @Override - protected void onCreate(Bundle savedInstanceState) { - - - ThemeUtils.setTheme(this); - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_select_file); - - Toolbar toolbar = (Toolbar) findViewById(R.id.my_awesome_toolbar); - setSupportActionBar(toolbar); - - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - - final Actions action = (Actions) getIntent().getExtras().getSerializable("action"); - wantAFile = action == Actions.SelectFile; - - listView = (ListView) findViewById(android.R.id.list); - listView.setOnItemClickListener(this); - listView.setTextFilterEnabled(true); - - FloatingActionButton mFab = (FloatingActionButton) findViewById(R.id.fabbutton); - mFab.setColor(getResources().getColor(R.color.fab_light)); - mFab.setDrawable(getResources().getDrawable(R.drawable.ic_fab_add)); - - mFab.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - PopupMenu popup = new PopupMenu(SelectFileActivity.this, v); - - popup.getMenuInflater().inflate(R.menu.popup_new_file, popup.getMenu()); - - popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem item) { - int i = item.getItemId(); - if (i == R.id.im_new_file) { - EditTextDialog.newInstance(EditTextDialog.Actions.NewFile).show(getFragmentManager().beginTransaction(), "dialog"); - return true; - } else if (i == R.id.im_new_folder) { - EditTextDialog.newInstance(EditTextDialog.Actions.NewFolder).show(getFragmentManager().beginTransaction(), "dialog"); - return true; - } else { - return false; - } - } - }); - - popup.show(); - } - }); - - mFab.listenTo(listView); - - String lastNavigatedPath = PreferenceHelper.getWorkingFolder(this); - - File file = new File(lastNavigatedPath); - - if (!file.exists()) { - PreferenceHelper.setWorkingFolder(this, PreferenceHelper.SD_CARD_ROOT); - file = new File(PreferenceHelper.SD_CARD_ROOT); - } - - new UpdateList().execute(file.getAbsolutePath()); - } - - @Override - public void onBackPressed() { - if (currentFolder.isEmpty() || currentFolder.equals("/")) { - finish(); - } else { - File file = new File(currentFolder); - String parentFolder = file.getParent(); - new UpdateList().execute(parentFolder); - } - } - - public boolean onQueryTextChange(String newText) { - if (filter == null) - return true; - - if (TextUtils.isEmpty(newText)) { - filter.filter(null); - } else { - filter.filter(newText); - } - return true; - } - - public boolean onQueryTextSubmit(String query) { - return false; - } - - void returnData(String path) { - final Intent returnIntent = new Intent(); - returnIntent.putExtra("path", path); - setResult(RESULT_OK, returnIntent); - // finish the activity - finish(); - } - - - @Override - public void onItemClick(AdapterView parent, - View view, int position, long id) { - final String name = ((TextView) view.findViewById(android.R.id.text1)).getText().toString(); - if (name.equals("..")) { - if (currentFolder.equals("/")) { - new UpdateList().execute(PreferenceHelper.getWorkingFolder(this)); - } else { - File tempFile = new File(currentFolder); - if (tempFile.isFile()) { - tempFile = tempFile.getParentFile() - .getParentFile(); - } else { - tempFile = tempFile.getParentFile(); - } - new UpdateList().execute(tempFile.getAbsolutePath()); - } - return; - } else if (name.equals(getString(R.string.home))) { - new UpdateList().execute(PreferenceHelper.getWorkingFolder(this)); - return; - } - - final File selectedFile = new File(currentFolder, name); - - if (selectedFile.isFile() && wantAFile) { - returnData(selectedFile.getAbsolutePath()); - } else if (selectedFile.isDirectory()) { - new UpdateList().execute(selectedFile.getAbsolutePath()); - } - } - - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.activity_select_file, menu); - mSearchViewMenuItem = menu.findItem(R.id.im_search); - mSearchView = (SearchView) MenuItemCompat.getActionView(mSearchViewMenuItem); - mSearchView.setIconifiedByDefault(true); - mSearchView.setOnQueryTextListener(this); - mSearchView.setSubmitButtonEnabled(false); - return super.onCreateOptionsMenu(menu); - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - // menu items - MenuItem imSetAsWorkingFolder = menu.findItem(R.id.im_set_as_working_folder); - MenuItem imIsWorkingFolder = menu.findItem(R.id.im_is_working_folder); - MenuItem imSelectFolder = menu.findItem(R.id.im_select_folder); - if (imSetAsWorkingFolder != null) { - // set the imSetAsWorkingFolder visible only if the two folder dont concide - imSetAsWorkingFolder.setVisible(!currentFolder.equals(PreferenceHelper.getWorkingFolder(SelectFileActivity.this))); - } - if (imIsWorkingFolder != null) { - // set visible is the other is invisible - imIsWorkingFolder.setVisible(!imSetAsWorkingFolder.isVisible()); - } - if (imSelectFolder != null) { - imSelectFolder.setVisible(!wantAFile); - } - return super.onPrepareOptionsMenu(menu); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - int i = item.getItemId(); - if (i == android.R.id.home) { - finish(); - return true; - } else if (i == R.id.im_set_as_working_folder) { - PreferenceHelper.setWorkingFolder(SelectFileActivity.this, currentFolder); - invalidateOptionsMenu(); - return true; - } else if (i == R.id.im_is_working_folder) { - Toast.makeText(getBaseContext(), R.string.is_the_working_folder, Toast.LENGTH_SHORT).show(); - return true; - } else if (i == R.id.im_select_folder) { - returnData(currentFolder); - return true; - } - return super.onOptionsItemSelected(item); - } - - - @Override - public void onFinishEditDialog(final String inputText, final String hint, final EditTextDialog.Actions actions) { - if (actions == EditTextDialog.Actions.NewFile && !TextUtils.isEmpty(inputText)) { - File file = new File(currentFolder, inputText); - returnData(file.getAbsolutePath()); - } else if (actions == EditTextDialog.Actions.NewFolder && !TextUtils.isEmpty(inputText)) { - File file = new File(currentFolder, inputText); - file.mkdirs(); - new UpdateList().execute(currentFolder); - } - } - - public enum Actions { - SelectFile, SelectFolder - } - - private class UpdateList extends AsyncTask> { - - String exceptionMessage; - - @Override - protected void onPreExecute() { - super.onPreExecute(); - if (mSearchView != null) { - mSearchView.setIconified(true); - MenuItemCompat.collapseActionView(mSearchViewMenuItem); - mSearchView.setQuery("", false); - } - - } - - /** - * {@inheritDoc} - */ - @Override - protected LinkedList doInBackground(final String... params) { - try { - - final String path = params[0]; - if (TextUtils.isEmpty(path)) { - return null; - } - - File tempFile = new File(path); - if (tempFile.isFile()) { - tempFile = tempFile.getParentFile(); - } - - String[] unopenableExtensions = {"apk", "mp3", "mp4", "png", "jpg", "jpeg"}; - - final LinkedList fileDetails = new LinkedList<>(); - final LinkedList folderDetails = new LinkedList<>(); - final ArrayList files; - currentFolder = tempFile.getAbsolutePath(); - - boolean isRoot = false; - if (!tempFile.canRead()) { - try { - Shell shell = null; - shell = Shell.startRootShell(); - Toolbox tb = new Toolbox(shell); - isRoot = tb.isRootAccessGiven(); - } catch (IOException | TimeoutException e) { - e.printStackTrace(); - isRoot = false; - } - } - - files = RootUtils.getFileList(currentFolder, isRoot); - - Collections.sort(files, getFileNameComparator()); - - if (files != null) { - for (final File f : files) { - if (f.isDirectory()) { - folderDetails.add(new AdapterDetailedList.FileDetail(f.getName(), - getString(R.string.folder), - "")); - } else if (f.isFile() - && !FilenameUtils.isExtension(f.getName().toLowerCase(), unopenableExtensions) - && FileUtils.sizeOf(f) <= Build.MAX_FILE_SIZE * FileUtils.ONE_KB) { - final long fileSize = f.length(); - SimpleDateFormat format = new SimpleDateFormat("MMM dd, yyyy hh:mm a"); - String date = format.format(f.lastModified()); - fileDetails.add(new AdapterDetailedList.FileDetail(f.getName(), - FileUtils.byteCountToDisplaySize(fileSize), date)); - } - } - } - - folderDetails.addAll(fileDetails); - return folderDetails; - } catch (Exception e) { - exceptionMessage = e.getMessage(); - return null; - } - } - - /** - * {@inheritDoc} - */ - @Override - protected void onPostExecute(final LinkedList names) { - if (names != null) { - boolean isRoot = currentFolder.equals("/"); - AdapterDetailedList mAdapter = new AdapterDetailedList(getBaseContext(), names, isRoot); - listView.setAdapter(mAdapter); - filter = mAdapter.getFilter(); - } - if (exceptionMessage != null) { - Toast.makeText(SelectFileActivity.this, exceptionMessage, Toast.LENGTH_SHORT).show(); - } - invalidateOptionsMenu(); - super.onPostExecute(names); - } - - public final Comparator getFileNameComparator() { - return new AlphanumComparator() { - /** - * {@inheritDoc} - */ - @Override - public String getTheString(Object obj) { - return ((File) obj).getName() - .toLowerCase(); - } - }; - } - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.activity; + +import android.content.Intent; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.v4.view.MenuItemCompat; +import android.support.v7.app.ActionBarActivity; +import android.support.v7.widget.SearchView; +import android.support.v7.widget.Toolbar; +import android.text.TextUtils; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.AdapterView; +import android.widget.Filter; +import android.widget.ListView; +import android.widget.PopupMenu; +import android.widget.TextView; +import android.widget.Toast; + +import com.faizmalkani.floatingactionbutton.FloatingActionButton; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.FilenameUtils; +import org.sufficientlysecure.rootcommands.Shell; +import org.sufficientlysecure.rootcommands.Toolbox; + +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedList; +import java.util.concurrent.TimeoutException; + +import sharedcode.turboeditor.R; +import sharedcode.turboeditor.adapter.AdapterDetailedList; +import sharedcode.turboeditor.dialogfragment.EditTextDialog; +import sharedcode.turboeditor.preferences.PreferenceHelper; +import sharedcode.turboeditor.root.RootUtils; +import sharedcode.turboeditor.util.AlphanumComparator; +import sharedcode.turboeditor.util.Build; +import sharedcode.turboeditor.util.ThemeUtils; + +public class SelectFileActivity extends ActionBarActivity implements SearchView.OnQueryTextListener, AdapterView.OnItemClickListener, EditTextDialog.EditDialogListener { + private String currentFolder = PreferenceHelper.SD_CARD_ROOT; + private ListView listView; + private boolean wantAFile = true; + private MenuItem mSearchViewMenuItem; + private SearchView mSearchView; + private Filter filter; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + + + ThemeUtils.setTheme(this); + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_select_file); + + Toolbar toolbar = (Toolbar) findViewById(R.id.my_awesome_toolbar); + setSupportActionBar(toolbar); + + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + final Actions action = (Actions) getIntent().getExtras().getSerializable("action"); + wantAFile = action == Actions.SelectFile; + + listView = (ListView) findViewById(android.R.id.list); + listView.setOnItemClickListener(this); + listView.setTextFilterEnabled(true); + + FloatingActionButton mFab = (FloatingActionButton) findViewById(R.id.fabbutton); + mFab.setColor(getResources().getColor(R.color.fab_light)); + mFab.setDrawable(getResources().getDrawable(R.drawable.ic_fab_add)); + + mFab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + PopupMenu popup = new PopupMenu(SelectFileActivity.this, v); + + popup.getMenuInflater().inflate(R.menu.popup_new_file, popup.getMenu()); + + popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + int i = item.getItemId(); + if (i == R.id.im_new_file) { + EditTextDialog.newInstance(EditTextDialog.Actions.NewFile).show(getFragmentManager().beginTransaction(), "dialog"); + return true; + } else if (i == R.id.im_new_folder) { + EditTextDialog.newInstance(EditTextDialog.Actions.NewFolder).show(getFragmentManager().beginTransaction(), "dialog"); + return true; + } else { + return false; + } + } + }); + + popup.show(); + } + }); + + mFab.listenTo(listView); + + String lastNavigatedPath = PreferenceHelper.getWorkingFolder(this); + + File file = new File(lastNavigatedPath); + + if (!file.exists()) { + PreferenceHelper.setWorkingFolder(this, PreferenceHelper.SD_CARD_ROOT); + file = new File(PreferenceHelper.SD_CARD_ROOT); + } + + new UpdateList().execute(file.getAbsolutePath()); + } + + @Override + public void onBackPressed() { + if (currentFolder.isEmpty() || currentFolder.equals("/")) { + finish(); + } else { + File file = new File(currentFolder); + String parentFolder = file.getParent(); + new UpdateList().execute(parentFolder); + } + } + + public boolean onQueryTextChange(String newText) { + if (filter == null) + return true; + + if (TextUtils.isEmpty(newText)) { + filter.filter(null); + } else { + filter.filter(newText); + } + return true; + } + + public boolean onQueryTextSubmit(String query) { + return false; + } + + void returnData(String path) { + final Intent returnIntent = new Intent(); + returnIntent.putExtra("path", path); + setResult(RESULT_OK, returnIntent); + // finish the activity + finish(); + } + + + @Override + public void onItemClick(AdapterView parent, + View view, int position, long id) { + final String name = ((TextView) view.findViewById(android.R.id.text1)).getText().toString(); + if (name.equals("..")) { + if (currentFolder.equals("/")) { + new UpdateList().execute(PreferenceHelper.getWorkingFolder(this)); + } else { + File tempFile = new File(currentFolder); + if (tempFile.isFile()) { + tempFile = tempFile.getParentFile() + .getParentFile(); + } else { + tempFile = tempFile.getParentFile(); + } + new UpdateList().execute(tempFile.getAbsolutePath()); + } + return; + } else if (name.equals(getString(R.string.home))) { + new UpdateList().execute(PreferenceHelper.getWorkingFolder(this)); + return; + } + + final File selectedFile = new File(currentFolder, name); + + if (selectedFile.isFile() && wantAFile) { + returnData(selectedFile.getAbsolutePath()); + } else if (selectedFile.isDirectory()) { + new UpdateList().execute(selectedFile.getAbsolutePath()); + } + } + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.activity_select_file, menu); + mSearchViewMenuItem = menu.findItem(R.id.im_search); + mSearchView = (SearchView) MenuItemCompat.getActionView(mSearchViewMenuItem); + mSearchView.setIconifiedByDefault(true); + mSearchView.setOnQueryTextListener(this); + mSearchView.setSubmitButtonEnabled(false); + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + // menu items + MenuItem imSetAsWorkingFolder = menu.findItem(R.id.im_set_as_working_folder); + MenuItem imIsWorkingFolder = menu.findItem(R.id.im_is_working_folder); + MenuItem imSelectFolder = menu.findItem(R.id.im_select_folder); + if (imSetAsWorkingFolder != null) { + // set the imSetAsWorkingFolder visible only if the two folder dont concide + imSetAsWorkingFolder.setVisible(!currentFolder.equals(PreferenceHelper.getWorkingFolder(SelectFileActivity.this))); + } + if (imIsWorkingFolder != null) { + // set visible is the other is invisible + imIsWorkingFolder.setVisible(!imSetAsWorkingFolder.isVisible()); + } + if (imSelectFolder != null) { + imSelectFolder.setVisible(!wantAFile); + } + return super.onPrepareOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int i = item.getItemId(); + if (i == android.R.id.home) { + finish(); + return true; + } else if (i == R.id.im_set_as_working_folder) { + PreferenceHelper.setWorkingFolder(SelectFileActivity.this, currentFolder); + invalidateOptionsMenu(); + return true; + } else if (i == R.id.im_is_working_folder) { + Toast.makeText(getBaseContext(), R.string.is_the_working_folder, Toast.LENGTH_SHORT).show(); + return true; + } else if (i == R.id.im_select_folder) { + returnData(currentFolder); + return true; + } + return super.onOptionsItemSelected(item); + } + + + @Override + public void onFinishEditDialog(final String inputText, final String hint, final EditTextDialog.Actions actions) { + if (actions == EditTextDialog.Actions.NewFile && !TextUtils.isEmpty(inputText)) { + File file = new File(currentFolder, inputText); + returnData(file.getAbsolutePath()); + } else if (actions == EditTextDialog.Actions.NewFolder && !TextUtils.isEmpty(inputText)) { + File file = new File(currentFolder, inputText); + file.mkdirs(); + new UpdateList().execute(currentFolder); + } + } + + public enum Actions { + SelectFile, SelectFolder + } + + private class UpdateList extends AsyncTask> { + + String exceptionMessage; + + @Override + protected void onPreExecute() { + super.onPreExecute(); + if (mSearchView != null) { + mSearchView.setIconified(true); + MenuItemCompat.collapseActionView(mSearchViewMenuItem); + mSearchView.setQuery("", false); + } + + } + + /** + * {@inheritDoc} + */ + @Override + protected LinkedList doInBackground(final String... params) { + try { + + final String path = params[0]; + if (TextUtils.isEmpty(path)) { + return null; + } + + File tempFile = new File(path); + if (tempFile.isFile()) { + tempFile = tempFile.getParentFile(); + } + + String[] unopenableExtensions = {"apk", "mp3", "mp4", "png", "jpg", "jpeg"}; + + final LinkedList fileDetails = new LinkedList<>(); + final LinkedList folderDetails = new LinkedList<>(); + final ArrayList files; + currentFolder = tempFile.getAbsolutePath(); + + boolean isRoot = false; + if (!tempFile.canRead()) { + try { + Shell shell = null; + shell = Shell.startRootShell(); + Toolbox tb = new Toolbox(shell); + isRoot = tb.isRootAccessGiven(); + } catch (IOException | TimeoutException e) { + e.printStackTrace(); + isRoot = false; + } + } + + files = RootUtils.getFileList(currentFolder, isRoot); + + Collections.sort(files, getFileNameComparator()); + + if (files != null) { + for (final File f : files) { + if (f.isDirectory()) { + folderDetails.add(new AdapterDetailedList.FileDetail(f.getName(), + getString(R.string.folder), + "")); + } else if (f.isFile() + && !FilenameUtils.isExtension(f.getName().toLowerCase(), unopenableExtensions) + && FileUtils.sizeOf(f) <= Build.MAX_FILE_SIZE * FileUtils.ONE_KB) { + final long fileSize = f.length(); + SimpleDateFormat format = new SimpleDateFormat("MMM dd, yyyy hh:mm a"); + String date = format.format(f.lastModified()); + fileDetails.add(new AdapterDetailedList.FileDetail(f.getName(), + FileUtils.byteCountToDisplaySize(fileSize), date)); + } + } + } + + folderDetails.addAll(fileDetails); + return folderDetails; + } catch (Exception e) { + exceptionMessage = e.getMessage(); + return null; + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void onPostExecute(final LinkedList names) { + if (names != null) { + boolean isRoot = currentFolder.equals("/"); + AdapterDetailedList mAdapter = new AdapterDetailedList(getBaseContext(), names, isRoot); + listView.setAdapter(mAdapter); + filter = mAdapter.getFilter(); + } + if (exceptionMessage != null) { + Toast.makeText(SelectFileActivity.this, exceptionMessage, Toast.LENGTH_SHORT).show(); + } + invalidateOptionsMenu(); + super.onPostExecute(names); + } + + public final Comparator getFileNameComparator() { + return new AlphanumComparator() { + /** + * {@inheritDoc} + */ + @Override + public String getTheString(Object obj) { + return ((File) obj).getName() + .toLowerCase(); + } + }; + } + } +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/adapter/AdapterDetailedList.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/adapter/AdapterDetailedList.java index 0b44d7f..dfe867b 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/adapter/AdapterDetailedList.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/adapter/AdapterDetailedList.java @@ -1,204 +1,204 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.adapter; - -import android.content.Context; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.Filter; -import android.widget.ImageView; -import android.widget.TextView; - -import org.apache.commons.io.FilenameUtils; - -import java.util.Arrays; -import java.util.LinkedList; - -import sharedcode.turboeditor.R; -import sharedcode.turboeditor.util.MimeTypes; - -public class AdapterDetailedList extends - ArrayAdapter { - - // Layout Inflater - private final LayoutInflater inflater; - private final LinkedList orig; - private CustomFilter customFilter; - // List of file details - private LinkedList fileDetails; - - public AdapterDetailedList(final Context context, - final LinkedList fileDetails, - final boolean isRoot) { - super(context, R.layout.item_file_list, fileDetails); - this.fileDetails = fileDetails; - this.orig = fileDetails; - this.inflater = LayoutInflater.from(context); - if (!isRoot) { - this.fileDetails.addFirst(new FileDetail("..", context.getString(R.string.folder), "")); - } else { - this.fileDetails.addFirst(new FileDetail(context.getString(R.string.home), context.getString(R.string.folder), "")); - } - } - - - @Override - public View getView(final int position, - View convertView, final ViewGroup parent) { - if (convertView == null) { - convertView = this.inflater - .inflate(R.layout.item_file_list, - null); - final ViewHolder hold = new ViewHolder(); - hold.nameLabel = (TextView) convertView.findViewById(android.R.id.text1); - hold.detailLabel = (TextView) convertView.findViewById(android.R.id.text2); - hold.icon = (ImageView) convertView.findViewById(android.R.id.icon); - convertView.setTag(hold); - final FileDetail fileDetail = fileDetails.get(position); - final String fileName = fileDetail.getName(); - setIcon(hold, fileDetail); - hold.nameLabel.setText(fileName); - hold.detailLabel.setText(fileDetail.getSize() + "\t\t" + fileDetail.getDateModified()); - } else { - final ViewHolder hold = ((ViewHolder) convertView.getTag()); - final FileDetail fileDetail = fileDetails.get(position); - final String fileName = fileDetail.getName(); - setIcon(hold, fileDetail); - hold.nameLabel.setText(fileName); - hold.detailLabel.setText(fileDetail.getSize() + "\t\t" + fileDetail.getDateModified()); - } - return convertView; - } - - @Override - public int getCount() { - return fileDetails.size(); - } - - private void setIcon(final ViewHolder viewHolder, final FileDetail fileDetail) { - final String fileName = fileDetail.getName(); - final String ext = FilenameUtils.getExtension(fileName); - if (fileDetail.isFolder()) { - viewHolder.icon.setImageResource(R.color.file_folder); - } else if (Arrays.asList(MimeTypes.MIME_HTML).contains(ext) || ext.endsWith("html")) { - viewHolder.icon.setImageResource(R.color.file_html); - } else if (Arrays.asList(MimeTypes.MIME_CODE).contains(ext) - || fileName.endsWith("css") - || fileName.endsWith("js")) { - viewHolder.icon.setImageResource(R.color.file_code); - } else if (Arrays.asList(MimeTypes.MIME_ARCHIVE).contains(ext)) { - viewHolder.icon.setImageResource(R.color.file_archive); - } else if (Arrays.asList(MimeTypes.MIME_MUSIC) - .contains(ext)) { - viewHolder.icon.setImageResource(R.color.file_media_music); - } else if (Arrays.asList(MimeTypes.MIME_PICTURE).contains(ext)) { - viewHolder.icon.setImageResource(R.color.file_media_picture); - } else if (Arrays.asList(MimeTypes.MIME_VIDEO).contains(ext)) { - viewHolder.icon.setImageResource(R.color.file_media_video); - } else { - viewHolder.icon.setImageResource(R.color.file_text); - } - } - - @Override - public Filter getFilter() { - if (customFilter == null) { - customFilter = new CustomFilter(); - } - return customFilter; - } - - public static class ViewHolder { - - // Name of the file - public TextView nameLabel; - - // Size of the file - public TextView detailLabel; - - // Icon of the file - public ImageView icon; - } - - public static class FileDetail { - private final String name; - private final String size; - private final String dateModified; - private final boolean isFolder; - - public FileDetail(String name, String size, - String dateModified) { - this.name = name; - this.size = size; - this.dateModified = dateModified; - isFolder = TextUtils.isEmpty(dateModified); - } - - public String getDateModified() { - return dateModified; - } - - public String getSize() { - return size; - } - - public String getName() { - return name; - } - - public boolean isFolder() { - return isFolder; - } - } - - private class CustomFilter extends Filter { - - @Override - protected FilterResults performFiltering(CharSequence constraint) { - FilterResults results = new FilterResults(); - if (constraint == null || constraint.length() == 0) { - results.values = orig; - results.count = orig.size(); - } else { - LinkedList nHolderList = new LinkedList<>(); - for (FileDetail h : orig) { - if (h.getName().toLowerCase().contains(constraint.toString().toLowerCase())) - nHolderList.add(h); - } - results.values = nHolderList; - results.count = nHolderList.size(); - } - return results; - } - - @SuppressWarnings("unchecked") - @Override - protected void publishResults(CharSequence constraint, - FilterResults results) { - - - fileDetails = (LinkedList) results.values; - notifyDataSetChanged(); - } - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.adapter; + +import android.content.Context; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.Filter; +import android.widget.ImageView; +import android.widget.TextView; + +import org.apache.commons.io.FilenameUtils; + +import java.util.Arrays; +import java.util.LinkedList; + +import sharedcode.turboeditor.R; +import sharedcode.turboeditor.util.MimeTypes; + +public class AdapterDetailedList extends + ArrayAdapter { + + // Layout Inflater + private final LayoutInflater inflater; + private final LinkedList orig; + private CustomFilter customFilter; + // List of file details + private LinkedList fileDetails; + + public AdapterDetailedList(final Context context, + final LinkedList fileDetails, + final boolean isRoot) { + super(context, R.layout.item_file_list, fileDetails); + this.fileDetails = fileDetails; + this.orig = fileDetails; + this.inflater = LayoutInflater.from(context); + if (!isRoot) { + this.fileDetails.addFirst(new FileDetail("..", context.getString(R.string.folder), "")); + } else { + this.fileDetails.addFirst(new FileDetail(context.getString(R.string.home), context.getString(R.string.folder), "")); + } + } + + + @Override + public View getView(final int position, + View convertView, final ViewGroup parent) { + if (convertView == null) { + convertView = this.inflater + .inflate(R.layout.item_file_list, + null); + final ViewHolder hold = new ViewHolder(); + hold.nameLabel = (TextView) convertView.findViewById(android.R.id.text1); + hold.detailLabel = (TextView) convertView.findViewById(android.R.id.text2); + hold.icon = (ImageView) convertView.findViewById(android.R.id.icon); + convertView.setTag(hold); + final FileDetail fileDetail = fileDetails.get(position); + final String fileName = fileDetail.getName(); + setIcon(hold, fileDetail); + hold.nameLabel.setText(fileName); + hold.detailLabel.setText(fileDetail.getSize() + "\t\t" + fileDetail.getDateModified()); + } else { + final ViewHolder hold = ((ViewHolder) convertView.getTag()); + final FileDetail fileDetail = fileDetails.get(position); + final String fileName = fileDetail.getName(); + setIcon(hold, fileDetail); + hold.nameLabel.setText(fileName); + hold.detailLabel.setText(fileDetail.getSize() + "\t\t" + fileDetail.getDateModified()); + } + return convertView; + } + + @Override + public int getCount() { + return fileDetails.size(); + } + + private void setIcon(final ViewHolder viewHolder, final FileDetail fileDetail) { + final String fileName = fileDetail.getName(); + final String ext = FilenameUtils.getExtension(fileName); + if (fileDetail.isFolder()) { + viewHolder.icon.setImageResource(R.color.file_folder); + } else if (Arrays.asList(MimeTypes.MIME_HTML).contains(ext) || ext.endsWith("html")) { + viewHolder.icon.setImageResource(R.color.file_html); + } else if (Arrays.asList(MimeTypes.MIME_CODE).contains(ext) + || fileName.endsWith("css") + || fileName.endsWith("js")) { + viewHolder.icon.setImageResource(R.color.file_code); + } else if (Arrays.asList(MimeTypes.MIME_ARCHIVE).contains(ext)) { + viewHolder.icon.setImageResource(R.color.file_archive); + } else if (Arrays.asList(MimeTypes.MIME_MUSIC) + .contains(ext)) { + viewHolder.icon.setImageResource(R.color.file_media_music); + } else if (Arrays.asList(MimeTypes.MIME_PICTURE).contains(ext)) { + viewHolder.icon.setImageResource(R.color.file_media_picture); + } else if (Arrays.asList(MimeTypes.MIME_VIDEO).contains(ext)) { + viewHolder.icon.setImageResource(R.color.file_media_video); + } else { + viewHolder.icon.setImageResource(R.color.file_text); + } + } + + @Override + public Filter getFilter() { + if (customFilter == null) { + customFilter = new CustomFilter(); + } + return customFilter; + } + + public static class ViewHolder { + + // Name of the file + public TextView nameLabel; + + // Size of the file + public TextView detailLabel; + + // Icon of the file + public ImageView icon; + } + + public static class FileDetail { + private final String name; + private final String size; + private final String dateModified; + private final boolean isFolder; + + public FileDetail(String name, String size, + String dateModified) { + this.name = name; + this.size = size; + this.dateModified = dateModified; + isFolder = TextUtils.isEmpty(dateModified); + } + + public String getDateModified() { + return dateModified; + } + + public String getSize() { + return size; + } + + public String getName() { + return name; + } + + public boolean isFolder() { + return isFolder; + } + } + + private class CustomFilter extends Filter { + + @Override + protected FilterResults performFiltering(CharSequence constraint) { + FilterResults results = new FilterResults(); + if (constraint == null || constraint.length() == 0) { + results.values = orig; + results.count = orig.size(); + } else { + LinkedList nHolderList = new LinkedList<>(); + for (FileDetail h : orig) { + if (h.getName().toLowerCase().contains(constraint.toString().toLowerCase())) + nHolderList.add(h); + } + results.values = nHolderList; + results.count = nHolderList.size(); + } + return results; + } + + @SuppressWarnings("unchecked") + @Override + protected void publishResults(CharSequence constraint, + FilterResults results) { + + + fileDetails = (LinkedList) results.values; + notifyDataSetChanged(); + } + } +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/adapter/AdapterDrawer.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/adapter/AdapterDrawer.java index a076be7..7851294 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/adapter/AdapterDrawer.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/adapter/AdapterDrawer.java @@ -1,130 +1,130 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.adapter; - -import android.content.Context; -import android.graphics.Typeface; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.ImageView; -import android.widget.TextView; - -import java.io.File; -import java.util.List; - -import sharedcode.turboeditor.R; - -public class AdapterDrawer extends - ArrayAdapter { - - private final Callbacks callbacks; - // Layout Inflater - private final LayoutInflater inflater; - // List of file details - private final List files; - private String selectedPath = ""; - - public AdapterDrawer(Context context, - List files, - Callbacks callbacks) { - super(context, R.layout.item_file_list, files); - this.files = files; - this.inflater = LayoutInflater.from(context); - this.callbacks = callbacks; - } - - - @Override - public View getView(final int position, - View convertView, final ViewGroup parent) { - if (convertView == null) { - convertView = this.inflater - .inflate(R.layout.item_drawer_list, - parent, false); - final ViewHolder hold = new ViewHolder(); - hold.nameLabel = (TextView) convertView.findViewById(android.R.id.text1); - hold.cancelButton = (ImageView) convertView.findViewById(R.id.button_remove_from_list); - convertView.setTag(hold); - - final String fileName = files.get(position).getName(); - hold.nameLabel.setText(fileName); - hold.cancelButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - boolean closeOpenedFile = TextUtils.equals(selectedPath, files.get(position).getAbsolutePath()); - callbacks.CancelItem(position, closeOpenedFile); - if (closeOpenedFile) - selectedPath = ""; - - } - }); - - if (TextUtils.equals(selectedPath, files.get(position).getAbsolutePath())) { - hold.nameLabel.setTypeface(null, Typeface.BOLD); - } else { - hold.nameLabel.setTypeface(null, Typeface.NORMAL); - } - - } else { - final ViewHolder hold = ((ViewHolder) convertView.getTag()); - final String fileName = files.get(position).getName(); - hold.nameLabel.setText(fileName); - hold.cancelButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - boolean closeOpenedFile = TextUtils.equals(selectedPath, files.get(position).getAbsolutePath()); - callbacks.CancelItem(position, closeOpenedFile); - if (closeOpenedFile) - selectedPath = ""; - } - }); - - if (TextUtils.equals(selectedPath, files.get(position).getAbsolutePath())) { - hold.nameLabel.setTypeface(null, Typeface.BOLD); - } else { - hold.nameLabel.setTypeface(null, Typeface.NORMAL); - } - } - return convertView; - } - - public void selectView(String selectedPath) { - //callbacks.ItemSelected(selectedPath); - this.selectedPath = selectedPath; - notifyDataSetChanged(); - } - - public interface Callbacks { - void CancelItem(int position, boolean andCloseOpenedFile); - - //void ItemSelected(String path); - } - - public static class ViewHolder { - - // Name of the file - public TextView nameLabel; - - public ImageView cancelButton; - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.adapter; + +import android.content.Context; +import android.graphics.Typeface; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.TextView; + +import java.io.File; +import java.util.List; + +import sharedcode.turboeditor.R; + +public class AdapterDrawer extends + ArrayAdapter { + + private final Callbacks callbacks; + // Layout Inflater + private final LayoutInflater inflater; + // List of file details + private final List files; + private String selectedPath = ""; + + public AdapterDrawer(Context context, + List files, + Callbacks callbacks) { + super(context, R.layout.item_file_list, files); + this.files = files; + this.inflater = LayoutInflater.from(context); + this.callbacks = callbacks; + } + + + @Override + public View getView(final int position, + View convertView, final ViewGroup parent) { + if (convertView == null) { + convertView = this.inflater + .inflate(R.layout.item_drawer_list, + parent, false); + final ViewHolder hold = new ViewHolder(); + hold.nameLabel = (TextView) convertView.findViewById(android.R.id.text1); + hold.cancelButton = (ImageView) convertView.findViewById(R.id.button_remove_from_list); + convertView.setTag(hold); + + final String fileName = files.get(position).getName(); + hold.nameLabel.setText(fileName); + hold.cancelButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + boolean closeOpenedFile = TextUtils.equals(selectedPath, files.get(position).getAbsolutePath()); + callbacks.CancelItem(position, closeOpenedFile); + if (closeOpenedFile) + selectedPath = ""; + + } + }); + + if (TextUtils.equals(selectedPath, files.get(position).getAbsolutePath())) { + hold.nameLabel.setTypeface(null, Typeface.BOLD); + } else { + hold.nameLabel.setTypeface(null, Typeface.NORMAL); + } + + } else { + final ViewHolder hold = ((ViewHolder) convertView.getTag()); + final String fileName = files.get(position).getName(); + hold.nameLabel.setText(fileName); + hold.cancelButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + boolean closeOpenedFile = TextUtils.equals(selectedPath, files.get(position).getAbsolutePath()); + callbacks.CancelItem(position, closeOpenedFile); + if (closeOpenedFile) + selectedPath = ""; + } + }); + + if (TextUtils.equals(selectedPath, files.get(position).getAbsolutePath())) { + hold.nameLabel.setTypeface(null, Typeface.BOLD); + } else { + hold.nameLabel.setTypeface(null, Typeface.NORMAL); + } + } + return convertView; + } + + public void selectView(String selectedPath) { + //callbacks.ItemSelected(selectedPath); + this.selectedPath = selectedPath; + notifyDataSetChanged(); + } + + public interface Callbacks { + void CancelItem(int position, boolean andCloseOpenedFile); + + //void ItemSelected(String path); + } + + public static class ViewHolder { + + // Name of the file + public TextView nameLabel; + + public ImageView cancelButton; + } +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/adapter/AdapterTwoItem.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/adapter/AdapterTwoItem.java index bb2782c..b666854 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/adapter/AdapterTwoItem.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/adapter/AdapterTwoItem.java @@ -1,75 +1,75 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.adapter; - -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.TextView; - -import sharedcode.turboeditor.R; - -public class AdapterTwoItem extends - ArrayAdapter { - - private final LayoutInflater inflater; - private final String[] lines1; - private final String[] lines2; - - public AdapterTwoItem(Context context, - String[] lines1, - String[] lines2) { - super(context, R.layout.item_two_lines, lines1); - this.lines1 = lines1; - this.lines2 = lines2; - this.inflater = LayoutInflater.from(context); - } - - - @Override - public View getView(final int position, - View convertView, final ViewGroup parent) { - if (convertView == null) { - convertView = this.inflater - .inflate(R.layout.item_two_lines, - parent, false); - final ViewHolder hold = new ViewHolder(); - hold.line1 = (TextView) convertView.findViewById(android.R.id.text1); - hold.line2 = (TextView) convertView.findViewById(android.R.id.text2); - convertView.setTag(hold); - - hold.line1.setText(lines1[position]); - hold.line2.setText(lines2[position]); - } else { - final ViewHolder hold = ((ViewHolder) convertView.getTag()); - hold.line1.setText(lines1[position]); - hold.line2.setText(lines2[position]); - } - return convertView; - } - - public static class ViewHolder { - - public TextView line1; - public TextView line2; - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.adapter; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.TextView; + +import sharedcode.turboeditor.R; + +public class AdapterTwoItem extends + ArrayAdapter { + + private final LayoutInflater inflater; + private final String[] lines1; + private final String[] lines2; + + public AdapterTwoItem(Context context, + String[] lines1, + String[] lines2) { + super(context, R.layout.item_two_lines, lines1); + this.lines1 = lines1; + this.lines2 = lines2; + this.inflater = LayoutInflater.from(context); + } + + + @Override + public View getView(final int position, + View convertView, final ViewGroup parent) { + if (convertView == null) { + convertView = this.inflater + .inflate(R.layout.item_two_lines, + parent, false); + final ViewHolder hold = new ViewHolder(); + hold.line1 = (TextView) convertView.findViewById(android.R.id.text1); + hold.line2 = (TextView) convertView.findViewById(android.R.id.text2); + convertView.setTag(hold); + + hold.line1.setText(lines1[position]); + hold.line2.setText(lines2[position]); + } else { + final ViewHolder hold = ((ViewHolder) convertView.getTag()); + hold.line1.setText(lines1[position]); + hold.line2.setText(lines2[position]); + } + return convertView; + } + + public static class ViewHolder { + + public TextView line1; + public TextView line2; + } +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/application/MyApp.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/application/MyApp.java index 1c84e71..db3a1c0 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/application/MyApp.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/application/MyApp.java @@ -1,44 +1,44 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.application; - -import android.app.Application; -import android.view.ViewConfiguration; - -import java.lang.reflect.Field; - -public class MyApp extends Application { - - @Override - public void onCreate() { - super.onCreate(); - // force to sow the overflow menu icon - try { - ViewConfiguration config = ViewConfiguration.get(this); - Field menuKeyField = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey"); - if (menuKeyField != null) { - menuKeyField.setAccessible(true); - menuKeyField.setBoolean(config, false); - } - } catch (Exception ex) { - // Ignore - } - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.application; + +import android.app.Application; +import android.view.ViewConfiguration; + +import java.lang.reflect.Field; + +public class MyApp extends Application { + + @Override + public void onCreate() { + super.onCreate(); + // force to sow the overflow menu icon + try { + ViewConfiguration config = ViewConfiguration.get(this); + Field menuKeyField = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey"); + if (menuKeyField != null) { + menuKeyField.setAccessible(true); + menuKeyField.setBoolean(config, false); + } + } catch (Exception ex) { + // Ignore + } + } +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/ChangelogDialog.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/ChangelogDialog.java index 2d2818a..c2a70ef 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/ChangelogDialog.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/ChangelogDialog.java @@ -1,91 +1,91 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.dialogfragment; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.DialogFragment; -import android.app.Fragment; -import android.app.FragmentManager; -import android.app.FragmentTransaction; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.view.LayoutInflater; - -import it.gmariotti.changelibs.library.view.ChangeLogListView; -import sharedcode.turboeditor.R; -import sharedcode.turboeditor.util.Build; - -public class ChangelogDialog extends DialogFragment { - - public static void showChangeLogDialog(FragmentManager fragmentManager) { - ChangelogDialog changelogDialog = new ChangelogDialog(); - FragmentTransaction ft = fragmentManager.beginTransaction(); - Fragment prev = fragmentManager.findFragmentByTag("changelogdemo_dialog"); - if (prev != null) { - ft.remove(prev); - } - ft.addToBackStack(null); - changelogDialog.show(ft, "changelogdemo_dialog"); - } - - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - - LayoutInflater layoutInflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); - ChangeLogListView chgList = (ChangeLogListView) layoutInflater.inflate(R.layout.demo_changelog_fragment_dialogstandard, null); - - return new AlertDialog.Builder(getActivity()) - .setTitle(R.string.changelog) - .setView(chgList) - .setNegativeButton(android.R.string.cancel, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) { - dialog.dismiss(); - } - } - ) - .setPositiveButton(R.string.vota, new DialogInterface.OnClickListener() { - /** - * {@inheritDoc} - */ - @Override - public void onClick(final DialogInterface dialog, final int which) { - try { - if (Build.FOR_AMAZON) { - String url = "amzn://apps/android?p=com.maskyn.fileeditor"; - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); - } else { - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.maskyn.fileeditor")) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); - } - } catch (Exception e) { - } - } - }) - .create(); - - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.dialogfragment; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.app.Fragment; +import android.app.FragmentManager; +import android.app.FragmentTransaction; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.view.LayoutInflater; + +import it.gmariotti.changelibs.library.view.ChangeLogListView; +import sharedcode.turboeditor.R; +import sharedcode.turboeditor.util.Build; + +public class ChangelogDialog extends DialogFragment { + + public static void showChangeLogDialog(FragmentManager fragmentManager) { + ChangelogDialog changelogDialog = new ChangelogDialog(); + FragmentTransaction ft = fragmentManager.beginTransaction(); + Fragment prev = fragmentManager.findFragmentByTag("changelogdemo_dialog"); + if (prev != null) { + ft.remove(prev); + } + ft.addToBackStack(null); + changelogDialog.show(ft, "changelogdemo_dialog"); + } + + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + + LayoutInflater layoutInflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + ChangeLogListView chgList = (ChangeLogListView) layoutInflater.inflate(R.layout.demo_changelog_fragment_dialogstandard, null); + + return new AlertDialog.Builder(getActivity()) + .setTitle(R.string.changelog) + .setView(chgList) + .setNegativeButton(android.R.string.cancel, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + dialog.dismiss(); + } + } + ) + .setPositiveButton(R.string.vota, new DialogInterface.OnClickListener() { + /** + * {@inheritDoc} + */ + @Override + public void onClick(final DialogInterface dialog, final int which) { + try { + if (Build.FOR_AMAZON) { + String url = "amzn://apps/android?p=com.maskyn.fileeditor"; + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + } else { + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.maskyn.fileeditor")) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + } + } catch (Exception e) { + } + } + }) + .create(); + + } +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/EditTextDialog.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/EditTextDialog.java index 50a2a8d..183a6ae 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/EditTextDialog.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/EditTextDialog.java @@ -1,132 +1,132 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.dialogfragment; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.DialogFragment; -import android.content.DialogInterface; -import android.os.Bundle; -import android.view.KeyEvent; -import android.view.View; -import android.view.WindowManager; -import android.view.inputmethod.EditorInfo; -import android.widget.EditText; -import android.widget.TextView; - -import sharedcode.turboeditor.R; -import sharedcode.turboeditor.views.DialogHelper; - -// ... -public class EditTextDialog extends DialogFragment implements TextView.OnEditorActionListener { - - private EditText mEditText; - - public static EditTextDialog newInstance(final Actions action) { - return EditTextDialog.newInstance(action, ""); - } - - public static EditTextDialog newInstance(final Actions action, final String hint) { - final EditTextDialog f = new EditTextDialog(); - final Bundle args = new Bundle(); - args.putSerializable("action", action); - args.putString("hint", hint); - f.setArguments(args); - return f; - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - - final Actions action = (Actions) getArguments().getSerializable("action"); - final String title; - switch (action) { - case NewFile: - title = getString(R.string.file); - break; - case NewFolder: - title = getString(R.string.folder); - break; - default: - title = null; - break; - } - - View view = new DialogHelper.Builder(getActivity()) - .setTitle(title) - .setView(R.layout.dialog_fragment_edittext) - .createSkeletonView(); - this.mEditText = (EditText) view.findViewById(android.R.id.edit); - this.mEditText.setHint(R.string.name); - - // Show soft keyboard automatically - this.mEditText.setText(getArguments().getString("hint")); - this.mEditText.requestFocus(); - getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); - this.mEditText.setOnEditorActionListener(this); - - return new AlertDialog.Builder(getActivity()) - .setView(view) - .setPositiveButton(android.R.string.ok, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - returnData(); - } - } - ) - .setNegativeButton(android.R.string.cancel, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - - } - } - ) - .create(); - } - - void returnData() { - EditDialogListener target = (EditDialogListener) getTargetFragment(); - if (target == null) { - target = (EditDialogListener) getActivity(); - } - target.onFinishEditDialog(this.mEditText.getText().toString(), getArguments().getString("hint"), - (Actions) getArguments().getSerializable("action")); - this.dismiss(); - } - - @Override - public boolean onEditorAction(final TextView v, final int actionId, final KeyEvent event) { - if (EditorInfo.IME_ACTION_DONE == actionId) { - returnData(); - return true; - } - return false; - } - - public enum Actions { - NewFile, NewFolder - } - - public interface EditDialogListener { - void onFinishEditDialog(String result, String hint, Actions action); - } +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.dialogfragment; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.DialogInterface; +import android.os.Bundle; +import android.view.KeyEvent; +import android.view.View; +import android.view.WindowManager; +import android.view.inputmethod.EditorInfo; +import android.widget.EditText; +import android.widget.TextView; + +import sharedcode.turboeditor.R; +import sharedcode.turboeditor.views.DialogHelper; + +// ... +public class EditTextDialog extends DialogFragment implements TextView.OnEditorActionListener { + + private EditText mEditText; + + public static EditTextDialog newInstance(final Actions action) { + return EditTextDialog.newInstance(action, ""); + } + + public static EditTextDialog newInstance(final Actions action, final String hint) { + final EditTextDialog f = new EditTextDialog(); + final Bundle args = new Bundle(); + args.putSerializable("action", action); + args.putString("hint", hint); + f.setArguments(args); + return f; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + + final Actions action = (Actions) getArguments().getSerializable("action"); + final String title; + switch (action) { + case NewFile: + title = getString(R.string.file); + break; + case NewFolder: + title = getString(R.string.folder); + break; + default: + title = null; + break; + } + + View view = new DialogHelper.Builder(getActivity()) + .setTitle(title) + .setView(R.layout.dialog_fragment_edittext) + .createSkeletonView(); + this.mEditText = (EditText) view.findViewById(android.R.id.edit); + this.mEditText.setHint(R.string.name); + + // Show soft keyboard automatically + this.mEditText.setText(getArguments().getString("hint")); + this.mEditText.requestFocus(); + getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); + this.mEditText.setOnEditorActionListener(this); + + return new AlertDialog.Builder(getActivity()) + .setView(view) + .setPositiveButton(android.R.string.ok, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + returnData(); + } + } + ) + .setNegativeButton(android.R.string.cancel, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + + } + } + ) + .create(); + } + + void returnData() { + EditDialogListener target = (EditDialogListener) getTargetFragment(); + if (target == null) { + target = (EditDialogListener) getActivity(); + } + target.onFinishEditDialog(this.mEditText.getText().toString(), getArguments().getString("hint"), + (Actions) getArguments().getSerializable("action")); + this.dismiss(); + } + + @Override + public boolean onEditorAction(final TextView v, final int actionId, final KeyEvent event) { + if (EditorInfo.IME_ACTION_DONE == actionId) { + returnData(); + return true; + } + return false; + } + + public enum Actions { + NewFile, NewFolder + } + + public interface EditDialogListener { + void onFinishEditDialog(String result, String hint, Actions action); + } } \ No newline at end of file diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/EncodingDialog.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/EncodingDialog.java index b89b3e7..369d4a7 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/EncodingDialog.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/EncodingDialog.java @@ -1,120 +1,120 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.dialogfragment; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.DialogFragment; -import android.os.Bundle; -import android.support.v7.widget.SwitchCompat; -import android.view.View; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.CompoundButton; -import android.widget.ListView; - -import org.mozilla.universalchardet.Constants; - -import sharedcode.turboeditor.R; -import sharedcode.turboeditor.preferences.PreferenceHelper; - -public class EncodingDialog extends DialogFragment implements AdapterView.OnItemClickListener { - - private final String[] encodings = new String[]{ - Constants.CHARSET_BIG5, - Constants.CHARSET_EUC_JP, - Constants.CHARSET_EUC_KR, - Constants.CHARSET_EUC_TW, - Constants.CHARSET_GB18030, - "GB2312", - Constants.CHARSET_IBM855, - Constants.CHARSET_IBM866, - Constants.CHARSET_ISO_2022_CN, - Constants.CHARSET_ISO_2022_JP, - Constants.CHARSET_ISO_2022_KR, - Constants.CHARSET_ISO_8859_5, - Constants.CHARSET_ISO_8859_7, - Constants.CHARSET_ISO_8859_8, - Constants.CHARSET_KOI8_R, - Constants.CHARSET_MACCYRILLIC, - Constants.CHARSET_SHIFT_JIS, - Constants.CHARSET_UTF_16BE, - Constants.CHARSET_UTF_16LE, - Constants.CHARSET_UTF_32BE, - Constants.CHARSET_UTF_32LE, - Constants.CHARSET_UTF_8, - Constants.CHARSET_WINDOWS_1251, - Constants.CHARSET_WINDOWS_1252, - Constants.CHARSET_WINDOWS_1253, - Constants.CHARSET_WINDOWS_1255 - }; - private ListView list; - - public static EncodingDialog newInstance() { - final EncodingDialog f = new EncodingDialog(); - return f; - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - - final View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_encoding_list, null); - list = (ListView) view.findViewById(android.R.id.list); - SwitchCompat autoencoding = (SwitchCompat) view.findViewById(android.R.id.checkbox); - autoencoding.setChecked(PreferenceHelper.getAutoEncoding(getActivity())); - - autoencoding.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - PreferenceHelper.setAutoencoding(getActivity(), isChecked); - } - }); - - list.setAdapter(new ArrayAdapter<>(getActivity(), R.layout.item_single_choice, encodings)); - list.setOnItemClickListener(this); - - String currentEncoding = PreferenceHelper.getEncoding(getActivity()); - - for (int i = 0; i < encodings.length; i++) { - if (currentEncoding.equals(encodings[i])) { - list.setItemChecked(i, true); - } - - } - - return new AlertDialog.Builder(getActivity()) - .setView(view) - .create(); - } - - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - DialogListener target = (DialogListener) getTargetFragment(); - if (target == null) { - target = (DialogListener) getActivity(); - } - target.onEncodingSelected(encodings[position]); - this.dismiss(); - } - - public interface DialogListener { - void onEncodingSelected(String result); - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.dialogfragment; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.os.Bundle; +import android.support.v7.widget.SwitchCompat; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.CompoundButton; +import android.widget.ListView; + +import org.mozilla.universalchardet.Constants; + +import sharedcode.turboeditor.R; +import sharedcode.turboeditor.preferences.PreferenceHelper; + +public class EncodingDialog extends DialogFragment implements AdapterView.OnItemClickListener { + + private final String[] encodings = new String[]{ + Constants.CHARSET_BIG5, + Constants.CHARSET_EUC_JP, + Constants.CHARSET_EUC_KR, + Constants.CHARSET_EUC_TW, + Constants.CHARSET_GB18030, + "GB2312", + Constants.CHARSET_IBM855, + Constants.CHARSET_IBM866, + Constants.CHARSET_ISO_2022_CN, + Constants.CHARSET_ISO_2022_JP, + Constants.CHARSET_ISO_2022_KR, + Constants.CHARSET_ISO_8859_5, + Constants.CHARSET_ISO_8859_7, + Constants.CHARSET_ISO_8859_8, + Constants.CHARSET_KOI8_R, + Constants.CHARSET_MACCYRILLIC, + Constants.CHARSET_SHIFT_JIS, + Constants.CHARSET_UTF_16BE, + Constants.CHARSET_UTF_16LE, + Constants.CHARSET_UTF_32BE, + Constants.CHARSET_UTF_32LE, + Constants.CHARSET_UTF_8, + Constants.CHARSET_WINDOWS_1251, + Constants.CHARSET_WINDOWS_1252, + Constants.CHARSET_WINDOWS_1253, + Constants.CHARSET_WINDOWS_1255 + }; + private ListView list; + + public static EncodingDialog newInstance() { + final EncodingDialog f = new EncodingDialog(); + return f; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + + final View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_encoding_list, null); + list = (ListView) view.findViewById(android.R.id.list); + SwitchCompat autoencoding = (SwitchCompat) view.findViewById(android.R.id.checkbox); + autoencoding.setChecked(PreferenceHelper.getAutoEncoding(getActivity())); + + autoencoding.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + PreferenceHelper.setAutoencoding(getActivity(), isChecked); + } + }); + + list.setAdapter(new ArrayAdapter<>(getActivity(), R.layout.item_single_choice, encodings)); + list.setOnItemClickListener(this); + + String currentEncoding = PreferenceHelper.getEncoding(getActivity()); + + for (int i = 0; i < encodings.length; i++) { + if (currentEncoding.equals(encodings[i])) { + list.setItemChecked(i, true); + } + + } + + return new AlertDialog.Builder(getActivity()) + .setView(view) + .create(); + } + + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + DialogListener target = (DialogListener) getTargetFragment(); + if (target == null) { + target = (DialogListener) getActivity(); + } + target.onEncodingSelected(encodings[position]); + this.dismiss(); + } + + public interface DialogListener { + void onEncodingSelected(String result); + } +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/FileInfoDialog.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/FileInfoDialog.java index aad6ecf..dfc93df 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/FileInfoDialog.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/FileInfoDialog.java @@ -1,97 +1,97 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.dialogfragment; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.DialogFragment; -import android.content.DialogInterface; -import android.os.Bundle; -import android.view.View; -import android.widget.ListView; - -import org.apache.commons.io.FileUtils; - -import java.io.File; -import java.util.Date; - -import sharedcode.turboeditor.R; -import sharedcode.turboeditor.adapter.AdapterTwoItem; -import sharedcode.turboeditor.views.DialogHelper; - -// ... -public class FileInfoDialog extends DialogFragment { - - public static FileInfoDialog newInstance(String filePath) { - final FileInfoDialog f = new FileInfoDialog(); - final Bundle args = new Bundle(); - args.putString("filePath", filePath); - f.setArguments(args); - return f; - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - - View view = new DialogHelper.Builder(getActivity()) - .setTitle(R.string.info) - .setView(R.layout.dialog_fragment_file_info) - .createSkeletonView(); - //final View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_fragment_file_info, null); - - ListView list = (ListView) view.findViewById(android.R.id.list); - - File file = new File(getArguments().getString("filePath")); - - // Get the last modification information. - Long lastModified = file.lastModified(); - - // Create a new date object and pass last modified information - // to the date object. - Date date = new Date(lastModified); - - String[] lines1 = { - getString(R.string.name), - getString(R.string.folder), - getString(R.string.size), - getString(R.string.modification_date) - }; - String[] lines2 = { - file.getName(), - file.getParent(), - FileUtils.byteCountToDisplaySize(file.length()), - date.toString() - }; - - list.setAdapter(new AdapterTwoItem(getActivity(), lines1, lines2)); - - - return new AlertDialog.Builder(getActivity()) - .setView(view) - .setPositiveButton(android.R.string.ok, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - } - } - ) - .create(); - } +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.dialogfragment; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.DialogInterface; +import android.os.Bundle; +import android.view.View; +import android.widget.ListView; + +import org.apache.commons.io.FileUtils; + +import java.io.File; +import java.util.Date; + +import sharedcode.turboeditor.R; +import sharedcode.turboeditor.adapter.AdapterTwoItem; +import sharedcode.turboeditor.views.DialogHelper; + +// ... +public class FileInfoDialog extends DialogFragment { + + public static FileInfoDialog newInstance(String filePath) { + final FileInfoDialog f = new FileInfoDialog(); + final Bundle args = new Bundle(); + args.putString("filePath", filePath); + f.setArguments(args); + return f; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + + View view = new DialogHelper.Builder(getActivity()) + .setTitle(R.string.info) + .setView(R.layout.dialog_fragment_file_info) + .createSkeletonView(); + //final View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_fragment_file_info, null); + + ListView list = (ListView) view.findViewById(android.R.id.list); + + File file = new File(getArguments().getString("filePath")); + + // Get the last modification information. + Long lastModified = file.lastModified(); + + // Create a new date object and pass last modified information + // to the date object. + Date date = new Date(lastModified); + + String[] lines1 = { + getString(R.string.name), + getString(R.string.folder), + getString(R.string.size), + getString(R.string.modification_date) + }; + String[] lines2 = { + file.getName(), + file.getParent(), + FileUtils.byteCountToDisplaySize(file.length()), + date.toString() + }; + + list.setAdapter(new AdapterTwoItem(getActivity(), lines1, lines2)); + + + return new AlertDialog.Builder(getActivity()) + .setView(view) + .setPositiveButton(android.R.string.ok, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + } + } + ) + .create(); + } } \ No newline at end of file diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/FindTextDialog.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/FindTextDialog.java index bdf1f7b..a628871 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/FindTextDialog.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/FindTextDialog.java @@ -1,207 +1,207 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.dialogfragment; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.DialogFragment; -import android.content.DialogInterface; -import android.os.AsyncTask; -import android.os.Bundle; -import android.view.View; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.CompoundButton; -import android.widget.EditText; -import android.widget.Toast; - -import java.util.LinkedList; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import sharedcode.turboeditor.R; -import sharedcode.turboeditor.texteditor.SearchResult; -import sharedcode.turboeditor.views.DialogHelper; - -// ... -public class FindTextDialog extends DialogFragment { - - private EditText textToFind, textToReplace; - private CheckBox regexCheck, replaceCheck, matchCaseCheck; - - public static FindTextDialog newInstance(String allText) { - final FindTextDialog f = new FindTextDialog(); - final Bundle args = new Bundle(); - args.putString("allText", allText); - f.setArguments(args); - return f; - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - - //final View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_fragment_find_text, null); - - View view = new DialogHelper.Builder(getActivity()) - .setTitle(R.string.find) - .setView(R.layout.dialog_fragment_find_text) - .createSkeletonView(); - - this.textToFind = (EditText) view.findViewById(R.id.text_to_find); - this.textToReplace = (EditText) view.findViewById(R.id.text_to_replace); - this.regexCheck = (CheckBox) view.findViewById(R.id.regex_check); - this.replaceCheck = (CheckBox) view.findViewById(R.id.replace_check); - this.matchCaseCheck = (CheckBox) view.findViewById(R.id.match_case_check); - - replaceCheck.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - textToReplace.setVisibility(isChecked ? View.VISIBLE : View.GONE); - } - }); - - return new AlertDialog.Builder(getActivity()) - .setView(view) - .setPositiveButton(R.string.find, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - } - } - ) - .setNegativeButton(android.R.string.cancel, null) - .create(); - } - - @Override - public void onStart() { - super.onStart(); //super.onStart() is where dialog.show() is actually called on the underlying dialog, so we have to do it after this point - AlertDialog d = (AlertDialog) getDialog(); - if (d != null) { - Button positiveButton = (Button) d.getButton(Dialog.BUTTON_POSITIVE); - positiveButton.setText(getString(R.string.find)); - positiveButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - returnData(); - } - }); - - Button negativeButton = (Button) d.getButton(Dialog.BUTTON_NEGATIVE); - negativeButton.setText(getString(android.R.string.cancel)); - negativeButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - dismiss(); - } - }); - } - } - - void returnData() { - if (textToFind.getText().toString().isEmpty()) { - this.dismiss(); - } else { - // we disable the okButton while we search - new SearchTask().execute(); - } - } - - public interface SearchDialogInterface { - void onSearchDone(SearchResult searchResult); - } - - private class SearchTask extends AsyncTask { - - LinkedList foundIndex; - boolean foundSomething; - - @Override - protected Void doInBackground(Void... params) { - String allText = getArguments().getString("allText"); - String whatToSearch = textToFind.getText().toString(); - boolean caseSensitive = matchCaseCheck.isChecked(); - boolean isRegex = regexCheck.isChecked(); - foundIndex = new LinkedList<>(); - Matcher matcher = null; - foundSomething = false; - - if (isRegex) { - try { - if (caseSensitive) - matcher = Pattern.compile(whatToSearch, Pattern.MULTILINE).matcher(allText); - else - matcher = Pattern.compile(whatToSearch, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE).matcher(allText); - } catch (Exception e) { - isRegex = false; - } - } - - if (isRegex) { - while (matcher.find()) { - foundSomething = true; - - foundIndex.add(matcher.start()); - } - } else { - if (caseSensitive == false) { // by default is case sensitive - whatToSearch = whatToSearch.toLowerCase(); - allText = allText.toLowerCase(); - } - int index = allText.indexOf(whatToSearch); - while (index >= 0) { - foundSomething = true; - - foundIndex.add(index); - - index = allText.indexOf(whatToSearch, index + 1); - } - } - - return null; - } - - @Override - protected void onPostExecute(Void aVoid) { - super.onPostExecute(aVoid); - if (foundSomething) { - // the class that called this Dialog should implement the SearchDialogIterface - SearchDialogInterface searchDialogInterface; - searchDialogInterface = ((SearchDialogInterface) getTargetFragment()); - if (searchDialogInterface == null) - searchDialogInterface = ((SearchDialogInterface) getActivity()); - - // if who called this has not implemented the interface we return nothing - if (searchDialogInterface == null) - return; - // else we return positions and other things - else { - SearchResult searchResult = new SearchResult(foundIndex, textToFind.length(), replaceCheck.isChecked(), textToReplace.getText().toString()); - searchDialogInterface.onSearchDone(searchResult); - } - } else { - - } - Toast.makeText(getActivity(), String.format(getString(R.string.occurrences_found), foundIndex.size()), Toast.LENGTH_SHORT).show(); - // dismiss the dialog - FindTextDialog.this.dismiss(); - } - } +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.dialogfragment; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.DialogInterface; +import android.os.AsyncTask; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.EditText; +import android.widget.Toast; + +import java.util.LinkedList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import sharedcode.turboeditor.R; +import sharedcode.turboeditor.texteditor.SearchResult; +import sharedcode.turboeditor.views.DialogHelper; + +// ... +public class FindTextDialog extends DialogFragment { + + private EditText textToFind, textToReplace; + private CheckBox regexCheck, replaceCheck, matchCaseCheck; + + public static FindTextDialog newInstance(String allText) { + final FindTextDialog f = new FindTextDialog(); + final Bundle args = new Bundle(); + args.putString("allText", allText); + f.setArguments(args); + return f; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + + //final View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_fragment_find_text, null); + + View view = new DialogHelper.Builder(getActivity()) + .setTitle(R.string.find) + .setView(R.layout.dialog_fragment_find_text) + .createSkeletonView(); + + this.textToFind = (EditText) view.findViewById(R.id.text_to_find); + this.textToReplace = (EditText) view.findViewById(R.id.text_to_replace); + this.regexCheck = (CheckBox) view.findViewById(R.id.regex_check); + this.replaceCheck = (CheckBox) view.findViewById(R.id.replace_check); + this.matchCaseCheck = (CheckBox) view.findViewById(R.id.match_case_check); + + replaceCheck.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + textToReplace.setVisibility(isChecked ? View.VISIBLE : View.GONE); + } + }); + + return new AlertDialog.Builder(getActivity()) + .setView(view) + .setPositiveButton(R.string.find, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + } + } + ) + .setNegativeButton(android.R.string.cancel, null) + .create(); + } + + @Override + public void onStart() { + super.onStart(); //super.onStart() is where dialog.show() is actually called on the underlying dialog, so we have to do it after this point + AlertDialog d = (AlertDialog) getDialog(); + if (d != null) { + Button positiveButton = (Button) d.getButton(Dialog.BUTTON_POSITIVE); + positiveButton.setText(getString(R.string.find)); + positiveButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + returnData(); + } + }); + + Button negativeButton = (Button) d.getButton(Dialog.BUTTON_NEGATIVE); + negativeButton.setText(getString(android.R.string.cancel)); + negativeButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + dismiss(); + } + }); + } + } + + void returnData() { + if (textToFind.getText().toString().isEmpty()) { + this.dismiss(); + } else { + // we disable the okButton while we search + new SearchTask().execute(); + } + } + + public interface SearchDialogInterface { + void onSearchDone(SearchResult searchResult); + } + + private class SearchTask extends AsyncTask { + + LinkedList foundIndex; + boolean foundSomething; + + @Override + protected Void doInBackground(Void... params) { + String allText = getArguments().getString("allText"); + String whatToSearch = textToFind.getText().toString(); + boolean caseSensitive = matchCaseCheck.isChecked(); + boolean isRegex = regexCheck.isChecked(); + foundIndex = new LinkedList<>(); + Matcher matcher = null; + foundSomething = false; + + if (isRegex) { + try { + if (caseSensitive) + matcher = Pattern.compile(whatToSearch, Pattern.MULTILINE).matcher(allText); + else + matcher = Pattern.compile(whatToSearch, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE).matcher(allText); + } catch (Exception e) { + isRegex = false; + } + } + + if (isRegex) { + while (matcher.find()) { + foundSomething = true; + + foundIndex.add(matcher.start()); + } + } else { + if (caseSensitive == false) { // by default is case sensitive + whatToSearch = whatToSearch.toLowerCase(); + allText = allText.toLowerCase(); + } + int index = allText.indexOf(whatToSearch); + while (index >= 0) { + foundSomething = true; + + foundIndex.add(index); + + index = allText.indexOf(whatToSearch, index + 1); + } + } + + return null; + } + + @Override + protected void onPostExecute(Void aVoid) { + super.onPostExecute(aVoid); + if (foundSomething) { + // the class that called this Dialog should implement the SearchDialogIterface + SearchDialogInterface searchDialogInterface; + searchDialogInterface = ((SearchDialogInterface) getTargetFragment()); + if (searchDialogInterface == null) + searchDialogInterface = ((SearchDialogInterface) getActivity()); + + // if who called this has not implemented the interface we return nothing + if (searchDialogInterface == null) + return; + // else we return positions and other things + else { + SearchResult searchResult = new SearchResult(foundIndex, textToFind.length(), replaceCheck.isChecked(), textToReplace.getText().toString()); + searchDialogInterface.onSearchDone(searchResult); + } + } else { + + } + Toast.makeText(getActivity(), String.format(getString(R.string.occurrences_found), foundIndex.size()), Toast.LENGTH_SHORT).show(); + // dismiss the dialog + FindTextDialog.this.dismiss(); + } + } } \ No newline at end of file diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/NewFileDetailsDialog.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/NewFileDetailsDialog.java index f27f65f..14e3b4f 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/NewFileDetailsDialog.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/NewFileDetailsDialog.java @@ -1,98 +1,98 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.dialogfragment; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.DialogFragment; -import android.content.DialogInterface; -import android.os.Bundle; -import android.view.View; -import android.view.WindowManager; -import android.widget.EditText; - -import java.io.File; - -import sharedcode.turboeditor.R; -import sharedcode.turboeditor.activity.MainActivity; -import sharedcode.turboeditor.preferences.PreferenceHelper; -import sharedcode.turboeditor.task.SaveFileTask; -import sharedcode.turboeditor.views.DialogHelper; - -// ... -public class NewFileDetailsDialog extends DialogFragment { - - private EditText mName; - private EditText mFolder; - - public static NewFileDetailsDialog newInstance(String fileText, String fileEncoding) { - final NewFileDetailsDialog f = new NewFileDetailsDialog(); - final Bundle args = new Bundle(); - args.putString("fileText", fileText); - args.putString("fileEncoding", fileEncoding); - f.setArguments(args); - return f; - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - - View view = new DialogHelper.Builder(getActivity()) - .setTitle(R.string.file) - .setView(R.layout.dialog_fragment_new_file_details) - .createSkeletonView(); - - this.mName = (EditText) view.findViewById(android.R.id.text1); - this.mFolder = (EditText) view.findViewById(android.R.id.text2); - - this.mName.setText(".txt"); - this.mFolder.setText(PreferenceHelper.getWorkingFolder(getActivity())); - - // Show soft keyboard automatically - this.mName.requestFocus(); - this.mName.setSelection(0); - getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); - - return new AlertDialog.Builder(getActivity()) - .setView(view) - .setPositiveButton(android.R.string.ok, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (!mName.getText().toString().isEmpty() && !mFolder.getText().toString().isEmpty()) { - File file = new File(mFolder.getText().toString(), mName.getText().toString()); - new SaveFileTask((MainActivity) getActivity(), file.getPath(), getArguments().getString("fileText"), getArguments().getString("fileEncoding")).execute(); - PreferenceHelper.setWorkingFolder(getActivity(), file.getParent()); - } - } - } - ) - .setNegativeButton(android.R.string.cancel, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - - } - } - ) - .create(); - } - +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.dialogfragment; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.DialogInterface; +import android.os.Bundle; +import android.view.View; +import android.view.WindowManager; +import android.widget.EditText; + +import java.io.File; + +import sharedcode.turboeditor.R; +import sharedcode.turboeditor.activity.MainActivity; +import sharedcode.turboeditor.preferences.PreferenceHelper; +import sharedcode.turboeditor.task.SaveFileTask; +import sharedcode.turboeditor.views.DialogHelper; + +// ... +public class NewFileDetailsDialog extends DialogFragment { + + private EditText mName; + private EditText mFolder; + + public static NewFileDetailsDialog newInstance(String fileText, String fileEncoding) { + final NewFileDetailsDialog f = new NewFileDetailsDialog(); + final Bundle args = new Bundle(); + args.putString("fileText", fileText); + args.putString("fileEncoding", fileEncoding); + f.setArguments(args); + return f; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + + View view = new DialogHelper.Builder(getActivity()) + .setTitle(R.string.file) + .setView(R.layout.dialog_fragment_new_file_details) + .createSkeletonView(); + + this.mName = (EditText) view.findViewById(android.R.id.text1); + this.mFolder = (EditText) view.findViewById(android.R.id.text2); + + this.mName.setText(".txt"); + this.mFolder.setText(PreferenceHelper.getWorkingFolder(getActivity())); + + // Show soft keyboard automatically + this.mName.requestFocus(); + this.mName.setSelection(0); + getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); + + return new AlertDialog.Builder(getActivity()) + .setView(view) + .setPositiveButton(android.R.string.ok, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (!mName.getText().toString().isEmpty() && !mFolder.getText().toString().isEmpty()) { + File file = new File(mFolder.getText().toString(), mName.getText().toString()); + new SaveFileTask((MainActivity) getActivity(), file.getPath(), getArguments().getString("fileText"), getArguments().getString("fileEncoding")).execute(); + PreferenceHelper.setWorkingFolder(getActivity(), file.getParent()); + } + } + } + ) + .setNegativeButton(android.R.string.cancel, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + + } + } + ) + .create(); + } + } \ No newline at end of file diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/NumberPickerDialog.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/NumberPickerDialog.java index 8368647..56f84ae 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/NumberPickerDialog.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/NumberPickerDialog.java @@ -1,123 +1,123 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.dialogfragment; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.DialogFragment; -import android.content.DialogInterface; -import android.os.Bundle; -import android.view.View; -import android.widget.NumberPicker; - -import sharedcode.turboeditor.R; -import sharedcode.turboeditor.views.DialogHelper; - -// ... -public class NumberPickerDialog extends DialogFragment { - - private NumberPicker mSeekBar; - - public static NumberPickerDialog newInstance(final Actions action) { - return NumberPickerDialog.newInstance(action, 0, 50, 100); - } - - public static NumberPickerDialog newInstance(final Actions action, final int min, final int current, final int max) { - final NumberPickerDialog f = new NumberPickerDialog(); - final Bundle args = new Bundle(); - args.putSerializable("action", action); - args.putInt("min", min); - args.putInt("current", current); - args.putInt("max", max); - f.setArguments(args); - return f; - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - - Actions action = (Actions) getArguments().getSerializable("action"); - int title; - switch (action){ - case FontSize: - title = R.string.font_size; - break; - case SelectPage: - title = R.string.goto_page; - break; - case GoToLine: - title = R.string.goto_line; - break; - default: - title = R.string.nome_app_turbo_editor; - break; - } - - View view = new DialogHelper.Builder(getActivity()) - .setTitle(title) - .setView(R.layout.dialog_fragment_seekbar) - .createSkeletonView(); - - this.mSeekBar = (NumberPicker) view.findViewById(android.R.id.input); - this.mSeekBar.setMaxValue(getArguments().getInt("max")); - this.mSeekBar.setMinValue(getArguments().getInt("min")); - this.mSeekBar.setValue(getArguments().getInt("current")); - return new AlertDialog.Builder(getActivity()) - //.setTitle(title) - .setView(view) - .setPositiveButton(android.R.string.ok, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - returnData(); - } - } - ) - .setNegativeButton(android.R.string.cancel, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - - } - } - ) - .create(); - } - - void returnData() { - INumberPickerDialog target = (INumberPickerDialog) getTargetFragment(); - if (target == null) { - target = (INumberPickerDialog) getActivity(); - } - target.onNumberPickerDialogDismissed( - (Actions) getArguments().getSerializable("action"), - mSeekBar.getValue() - ); - this.dismiss(); - } - - public enum Actions { - FontSize, SelectPage, GoToLine - } - - public interface INumberPickerDialog { - void onNumberPickerDialogDismissed(Actions action, int value); - } +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.dialogfragment; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.DialogInterface; +import android.os.Bundle; +import android.view.View; +import android.widget.NumberPicker; + +import sharedcode.turboeditor.R; +import sharedcode.turboeditor.views.DialogHelper; + +// ... +public class NumberPickerDialog extends DialogFragment { + + private NumberPicker mSeekBar; + + public static NumberPickerDialog newInstance(final Actions action) { + return NumberPickerDialog.newInstance(action, 0, 50, 100); + } + + public static NumberPickerDialog newInstance(final Actions action, final int min, final int current, final int max) { + final NumberPickerDialog f = new NumberPickerDialog(); + final Bundle args = new Bundle(); + args.putSerializable("action", action); + args.putInt("min", min); + args.putInt("current", current); + args.putInt("max", max); + f.setArguments(args); + return f; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + + Actions action = (Actions) getArguments().getSerializable("action"); + int title; + switch (action){ + case FontSize: + title = R.string.font_size; + break; + case SelectPage: + title = R.string.goto_page; + break; + case GoToLine: + title = R.string.goto_line; + break; + default: + title = R.string.nome_app_turbo_editor; + break; + } + + View view = new DialogHelper.Builder(getActivity()) + .setTitle(title) + .setView(R.layout.dialog_fragment_seekbar) + .createSkeletonView(); + + this.mSeekBar = (NumberPicker) view.findViewById(android.R.id.input); + this.mSeekBar.setMaxValue(getArguments().getInt("max")); + this.mSeekBar.setMinValue(getArguments().getInt("min")); + this.mSeekBar.setValue(getArguments().getInt("current")); + return new AlertDialog.Builder(getActivity()) + //.setTitle(title) + .setView(view) + .setPositiveButton(android.R.string.ok, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + returnData(); + } + } + ) + .setNegativeButton(android.R.string.cancel, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + + } + } + ) + .create(); + } + + void returnData() { + INumberPickerDialog target = (INumberPickerDialog) getTargetFragment(); + if (target == null) { + target = (INumberPickerDialog) getActivity(); + } + target.onNumberPickerDialogDismissed( + (Actions) getArguments().getSerializable("action"), + mSeekBar.getValue() + ); + this.dismiss(); + } + + public enum Actions { + FontSize, SelectPage, GoToLine + } + + public interface INumberPickerDialog { + void onNumberPickerDialogDismissed(Actions action, int value); + } } \ No newline at end of file diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/SaveFileDialog.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/SaveFileDialog.java index 9a4acab..bd7f44c 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/SaveFileDialog.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/dialogfragment/SaveFileDialog.java @@ -1,113 +1,113 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.dialogfragment; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.DialogFragment; -import android.content.DialogInterface; -import android.os.Bundle; -import android.view.View; - -import org.apache.commons.io.FilenameUtils; - -import java.io.File; - -import sharedcode.turboeditor.R; -import sharedcode.turboeditor.activity.MainActivity; -import sharedcode.turboeditor.task.SaveFileTask; -import sharedcode.turboeditor.views.DialogHelper; - -public class SaveFileDialog extends DialogFragment { - - public static SaveFileDialog newInstance(String filePath, String text, String encoding) { - return newInstance(filePath, text, encoding, false, ""); - } - - public static SaveFileDialog newInstance(String filePath, String text, String encoding, boolean openNewFileAfter, String pathOfNewFile) { - SaveFileDialog frag = new SaveFileDialog(); - Bundle args = new Bundle(); - args.putString("filePath", filePath); - args.putString("text", text); - args.putString("encoding", encoding); - args.putBoolean("openNewFileAfter", openNewFileAfter); - args.putString("pathOfNewFile", pathOfNewFile); - frag.setArguments(args); - return frag; - } - - - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - final String filePath = getArguments().getString("filePath"); - final String text = getArguments().getString("text"); - final String encoding = getArguments().getString("encoding"); - final String fileName = FilenameUtils.getName(filePath); - final File file = new File(filePath); - - View view = new DialogHelper.Builder(getActivity()) - .setIcon(getResources().getDrawable(R.drawable.ic_action_save)) - .setTitle(R.string.salva) - .setMessage(String.format(getString(R.string.save_changes), fileName)) - .createCommonView(); - - return new AlertDialog.Builder(getActivity()) - .setView(view) - .setPositiveButton(R.string.salva, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (!fileName.isEmpty()) - new SaveFileTask((MainActivity) getActivity(), filePath, text, - encoding).execute(); - else { - NewFileDetailsDialog dialogFrag = - NewFileDetailsDialog.newInstance(text, - encoding); - dialogFrag.show(getFragmentManager().beginTransaction(), - "dialog"); - } - } - } - ) - .setNeutralButton(android.R.string.cancel, null) - .setNegativeButton(R.string.no, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - ISaveDialog target = (ISaveDialog) getTargetFragment(); - if (target == null) { - target = (ISaveDialog) getActivity(); - } - target.userDoesntWantToSave( - getArguments().getBoolean("openNewFileAfter"), - getArguments().getString("pathOfNewFile") - ); - } - } - ) - .create(); - } - - public interface ISaveDialog { - void userDoesntWantToSave(boolean openNewFile, String pathOfNewFile); - } +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.dialogfragment; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.DialogInterface; +import android.os.Bundle; +import android.view.View; + +import org.apache.commons.io.FilenameUtils; + +import java.io.File; + +import sharedcode.turboeditor.R; +import sharedcode.turboeditor.activity.MainActivity; +import sharedcode.turboeditor.task.SaveFileTask; +import sharedcode.turboeditor.views.DialogHelper; + +public class SaveFileDialog extends DialogFragment { + + public static SaveFileDialog newInstance(String filePath, String text, String encoding) { + return newInstance(filePath, text, encoding, false, ""); + } + + public static SaveFileDialog newInstance(String filePath, String text, String encoding, boolean openNewFileAfter, String pathOfNewFile) { + SaveFileDialog frag = new SaveFileDialog(); + Bundle args = new Bundle(); + args.putString("filePath", filePath); + args.putString("text", text); + args.putString("encoding", encoding); + args.putBoolean("openNewFileAfter", openNewFileAfter); + args.putString("pathOfNewFile", pathOfNewFile); + frag.setArguments(args); + return frag; + } + + + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final String filePath = getArguments().getString("filePath"); + final String text = getArguments().getString("text"); + final String encoding = getArguments().getString("encoding"); + final String fileName = FilenameUtils.getName(filePath); + final File file = new File(filePath); + + View view = new DialogHelper.Builder(getActivity()) + .setIcon(getResources().getDrawable(R.drawable.ic_action_save)) + .setTitle(R.string.salva) + .setMessage(String.format(getString(R.string.save_changes), fileName)) + .createCommonView(); + + return new AlertDialog.Builder(getActivity()) + .setView(view) + .setPositiveButton(R.string.salva, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (!fileName.isEmpty()) + new SaveFileTask((MainActivity) getActivity(), filePath, text, + encoding).execute(); + else { + NewFileDetailsDialog dialogFrag = + NewFileDetailsDialog.newInstance(text, + encoding); + dialogFrag.show(getFragmentManager().beginTransaction(), + "dialog"); + } + } + } + ) + .setNeutralButton(android.R.string.cancel, null) + .setNegativeButton(R.string.no, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + ISaveDialog target = (ISaveDialog) getTargetFragment(); + if (target == null) { + target = (ISaveDialog) getActivity(); + } + target.userDoesntWantToSave( + getArguments().getBoolean("openNewFileAfter"), + getArguments().getString("pathOfNewFile") + ); + } + } + ) + .create(); + } + + public interface ISaveDialog { + void userDoesntWantToSave(boolean openNewFile, String pathOfNewFile); + } } \ No newline at end of file diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/preferences/PreferenceHelper.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/preferences/PreferenceHelper.java index 408e575..5b024d1 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/preferences/PreferenceHelper.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/preferences/PreferenceHelper.java @@ -1,188 +1,188 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.preferences; - -import android.content.Context; -import android.content.SharedPreferences; -import android.os.Environment; -import android.preference.PreferenceManager; - -public final class PreferenceHelper { - - public static final String SD_CARD_ROOT = Environment.getExternalStorageDirectory().getAbsolutePath(); - - private PreferenceHelper() { - } - - // Getter Methods - - private static SharedPreferences getPrefs(Context context) { - return PreferenceManager.getDefaultSharedPreferences(context); - } - - private static SharedPreferences.Editor getEditor(Context context) { - return getPrefs(context).edit(); - } - - public static boolean getUseMonospace(Context context) { - return getPrefs(context).getBoolean("use_monospace", false); - } - - public static boolean getLineNumbers(Context context) { - return getPrefs(context).getBoolean("editor_line_numbers", true); - } - - public static boolean getSyntaxHighlight(Context context) { - return getPrefs(context).getBoolean("editor_syntax_highlight", false); - } - - public static boolean getWrapContent(Context context) { - return getPrefs(context).getBoolean("editor_wrap_content", true); - } - - public static boolean getLightTheme(Context context) { - return getPrefs(context).getBoolean("light_theme", false); - } - - public static boolean getSuggestionActive(Context context) { - return getPrefs(context).getBoolean("suggestion_active", false); - } - - public static boolean getAutoEncoding(Context context) { - return getPrefs(context).getBoolean("autoencoding", true); - } - - public static boolean getSendErrorReports(Context context) { - return getPrefs(context).getBoolean("send_error_reports", true); - } - - public static String getEncoding(Context context) { - return getPrefs(context).getString("editor_encoding", "UTF-8"); - } - - public static int getFontSize(Context context) { - return getPrefs(context).getInt("font_size", 16); - } - - public static String getWorkingFolder(Context context) { - return getPrefs(context).getString("working_folder", SD_CARD_ROOT); - } - - public static String[] getSavedPaths(Context context) { - return getPrefs(context).getString("savedPaths", "").split(","); - } - - public static boolean getPageSystemButtonsPopupShown(Context context) { - return getPrefs(context).getBoolean("page_system_button_popup_shown", false); - } - - public static boolean getAutoSave(Context context) { - return getPrefs(context).getBoolean("auto_save", false); - } - - public static boolean getReadOnly(Context context) { - return getPrefs(context).getBoolean("read_only", false); - } - - public static boolean getIgnoreBackButton(Context context) { - return getPrefs(context).getBoolean("ignore_back_button", false); - } - - public static boolean getSplitText(Context context) { - return getPrefs(context).getBoolean("page_system_active", true); - } - - public static boolean hasDonated(Context context) { - return getPrefs(context).getBoolean("has_donated", false); - } - // Setter methods - - public static void setUseMonospace(Context context, boolean value) { - getEditor(context).putBoolean("use_monospace", value).commit(); - } - - public static void setLineNumbers(Context context, boolean value) { - getEditor(context).putBoolean("editor_line_numbers", value).commit(); - } - - public static void setSyntaxHighlight(Context context, boolean value) { - getEditor(context).putBoolean("editor_syntax_highlight", value).commit(); - } - - public static void setWrapContent(Context context, boolean value) { - getEditor(context).putBoolean("editor_wrap_content", value).commit(); - } - - public static void setAutoencoding(Context context, boolean value) { - getEditor(context).putBoolean("autoencoding", value).commit(); - } - - public static void setFontSize(Context context, int value) { - getEditor(context).putInt("font_size", value).commit(); - } - - public static void setWorkingFolder(Context context, String value) { - getEditor(context).putString("working_folder", value).commit(); - } - - public static void setSavedPaths(Context context, StringBuilder stringBuilder) { - getEditor(context).putString("savedPaths", stringBuilder.toString()).commit(); - } - - public static void setPageSystemButtonsPopupShown(Context context, boolean value) { - getEditor(context).putBoolean("page_system_button_popup_shown", value).commit(); - } - - public static void setReadOnly(Context context, boolean value) { - getEditor(context).putBoolean("read_only", value).commit(); - } - - public static void setHasDonated(Context context, boolean value) { - getEditor(context).putBoolean("has_donated", value).commit(); - } - - public static void setLightTheme(Context context, boolean value) { - getEditor(context).putBoolean("light_theme", value).commit(); - } - - public static void setSuggestionsActive(Context context, boolean value) { - getEditor(context).putBoolean("suggestion_active", value).commit(); - } - - public static void setAutoSave(Context context, boolean value) { - getEditor(context).putBoolean("auto_save", value).commit(); - } - - public static void setIgnoreBackButton(Context context, boolean value) { - getEditor(context).putBoolean("ignore_back_button", value).commit(); - } - - public static void setSplitText(Context context, boolean value) { - getEditor(context).putBoolean("page_system_active", value).commit(); - } - - public static void setSendErrorReport(Context context, boolean value) { - getEditor(context).putBoolean("ignore_back_button", value).commit(); - } - - public static void setEncoding(Context context, String value) { - getEditor(context).putString("editor_encoding", value).commit(); - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.preferences; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Environment; +import android.preference.PreferenceManager; + +public final class PreferenceHelper { + + public static final String SD_CARD_ROOT = Environment.getExternalStorageDirectory().getAbsolutePath(); + + private PreferenceHelper() { + } + + // Getter Methods + + private static SharedPreferences getPrefs(Context context) { + return PreferenceManager.getDefaultSharedPreferences(context); + } + + private static SharedPreferences.Editor getEditor(Context context) { + return getPrefs(context).edit(); + } + + public static boolean getUseMonospace(Context context) { + return getPrefs(context).getBoolean("use_monospace", false); + } + + public static boolean getLineNumbers(Context context) { + return getPrefs(context).getBoolean("editor_line_numbers", true); + } + + public static boolean getSyntaxHighlight(Context context) { + return getPrefs(context).getBoolean("editor_syntax_highlight", false); + } + + public static boolean getWrapContent(Context context) { + return getPrefs(context).getBoolean("editor_wrap_content", true); + } + + public static boolean getLightTheme(Context context) { + return getPrefs(context).getBoolean("light_theme", false); + } + + public static boolean getSuggestionActive(Context context) { + return getPrefs(context).getBoolean("suggestion_active", false); + } + + public static boolean getAutoEncoding(Context context) { + return getPrefs(context).getBoolean("autoencoding", true); + } + + public static boolean getSendErrorReports(Context context) { + return getPrefs(context).getBoolean("send_error_reports", true); + } + + public static String getEncoding(Context context) { + return getPrefs(context).getString("editor_encoding", "UTF-8"); + } + + public static int getFontSize(Context context) { + return getPrefs(context).getInt("font_size", 16); + } + + public static String getWorkingFolder(Context context) { + return getPrefs(context).getString("working_folder", SD_CARD_ROOT); + } + + public static String[] getSavedPaths(Context context) { + return getPrefs(context).getString("savedPaths", "").split(","); + } + + public static boolean getPageSystemButtonsPopupShown(Context context) { + return getPrefs(context).getBoolean("page_system_button_popup_shown", false); + } + + public static boolean getAutoSave(Context context) { + return getPrefs(context).getBoolean("auto_save", false); + } + + public static boolean getReadOnly(Context context) { + return getPrefs(context).getBoolean("read_only", false); + } + + public static boolean getIgnoreBackButton(Context context) { + return getPrefs(context).getBoolean("ignore_back_button", false); + } + + public static boolean getSplitText(Context context) { + return getPrefs(context).getBoolean("page_system_active", true); + } + + public static boolean hasDonated(Context context) { + return getPrefs(context).getBoolean("has_donated", false); + } + // Setter methods + + public static void setUseMonospace(Context context, boolean value) { + getEditor(context).putBoolean("use_monospace", value).commit(); + } + + public static void setLineNumbers(Context context, boolean value) { + getEditor(context).putBoolean("editor_line_numbers", value).commit(); + } + + public static void setSyntaxHighlight(Context context, boolean value) { + getEditor(context).putBoolean("editor_syntax_highlight", value).commit(); + } + + public static void setWrapContent(Context context, boolean value) { + getEditor(context).putBoolean("editor_wrap_content", value).commit(); + } + + public static void setAutoencoding(Context context, boolean value) { + getEditor(context).putBoolean("autoencoding", value).commit(); + } + + public static void setFontSize(Context context, int value) { + getEditor(context).putInt("font_size", value).commit(); + } + + public static void setWorkingFolder(Context context, String value) { + getEditor(context).putString("working_folder", value).commit(); + } + + public static void setSavedPaths(Context context, StringBuilder stringBuilder) { + getEditor(context).putString("savedPaths", stringBuilder.toString()).commit(); + } + + public static void setPageSystemButtonsPopupShown(Context context, boolean value) { + getEditor(context).putBoolean("page_system_button_popup_shown", value).commit(); + } + + public static void setReadOnly(Context context, boolean value) { + getEditor(context).putBoolean("read_only", value).commit(); + } + + public static void setHasDonated(Context context, boolean value) { + getEditor(context).putBoolean("has_donated", value).commit(); + } + + public static void setLightTheme(Context context, boolean value) { + getEditor(context).putBoolean("light_theme", value).commit(); + } + + public static void setSuggestionsActive(Context context, boolean value) { + getEditor(context).putBoolean("suggestion_active", value).commit(); + } + + public static void setAutoSave(Context context, boolean value) { + getEditor(context).putBoolean("auto_save", value).commit(); + } + + public static void setIgnoreBackButton(Context context, boolean value) { + getEditor(context).putBoolean("ignore_back_button", value).commit(); + } + + public static void setSplitText(Context context, boolean value) { + getEditor(context).putBoolean("page_system_active", value).commit(); + } + + public static void setSendErrorReport(Context context, boolean value) { + getEditor(context).putBoolean("ignore_back_button", value).commit(); + } + + public static void setEncoding(Context context, String value) { + getEditor(context).putString("editor_encoding", value).commit(); + } +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/preferences/SettingsFragment.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/preferences/SettingsFragment.java index 8a51d4c..5db0fc2 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/preferences/SettingsFragment.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/preferences/SettingsFragment.java @@ -1,269 +1,269 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.preferences; - -import android.app.Fragment; -import android.os.Bundle; -import android.support.v7.widget.SwitchCompat; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.CompoundButton; -import android.widget.TextView; - -import sharedcode.turboeditor.R; -import sharedcode.turboeditor.activity.MainActivity; -import sharedcode.turboeditor.dialogfragment.EncodingDialog; -import sharedcode.turboeditor.dialogfragment.NumberPickerDialog; -import sharedcode.turboeditor.util.ProCheckUtils; -import sharedcode.turboeditor.util.ViewUtils; -import sharedcode.turboeditor.views.DialogHelper; - -import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged; -import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.ENCODING; -import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.FONT_SIZE; -import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.LINE_NUMERS; -import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.MONOSPACE; -import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.READ_ONLY; -import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.SYNTAX; -import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.TEXT_SUGGESTIONS; -import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.THEME_CHANGE; -import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.WRAP_CONTENT; - -public class SettingsFragment extends Fragment implements NumberPickerDialog.INumberPickerDialog, EncodingDialog.DialogListener { - - // Editor Variables - private boolean sLineNumbers; - private boolean sColorSyntax; - private boolean sWrapContent; - private boolean sUseMonospace; - private boolean sReadOnly; - - private boolean sLightTheme; - private boolean sSuggestions; - private boolean sAutoSave; - private boolean sIgnoreBackButton; - private boolean sSplitText; - private boolean sErrorReports; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - sUseMonospace = PreferenceHelper.getUseMonospace(getActivity()); - sColorSyntax = PreferenceHelper.getSyntaxHighlight(getActivity()); - sWrapContent = PreferenceHelper.getWrapContent(getActivity()); - sLineNumbers = PreferenceHelper.getLineNumbers(getActivity()); - sReadOnly = PreferenceHelper.getReadOnly(getActivity()); - - sLightTheme = PreferenceHelper.getLightTheme(getActivity()); - sSuggestions = PreferenceHelper.getSuggestionActive(getActivity()); - sAutoSave = PreferenceHelper.getAutoSave(getActivity()); - sIgnoreBackButton = PreferenceHelper.getIgnoreBackButton(getActivity()); - sSplitText = PreferenceHelper.getSplitText(getActivity()); - sErrorReports = PreferenceHelper.getSendErrorReports(getActivity()); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - // Our custom layout - final View rootView = inflater.inflate(R.layout.fragment_settings, container, false); - final SwitchCompat swLineNumbers, swSyntax, swWrapContent, swMonospace, swReadOnly; - final SwitchCompat swLightTheme, swSuggestions, swAutoSave, swIgnoreBackButton, swSplitText, swErrorReports; - - swLineNumbers = (SwitchCompat) rootView.findViewById(R.id.switch_line_numbers); - swSyntax = (SwitchCompat) rootView.findViewById(R.id.switch_syntax); - swWrapContent = (SwitchCompat) rootView.findViewById(R.id.switch_wrap_content); - swMonospace = (SwitchCompat) rootView.findViewById(R.id.switch_monospace); - swReadOnly = (SwitchCompat) rootView.findViewById(R.id.switch_read_only); - - swLightTheme = (SwitchCompat) rootView.findViewById(R.id.switch_light_theme); - swSuggestions = (SwitchCompat) rootView.findViewById(R.id.switch_suggestions_active); - swAutoSave = (SwitchCompat) rootView.findViewById(R.id.switch_auto_save); - swIgnoreBackButton = (SwitchCompat) rootView.findViewById(R.id.switch_ignore_backbutton); - swSplitText = (SwitchCompat) rootView.findViewById(R.id.switch_page_system); - swErrorReports = (SwitchCompat) rootView.findViewById(R.id.switch_send_error_reports); - - swLineNumbers.setChecked(sLineNumbers); - swSyntax.setChecked(sColorSyntax); - swWrapContent.setChecked(sWrapContent); - swMonospace.setChecked(sUseMonospace); - swReadOnly.setChecked(sReadOnly); - - swLightTheme.setChecked(sLightTheme); - swSuggestions.setChecked(sSuggestions); - swAutoSave.setChecked(sAutoSave); - swIgnoreBackButton.setChecked(sIgnoreBackButton); - swSplitText.setChecked(sSplitText); - swErrorReports.setChecked(sErrorReports); - - TextView fontSizeView, encodingView, donateView, extraOptionsView; - fontSizeView = (TextView) rootView.findViewById(R.id.drawer_button_font_size); - encodingView = (TextView) rootView.findViewById(R.id.drawer_button_encoding); - extraOptionsView = (TextView) rootView.findViewById(R.id.drawer_button_extra_options); - donateView = (TextView) rootView.findViewById(R.id.drawer_button_go_pro); - - if(ProCheckUtils.isPro(getActivity(), false)) - ViewUtils.setVisible(donateView, false); - - swLineNumbers.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - PreferenceHelper.setLineNumbers(getActivity(), isChecked); - ((MainActivity) getActivity()).onEvent(new APreferenceValueWasChanged(LINE_NUMERS)); - } - }); - - swSyntax.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - sColorSyntax = isChecked; - PreferenceHelper.setSyntaxHighlight(getActivity(), isChecked); - ((MainActivity) getActivity()).onEvent(new APreferenceValueWasChanged(SYNTAX)); - - } - }); - - swWrapContent.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - PreferenceHelper.setWrapContent(getActivity(), isChecked); - ((MainActivity) getActivity()).onEvent(new APreferenceValueWasChanged(WRAP_CONTENT)); - } - }); - - swMonospace.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - sUseMonospace = isChecked; - PreferenceHelper.setUseMonospace(getActivity(), isChecked); - ((MainActivity) getActivity()).onEvent(new APreferenceValueWasChanged(MONOSPACE)); - - } - }); - - swReadOnly.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - PreferenceHelper.setReadOnly(getActivity(), isChecked); - ((MainActivity) getActivity()).onEvent(new APreferenceValueWasChanged(READ_ONLY)); - } - }); - - fontSizeView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - int fontMax = 36; - int fontCurrent = //(int) (mEditor.getTextSize() / scaledDensity); - //fontMax / 2; - PreferenceHelper.getFontSize(getActivity()); - NumberPickerDialog dialogFrag = NumberPickerDialog.newInstance(NumberPickerDialog - .Actions - .FontSize, 1, fontCurrent, fontMax); - dialogFrag.setTargetFragment(SettingsFragment.this, 0); - dialogFrag.show(getFragmentManager().beginTransaction(), "dialog"); - } - }); - - encodingView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - EncodingDialog dialogFrag = EncodingDialog.newInstance(); - dialogFrag.setTargetFragment(SettingsFragment.this, 0); - dialogFrag.show(getFragmentManager().beginTransaction(), "dialog"); - } - }); - - extraOptionsView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - View otherOptions = rootView.findViewById(R.id.other_options); - boolean isVisible = otherOptions.getVisibility() == View.VISIBLE; - ViewUtils.setVisible(otherOptions, !isVisible); - } - }); - - donateView.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - DialogHelper.showDonateDialog(getActivity()); - } - }); - - swLightTheme.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - PreferenceHelper.setLightTheme(getActivity(), isChecked); - ((MainActivity) getActivity()).onEvent(new APreferenceValueWasChanged(THEME_CHANGE)); - } - }); - - swSuggestions.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - PreferenceHelper.setSuggestionsActive(getActivity(), isChecked); - ((MainActivity) getActivity()).onEvent(new APreferenceValueWasChanged(TEXT_SUGGESTIONS)); - } - }); - - swAutoSave.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - PreferenceHelper.setAutoSave(getActivity(), isChecked); - } - }); - - swIgnoreBackButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - PreferenceHelper.setIgnoreBackButton(getActivity(), isChecked); - } - }); - - swSplitText.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - PreferenceHelper.setSplitText(getActivity(), isChecked); - } - }); - - swErrorReports.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - PreferenceHelper.setSendErrorReport(getActivity(), isChecked); - } - }); - - return rootView; - } - - @Override - public void onNumberPickerDialogDismissed(NumberPickerDialog.Actions action, int value) { - PreferenceHelper.setFontSize(getActivity(), value); - ((MainActivity) getActivity()).onEvent(new APreferenceValueWasChanged(FONT_SIZE)); - - } - - @Override - public void onEncodingSelected(String result) { - PreferenceHelper.setEncoding(getActivity(), result); - ((MainActivity) getActivity()).onEvent(new APreferenceValueWasChanged(ENCODING)); - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.preferences; + +import android.app.Fragment; +import android.os.Bundle; +import android.support.v7.widget.SwitchCompat; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CompoundButton; +import android.widget.TextView; + +import sharedcode.turboeditor.R; +import sharedcode.turboeditor.activity.MainActivity; +import sharedcode.turboeditor.dialogfragment.EncodingDialog; +import sharedcode.turboeditor.dialogfragment.NumberPickerDialog; +import sharedcode.turboeditor.util.ProCheckUtils; +import sharedcode.turboeditor.util.ViewUtils; +import sharedcode.turboeditor.views.DialogHelper; + +import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged; +import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.ENCODING; +import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.FONT_SIZE; +import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.LINE_NUMERS; +import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.MONOSPACE; +import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.READ_ONLY; +import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.SYNTAX; +import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.TEXT_SUGGESTIONS; +import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.THEME_CHANGE; +import static sharedcode.turboeditor.util.EventBusEvents.APreferenceValueWasChanged.Type.WRAP_CONTENT; + +public class SettingsFragment extends Fragment implements NumberPickerDialog.INumberPickerDialog, EncodingDialog.DialogListener { + + // Editor Variables + private boolean sLineNumbers; + private boolean sColorSyntax; + private boolean sWrapContent; + private boolean sUseMonospace; + private boolean sReadOnly; + + private boolean sLightTheme; + private boolean sSuggestions; + private boolean sAutoSave; + private boolean sIgnoreBackButton; + private boolean sSplitText; + private boolean sErrorReports; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + sUseMonospace = PreferenceHelper.getUseMonospace(getActivity()); + sColorSyntax = PreferenceHelper.getSyntaxHighlight(getActivity()); + sWrapContent = PreferenceHelper.getWrapContent(getActivity()); + sLineNumbers = PreferenceHelper.getLineNumbers(getActivity()); + sReadOnly = PreferenceHelper.getReadOnly(getActivity()); + + sLightTheme = PreferenceHelper.getLightTheme(getActivity()); + sSuggestions = PreferenceHelper.getSuggestionActive(getActivity()); + sAutoSave = PreferenceHelper.getAutoSave(getActivity()); + sIgnoreBackButton = PreferenceHelper.getIgnoreBackButton(getActivity()); + sSplitText = PreferenceHelper.getSplitText(getActivity()); + sErrorReports = PreferenceHelper.getSendErrorReports(getActivity()); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Our custom layout + final View rootView = inflater.inflate(R.layout.fragment_settings, container, false); + final SwitchCompat swLineNumbers, swSyntax, swWrapContent, swMonospace, swReadOnly; + final SwitchCompat swLightTheme, swSuggestions, swAutoSave, swIgnoreBackButton, swSplitText, swErrorReports; + + swLineNumbers = (SwitchCompat) rootView.findViewById(R.id.switch_line_numbers); + swSyntax = (SwitchCompat) rootView.findViewById(R.id.switch_syntax); + swWrapContent = (SwitchCompat) rootView.findViewById(R.id.switch_wrap_content); + swMonospace = (SwitchCompat) rootView.findViewById(R.id.switch_monospace); + swReadOnly = (SwitchCompat) rootView.findViewById(R.id.switch_read_only); + + swLightTheme = (SwitchCompat) rootView.findViewById(R.id.switch_light_theme); + swSuggestions = (SwitchCompat) rootView.findViewById(R.id.switch_suggestions_active); + swAutoSave = (SwitchCompat) rootView.findViewById(R.id.switch_auto_save); + swIgnoreBackButton = (SwitchCompat) rootView.findViewById(R.id.switch_ignore_backbutton); + swSplitText = (SwitchCompat) rootView.findViewById(R.id.switch_page_system); + swErrorReports = (SwitchCompat) rootView.findViewById(R.id.switch_send_error_reports); + + swLineNumbers.setChecked(sLineNumbers); + swSyntax.setChecked(sColorSyntax); + swWrapContent.setChecked(sWrapContent); + swMonospace.setChecked(sUseMonospace); + swReadOnly.setChecked(sReadOnly); + + swLightTheme.setChecked(sLightTheme); + swSuggestions.setChecked(sSuggestions); + swAutoSave.setChecked(sAutoSave); + swIgnoreBackButton.setChecked(sIgnoreBackButton); + swSplitText.setChecked(sSplitText); + swErrorReports.setChecked(sErrorReports); + + TextView fontSizeView, encodingView, donateView, extraOptionsView; + fontSizeView = (TextView) rootView.findViewById(R.id.drawer_button_font_size); + encodingView = (TextView) rootView.findViewById(R.id.drawer_button_encoding); + extraOptionsView = (TextView) rootView.findViewById(R.id.drawer_button_extra_options); + donateView = (TextView) rootView.findViewById(R.id.drawer_button_go_pro); + + if(ProCheckUtils.isPro(getActivity(), false)) + ViewUtils.setVisible(donateView, false); + + swLineNumbers.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + PreferenceHelper.setLineNumbers(getActivity(), isChecked); + ((MainActivity) getActivity()).onEvent(new APreferenceValueWasChanged(LINE_NUMERS)); + } + }); + + swSyntax.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + sColorSyntax = isChecked; + PreferenceHelper.setSyntaxHighlight(getActivity(), isChecked); + ((MainActivity) getActivity()).onEvent(new APreferenceValueWasChanged(SYNTAX)); + + } + }); + + swWrapContent.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + PreferenceHelper.setWrapContent(getActivity(), isChecked); + ((MainActivity) getActivity()).onEvent(new APreferenceValueWasChanged(WRAP_CONTENT)); + } + }); + + swMonospace.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + sUseMonospace = isChecked; + PreferenceHelper.setUseMonospace(getActivity(), isChecked); + ((MainActivity) getActivity()).onEvent(new APreferenceValueWasChanged(MONOSPACE)); + + } + }); + + swReadOnly.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + PreferenceHelper.setReadOnly(getActivity(), isChecked); + ((MainActivity) getActivity()).onEvent(new APreferenceValueWasChanged(READ_ONLY)); + } + }); + + fontSizeView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + int fontMax = 36; + int fontCurrent = //(int) (mEditor.getTextSize() / scaledDensity); + //fontMax / 2; + PreferenceHelper.getFontSize(getActivity()); + NumberPickerDialog dialogFrag = NumberPickerDialog.newInstance(NumberPickerDialog + .Actions + .FontSize, 1, fontCurrent, fontMax); + dialogFrag.setTargetFragment(SettingsFragment.this, 0); + dialogFrag.show(getFragmentManager().beginTransaction(), "dialog"); + } + }); + + encodingView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + EncodingDialog dialogFrag = EncodingDialog.newInstance(); + dialogFrag.setTargetFragment(SettingsFragment.this, 0); + dialogFrag.show(getFragmentManager().beginTransaction(), "dialog"); + } + }); + + extraOptionsView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + View otherOptions = rootView.findViewById(R.id.other_options); + boolean isVisible = otherOptions.getVisibility() == View.VISIBLE; + ViewUtils.setVisible(otherOptions, !isVisible); + } + }); + + donateView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + DialogHelper.showDonateDialog(getActivity()); + } + }); + + swLightTheme.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + PreferenceHelper.setLightTheme(getActivity(), isChecked); + ((MainActivity) getActivity()).onEvent(new APreferenceValueWasChanged(THEME_CHANGE)); + } + }); + + swSuggestions.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + PreferenceHelper.setSuggestionsActive(getActivity(), isChecked); + ((MainActivity) getActivity()).onEvent(new APreferenceValueWasChanged(TEXT_SUGGESTIONS)); + } + }); + + swAutoSave.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + PreferenceHelper.setAutoSave(getActivity(), isChecked); + } + }); + + swIgnoreBackButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + PreferenceHelper.setIgnoreBackButton(getActivity(), isChecked); + } + }); + + swSplitText.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + PreferenceHelper.setSplitText(getActivity(), isChecked); + } + }); + + swErrorReports.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + PreferenceHelper.setSendErrorReport(getActivity(), isChecked); + } + }); + + return rootView; + } + + @Override + public void onNumberPickerDialogDismissed(NumberPickerDialog.Actions action, int value) { + PreferenceHelper.setFontSize(getActivity(), value); + ((MainActivity) getActivity()).onEvent(new APreferenceValueWasChanged(FONT_SIZE)); + + } + + @Override + public void onEncodingSelected(String result) { + PreferenceHelper.setEncoding(getActivity(), result); + ((MainActivity) getActivity()).onEvent(new APreferenceValueWasChanged(ENCODING)); + } +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/root/LinuxShell.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/root/LinuxShell.java index 82e2e7c..8162dd2 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/root/LinuxShell.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/root/LinuxShell.java @@ -1,77 +1,77 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.root; - -import android.util.Log; - -import java.io.BufferedReader; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class LinuxShell { - - private static final String UNIX_ESCAPE_EXPRESSION = "(\\(|\\)|\\[|\\]|\\s|\'|\"|`|\\{|\\}|&|\\\\|\\?)"; - - public static String getCommandLineString(String input) { - return input.replaceAll(UNIX_ESCAPE_EXPRESSION, "\\\\$1"); - } - - public static BufferedReader execute(String cmd) { - BufferedReader reader = null; - try { - Process process = Runtime.getRuntime().exec("su"); - DataOutputStream os = new DataOutputStream( - process.getOutputStream()); - os.writeBytes(cmd + "\n"); - os.writeBytes("exit\n"); - reader = new BufferedReader(new InputStreamReader( - process.getInputStream())); - String err = (new BufferedReader(new InputStreamReader( - process.getErrorStream()))).readLine(); - os.flush(); - - if (process.waitFor() != 0 || (!"".equals(err) && null != err) - && containsIllegals(err) != true) { - Log.e("Root Error, cmd: " + cmd, err); - return null; - } - return reader; - - } catch (IOException e) { - e.printStackTrace(); - } catch (InterruptedException e) { - e.printStackTrace(); - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } - - public static boolean containsIllegals(String toExamine) { - // checks for "+" sign so the program doesn't throw an error when its - // not erroring. - Pattern pattern = Pattern.compile("[+]"); - Matcher matcher = pattern.matcher(toExamine); - return matcher.find(); - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.root; + +import android.util.Log; + +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class LinuxShell { + + private static final String UNIX_ESCAPE_EXPRESSION = "(\\(|\\)|\\[|\\]|\\s|\'|\"|`|\\{|\\}|&|\\\\|\\?)"; + + public static String getCommandLineString(String input) { + return input.replaceAll(UNIX_ESCAPE_EXPRESSION, "\\\\$1"); + } + + public static BufferedReader execute(String cmd) { + BufferedReader reader = null; + try { + Process process = Runtime.getRuntime().exec("su"); + DataOutputStream os = new DataOutputStream( + process.getOutputStream()); + os.writeBytes(cmd + "\n"); + os.writeBytes("exit\n"); + reader = new BufferedReader(new InputStreamReader( + process.getInputStream())); + String err = (new BufferedReader(new InputStreamReader( + process.getErrorStream()))).readLine(); + os.flush(); + + if (process.waitFor() != 0 || (!"".equals(err) && null != err) + && containsIllegals(err) != true) { + Log.e("Root Error, cmd: " + cmd, err); + return null; + } + return reader; + + } catch (IOException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + public static boolean containsIllegals(String toExamine) { + // checks for "+" sign so the program doesn't throw an error when its + // not erroring. + Pattern pattern = Pattern.compile("[+]"); + Matcher matcher = pattern.matcher(toExamine); + return matcher.find(); + } +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/root/RootUtils.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/root/RootUtils.java index 7ed8115..d0c30bf 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/root/RootUtils.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/root/RootUtils.java @@ -1,101 +1,101 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 . - */ - -/** - * 920 Text Editor 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. - * - * 920 Text Editor 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 920 Text Editor. If not, see . - */ - -package sharedcode.turboeditor.root; - -import android.content.Context; - -import org.apache.commons.io.FileUtils; -import org.sufficientlysecure.rootcommands.Shell; -import org.sufficientlysecure.rootcommands.Toolbox; - -import java.io.BufferedReader; -import java.io.File; -import java.util.ArrayList; -import java.util.Collections; - - -public class RootUtils { - - public static void writeFile(Context context, String path, String text, String encoding, boolean isRoot) throws Exception { - File file = new File(path); - if (!file.canWrite() && isRoot) { - File appFolder = context.getFilesDir(); - File tempFile = new File(appFolder, "temp.root.file"); - if (!tempFile.exists()) - tempFile.createNewFile(); - FileUtils.write(tempFile, text, encoding); - Shell shell = Shell.startRootShell(); - Toolbox tb = new Toolbox(shell); - String mount = tb.getFilePermissions(path); - tb.copyFile(tempFile.getAbsolutePath(), path, true, false); - tb.setFilePermissions(path, mount); - tempFile.delete(); - } else { - FileUtils.write(file, - text, - encoding); - } - } - - public static ArrayList getFileList(String path, boolean runAtRoot) { - ArrayList filesList = new ArrayList(); - if (runAtRoot == false) { - File base = new File(path); - File[] files = base.listFiles(); - if (files == null) - return null; - Collections.addAll(filesList, files); - } else { - BufferedReader reader = null; //errReader = null; - try { - LinuxShell.execute("ls -a " + LinuxShell.getCommandLineString(path)); - if (reader == null) - return null; - - File f; - String line; - while ((line = reader.readLine()) != null) { - f = new File(line.substring(2)); - filesList.add(f); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - return filesList; - } - +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 . + */ + +/** + * 920 Text Editor 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. + * + * 920 Text Editor 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 920 Text Editor. If not, see . + */ + +package sharedcode.turboeditor.root; + +import android.content.Context; + +import org.apache.commons.io.FileUtils; +import org.sufficientlysecure.rootcommands.Shell; +import org.sufficientlysecure.rootcommands.Toolbox; + +import java.io.BufferedReader; +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; + + +public class RootUtils { + + public static void writeFile(Context context, String path, String text, String encoding, boolean isRoot) throws Exception { + File file = new File(path); + if (!file.canWrite() && isRoot) { + File appFolder = context.getFilesDir(); + File tempFile = new File(appFolder, "temp.root.file"); + if (!tempFile.exists()) + tempFile.createNewFile(); + FileUtils.write(tempFile, text, encoding); + Shell shell = Shell.startRootShell(); + Toolbox tb = new Toolbox(shell); + String mount = tb.getFilePermissions(path); + tb.copyFile(tempFile.getAbsolutePath(), path, true, false); + tb.setFilePermissions(path, mount); + tempFile.delete(); + } else { + FileUtils.write(file, + text, + encoding); + } + } + + public static ArrayList getFileList(String path, boolean runAtRoot) { + ArrayList filesList = new ArrayList(); + if (runAtRoot == false) { + File base = new File(path); + File[] files = base.listFiles(); + if (files == null) + return null; + Collections.addAll(filesList, files); + } else { + BufferedReader reader = null; //errReader = null; + try { + LinuxShell.execute("ls -a " + LinuxShell.getCommandLineString(path)); + if (reader == null) + return null; + + File f; + String line; + while ((line = reader.readLine()) != null) { + f = new File(line.substring(2)); + filesList.add(f); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + return filesList; + } + } \ No newline at end of file diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/task/SaveFileTask.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/task/SaveFileTask.java index cc6284d..69b8bb2 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/task/SaveFileTask.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/task/SaveFileTask.java @@ -1,106 +1,106 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.task; - -import android.os.AsyncTask; -import android.widget.Toast; - -import org.sufficientlysecure.rootcommands.Shell; -import org.sufficientlysecure.rootcommands.Toolbox; - -import java.io.File; -import java.io.IOException; -import java.util.concurrent.TimeoutException; - -import sharedcode.turboeditor.R; -import sharedcode.turboeditor.activity.MainActivity; -import sharedcode.turboeditor.root.RootUtils; -import sharedcode.turboeditor.util.EventBusEvents; - -public class SaveFileTask extends AsyncTask { - - private final MainActivity activity; - private final String filePath; - private final String text; - private final String encoding; - private File file; - private String message; - private String positiveMessage; - - public SaveFileTask(MainActivity activity, String filePath, String text, String encoding) { - this.activity = activity; - this.filePath = filePath; - this.text = text; - this.encoding = encoding; - } - - @Override - protected void onPreExecute() { - super.onPreExecute(); - file = new File(filePath); - positiveMessage = String.format(activity.getString(R.string.file_saved_with_success), file.getName()); - } - - /** - * {@inheritDoc} - */ - @Override - protected Void doInBackground(final Void... voids) { - - try { - - if (!file.exists()) { - file.getParentFile().mkdirs(); - file.createNewFile(); - } - - boolean isRoot = false; - if (!file.canWrite()) { - try { - Shell shell = null; - shell = Shell.startRootShell(); - Toolbox tb = new Toolbox(shell); - isRoot = tb.isRootAccessGiven(); - } catch (IOException | TimeoutException e) { - e.printStackTrace(); - isRoot = false; - } - } - - RootUtils.writeFile(activity, file.getAbsolutePath(), text, encoding, isRoot); - - message = positiveMessage; - } catch (Exception e) { - message = e.getMessage(); - } - return null; - } - - /** - * {@inheritDoc} - */ - @Override - protected void onPostExecute(final Void aVoid) { - super.onPostExecute(aVoid); - Toast.makeText(activity, message, Toast.LENGTH_LONG).show(); - if (message.equals(positiveMessage)) - activity.onEvent(new EventBusEvents.SavedAFile(filePath)); - } +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.task; + +import android.os.AsyncTask; +import android.widget.Toast; + +import org.sufficientlysecure.rootcommands.Shell; +import org.sufficientlysecure.rootcommands.Toolbox; + +import java.io.File; +import java.io.IOException; +import java.util.concurrent.TimeoutException; + +import sharedcode.turboeditor.R; +import sharedcode.turboeditor.activity.MainActivity; +import sharedcode.turboeditor.root.RootUtils; +import sharedcode.turboeditor.util.EventBusEvents; + +public class SaveFileTask extends AsyncTask { + + private final MainActivity activity; + private final String filePath; + private final String text; + private final String encoding; + private File file; + private String message; + private String positiveMessage; + + public SaveFileTask(MainActivity activity, String filePath, String text, String encoding) { + this.activity = activity; + this.filePath = filePath; + this.text = text; + this.encoding = encoding; + } + + @Override + protected void onPreExecute() { + super.onPreExecute(); + file = new File(filePath); + positiveMessage = String.format(activity.getString(R.string.file_saved_with_success), file.getName()); + } + + /** + * {@inheritDoc} + */ + @Override + protected Void doInBackground(final Void... voids) { + + try { + + if (!file.exists()) { + file.getParentFile().mkdirs(); + file.createNewFile(); + } + + boolean isRoot = false; + if (!file.canWrite()) { + try { + Shell shell = null; + shell = Shell.startRootShell(); + Toolbox tb = new Toolbox(shell); + isRoot = tb.isRootAccessGiven(); + } catch (IOException | TimeoutException e) { + e.printStackTrace(); + isRoot = false; + } + } + + RootUtils.writeFile(activity, file.getAbsolutePath(), text, encoding, isRoot); + + message = positiveMessage; + } catch (Exception e) { + message = e.getMessage(); + } + return null; + } + + /** + * {@inheritDoc} + */ + @Override + protected void onPostExecute(final Void aVoid) { + super.onPostExecute(aVoid); + Toast.makeText(activity, message, Toast.LENGTH_LONG).show(); + if (message.equals(positiveMessage)) + activity.onEvent(new EventBusEvents.SavedAFile(filePath)); + } } \ No newline at end of file diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/texteditor/EditTextPadding.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/texteditor/EditTextPadding.java index b378e7e..b5de286 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/texteditor/EditTextPadding.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/texteditor/EditTextPadding.java @@ -1,39 +1,39 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.texteditor; - -import android.content.Context; - -import sharedcode.turboeditor.util.PixelDipConverter; - -public class EditTextPadding { - - public static int getPaddingWithoutLineNumbers(Context context) { - return (int) PixelDipConverter.convertDpToPixel(5, context); - } - - public static int getPaddingWithLineNumbers(Context context, float fontSize) { - return (int) PixelDipConverter.convertDpToPixel(fontSize * 2f, context); - } - - public static int getPaddingTop(Context context) { - return getPaddingWithoutLineNumbers(context); - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.texteditor; + +import android.content.Context; + +import sharedcode.turboeditor.util.PixelDipConverter; + +public class EditTextPadding { + + public static int getPaddingWithoutLineNumbers(Context context) { + return (int) PixelDipConverter.convertDpToPixel(5, context); + } + + public static int getPaddingWithLineNumbers(Context context, float fontSize) { + return (int) PixelDipConverter.convertDpToPixel(fontSize * 2f, context); + } + + public static int getPaddingTop(Context context) { + return getPaddingWithoutLineNumbers(context); + } +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/texteditor/FileUtils.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/texteditor/FileUtils.java index 5ad2a3e..54b3ccd 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/texteditor/FileUtils.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/texteditor/FileUtils.java @@ -1,58 +1,58 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.texteditor; - -import org.mozilla.universalchardet.UniversalDetector; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.Charset; - -public class FileUtils { - public static String getDetectedEncoding(File file) { - InputStream is = null; - String encoding = null; - try { - is = new FileInputStream(file); - UniversalDetector detector = new UniversalDetector(null); - byte[] buf = new byte[4096]; - int nread; - while ((nread = is.read(buf)) > 0 && !detector.isDone()) { - detector.handleData(buf, 0, nread); - } - detector.dataEnd(); - encoding = detector.getDetectedCharset(); - } catch (IOException e) { - // nothing to do - } finally { - try { - is.close(); - } catch (IOException e) { - e.printStackTrace(); - } - if (encoding == null) { - return Charset.defaultCharset().name(); - } - } - return encoding; - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.texteditor; + +import org.mozilla.universalchardet.UniversalDetector; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; + +public class FileUtils { + public static String getDetectedEncoding(File file) { + InputStream is = null; + String encoding = null; + try { + is = new FileInputStream(file); + UniversalDetector detector = new UniversalDetector(null); + byte[] buf = new byte[4096]; + int nread; + while ((nread = is.read(buf)) > 0 && !detector.isDone()) { + detector.handleData(buf, 0, nread); + } + detector.dataEnd(); + encoding = detector.getDetectedCharset(); + } catch (IOException e) { + // nothing to do + } finally { + try { + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + if (encoding == null) { + return Charset.defaultCharset().name(); + } + } + return encoding; + } +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/texteditor/LineUtils.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/texteditor/LineUtils.java index 71b4b62..f6505ce 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/texteditor/LineUtils.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/texteditor/LineUtils.java @@ -1,133 +1,136 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.texteditor; - -import android.text.Layout; -import android.text.TextUtils; -import android.widget.ScrollView; - -public class LineUtils { - private boolean[] toCountLinesArray; - private int[] realLines; - - public boolean[] getToCountLinesArray() { - return toCountLinesArray; - } - - public int[] getRealLines() { - return realLines; - } - - public static int getYAtLine(ScrollView scrollView, int lineCount, int line) { - return scrollView.getChildAt(0).getHeight() / lineCount * line; - } - - public static int getFirstVisibleLine(ScrollView scrollView, int childHeight, int lineCount) throws ArithmeticException { - int line = (scrollView.getScrollY() * lineCount) / childHeight; - if (line < 0) line = 0; - return line; - } - - public static int getLastVisibleLine(ScrollView scrollView, int childHeight, int lineCount, int deviceHeight) { - int line = ((scrollView.getScrollY() + deviceHeight) * lineCount) / childHeight; - if (line > lineCount) line = lineCount; - return line; - } - - public void updateHasNewLineArray(int startingLine, int lineCount, Layout layout, String text) { - - boolean[] hasNewLineArray = new boolean[lineCount]; - toCountLinesArray = new boolean[lineCount]; - realLines = new int[lineCount]; - - if(TextUtils.isEmpty(text)) - return; - - int i; - - // for every line on the edittext - for (i = 0; i < lineCount; i++) { - // check if this line contains "\n" - //hasNewLineArray[i] = text.substring(layout.getLineStart(i), layout.getLineEnd(i)).endsWith("\n"); - hasNewLineArray[i] = text.charAt(layout.getLineEnd(i) - 1) == '\n'; - // if true - if (hasNewLineArray[i]) { - int j = i - 1; - while (j > -1 && !hasNewLineArray[j]) { - j--; - } - toCountLinesArray[j + 1] = true; - - } - } - - int realLine = startingLine; // the first line is not 0, is 1. We start counting from 1 - - for (i = 0; i < toCountLinesArray.length; i++) { - if (toCountLinesArray[i]) { - realLine++; - } - realLines[i] = realLine; - } - } - - /** - * Gets the line from the index of the letter in the text - * - * @param index - * @param lineCount - * @param layout - * @return - */ - public static int getLineFromIndex(int index, int lineCount, Layout layout) { - int line; - int currentIndex = 0; - - for (line = 0; line < lineCount; line++) { - currentIndex += layout.getLineEnd(line) - layout.getLineStart(line); - if (currentIndex > index) { - break; - } - } - - return line; - } - - public int firstReadLine() { - return realLines[0]; - } - - public int lastReadLine() { - return realLines[realLines.length - 1]; - } - - public int fakeLineFromRealLine(int realLine) { - int i; - int fakeLine = 0; - for (i = 0; i < realLines.length; i++) { - if (realLine == realLines[i]) { - fakeLine = i; - break; - } - } - return fakeLine; - } - +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.texteditor; + +import android.text.Layout; +import android.text.TextUtils; +import android.widget.ScrollView; + +public class LineUtils { + private boolean[] toCountLinesArray; + private int[] realLines; + + public boolean[] getToCountLinesArray() { + return toCountLinesArray; + } + + public int[] getRealLines() { + return realLines; + } + + public static int getYAtLine(ScrollView scrollView, int lineCount, int line) { + return scrollView.getChildAt(0).getHeight() / lineCount * line; + } + + public static int getFirstVisibleLine(ScrollView scrollView, int childHeight, int lineCount) throws ArithmeticException { + int line = (scrollView.getScrollY() * lineCount) / childHeight; + if (line < 0) line = 0; + return line; + } + + public static int getLastVisibleLine(ScrollView scrollView, int childHeight, int lineCount, int deviceHeight) { + int line = ((scrollView.getScrollY() + deviceHeight) * lineCount) / childHeight; + if (line > lineCount) line = lineCount; + return line; + } + + public void updateHasNewLineArray(int startingLine, int lineCount, Layout layout, String text) { + + boolean[] hasNewLineArray = new boolean[lineCount]; + toCountLinesArray = new boolean[lineCount]; + realLines = new int[lineCount]; + + if(TextUtils.isEmpty(text)) { + toCountLinesArray[0] = false; + realLines[0] = 1; + return; + } + + int i; + + // for every line on the edittext + for (i = 0; i < lineCount; i++) { + // check if this line contains "\n" + //hasNewLineArray[i] = text.substring(layout.getLineStart(i), layout.getLineEnd(i)).endsWith("\n"); + hasNewLineArray[i] = text.charAt(layout.getLineEnd(i) - 1) == '\n'; + // if true + if (hasNewLineArray[i]) { + int j = i - 1; + while (j > -1 && !hasNewLineArray[j]) { + j--; + } + toCountLinesArray[j + 1] = true; + + } + } + + int realLine = startingLine; // the first line is not 0, is 1. We start counting from 1 + + for (i = 0; i < toCountLinesArray.length; i++) { + if (toCountLinesArray[i]) { + realLine++; + } + realLines[i] = realLine; + } + } + + /** + * Gets the line from the index of the letter in the text + * + * @param index + * @param lineCount + * @param layout + * @return + */ + public static int getLineFromIndex(int index, int lineCount, Layout layout) { + int line; + int currentIndex = 0; + + for (line = 0; line < lineCount; line++) { + currentIndex += layout.getLineEnd(line) - layout.getLineStart(line); + if (currentIndex > index) { + break; + } + } + + return line; + } + + public int firstReadLine() { + return realLines[0]; + } + + public int lastReadLine() { + return realLines[realLines.length - 1]; + } + + public int fakeLineFromRealLine(int realLine) { + int i; + int fakeLine = 0; + for (i = 0; i < realLines.length; i++) { + if (realLine == realLines[i]) { + fakeLine = i; + break; + } + } + return fakeLine; + } + } \ No newline at end of file diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/texteditor/PageSystem.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/texteditor/PageSystem.java index 816ac99..6dc47cd 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/texteditor/PageSystem.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/texteditor/PageSystem.java @@ -1,183 +1,185 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.texteditor; - -import android.content.Context; -import android.support.annotation.Nullable; - -import org.apache.commons.io.FileUtils; - -import java.io.File; -import java.util.LinkedList; -import java.util.List; - -import sharedcode.turboeditor.preferences.PreferenceHelper; - -public class PageSystem { - - private List pages; - private int[] startingLines; - private int currentPage = 0; - private PageSystemInterface pageSystemInterface; - - public PageSystem(Context context, PageSystemInterface pageSystemInterface, String text, @Nullable File file) { - - final int charForPage = 15000; - final int MAX_KBs_WITHOUT_PAGE_SYSTEM = 50; - - this.pageSystemInterface = pageSystemInterface; - pages = new LinkedList<>(); - - final boolean dimensionOverLimit; - if(file != null && file.exists() && file.isFile()) - dimensionOverLimit = FileUtils.sizeOf(file) >= MAX_KBs_WITHOUT_PAGE_SYSTEM * FileUtils.ONE_KB; - else - dimensionOverLimit = false; - - int i = 0; - int to; - int nextIndexOfReturn; - final int textLength = text.length(); - boolean pageSystemEnabled = PreferenceHelper.getSplitText(context); - - if (pageSystemEnabled && dimensionOverLimit) { - while (i < textLength) { - to = i + charForPage; - nextIndexOfReturn = text.indexOf("\n", to); - if (nextIndexOfReturn > to) to = nextIndexOfReturn; - if (to > text.length()) to = text.length(); - pages.add(text.substring(i, to)); - i = to + 1; - } - - - if (i == 0) - pages.add(""); - } else { - pages.add(text); - } - - startingLines = new int[pages.size()]; - setStartingLines(); - } - - public int getStartingLine() { - return startingLines[currentPage]; - } - - public String getCurrentPageText() { - return pages.get(currentPage); - } - - public String getTextOfNextPages(boolean includeCurrent, int nOfPages) { - StringBuilder stringBuilder = new StringBuilder(); - int i; - for (i = includeCurrent ? 0 : 1; i < nOfPages; i++) { - if (pages.size() > (currentPage + i)) { - stringBuilder.append(pages.get(currentPage + 1)); - } - } - - return stringBuilder.toString(); - } - - public void savePage(String currentText) { - pages.set(currentPage, currentText); - } - - public void nextPage() { - if (!canReadNextPage()) return; - goToPage(currentPage + 1); - } - - public void prevPage() { - if (!canReadPrevPage()) return; - goToPage(currentPage - 1); - } - - public void goToPage(int page) { - if (page >= pages.size()) page = pages.size() - 1; - if (page < 0) page = 0; - boolean shouldUpdateLines = page > currentPage && canReadNextPage(); - if (shouldUpdateLines) { - String text = getCurrentPageText(); - int nOfNewLineNow = (text.length() - text.replace("\n", "").length()) + 1; // normally the last line is not counted so we have to add 1 - int nOfNewLineBefore = startingLines[currentPage + 1] - startingLines[currentPage]; - int difference = nOfNewLineNow - nOfNewLineBefore; - updateStartingLines(currentPage + 1, difference); - } - currentPage = page; - pageSystemInterface.onPageChanged(page); - } - - public void setStartingLines() { - int i; - int startingLine; - int nOfNewLines; - String text; - startingLines[0] = 0; - for (i = 1; i < pages.size(); i++) { - text = pages.get(i - 1); - nOfNewLines = text.length() - text.replace("\n", "").length() + 1; - startingLine = startingLines[i - 1] + nOfNewLines; - startingLines[i] = startingLine; - } - } - - public void updateStartingLines(int fromPage, int difference) { - if (difference == 0) - return; - int i; - if (fromPage < 1) fromPage = 1; - for (i = fromPage; i < pages.size(); i++) { - startingLines[i] += difference; - } - } - - public int getMaxPage() { - return pages.size() - 1; - } - - public int getCurrentPage() { - return currentPage; - } - - public String getAllText(String currentPageText) { - pages.set(currentPage, currentPageText); - int i; - StringBuilder allText = new StringBuilder(); - for (i = 0; i < pages.size(); i++) { - allText.append(pages.get(i)).append("\n"); - } - return allText.toString(); - } - - public boolean canReadNextPage() { - return currentPage < pages.size() - 1; - } - - public boolean canReadPrevPage() { - return currentPage >= 1; - } - - public interface PageSystemInterface { - void onPageChanged(int page); - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.texteditor; + +import android.content.Context; +import android.support.annotation.Nullable; + +import org.apache.commons.io.FileUtils; + +import java.io.File; +import java.util.LinkedList; +import java.util.List; + +import sharedcode.turboeditor.preferences.PreferenceHelper; + +public class PageSystem { + + private List pages; + private int[] startingLines; + private int currentPage = 0; + private PageSystemInterface pageSystemInterface; + + public PageSystem(Context context, PageSystemInterface pageSystemInterface, String text, @Nullable File file) { + + final int charForPage = 15000; + final int MAX_KBs_WITHOUT_PAGE_SYSTEM = 50; + + this.pageSystemInterface = pageSystemInterface; + pages = new LinkedList<>(); + + final boolean dimensionOverLimit; + if(file != null && file.exists() && file.isFile()) + dimensionOverLimit = FileUtils.sizeOf(file) >= MAX_KBs_WITHOUT_PAGE_SYSTEM * FileUtils.ONE_KB; + else + dimensionOverLimit = false; + + int i = 0; + int to; + int nextIndexOfReturn; + final int textLength = text.length(); + boolean pageSystemEnabled = PreferenceHelper.getSplitText(context); + + if (pageSystemEnabled && dimensionOverLimit) { + while (i < textLength) { + to = i + charForPage; + nextIndexOfReturn = text.indexOf("\n", to); + if (nextIndexOfReturn > to) to = nextIndexOfReturn; + if (to > text.length()) to = text.length(); + pages.add(text.substring(i, to)); + i = to + 1; + } + + + if (i == 0) + pages.add(""); + } else { + pages.add(text); + } + + startingLines = new int[pages.size()]; + setStartingLines(); + } + + public int getStartingLine() { + return startingLines[currentPage]; + } + + public String getCurrentPageText() { + return pages.get(currentPage); + } + + public String getTextOfNextPages(boolean includeCurrent, int nOfPages) { + StringBuilder stringBuilder = new StringBuilder(); + int i; + for (i = includeCurrent ? 0 : 1; i < nOfPages; i++) { + if (pages.size() > (currentPage + i)) { + stringBuilder.append(pages.get(currentPage + 1)); + } + } + + return stringBuilder.toString(); + } + + public void savePage(String currentText) { + pages.set(currentPage, currentText); + } + + public void nextPage() { + if (!canReadNextPage()) return; + goToPage(currentPage + 1); + } + + public void prevPage() { + if (!canReadPrevPage()) return; + goToPage(currentPage - 1); + } + + public void goToPage(int page) { + if (page >= pages.size()) page = pages.size() - 1; + if (page < 0) page = 0; + boolean shouldUpdateLines = page > currentPage && canReadNextPage(); + if (shouldUpdateLines) { + String text = getCurrentPageText(); + int nOfNewLineNow = (text.length() - text.replace("\n", "").length()) + 1; // normally the last line is not counted so we have to add 1 + int nOfNewLineBefore = startingLines[currentPage + 1] - startingLines[currentPage]; + int difference = nOfNewLineNow - nOfNewLineBefore; + updateStartingLines(currentPage + 1, difference); + } + currentPage = page; + pageSystemInterface.onPageChanged(page); + } + + public void setStartingLines() { + int i; + int startingLine; + int nOfNewLines; + String text; + startingLines[0] = 0; + for (i = 1; i < pages.size(); i++) { + text = pages.get(i - 1); + nOfNewLines = text.length() - text.replace("\n", "").length() + 1; + startingLine = startingLines[i - 1] + nOfNewLines; + startingLines[i] = startingLine; + } + } + + public void updateStartingLines(int fromPage, int difference) { + if (difference == 0) + return; + int i; + if (fromPage < 1) fromPage = 1; + for (i = fromPage; i < pages.size(); i++) { + startingLines[i] += difference; + } + } + + public int getMaxPage() { + return pages.size() - 1; + } + + public int getCurrentPage() { + return currentPage; + } + + public String getAllText(String currentPageText) { + pages.set(currentPage, currentPageText); + int i; + StringBuilder allText = new StringBuilder(); + for (i = 0; i < pages.size(); i++) { + allText.append(pages.get(i)); + if(i < pages.size() - 1) + allText.append("\n"); + } + return allText.toString(); + } + + public boolean canReadNextPage() { + return currentPage < pages.size() - 1; + } + + public boolean canReadPrevPage() { + return currentPage >= 1; + } + + public interface PageSystemInterface { + void onPageChanged(int page); + } +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/texteditor/PageSystemButtons.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/texteditor/PageSystemButtons.java index 0f6a50e..3afe57b 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/texteditor/PageSystemButtons.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/texteditor/PageSystemButtons.java @@ -1,134 +1,134 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.texteditor; - -import android.content.Context; -import android.os.Handler; -import android.view.View; - -import com.faizmalkani.floatingactionbutton.FloatingActionButton; - -import sharedcode.turboeditor.R; - -public class PageSystemButtons { - - private static final int TIME_TO_SHOW_FABS = 2000; - final Handler handler = new Handler(); - final Runnable runnable = new Runnable() { - @Override - public void run() { - PageSystemButtons.this.next.setVisibility(View.GONE); - PageSystemButtons.this.prev.setVisibility(View.GONE); - } - }; - FloatingActionButton prev, next; - PageButtonsInterface pageButtonsInterface; - - public PageSystemButtons(Context context, final PageButtonsInterface pageButtonsInterface, FloatingActionButton prev, FloatingActionButton next) { - this.prev = prev; - this.next = next; - this.pageButtonsInterface = pageButtonsInterface; - - this.next.setColor(context.getResources().getColor(R.color.fab_light)); - this.next.setDrawable(context.getResources().getDrawable(R.drawable.ic_keyboard_arrow_right)); - - this.prev.setColor(context.getResources().getColor(R.color.fab_light)); - this.prev.setDrawable(context.getResources().getDrawable(R.drawable.ic_keyboard_arrow_left)); - - if (pageButtonsInterface.canReadNextPage()) - next.setVisibility(View.VISIBLE); - - if (pageButtonsInterface.canReadPrevPage()) - prev.setVisibility(View.VISIBLE); - - this.next.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - pageButtonsInterface.nextPageClicked(); - } - }); - - this.next.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - pageButtonsInterface.pageSystemButtonLongClicked(); - return true; - } - }); - - this.prev.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - pageButtonsInterface.prevPageClicked(); - } - }); - - this.prev.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - pageButtonsInterface.pageSystemButtonLongClicked(); - return true; - } - }); - } - - public void updateVisibility(boolean autoHide) { - - if (pageButtonsInterface.canReadNextPage()) - PageSystemButtons.this.next.setVisibility(View.VISIBLE); - else - PageSystemButtons.this.next.setVisibility(View.GONE); - - if (pageButtonsInterface.canReadPrevPage()) - PageSystemButtons.this.prev.setVisibility(View.VISIBLE); - else - PageSystemButtons.this.prev.setVisibility(View.GONE); - - /*if(pageButtonsInterface.hasNext()) - next.showFab(); - else - next.hideFab(); - - if(pageButtonsInterface.hasPrev()) - prev.showFab(); - else - prev.hideFab();*/ - - if (autoHide) { - handler.removeCallbacks(runnable); - handler.postDelayed(runnable, TIME_TO_SHOW_FABS); - } else { - handler.removeCallbacks(runnable); - } - } - - public interface PageButtonsInterface { - public void nextPageClicked(); - - public void prevPageClicked(); - - public void pageSystemButtonLongClicked(); - - public boolean canReadNextPage(); - - public boolean canReadPrevPage(); - } - -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.texteditor; + +import android.content.Context; +import android.os.Handler; +import android.view.View; + +import com.faizmalkani.floatingactionbutton.FloatingActionButton; + +import sharedcode.turboeditor.R; + +public class PageSystemButtons { + + private static final int TIME_TO_SHOW_FABS = 2000; + final Handler handler = new Handler(); + final Runnable runnable = new Runnable() { + @Override + public void run() { + PageSystemButtons.this.next.setVisibility(View.GONE); + PageSystemButtons.this.prev.setVisibility(View.GONE); + } + }; + FloatingActionButton prev, next; + PageButtonsInterface pageButtonsInterface; + + public PageSystemButtons(Context context, final PageButtonsInterface pageButtonsInterface, FloatingActionButton prev, FloatingActionButton next) { + this.prev = prev; + this.next = next; + this.pageButtonsInterface = pageButtonsInterface; + + this.next.setColor(context.getResources().getColor(R.color.fab_light)); + this.next.setDrawable(context.getResources().getDrawable(R.drawable.ic_keyboard_arrow_right)); + + this.prev.setColor(context.getResources().getColor(R.color.fab_light)); + this.prev.setDrawable(context.getResources().getDrawable(R.drawable.ic_keyboard_arrow_left)); + + if (pageButtonsInterface.canReadNextPage()) + next.setVisibility(View.VISIBLE); + + if (pageButtonsInterface.canReadPrevPage()) + prev.setVisibility(View.VISIBLE); + + this.next.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + pageButtonsInterface.nextPageClicked(); + } + }); + + this.next.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + pageButtonsInterface.pageSystemButtonLongClicked(); + return true; + } + }); + + this.prev.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + pageButtonsInterface.prevPageClicked(); + } + }); + + this.prev.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + pageButtonsInterface.pageSystemButtonLongClicked(); + return true; + } + }); + } + + public void updateVisibility(boolean autoHide) { + + if (pageButtonsInterface.canReadNextPage()) + PageSystemButtons.this.next.setVisibility(View.VISIBLE); + else + PageSystemButtons.this.next.setVisibility(View.GONE); + + if (pageButtonsInterface.canReadPrevPage()) + PageSystemButtons.this.prev.setVisibility(View.VISIBLE); + else + PageSystemButtons.this.prev.setVisibility(View.GONE); + + /*if(pageButtonsInterface.hasNext()) + next.showFab(); + else + next.hideFab(); + + if(pageButtonsInterface.hasPrev()) + prev.showFab(); + else + prev.hideFab();*/ + + if (autoHide) { + handler.removeCallbacks(runnable); + handler.postDelayed(runnable, TIME_TO_SHOW_FABS); + } else { + handler.removeCallbacks(runnable); + } + } + + public interface PageButtonsInterface { + public void nextPageClicked(); + + public void prevPageClicked(); + + public void pageSystemButtonLongClicked(); + + public boolean canReadNextPage(); + + public boolean canReadPrevPage(); + } + +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/texteditor/Patterns.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/texteditor/Patterns.java index 59b0462..483fb96 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/texteditor/Patterns.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/texteditor/Patterns.java @@ -1,92 +1,92 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.texteditor; - -import java.util.regex.Pattern; - -public class Patterns { - - /* - public static final int COLOR_NUMBER = 0xffff6600; - public static final int COLOR_KEYWORD = 0xff2f6f9f; - public static final int COLOR_ATTR = 0xff4f9fcf; - public static final int COLOR_ATTR_VALUE = 0xffd44950; - public static final int COLOR_STRING = 0xffd44950; - public static final int COLOR_COMMENT = 0xff999999; - */ - - // Strings - public static final Pattern GENERAL_STRINGS = Pattern.compile("\"(.*?)\"|'(.*?)'"); - - public static final Pattern HTML_OPEN_TAGS = Pattern.compile( - "<([A-Za-z][A-Za-z0-9]*)\\b[^>]*>"); - public static final Pattern HTML_CLOSE_TAGS = Pattern.compile( - "]*>"); - public static final Pattern HTML_ATTRS = Pattern.compile( - "(\\S+)=[\"']?((?:.(?![\"']?\\s+(?:\\S+)=|[>\"']))+.)[\"']?"); - - //static final Pattern CSS_STYLE_NAME= Pattern.compile( - // "[ \\t\\n\\r\\f](.+?)\\{([^\\)]+)\\}"); - public static final Pattern CSS_ATTRS = Pattern.compile( - "(.+?):(.+?);"); - public static final Pattern CSS_ATTR_VALUE = Pattern.compile( - ":[ \t](.+?);"); - - public static final Pattern NUMBERS = Pattern.compile( - "(\\b(\\d*[.]?\\d+)\\b)"); - //public static final Pattern CSS_NUMBERS = Pattern.compile( - // "/^auto$|^[+-]?[0-9]+\\.?([0-9]+)?(px|em|ex|%|in|cm|mm|pt|pc)?$/ig"); - public static final Pattern SYMBOLS = Pattern.compile( - "(!|,|\\(|\\)|\\+|\\-|\\*|<|>|=|\\.|\\?|;|\\{|\\}|\\[|\\])"); - public static final Pattern NUMBERS_OR_SYMBOLS = Pattern.compile(NUMBERS.pattern()+"|"+SYMBOLS.pattern()); - public static final Pattern GENERAL_KEYWORDS = Pattern.compile( - "\\b(alignas|alignof|and|and_eq|asm|auto|bitand|bitorbool|break|case|catch|char|" - + "char16_t|char32_t|class|compl|const|constexpr|const_cast|continue|decltype" - + "|default|delete|do|double|dynamic_cast|echo|else|enum|explicit|export|extern|" - + "false|float|for|friend|function|goto|if|inline|int|mutable|namespace|new|noexcept|" - + "not|not_eq|null|nullptr|operator|or|or_eq|private|protected|public|register|" - + "reinterpret_cast|return|short|signed|sizeof|static|static_assert|static_cast" - + "|struct|switch|template|this|thread_local|throw|true|try|typedef|typeid|typename|undefined" - + "|union|unsigned|using|var|virtual|void|volatile|wchar_t|while|xor|xor_eq|)\\b", Pattern.CASE_INSENSITIVE); - - public static final Pattern PY_KEYWORDS = Pattern.compile( - "\\b(int|float|long|complex|str|unicode|list|tuple|bytearray|buffer|xrange|set|frozenset|dict|bool)|(True|False|None|self|NotImplemented|Ellipsis|__debug__|__file__)|(and|del|from|not|while|as|elif|global|or|with|assert|else|if|pass|yield|break|except|import|print|class|exec|in|raise|continue|finally|is|return|def|for|lambda|try)|(ArithmeticError|AssertionError|AttributeError|BaseException|DeprecationWarning|EnvironmentError|EOFError|Exception|FloatingPointError|FutureWarning|GeneratorExit|IOError|ImportError|ImportWarning|IndexError|KeyError|KeyboardInterrupt|LookupError|MemoryError|NameError|NotImplementedError|OSError|OverflowError|PendingDeprecationWarning|ReferenceError|RuntimeError|RuntimeWarning|StandardError|StopIteration|SyntaxError|SyntaxWarning|SystemError|SystemExit|TypeError|UnboundLocalError|UserWarning|UnicodeError|UnicodeWarning|UnicodeEncodeError|UnicodeDecodeError|UnicodeTranslateError|ValueError|Warning|WindowsError|ZeroDivisionError)\\b", Pattern.CASE_INSENSITIVE); - - public static final Pattern LUA_KEYWORDS = Pattern.compile( - "@[A-Za-z0-9_\\.]*|\\b(local|global|boolean|number|userdata)\\b|\\b(true|false|nil)\\b|\\b(return|then|while|and|break|do|else|elseif|end|for|function|if|in|not|or|repeat|until|thread|table)\\b" + - "|(?i)\\b(editsetText|editText|inkey|touch|system.exit|system.expCall|system.getAppPath|system.getCardMnt|system.getSec|system.impCallActionSend|system.impCallActionView|system.setrun|system.setScreen|system.version|El_Psy_Congroo|canvas.drawCircle|canvas.drawCls|canvas.drawLine|canvas.drawRect|canvas.getBmpSize|canvas.getColor|canvas.getg|canvas.getviewSize|canvas.loadBmp|canvas.putCircle|canvas.putCls|canvas.putflush|canvas.putg|canvas.putLine|canvas.putRect|canvas.putrotg|canvas.putWork|canvas.saveBmp|canvas.setMainBmp|canvas.setWorkBmp|canvas.workCls|canvas.workflush|color|canvas.drawText|canvas.drawTextBox|canvas.drawTextCenter|canvas.drawTextRotate|canvas.putText|canvas.putTextBox|canvas.putTextRotate|http.addHeader|http.addParam|http.clrHeader|http.clrParam|http.get|http.post|http.setContentType|http.setPostFile|http.status|dialog|item.add|item.check|item.clear|item.list|item.radio|toast|sensor.getAccel|sensor.setdevAccel|sensor.setdevMagnet|sensor.setdevOrient|sensor.getGdirection|sensor.getMagnet|sensor.getOrient|sound.beep|sound.isPlay|sound.pause|sound.restart|sound.setSoundFile|sound.start|sound.stop|zip.addFile|zip.exec|zip.status|sock.close|sock.connectOpen|sock.getAddress|sock.listenOpen|sock.recv|sock.send|sprite.clear|sprite.define|sprite.init|sprite.move|sprite.put)\\b" + - "|(?i)\\b(assert|collectgarbage|coroutine.create|coroutine.resume|coroutine.running|coroutine.status|coroutine.wrap|coroutine.yield|debug.debug|debug.getfenv|debug.gethook|debug.getinfo|debug.getlocal|debug.getmetatable|debug.getregistry|debug.getupvalue|debug.setfenv|debug.sethook|debug.setlocal|debug.setmetatable|debug.setupvalue|debug.traceback|dofile|error|file:close|file:flush|file:lines|file:read|file:seek|file:setvbuf|file:write|getfenv|getmetatable|io.close|io.flush|io.input|io.lines|io.open|io.output|io.popen|io.read|io.tmpfile|io.type|io.write|ipairs|load|loadfile|loadstring|math.abs|math.acos|math.asin|math.atan2|math.atan|math.ceil|math.cosh|math.cos|math.deg|math.exp|math.floor|math.fmod|math.frexp|math.ldexp|math.log10|math.log|math.max|math.min|math.modf|math.pow|math.rad|math.random|math.randomseed|math.sinh|math.sin|math.sqrt|math.tanh|math.tan|module|next|os.clock|os.date|os.difftime|os.execute|os.exit|os.getenv|os.remove|os.rename|os.setlocale|os.time|os.tmpname|package.cpath|package.loaded|package.loadlib|package.path|package.preload|package.seeal|pairs|pcall|print|rawequal|rawget|rawset|require|select|setfenv|setmetatable|string.byte|string.char|string.dump|string.find|string.format|string.gmatch|string.gsub|string.len|string.lower|string.match|string.rep|string.reverse|string.sub|string.upper|table.concat|table.insert|table.maxn|table.remove|table.sort|tonumber|tostring|type|unpack|xpcall)\\b" - ); - - public static final Pattern PHP_VARIABLES = Pattern.compile("\\$\\s*(\\w+)"); - // Comments - public static final Pattern XML_COMMENTS = Pattern.compile("(?s)"); - public static final Pattern GENERAL_COMMENTS = Pattern.compile( - "/\\*(?:.|[\\n\\r])*?\\*/|(? // - public static final Pattern GENERAL_COMMENTS_NO_SLASH = Pattern.compile( - "/\\*(?:.|[\\n\\r])*?\\*/|#.*"); - public static final Pattern SQL_KEYWORDS = Pattern.compile( - "\\b(ADD|EXCEPT|PERCENT|ALL|EXEC|PLAN|ALTER|EXECUTE|PRECISION|AND|EXISTS|PRIMARY|ANY|EXIT|PRINT|AS|FETCH|PROC|ASC|FILE|PROCEDURE|AUTHORIZATION|FILLFACTOR|PUBLIC|BACKUP|FOR|RAISERROR|BEGIN|FOREIGN|READ|BETWEEN|FREETEXT|READTEXT|BREAK|FREETEXTTABLE|RECONFIGURE|BROWSE|FROM|REFERENCES|BULK|FULL|REPLICATION|BY|FUNCTION|RESTORE|CASCADE|GOTO|RESTRICT|CASE|GRANT|RETURN|CHECK|GROUP|REVOKE|CHECKPOINT|HAVING|RIGHT|CLOSE|HOLDLOCK|ROLLBACK|CLUSTERED|IDENTITY|ROWCOUNT|COALESCE|IDENTITY_INSERT|ROWGUIDCOL|COLLATE|IDENTITYCOL|RULE|COLUMN|IF|SAVE|COMMIT|IN|SCHEMA|COMPUTE|INDEX|SELECT|CONSTRAINT|INNER|SESSION_USER|CONTAINS|INSERT|SET|CONTAINSTABLE|INTERSECT|SETUSER|CONTINUE|INTO|SHUTDOWN|CONVERT|IS|SOME|CREATE|JOIN|STATISTICS|CROSS|KEY|SYSTEM_USER|CURRENT|KILL|TABLE|CURRENT_DATE|LEFT|TEXTSIZE|CURRENT_TIME|LIKE|THEN|CURRENT_TIMESTAMP|LINENO|TO|CURRENT_USER|LOAD|TOP|CURSOR|NATIONAL|TRAN|DATABASE|NOCHECK|TRANSACTION|DBCC|NONCLUSTERED|TRIGGER|DEALLOCATE|NOT|TRUNCATE|DECLARE|NULL|TSEQUAL|DEFAULT|NULLIF|UNION|DELETE|OF|UNIQUE|DENY|OFF|UPDATE|DESC|OFFSETS|UPDATETEXT|DISK|ON|USE|DISTINCT|OPEN|USER|DISTRIBUTED|OPENDATASOURCE|VALUES|DOUBLE|OPENQUERY|VARYING|DROP|OPENROWSET|VIEW|DUMMY|OPENXML|WAITFOR|DUMP|OPTION|WHEN|ELSE|OR|WHERE|END|ORDER|WHILE|ERRLVL|OUTER|WITH|ESCAPE|OVER|WRITETEXT)\\b", Pattern.CASE_INSENSITIVE); - - - public static final Pattern LINK = android.util.Patterns.WEB_URL; - -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.texteditor; + +import java.util.regex.Pattern; + +public class Patterns { + + /* + public static final int COLOR_NUMBER = 0xffff6600; + public static final int COLOR_KEYWORD = 0xff2f6f9f; + public static final int COLOR_ATTR = 0xff4f9fcf; + public static final int COLOR_ATTR_VALUE = 0xffd44950; + public static final int COLOR_STRING = 0xffd44950; + public static final int COLOR_COMMENT = 0xff999999; + */ + + // Strings + public static final Pattern GENERAL_STRINGS = Pattern.compile("\"(.*?)\"|'(.*?)'"); + + public static final Pattern HTML_OPEN_TAGS = Pattern.compile( + "<([A-Za-z][A-Za-z0-9]*)\\b[^>]*>"); + public static final Pattern HTML_CLOSE_TAGS = Pattern.compile( + "]*>"); + public static final Pattern HTML_ATTRS = Pattern.compile( + "(\\S+)=[\"']?((?:.(?![\"']?\\s+(?:\\S+)=|[>\"']))+.)[\"']?"); + + //static final Pattern CSS_STYLE_NAME= Pattern.compile( + // "[ \\t\\n\\r\\f](.+?)\\{([^\\)]+)\\}"); + public static final Pattern CSS_ATTRS = Pattern.compile( + "(.+?):(.+?);"); + public static final Pattern CSS_ATTR_VALUE = Pattern.compile( + ":[ \t](.+?);"); + + public static final Pattern NUMBERS = Pattern.compile( + "(\\b(\\d*[.]?\\d+)\\b)"); + //public static final Pattern CSS_NUMBERS = Pattern.compile( + // "/^auto$|^[+-]?[0-9]+\\.?([0-9]+)?(px|em|ex|%|in|cm|mm|pt|pc)?$/ig"); + public static final Pattern SYMBOLS = Pattern.compile( + "(!|,|\\(|\\)|\\+|\\-|\\*|<|>|=|\\.|\\?|;|\\{|\\}|\\[|\\])"); + public static final Pattern NUMBERS_OR_SYMBOLS = Pattern.compile(NUMBERS.pattern()+"|"+SYMBOLS.pattern()); + public static final Pattern GENERAL_KEYWORDS = Pattern.compile( + "\\b(alignas|alignof|and|and_eq|asm|auto|bitand|bitorbool|break|case|catch|char|" + + "char16_t|char32_t|class|compl|const|constexpr|const_cast|continue|decltype" + + "|default|delete|do|double|dynamic_cast|echo|else|enum|explicit|export|extern|" + + "false|float|for|friend|function|goto|if|inline|int|mutable|namespace|new|noexcept|" + + "not|not_eq|null|nullptr|operator|or|or_eq|private|protected|public|register|" + + "reinterpret_cast|return|short|signed|sizeof|static|static_assert|static_cast" + + "|struct|switch|template|this|thread_local|throw|true|try|typedef|typeid|typename|undefined" + + "|union|unsigned|using|var|virtual|void|volatile|wchar_t|while|xor|xor_eq|)\\b", Pattern.CASE_INSENSITIVE); + + public static final Pattern PY_KEYWORDS = Pattern.compile( + "\\b(int|float|long|complex|str|unicode|list|tuple|bytearray|buffer|xrange|set|frozenset|dict|bool)|(True|False|None|self|NotImplemented|Ellipsis|__debug__|__file__)|(and|del|from|not|while|as|elif|global|or|with|assert|else|if|pass|yield|break|except|import|print|class|exec|in|raise|continue|finally|is|return|def|for|lambda|try)|(ArithmeticError|AssertionError|AttributeError|BaseException|DeprecationWarning|EnvironmentError|EOFError|Exception|FloatingPointError|FutureWarning|GeneratorExit|IOError|ImportError|ImportWarning|IndexError|KeyError|KeyboardInterrupt|LookupError|MemoryError|NameError|NotImplementedError|OSError|OverflowError|PendingDeprecationWarning|ReferenceError|RuntimeError|RuntimeWarning|StandardError|StopIteration|SyntaxError|SyntaxWarning|SystemError|SystemExit|TypeError|UnboundLocalError|UserWarning|UnicodeError|UnicodeWarning|UnicodeEncodeError|UnicodeDecodeError|UnicodeTranslateError|ValueError|Warning|WindowsError|ZeroDivisionError)\\b", Pattern.CASE_INSENSITIVE); + + public static final Pattern LUA_KEYWORDS = Pattern.compile( + "@[A-Za-z0-9_\\.]*|\\b(local|global|boolean|number|userdata)\\b|\\b(true|false|nil)\\b|\\b(return|then|while|and|break|do|else|elseif|end|for|function|if|in|not|or|repeat|until|thread|table)\\b" + + "|(?i)\\b(editsetText|editText|inkey|touch|system.exit|system.expCall|system.getAppPath|system.getCardMnt|system.getSec|system.impCallActionSend|system.impCallActionView|system.setrun|system.setScreen|system.version|El_Psy_Congroo|canvas.drawCircle|canvas.drawCls|canvas.drawLine|canvas.drawRect|canvas.getBmpSize|canvas.getColor|canvas.getg|canvas.getviewSize|canvas.loadBmp|canvas.putCircle|canvas.putCls|canvas.putflush|canvas.putg|canvas.putLine|canvas.putRect|canvas.putrotg|canvas.putWork|canvas.saveBmp|canvas.setMainBmp|canvas.setWorkBmp|canvas.workCls|canvas.workflush|color|canvas.drawText|canvas.drawTextBox|canvas.drawTextCenter|canvas.drawTextRotate|canvas.putText|canvas.putTextBox|canvas.putTextRotate|http.addHeader|http.addParam|http.clrHeader|http.clrParam|http.get|http.post|http.setContentType|http.setPostFile|http.status|dialog|item.add|item.check|item.clear|item.list|item.radio|toast|sensor.getAccel|sensor.setdevAccel|sensor.setdevMagnet|sensor.setdevOrient|sensor.getGdirection|sensor.getMagnet|sensor.getOrient|sound.beep|sound.isPlay|sound.pause|sound.restart|sound.setSoundFile|sound.start|sound.stop|zip.addFile|zip.exec|zip.status|sock.close|sock.connectOpen|sock.getAddress|sock.listenOpen|sock.recv|sock.send|sprite.clear|sprite.define|sprite.init|sprite.move|sprite.put)\\b" + + "|(?i)\\b(assert|collectgarbage|coroutine.create|coroutine.resume|coroutine.running|coroutine.status|coroutine.wrap|coroutine.yield|debug.debug|debug.getfenv|debug.gethook|debug.getinfo|debug.getlocal|debug.getmetatable|debug.getregistry|debug.getupvalue|debug.setfenv|debug.sethook|debug.setlocal|debug.setmetatable|debug.setupvalue|debug.traceback|dofile|error|file:close|file:flush|file:lines|file:read|file:seek|file:setvbuf|file:write|getfenv|getmetatable|io.close|io.flush|io.input|io.lines|io.open|io.output|io.popen|io.read|io.tmpfile|io.type|io.write|ipairs|load|loadfile|loadstring|math.abs|math.acos|math.asin|math.atan2|math.atan|math.ceil|math.cosh|math.cos|math.deg|math.exp|math.floor|math.fmod|math.frexp|math.ldexp|math.log10|math.log|math.max|math.min|math.modf|math.pow|math.rad|math.random|math.randomseed|math.sinh|math.sin|math.sqrt|math.tanh|math.tan|module|next|os.clock|os.date|os.difftime|os.execute|os.exit|os.getenv|os.remove|os.rename|os.setlocale|os.time|os.tmpname|package.cpath|package.loaded|package.loadlib|package.path|package.preload|package.seeal|pairs|pcall|print|rawequal|rawget|rawset|require|select|setfenv|setmetatable|string.byte|string.char|string.dump|string.find|string.format|string.gmatch|string.gsub|string.len|string.lower|string.match|string.rep|string.reverse|string.sub|string.upper|table.concat|table.insert|table.maxn|table.remove|table.sort|tonumber|tostring|type|unpack|xpcall)\\b" + ); + + public static final Pattern PHP_VARIABLES = Pattern.compile("\\$\\s*(\\w+)"); + // Comments + public static final Pattern XML_COMMENTS = Pattern.compile("(?s)"); + public static final Pattern GENERAL_COMMENTS = Pattern.compile( + "/\\*(?:.|[\\n\\r])*?\\*/|(? // + public static final Pattern GENERAL_COMMENTS_NO_SLASH = Pattern.compile( + "/\\*(?:.|[\\n\\r])*?\\*/|#.*"); + public static final Pattern SQL_KEYWORDS = Pattern.compile( + "\\b(ADD|EXCEPT|PERCENT|ALL|EXEC|PLAN|ALTER|EXECUTE|PRECISION|AND|EXISTS|PRIMARY|ANY|EXIT|PRINT|AS|FETCH|PROC|ASC|FILE|PROCEDURE|AUTHORIZATION|FILLFACTOR|PUBLIC|BACKUP|FOR|RAISERROR|BEGIN|FOREIGN|READ|BETWEEN|FREETEXT|READTEXT|BREAK|FREETEXTTABLE|RECONFIGURE|BROWSE|FROM|REFERENCES|BULK|FULL|REPLICATION|BY|FUNCTION|RESTORE|CASCADE|GOTO|RESTRICT|CASE|GRANT|RETURN|CHECK|GROUP|REVOKE|CHECKPOINT|HAVING|RIGHT|CLOSE|HOLDLOCK|ROLLBACK|CLUSTERED|IDENTITY|ROWCOUNT|COALESCE|IDENTITY_INSERT|ROWGUIDCOL|COLLATE|IDENTITYCOL|RULE|COLUMN|IF|SAVE|COMMIT|IN|SCHEMA|COMPUTE|INDEX|SELECT|CONSTRAINT|INNER|SESSION_USER|CONTAINS|INSERT|SET|CONTAINSTABLE|INTERSECT|SETUSER|CONTINUE|INTO|SHUTDOWN|CONVERT|IS|SOME|CREATE|JOIN|STATISTICS|CROSS|KEY|SYSTEM_USER|CURRENT|KILL|TABLE|CURRENT_DATE|LEFT|TEXTSIZE|CURRENT_TIME|LIKE|THEN|CURRENT_TIMESTAMP|LINENO|TO|CURRENT_USER|LOAD|TOP|CURSOR|NATIONAL|TRAN|DATABASE|NOCHECK|TRANSACTION|DBCC|NONCLUSTERED|TRIGGER|DEALLOCATE|NOT|TRUNCATE|DECLARE|NULL|TSEQUAL|DEFAULT|NULLIF|UNION|DELETE|OF|UNIQUE|DENY|OFF|UPDATE|DESC|OFFSETS|UPDATETEXT|DISK|ON|USE|DISTINCT|OPEN|USER|DISTRIBUTED|OPENDATASOURCE|VALUES|DOUBLE|OPENQUERY|VARYING|DROP|OPENROWSET|VIEW|DUMMY|OPENXML|WAITFOR|DUMP|OPTION|WHEN|ELSE|OR|WHERE|END|ORDER|WHILE|ERRLVL|OUTER|WITH|ESCAPE|OVER|WRITETEXT)\\b", Pattern.CASE_INSENSITIVE); + + + public static final Pattern LINK = android.util.Patterns.WEB_URL; + +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/texteditor/SearchResult.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/texteditor/SearchResult.java index 5991dc9..1105101 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/texteditor/SearchResult.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/texteditor/SearchResult.java @@ -1,63 +1,63 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.texteditor; - -import java.util.LinkedList; - -public class SearchResult { - // list of index - public LinkedList foundIndex; - public int textLength; - public boolean isReplace; - public String textToReplace; - public int index; - - public SearchResult(LinkedList foundIndex, int textLength, boolean isReplace, String textToReplace) { - this.foundIndex = foundIndex; - this.textLength = textLength; - this.isReplace = isReplace; - this.textToReplace = textToReplace; - } - - public void doneReplace() { - foundIndex.remove(index); - int i; - for (i = index; i < foundIndex.size(); i++) { - foundIndex.set(i, foundIndex.get(i) + textToReplace.length() - textLength); - } - index--; // an element was removed so we decrease the index - } - - public int numberOfResults() { - return foundIndex.size(); - } - - public boolean hasNext() { - return index < foundIndex.size() - 1; - } - - public boolean hasPrevious() { - return index > 0; - } - - public boolean canReplaceSomething() { - return isReplace && foundIndex.size() > 0; - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.texteditor; + +import java.util.LinkedList; + +public class SearchResult { + // list of index + public LinkedList foundIndex; + public int textLength; + public boolean isReplace; + public String textToReplace; + public int index; + + public SearchResult(LinkedList foundIndex, int textLength, boolean isReplace, String textToReplace) { + this.foundIndex = foundIndex; + this.textLength = textLength; + this.isReplace = isReplace; + this.textToReplace = textToReplace; + } + + public void doneReplace() { + foundIndex.remove(index); + int i; + for (i = index; i < foundIndex.size(); i++) { + foundIndex.set(i, foundIndex.get(i) + textToReplace.length() - textLength); + } + index--; // an element was removed so we decrease the index + } + + public int numberOfResults() { + return foundIndex.size(); + } + + public boolean hasNext() { + return index < foundIndex.size() - 1; + } + + public boolean hasPrevious() { + return index > 0; + } + + public boolean canReplaceSomething() { + return isReplace && foundIndex.size() > 0; + } +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/AccessStorageApi.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/AccessStorageApi.java index cadedd9..a9d3bed 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/AccessStorageApi.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/AccessStorageApi.java @@ -1,208 +1,208 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.util; - -import android.annotation.SuppressLint; -import android.content.ContentUris; -import android.content.Context; -import android.database.Cursor; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.net.Uri; -import android.os.Build; -import android.os.Environment; -import android.provider.DocumentsContract; -import android.provider.MediaStore; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; - -public class AccessStorageApi { - - public static Bitmap loadPrescaledBitmap(String filename) throws IOException { - // Facebook image size - final int IMAGE_MAX_SIZE = 630; - - File file = null; - FileInputStream fis; - - BitmapFactory.Options opts; - int resizeScale; - Bitmap bmp; - - file = new File(filename); - - // This bit determines only the width/height of the bitmap without loading the contents - opts = new BitmapFactory.Options(); - opts.inJustDecodeBounds = true; - fis = new FileInputStream(file); - BitmapFactory.decodeStream(fis, null, opts); - fis.close(); - - // Find the correct scale value. It should be a power of 2 - resizeScale = 1; - - if (opts.outHeight > IMAGE_MAX_SIZE || opts.outWidth > IMAGE_MAX_SIZE) { - resizeScale = (int) Math.pow(2, (int) Math.round(Math.log(IMAGE_MAX_SIZE / (double) Math.max(opts.outHeight, opts.outWidth)) / Math.log(0.5))); - } - - // Load pre-scaled bitmap - opts = new BitmapFactory.Options(); - opts.inSampleSize = resizeScale; - fis = new FileInputStream(file); - bmp = BitmapFactory.decodeStream(fis, null, opts); - - fis.close(); - - return bmp; - } - - /** - * Get a file path from a Uri. This will get the the path for Storage Access - * Framework Documents, as well as the _data field for the MediaStore and - * other file-based ContentProviders. - * - * @param context The context. - * @param uri The Uri to query. - * @author paulburke - */ - @SuppressLint("NewApi") - public static String getPath(final Context context, final Uri uri) { - - final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; - - // DocumentProvider - if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { - // ExternalStorageProvider - if (isExternalStorageDocument(uri)) { - final String docId = DocumentsContract.getDocumentId(uri); - final String[] split = docId.split(":"); - final String type = split[0]; - - if ("primary".equalsIgnoreCase(type)) { - return Environment.getExternalStorageDirectory() + "/" + split[1]; - } - - // TODO handle non-primary volumes - } - // DownloadsProvider - else if (isDownloadsDocument(uri)) { - - final String id = DocumentsContract.getDocumentId(uri); - final Uri contentUri = ContentUris.withAppendedId( - Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); - - return getDataColumn(context, contentUri, null, null); - } - // MediaProvider - else if (isMediaDocument(uri)) { - final String docId = DocumentsContract.getDocumentId(uri); - final String[] split = docId.split(":"); - final String type = split[0]; - - Uri contentUri = null; - if ("image".equals(type)) { - contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; - } else if ("video".equals(type)) { - contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; - } else if ("audio".equals(type)) { - contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; - } - - final String selection = "_id=?"; - final String[] selectionArgs = new String[]{ - split[1] - }; - - return getDataColumn(context, contentUri, selection, selectionArgs); - } - } - // MediaStore (and general) - else if ("content".equalsIgnoreCase(uri.getScheme())) { - return getDataColumn(context, uri, null, null); - } - // File - else if ("file".equalsIgnoreCase(uri.getScheme())) { - return uri.getPath(); - } - - return null; - } - - /** - * Get the value of the data column for this Uri. This is useful for - * MediaStore Uris, and other file-based ContentProviders. - * - * @param context The context. - * @param uri The Uri to query. - * @param selection (Optional) Filter used in the query. - * @param selectionArgs (Optional) Selection arguments used in the query. - * @return The value of the _data column, which is typically a file path. - */ - public static String getDataColumn(Context context, Uri uri, String selection, - String[] selectionArgs) { - - Cursor cursor = null; - final String column = "_data"; - final String[] projection = { - column - }; - - try { - cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, - null); - if (cursor != null && cursor.moveToFirst()) { - final int column_index = cursor.getColumnIndexOrThrow(column); - return cursor.getString(column_index); - } - } finally { - if (cursor != null) - cursor.close(); - } - return null; - } - - - /** - * @param uri The Uri to check. - * @return Whether the Uri authority is ExternalStorageProvider. - */ - public static boolean isExternalStorageDocument(Uri uri) { - return "com.android.externalstorage.documents".equals(uri.getAuthority()); - } - - /** - * @param uri The Uri to check. - * @return Whether the Uri authority is DownloadsProvider. - */ - public static boolean isDownloadsDocument(Uri uri) { - return "com.android.providers.downloads.documents".equals(uri.getAuthority()); - } - - /** - * @param uri The Uri to check. - * @return Whether the Uri authority is MediaProvider. - */ - public static boolean isMediaDocument(Uri uri) { - return "com.android.providers.media.documents".equals(uri.getAuthority()); - } - -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.util; + +import android.annotation.SuppressLint; +import android.content.ContentUris; +import android.content.Context; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.os.Build; +import android.os.Environment; +import android.provider.DocumentsContract; +import android.provider.MediaStore; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + +public class AccessStorageApi { + + public static Bitmap loadPrescaledBitmap(String filename) throws IOException { + // Facebook image size + final int IMAGE_MAX_SIZE = 630; + + File file = null; + FileInputStream fis; + + BitmapFactory.Options opts; + int resizeScale; + Bitmap bmp; + + file = new File(filename); + + // This bit determines only the width/height of the bitmap without loading the contents + opts = new BitmapFactory.Options(); + opts.inJustDecodeBounds = true; + fis = new FileInputStream(file); + BitmapFactory.decodeStream(fis, null, opts); + fis.close(); + + // Find the correct scale value. It should be a power of 2 + resizeScale = 1; + + if (opts.outHeight > IMAGE_MAX_SIZE || opts.outWidth > IMAGE_MAX_SIZE) { + resizeScale = (int) Math.pow(2, (int) Math.round(Math.log(IMAGE_MAX_SIZE / (double) Math.max(opts.outHeight, opts.outWidth)) / Math.log(0.5))); + } + + // Load pre-scaled bitmap + opts = new BitmapFactory.Options(); + opts.inSampleSize = resizeScale; + fis = new FileInputStream(file); + bmp = BitmapFactory.decodeStream(fis, null, opts); + + fis.close(); + + return bmp; + } + + /** + * Get a file path from a Uri. This will get the the path for Storage Access + * Framework Documents, as well as the _data field for the MediaStore and + * other file-based ContentProviders. + * + * @param context The context. + * @param uri The Uri to query. + * @author paulburke + */ + @SuppressLint("NewApi") + public static String getPath(final Context context, final Uri uri) { + + final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; + + // DocumentProvider + if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { + // ExternalStorageProvider + if (isExternalStorageDocument(uri)) { + final String docId = DocumentsContract.getDocumentId(uri); + final String[] split = docId.split(":"); + final String type = split[0]; + + if ("primary".equalsIgnoreCase(type)) { + return Environment.getExternalStorageDirectory() + "/" + split[1]; + } + + // TODO handle non-primary volumes + } + // DownloadsProvider + else if (isDownloadsDocument(uri)) { + + final String id = DocumentsContract.getDocumentId(uri); + final Uri contentUri = ContentUris.withAppendedId( + Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); + + return getDataColumn(context, contentUri, null, null); + } + // MediaProvider + else if (isMediaDocument(uri)) { + final String docId = DocumentsContract.getDocumentId(uri); + final String[] split = docId.split(":"); + final String type = split[0]; + + Uri contentUri = null; + if ("image".equals(type)) { + contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; + } else if ("video".equals(type)) { + contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; + } else if ("audio".equals(type)) { + contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; + } + + final String selection = "_id=?"; + final String[] selectionArgs = new String[]{ + split[1] + }; + + return getDataColumn(context, contentUri, selection, selectionArgs); + } + } + // MediaStore (and general) + else if ("content".equalsIgnoreCase(uri.getScheme())) { + return getDataColumn(context, uri, null, null); + } + // File + else if ("file".equalsIgnoreCase(uri.getScheme())) { + return uri.getPath(); + } + + return null; + } + + /** + * Get the value of the data column for this Uri. This is useful for + * MediaStore Uris, and other file-based ContentProviders. + * + * @param context The context. + * @param uri The Uri to query. + * @param selection (Optional) Filter used in the query. + * @param selectionArgs (Optional) Selection arguments used in the query. + * @return The value of the _data column, which is typically a file path. + */ + public static String getDataColumn(Context context, Uri uri, String selection, + String[] selectionArgs) { + + Cursor cursor = null; + final String column = "_data"; + final String[] projection = { + column + }; + + try { + cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, + null); + if (cursor != null && cursor.moveToFirst()) { + final int column_index = cursor.getColumnIndexOrThrow(column); + return cursor.getString(column_index); + } + } finally { + if (cursor != null) + cursor.close(); + } + return null; + } + + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is ExternalStorageProvider. + */ + public static boolean isExternalStorageDocument(Uri uri) { + return "com.android.externalstorage.documents".equals(uri.getAuthority()); + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is DownloadsProvider. + */ + public static boolean isDownloadsDocument(Uri uri) { + return "com.android.providers.downloads.documents".equals(uri.getAuthority()); + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is MediaProvider. + */ + public static boolean isMediaDocument(Uri uri) { + return "com.android.providers.media.documents".equals(uri.getAuthority()); + } + +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/AlphanumComparator.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/AlphanumComparator.java index 0911107..98b5990 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/AlphanumComparator.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/AlphanumComparator.java @@ -1,138 +1,138 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.util;/* - * The Alphanum Algorithm is an improved sorting algorithm for strings - * containing numbers. Instead of sorting numbers in ASCII order like - * a standard sort, this algorithm sorts numbers in numeric order. - * - * The Alphanum Algorithm is discussed at http://www.DaveKoelle.com - * - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -import java.util.Comparator; - -/** - * This is an updated version with enhancements made by Daniel Migowski, Andre Bogus, and David - * Koelle - *

- * To convert to use Templates (Java 1.5+): - Change "implements Comparator" to "implements - * Comparator" - Change "compare(Object o1, Object o2)" to "compare(String s1, String s2)" - - * Remove the type checking and casting in compare(). - *

- * To use this class: Use the static "sort" method from the java.util.Collections class: - * Collections.sort(your list, new AlphanumComparator()); - */ -public class AlphanumComparator implements Comparator { - private boolean isDigit(char ch) { - return ch >= 48 && ch <= 57; - } - - /** - * Length of string is passed in for improved efficiency (only need to calculate it once) * - */ - private String getChunk(String s, int slength, int marker) { - StringBuilder chunk = new StringBuilder(); - char c = s.charAt(marker); - chunk.append(c); - marker++; - if (isDigit(c)) { - while (marker < slength) { - c = s.charAt(marker); - if (!isDigit(c)) { - break; - } - chunk.append(c); - marker++; - } - } else { - while (marker < slength) { - c = s.charAt(marker); - if (isDigit(c)) { - break; - } - chunk.append(c); - marker++; - } - } - return chunk.toString(); - } - - public String getTheString(Object obj) { - return (String) obj; - } - - public int compare(Object o1, Object o2) { - String s1 = getTheString(o1); - String s2 = getTheString(o2); - - int thisMarker = 0; - int thatMarker = 0; - int s1Length = s1.length(); - int s2Length = s2.length(); - - while (thisMarker < s1Length && thatMarker < s2Length) { - String thisChunk = getChunk(s1, s1Length, thisMarker); - thisMarker += thisChunk.length(); - - String thatChunk = getChunk(s2, s2Length, thatMarker); - thatMarker += thatChunk.length(); - - // If both chunks contain numeric characters, sort them numerically - int result = 0; - if (isDigit(thisChunk.charAt(0)) && isDigit(thatChunk.charAt(0))) { - // Simple chunk comparison by length. - int thisChunkLength = thisChunk.length(); - result = thisChunkLength - thatChunk.length(); - // If equal, the first different number counts - if (result == 0) { - for (int i = 0; i < thisChunkLength; i++) { - result = thisChunk.charAt(i) - thatChunk.charAt(i); - if (result != 0) { - return result; - } - } - } - } else { - result = thisChunk.compareTo(thatChunk); - } - - if (result != 0) { - return result; - } - } - - return s1Length - s2Length; - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.util;/* + * The Alphanum Algorithm is an improved sorting algorithm for strings + * containing numbers. Instead of sorting numbers in ASCII order like + * a standard sort, this algorithm sorts numbers in numeric order. + * + * The Alphanum Algorithm is discussed at http://www.DaveKoelle.com + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +import java.util.Comparator; + +/** + * This is an updated version with enhancements made by Daniel Migowski, Andre Bogus, and David + * Koelle + *

+ * To convert to use Templates (Java 1.5+): - Change "implements Comparator" to "implements + * Comparator" - Change "compare(Object o1, Object o2)" to "compare(String s1, String s2)" - + * Remove the type checking and casting in compare(). + *

+ * To use this class: Use the static "sort" method from the java.util.Collections class: + * Collections.sort(your list, new AlphanumComparator()); + */ +public class AlphanumComparator implements Comparator { + private boolean isDigit(char ch) { + return ch >= 48 && ch <= 57; + } + + /** + * Length of string is passed in for improved efficiency (only need to calculate it once) * + */ + private String getChunk(String s, int slength, int marker) { + StringBuilder chunk = new StringBuilder(); + char c = s.charAt(marker); + chunk.append(c); + marker++; + if (isDigit(c)) { + while (marker < slength) { + c = s.charAt(marker); + if (!isDigit(c)) { + break; + } + chunk.append(c); + marker++; + } + } else { + while (marker < slength) { + c = s.charAt(marker); + if (isDigit(c)) { + break; + } + chunk.append(c); + marker++; + } + } + return chunk.toString(); + } + + public String getTheString(Object obj) { + return (String) obj; + } + + public int compare(Object o1, Object o2) { + String s1 = getTheString(o1); + String s2 = getTheString(o2); + + int thisMarker = 0; + int thatMarker = 0; + int s1Length = s1.length(); + int s2Length = s2.length(); + + while (thisMarker < s1Length && thatMarker < s2Length) { + String thisChunk = getChunk(s1, s1Length, thisMarker); + thisMarker += thisChunk.length(); + + String thatChunk = getChunk(s2, s2Length, thatMarker); + thatMarker += thatChunk.length(); + + // If both chunks contain numeric characters, sort them numerically + int result = 0; + if (isDigit(thisChunk.charAt(0)) && isDigit(thatChunk.charAt(0))) { + // Simple chunk comparison by length. + int thisChunkLength = thisChunk.length(); + result = thisChunkLength - thatChunk.length(); + // If equal, the first different number counts + if (result == 0) { + for (int i = 0; i < thisChunkLength; i++) { + result = thisChunk.charAt(i) - thatChunk.charAt(i); + if (result != 0) { + return result; + } + } + } + } else { + result = thisChunk.compareTo(thatChunk); + } + + if (result != 0) { + return result; + } + } + + return s1Length - s2Length; + } +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/AnimationUtils.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/AnimationUtils.java index f89bb6b..54904e9 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/AnimationUtils.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/AnimationUtils.java @@ -1,52 +1,52 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.util; - -import android.app.Activity; -import android.content.Intent; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.ActivityOptionsCompat; -import android.view.View; - -public class AnimationUtils { - public static Bundle getScaleBundle(View view) { - return ActivityOptionsCompat.makeScaleUpAnimation( - view, 0, 0, view.getWidth(), view.getHeight()).toBundle(); - } - - public static void startActivityWithScale(@NonNull Activity startActivity, @NonNull Intent subActivity, @NonNull boolean forResult, @Nullable int code, @NonNull View view) { - if(forResult){ - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) - startActivity.startActivityForResult(subActivity, code, AnimationUtils.getScaleBundle - (view)); - else - startActivity.startActivityForResult(subActivity, code); - } - else { - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) - startActivity.startActivity(subActivity, AnimationUtils.getScaleBundle - (view)); - else - startActivity.startActivity(subActivity); - } - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.util; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.ActivityOptionsCompat; +import android.view.View; + +public class AnimationUtils { + public static Bundle getScaleBundle(View view) { + return ActivityOptionsCompat.makeScaleUpAnimation( + view, 0, 0, view.getWidth(), view.getHeight()).toBundle(); + } + + public static void startActivityWithScale(@NonNull Activity startActivity, @NonNull Intent subActivity, @NonNull boolean forResult, @Nullable int code, @NonNull View view) { + if(forResult){ + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) + startActivity.startActivityForResult(subActivity, code, AnimationUtils.getScaleBundle + (view)); + else + startActivity.startActivityForResult(subActivity, code); + } + else { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) + startActivity.startActivity(subActivity, AnimationUtils.getScaleBundle + (view)); + else + startActivity.startActivity(subActivity); + } + } +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/AppInfoHelper.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/AppInfoHelper.java index d0379ca..6855bcb 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/AppInfoHelper.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/AppInfoHelper.java @@ -1,42 +1,42 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.util; - -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; - -public class AppInfoHelper { - public static String getApplicationName(final Context context) { - final ApplicationInfo applicationInfo = context.getApplicationInfo(); - return context.getString(applicationInfo.labelRes); - } - - public static String getCurrentVersion(final Context context) { - try { - final PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), - 0); - return packageInfo.versionName; - } catch (PackageManager.NameNotFoundException e) { - return ""; - } - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.util; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; + +public class AppInfoHelper { + public static String getApplicationName(final Context context) { + final ApplicationInfo applicationInfo = context.getApplicationInfo(); + return context.getString(applicationInfo.labelRes); + } + + public static String getCurrentVersion(final Context context) { + try { + final PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), + 0); + return packageInfo.versionName; + } catch (PackageManager.NameNotFoundException e) { + return ""; + } + } +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/EventBusEvents.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/EventBusEvents.java index 916ea51..9505a42 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/EventBusEvents.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/EventBusEvents.java @@ -1,115 +1,115 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.util; - -import java.io.File; -import java.util.List; - -public class EventBusEvents { - public static class CannotOpenAFile { - - } - - public static class NewFileToOpen { - - private final File file; - private final String fileText; - - public NewFileToOpen(File file) { - this.file = file; - this.fileText = ""; - } - - public NewFileToOpen(String fileText) { - this.file = new File(""); - this.fileText = fileText; - } - - public File getFile() { - return file; - } - - public String getFileText() { - return fileText; - } - } - - public static class AFileIsSelected { - - private final String path; - - public AFileIsSelected(String path) { - this.path = path; - } - - public String getPath() { - return path; - } - } - - public static class APreferenceValueWasChanged { - private Type type; - private List types; - - public APreferenceValueWasChanged(Type type) { - this.type = type; - } - - public APreferenceValueWasChanged(List types) { - this.types = types; - } - - public boolean hasType(Type value) { - - if (type != null) { - return value == type; - } else { - return types.contains(value); - } - - } - - public enum Type { - FONT_SIZE, ENCODING, SYNTAX, WRAP_CONTENT, MONOSPACE, LINE_NUMERS, THEME_CHANGE, TEXT_SUGGESTIONS, READ_ONLY, - } - } - - public static class SaveAFile { - } - - public static class SavedAFile { - private final String path; - - public SavedAFile(String path) { - this.path = path; - } - - public String getPath() { - return path; - } - } - - public static class ClosedAFile { - } - - public static class InvalideTheMenu { - } - -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.util; + +import java.io.File; +import java.util.List; + +public class EventBusEvents { + public static class CannotOpenAFile { + + } + + public static class NewFileToOpen { + + private final File file; + private final String fileText; + + public NewFileToOpen(File file) { + this.file = file; + this.fileText = ""; + } + + public NewFileToOpen(String fileText) { + this.file = new File(""); + this.fileText = fileText; + } + + public File getFile() { + return file; + } + + public String getFileText() { + return fileText; + } + } + + public static class AFileIsSelected { + + private final String path; + + public AFileIsSelected(String path) { + this.path = path; + } + + public String getPath() { + return path; + } + } + + public static class APreferenceValueWasChanged { + private Type type; + private List types; + + public APreferenceValueWasChanged(Type type) { + this.type = type; + } + + public APreferenceValueWasChanged(List types) { + this.types = types; + } + + public boolean hasType(Type value) { + + if (type != null) { + return value == type; + } else { + return types.contains(value); + } + + } + + public enum Type { + FONT_SIZE, ENCODING, SYNTAX, WRAP_CONTENT, MONOSPACE, LINE_NUMERS, THEME_CHANGE, TEXT_SUGGESTIONS, READ_ONLY, + } + } + + public static class SaveAFile { + } + + public static class SavedAFile { + private final String path; + + public SavedAFile(String path) { + this.path = path; + } + + public String getPath() { + return path; + } + } + + public static class ClosedAFile { + } + + public static class InvalideTheMenu { + } + +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/IHomeActivity.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/IHomeActivity.java new file mode 100644 index 0000000..ec2c898 --- /dev/null +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/IHomeActivity.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.util; + +public interface IHomeActivity { + public abstract boolean showInterstitial(); +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/MimeTypes.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/MimeTypes.java index bba14c1..39ec0f3 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/MimeTypes.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/MimeTypes.java @@ -1,57 +1,57 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.util; - -public class MimeTypes { - public static final String[] MIME_TEXT = { - "ajx", "am", "asa", "asc", "asp", "aspx", "awk", "bat", "c", "cdf", "cf", "cfg", "cfm", "cgi", "cnf", "conf", - "cc", "cpp", "css", "csv", "ctl", "dat", "dhtml", "diz", "file", "forward", "grp", "h", "hh", "hpp", "hqx", "hta", "htaccess", - "htc", "htm", "html", "htpasswd", "htt", "htx", "in", "inc", "info", "ini", "ink", "java", "js", "jsp", "key", "latex", "log", - "logfile", "m3u", "m4", "m4a", "mak", "map", "md", "markdown", "model", "msg", "nfo", "nsi", "info", "old", "pas", "patch", "perl", - "php", "php2", "php3", "php4", "php5", "php6", "phtml", "pix", "pl", "pm", "po", "pwd", "py", "qmail", "rb", "rbl", "rbw", - "readme", "reg", "rss", "rtf", "ruby", "session", "setup", "sh", "shtm", "shtml", "sql", "ssh", "stm", "style", "svg", "tcl", - "tex", "text", "threads", "tmpl", "tpl", "txt", "ubb", "vbs", "xhtml", "xml", "xrc", "xsl" - }; - public static final String[] MIME_CODE = { - "cs", "php", "js", "java", "py", "rb", "aspx", "cshtml", "vbhtml", "go", "c", "h", "cc", "cpp", "hh", "hpp", "pl", "pm", "t", "pod", - "m", "f", "for", "f90", "f95", "asp", "json", "wiki", "lua", "r" - }; - public static final String[] MIME_HTML = { - "htm", "html", "xhtml" - }; - public static final String[] MIME_PICTURE = { - "bmp", "eps", "png", "jpeg", "jpg", "ico", "gif", "tiff", "webp" - }; - public static final String[] MIME_MUSIC = { - "aac", "flac", "mp3", "mpga", "oga", "ogg", "opus", "webma", "wav" - }; - public static final String[] MIME_VIDEO = { - "avi", "mp4", "mkv", "wmw", "ogv", "webm" - }; - public static final String[] MIME_ARCHIVE = { - "7z", "arj", "bz2", "gz", "rar", "tar", "tgz", "zip", "xz" - }; - public static final String[] MIME_SQL = { - "sql", "mdf", "ndf", "ldf" - }; - public static final String[] MIME_MARKDOWN = { - "md", "mdown", "markdown", - }; -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.util; + +public class MimeTypes { + public static final String[] MIME_TEXT = { + "ajx", "am", "asa", "asc", "asp", "aspx", "awk", "bat", "c", "cdf", "cf", "cfg", "cfm", "cgi", "cnf", "conf", + "cc", "cpp", "css", "csv", "ctl", "dat", "dhtml", "diz", "file", "forward", "grp", "h", "hh", "hpp", "hqx", "hta", "htaccess", + "htc", "htm", "html", "htpasswd", "htt", "htx", "in", "inc", "info", "ini", "ink", "java", "js", "jsp", "key", "latex", "log", + "logfile", "m3u", "m4", "m4a", "mak", "map", "md", "markdown", "model", "msg", "nfo", "nsi", "info", "old", "pas", "patch", "perl", + "php", "php2", "php3", "php4", "php5", "php6", "phtml", "pix", "pl", "pm", "po", "pwd", "py", "qmail", "rb", "rbl", "rbw", + "readme", "reg", "rss", "rtf", "ruby", "session", "setup", "sh", "shtm", "shtml", "sql", "ssh", "stm", "style", "svg", "tcl", + "tex", "text", "threads", "tmpl", "tpl", "txt", "ubb", "vbs", "xhtml", "xml", "xrc", "xsl" + }; + public static final String[] MIME_CODE = { + "cs", "php", "js", "java", "py", "rb", "aspx", "cshtml", "vbhtml", "go", "c", "h", "cc", "cpp", "hh", "hpp", "pl", "pm", "t", "pod", + "m", "f", "for", "f90", "f95", "asp", "json", "wiki", "lua", "r" + }; + public static final String[] MIME_HTML = { + "htm", "html", "xhtml" + }; + public static final String[] MIME_PICTURE = { + "bmp", "eps", "png", "jpeg", "jpg", "ico", "gif", "tiff", "webp" + }; + public static final String[] MIME_MUSIC = { + "aac", "flac", "mp3", "mpga", "oga", "ogg", "opus", "webma", "wav" + }; + public static final String[] MIME_VIDEO = { + "avi", "mp4", "mkv", "wmw", "ogv", "webm" + }; + public static final String[] MIME_ARCHIVE = { + "7z", "arj", "bz2", "gz", "rar", "tar", "tgz", "zip", "xz" + }; + public static final String[] MIME_SQL = { + "sql", "mdf", "ndf", "ldf" + }; + public static final String[] MIME_MARKDOWN = { + "md", "mdown", "markdown", + }; +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/PixelDipConverter.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/PixelDipConverter.java index 1c50d0e..0b6cb1b 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/PixelDipConverter.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/PixelDipConverter.java @@ -1,56 +1,56 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.util; - -import android.content.Context; -import android.content.res.Resources; -import android.util.DisplayMetrics; - -public final class PixelDipConverter { - - private PixelDipConverter() { - } - - /** - * This method convets dp unit to equivalent device specific value in pixels. - * - * @param dp A value in dp(Device independent pixels) unit. Which we need to convert into pixels - * @param context Context to get resources and device specific display metrics - * @return A float value to represent Pixels equivalent to dp according to device - */ - public static float convertDpToPixel(final float dp, final Context context) { - final Resources resources = context.getResources(); - final DisplayMetrics metrics = resources.getDisplayMetrics(); - return dp * metrics.densityDpi / 160f; - } - - /** - * This method converts device specific pixels to device independent pixels. - * - * @param px A value in px (pixels) unit. Which we need to convert into db - * @param context Context to get resources and device specific display metrics - * @return A float value to represent db equivalent to px value - */ - public static float convertPixelsToDp(final float px, final Context context) { - final Resources resources = context.getResources(); - final DisplayMetrics metrics = resources.getDisplayMetrics(); - return px / (metrics.densityDpi / 160f); - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.util; + +import android.content.Context; +import android.content.res.Resources; +import android.util.DisplayMetrics; + +public final class PixelDipConverter { + + private PixelDipConverter() { + } + + /** + * This method convets dp unit to equivalent device specific value in pixels. + * + * @param dp A value in dp(Device independent pixels) unit. Which we need to convert into pixels + * @param context Context to get resources and device specific display metrics + * @return A float value to represent Pixels equivalent to dp according to device + */ + public static float convertDpToPixel(final float dp, final Context context) { + final Resources resources = context.getResources(); + final DisplayMetrics metrics = resources.getDisplayMetrics(); + return dp * metrics.densityDpi / 160f; + } + + /** + * This method converts device specific pixels to device independent pixels. + * + * @param px A value in px (pixels) unit. Which we need to convert into db + * @param context Context to get resources and device specific display metrics + * @return A float value to represent db equivalent to px value + */ + public static float convertPixelsToDp(final float px, final Context context) { + final Resources resources = context.getResources(); + final DisplayMetrics metrics = resources.getDisplayMetrics(); + return px / (metrics.densityDpi / 160f); + } +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/ProCheckUtils.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/ProCheckUtils.java index f909309..c9af73c 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/ProCheckUtils.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/ProCheckUtils.java @@ -1,44 +1,44 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.util; - -import android.content.Context; - -import sharedcode.turboeditor.preferences.PreferenceHelper; - -public class ProCheckUtils { - public static boolean isPro(Context context, boolean includeDonations) { - - String packageName = context.getPackageName(); - - if (Build.FOR_AMAZON) - return true; - else if (packageName.equals("com.maskyn.fileeditorpro")) - return true; - else if (includeDonations && PreferenceHelper.hasDonated(context)) - return true; - else - return false; - } - - public static boolean isPro(Context context) { - return isPro(context, true); - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.util; + +import android.content.Context; + +import sharedcode.turboeditor.preferences.PreferenceHelper; + +public class ProCheckUtils { + public static boolean isPro(Context context, boolean includeDonations) { + + String packageName = context.getPackageName(); + + if (Build.FOR_AMAZON) + return true; + else if (packageName.equals("com.maskyn.fileeditorpro")) + return true; + else if (includeDonations && PreferenceHelper.hasDonated(context)) + return true; + else + return false; + } + + public static boolean isPro(Context context) { + return isPro(context, true); + } +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/ThemeUtils.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/ThemeUtils.java index acbeb5d..d36899d 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/ThemeUtils.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/ThemeUtils.java @@ -1,55 +1,55 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.util; - -import android.app.Activity; - -import sharedcode.turboeditor.R; -import sharedcode.turboeditor.preferences.PreferenceHelper; - -public class ThemeUtils { - - public static void setTheme(Activity activity){ - boolean light = PreferenceHelper.getLightTheme(activity); - if (light) { - activity.setTheme(R.style.AppThemeBaseLight); - } else { - activity.setTheme(R.style.AppThemeBaseDark); - } - } - - public static void setPreferenceTheme(Activity activity){ - boolean light = PreferenceHelper.getLightTheme(activity); - if (light) { - activity.setTheme(R.style.PreferenceLight); - } else { - activity.setTheme(R.style.PreferenceDark); - } - } - - public static void setWindowsBackground(Activity activity) { - boolean whiteTheme = PreferenceHelper.getLightTheme(activity); - if (whiteTheme) { - activity.getWindow().setBackgroundDrawableResource(R.color.window_background_light); - } else { - activity.getWindow().setBackgroundDrawableResource(R.color.window_background); - } - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.util; + +import android.app.Activity; + +import sharedcode.turboeditor.R; +import sharedcode.turboeditor.preferences.PreferenceHelper; + +public class ThemeUtils { + + public static void setTheme(Activity activity){ + boolean light = PreferenceHelper.getLightTheme(activity); + if (light) { + activity.setTheme(R.style.AppThemeBaseLight); + } else { + activity.setTheme(R.style.AppThemeBaseDark); + } + } + + public static void setPreferenceTheme(Activity activity){ + boolean light = PreferenceHelper.getLightTheme(activity); + if (light) { + activity.setTheme(R.style.PreferenceLight); + } else { + activity.setTheme(R.style.PreferenceDark); + } + } + + public static void setWindowsBackground(Activity activity) { + boolean whiteTheme = PreferenceHelper.getLightTheme(activity); + if (whiteTheme) { + activity.getWindow().setBackgroundDrawableResource(R.color.window_background_light); + } else { + activity.getWindow().setBackgroundDrawableResource(R.color.window_background); + } + } +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/systemui/SystemUiHelper.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/systemui/SystemUiHelper.java index dd5e419..5d83e98 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/systemui/SystemUiHelper.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/systemui/SystemUiHelper.java @@ -1,290 +1,290 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.util.systemui; - -import android.app.Activity; -import android.os.Build; -import android.os.Handler; -import android.os.Looper; -import android.view.WindowManager; - -/** - * Helper for controlling the visibility of the System UI across the various API levels. To use - * this API, instantiate an instance of this class with the required level. The level specifies the - * extent to which the System UI's visibility is changed when you call {@link #hide()} - * or {@link #toggle()}. - */ -public final class SystemUiHelper { - - /** - * In this level, the helper will toggle low profile mode. - */ - public static final int LEVEL_LOW_PROFILE = 0; - - /** - * In this level, the helper will toggle the visibility of the status bar. - * If there is a navigation bar, it will toggle low profile mode. - */ - public static final int LEVEL_HIDE_STATUS_BAR = 1; - - /** - * In this level, the helper will toggle the visibility of the navigation bar - * (if present and if possible) and status bar. In cases where the navigation - * bar is present but cannot be hidden, it will toggle low profile mode. - */ - public static final int LEVEL_LEAN_BACK = 2; - - /** - * In this level, the helper will toggle the visibility of the navigation bar - * (if present and if possible) and status bar, in an immersive mode. This means that the app - * will continue to receive all touch events. The user can reveal the system bars with an - * inward swipe along the region where the system bars normally appear. - * - *

The {@link #FLAG_IMMERSIVE_STICKY} flag can be used to control how the system bars are - * displayed. - */ - public static final int LEVEL_IMMERSIVE = 3; - - /** - * When this flag is set, the - * {@link android.view.WindowManager.LayoutParams#FLAG_LAYOUT_IN_SCREEN} - * flag will be set on older devices, making the status bar "float" on top - * of the activity layout. This is most useful when there are no controls at - * the top of the activity layout. - *

- * This flag isn't used on newer devices because the action - * bar, the most important structural element of an Android app, should - * be visible and not obscured by the system UI. - */ - public static final int FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES = 0x1; - - /** - * Used with {@link #LEVEL_IMMERSIVE}. When this flag is set, an inward swipe in the system - * bars areas will cause the system bars to temporarily appear in a semi-transparent state, - * but no flags are cleared, and your system UI visibility change listeners are not triggered. - * The bars automatically hide again after a short delay, or if the user interacts with the - * middle of the screen. - */ - public static final int FLAG_IMMERSIVE_STICKY = 0x2; - - private static final String LOG_TAG = SystemUiHelper.class.getSimpleName(); - - private final SystemUiHelperImpl mImpl; - - private final Handler mHandler; - private final Runnable mHideRunnable; - - /** - * Construct a new SystemUiHelper. - * - * @param activity The Activity who's system UI should be changed - * @param level The level of hiding. Should be either {@link #LEVEL_LOW_PROFILE}, - * {@link #LEVEL_HIDE_STATUS_BAR}, {@link #LEVEL_LEAN_BACK} or - * {@link #LEVEL_IMMERSIVE} - * @param flags Additional options. See {@link #FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES} and - * {@link #FLAG_IMMERSIVE_STICKY} - */ - public SystemUiHelper(Activity activity, int level, int flags) { - this(activity, level, flags, null); - } - - /** - * Construct a new SystemUiHelper. - * - * @param activity The Activity who's system UI should be changed - * @param level The level of hiding. Should be either {@link #LEVEL_LOW_PROFILE}, - * {@link #LEVEL_HIDE_STATUS_BAR}, {@link #LEVEL_LEAN_BACK} or - * {@link #LEVEL_IMMERSIVE} - * @param flags Additional options. See {@link #FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES} and - * {@link #FLAG_IMMERSIVE_STICKY} - * @param listener A listener which is called when the system visibility is changed - */ - public SystemUiHelper(Activity activity, int level, int flags, - OnVisibilityChangeListener listener) { - - mHandler = new Handler(Looper.getMainLooper()); - mHideRunnable = new HideRunnable(); - - // Create impl - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - mImpl = new SystemUiHelperImplKK(activity, level, flags, listener); - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - mImpl = new SystemUiHelperImplJB(activity, level, flags, listener); - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - mImpl = new SystemUiHelperImplICS(activity, level, flags, listener); - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - mImpl = new SystemUiHelperImplHC(activity, level, flags, listener); - } else { - mImpl = new SystemUiHelperImplBase(activity, level, flags, listener); - } - } - - /** - * @return true if the system UI is currently showing. What this means depends on the mode this - * {@link android.example.android.systemuivis.SystemUiHelper} was instantiated with. - */ - public boolean isShowing() { - return mImpl.isShowing(); - } - - /** - * Show the system UI. What this means depends on the mode this {@link android.example.android.systemuivis.SystemUiHelper} was - * instantiated with. - * - *

Any currently queued delayed hide requests will be removed. - */ - public void show() { - // Ensure that any currently queued hide calls are removed - removeQueuedRunnables(); - - mImpl.show(); - } - - /** - * Hide the system UI. What this means depends on the mode this {@link android.example.android.systemuivis.SystemUiHelper} was - * instantiated with. - * - *

Any currently queued delayed hide requests will be removed. - */ - public void hide() { - // Ensure that any currently queued hide calls are removed - removeQueuedRunnables(); - - mImpl.hide(); - } - - /** - * Request that the system UI is hidden after a delay. - * - *

Any currently queued delayed hide requests will be removed. - * - * @param delayMillis The delay (in milliseconds) until the Runnable - * will be executed. - */ - public void delayHide(long delayMillis) { - // Ensure that any currently queued hide calls are removed - removeQueuedRunnables(); - - mHandler.postDelayed(mHideRunnable, delayMillis); - } - - /** - * Toggle whether the system UI is displayed. - */ - public void toggle() { - if (mImpl.isShowing()) { - mImpl.hide(); - } else { - mImpl.show(); - } - } - - private void removeQueuedRunnables() { - // Ensure that any currently queued hide calls are removed - mHandler.removeCallbacks(mHideRunnable); - } - - /** - * A callback interface used to listen for system UI visibility changes. - */ - public interface OnVisibilityChangeListener { - /** - * Called when the system UI visibility has changed. - * - * @param visible True if the system UI is visible. - */ - public void onVisibilityChange(boolean visible); - } - - static abstract class SystemUiHelperImpl { - - final Activity mActivity; - final int mLevel; - final int mFlags; - final OnVisibilityChangeListener mOnVisibilityChangeListener; - - boolean mIsShowing = true; - - SystemUiHelperImpl(Activity activity, int level, int flags, - OnVisibilityChangeListener onVisibilityChangeListener) { - mActivity = activity; - mLevel = level; - mFlags = flags; - mOnVisibilityChangeListener = onVisibilityChangeListener; - } - - abstract void show(); - abstract void hide(); - - boolean isShowing() { - return mIsShowing; - } - - void setIsShowing(boolean isShowing) { - mIsShowing = isShowing; - - if (mOnVisibilityChangeListener != null) { - mOnVisibilityChangeListener.onVisibilityChange(mIsShowing); - } - } - } - - /** - * Base implementation. Used on API level 10 and below. - */ - static class SystemUiHelperImplBase extends SystemUiHelperImpl { - - SystemUiHelperImplBase(Activity activity, int level, int flags, - OnVisibilityChangeListener onVisibilityChangeListener) { - super(activity, level, flags, onVisibilityChangeListener); - - if ((mFlags & SystemUiHelper.FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES) != 0) { - mActivity.getWindow().addFlags( - WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); - } - } - - @Override - void show() { - if (mLevel > SystemUiHelper.LEVEL_LOW_PROFILE) { - mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); - setIsShowing(true); - } - } - - @Override - void hide() { - if (mLevel > SystemUiHelper.LEVEL_LOW_PROFILE) { - mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); - setIsShowing(false); - } - } - } - - private class HideRunnable implements Runnable { - @Override - public void run() { - hide(); - } - } - -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.util.systemui; + +import android.app.Activity; +import android.os.Build; +import android.os.Handler; +import android.os.Looper; +import android.view.WindowManager; + +/** + * Helper for controlling the visibility of the System UI across the various API levels. To use + * this API, instantiate an instance of this class with the required level. The level specifies the + * extent to which the System UI's visibility is changed when you call {@link #hide()} + * or {@link #toggle()}. + */ +public final class SystemUiHelper { + + /** + * In this level, the helper will toggle low profile mode. + */ + public static final int LEVEL_LOW_PROFILE = 0; + + /** + * In this level, the helper will toggle the visibility of the status bar. + * If there is a navigation bar, it will toggle low profile mode. + */ + public static final int LEVEL_HIDE_STATUS_BAR = 1; + + /** + * In this level, the helper will toggle the visibility of the navigation bar + * (if present and if possible) and status bar. In cases where the navigation + * bar is present but cannot be hidden, it will toggle low profile mode. + */ + public static final int LEVEL_LEAN_BACK = 2; + + /** + * In this level, the helper will toggle the visibility of the navigation bar + * (if present and if possible) and status bar, in an immersive mode. This means that the app + * will continue to receive all touch events. The user can reveal the system bars with an + * inward swipe along the region where the system bars normally appear. + * + *

The {@link #FLAG_IMMERSIVE_STICKY} flag can be used to control how the system bars are + * displayed. + */ + public static final int LEVEL_IMMERSIVE = 3; + + /** + * When this flag is set, the + * {@link android.view.WindowManager.LayoutParams#FLAG_LAYOUT_IN_SCREEN} + * flag will be set on older devices, making the status bar "float" on top + * of the activity layout. This is most useful when there are no controls at + * the top of the activity layout. + *

+ * This flag isn't used on newer devices because the action + * bar, the most important structural element of an Android app, should + * be visible and not obscured by the system UI. + */ + public static final int FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES = 0x1; + + /** + * Used with {@link #LEVEL_IMMERSIVE}. When this flag is set, an inward swipe in the system + * bars areas will cause the system bars to temporarily appear in a semi-transparent state, + * but no flags are cleared, and your system UI visibility change listeners are not triggered. + * The bars automatically hide again after a short delay, or if the user interacts with the + * middle of the screen. + */ + public static final int FLAG_IMMERSIVE_STICKY = 0x2; + + private static final String LOG_TAG = SystemUiHelper.class.getSimpleName(); + + private final SystemUiHelperImpl mImpl; + + private final Handler mHandler; + private final Runnable mHideRunnable; + + /** + * Construct a new SystemUiHelper. + * + * @param activity The Activity who's system UI should be changed + * @param level The level of hiding. Should be either {@link #LEVEL_LOW_PROFILE}, + * {@link #LEVEL_HIDE_STATUS_BAR}, {@link #LEVEL_LEAN_BACK} or + * {@link #LEVEL_IMMERSIVE} + * @param flags Additional options. See {@link #FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES} and + * {@link #FLAG_IMMERSIVE_STICKY} + */ + public SystemUiHelper(Activity activity, int level, int flags) { + this(activity, level, flags, null); + } + + /** + * Construct a new SystemUiHelper. + * + * @param activity The Activity who's system UI should be changed + * @param level The level of hiding. Should be either {@link #LEVEL_LOW_PROFILE}, + * {@link #LEVEL_HIDE_STATUS_BAR}, {@link #LEVEL_LEAN_BACK} or + * {@link #LEVEL_IMMERSIVE} + * @param flags Additional options. See {@link #FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES} and + * {@link #FLAG_IMMERSIVE_STICKY} + * @param listener A listener which is called when the system visibility is changed + */ + public SystemUiHelper(Activity activity, int level, int flags, + OnVisibilityChangeListener listener) { + + mHandler = new Handler(Looper.getMainLooper()); + mHideRunnable = new HideRunnable(); + + // Create impl + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + mImpl = new SystemUiHelperImplKK(activity, level, flags, listener); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + mImpl = new SystemUiHelperImplJB(activity, level, flags, listener); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + mImpl = new SystemUiHelperImplICS(activity, level, flags, listener); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + mImpl = new SystemUiHelperImplHC(activity, level, flags, listener); + } else { + mImpl = new SystemUiHelperImplBase(activity, level, flags, listener); + } + } + + /** + * @return true if the system UI is currently showing. What this means depends on the mode this + * {@link android.example.android.systemuivis.SystemUiHelper} was instantiated with. + */ + public boolean isShowing() { + return mImpl.isShowing(); + } + + /** + * Show the system UI. What this means depends on the mode this {@link android.example.android.systemuivis.SystemUiHelper} was + * instantiated with. + * + *

Any currently queued delayed hide requests will be removed. + */ + public void show() { + // Ensure that any currently queued hide calls are removed + removeQueuedRunnables(); + + mImpl.show(); + } + + /** + * Hide the system UI. What this means depends on the mode this {@link android.example.android.systemuivis.SystemUiHelper} was + * instantiated with. + * + *

Any currently queued delayed hide requests will be removed. + */ + public void hide() { + // Ensure that any currently queued hide calls are removed + removeQueuedRunnables(); + + mImpl.hide(); + } + + /** + * Request that the system UI is hidden after a delay. + * + *

Any currently queued delayed hide requests will be removed. + * + * @param delayMillis The delay (in milliseconds) until the Runnable + * will be executed. + */ + public void delayHide(long delayMillis) { + // Ensure that any currently queued hide calls are removed + removeQueuedRunnables(); + + mHandler.postDelayed(mHideRunnable, delayMillis); + } + + /** + * Toggle whether the system UI is displayed. + */ + public void toggle() { + if (mImpl.isShowing()) { + mImpl.hide(); + } else { + mImpl.show(); + } + } + + private void removeQueuedRunnables() { + // Ensure that any currently queued hide calls are removed + mHandler.removeCallbacks(mHideRunnable); + } + + /** + * A callback interface used to listen for system UI visibility changes. + */ + public interface OnVisibilityChangeListener { + /** + * Called when the system UI visibility has changed. + * + * @param visible True if the system UI is visible. + */ + public void onVisibilityChange(boolean visible); + } + + static abstract class SystemUiHelperImpl { + + final Activity mActivity; + final int mLevel; + final int mFlags; + final OnVisibilityChangeListener mOnVisibilityChangeListener; + + boolean mIsShowing = true; + + SystemUiHelperImpl(Activity activity, int level, int flags, + OnVisibilityChangeListener onVisibilityChangeListener) { + mActivity = activity; + mLevel = level; + mFlags = flags; + mOnVisibilityChangeListener = onVisibilityChangeListener; + } + + abstract void show(); + abstract void hide(); + + boolean isShowing() { + return mIsShowing; + } + + void setIsShowing(boolean isShowing) { + mIsShowing = isShowing; + + if (mOnVisibilityChangeListener != null) { + mOnVisibilityChangeListener.onVisibilityChange(mIsShowing); + } + } + } + + /** + * Base implementation. Used on API level 10 and below. + */ + static class SystemUiHelperImplBase extends SystemUiHelperImpl { + + SystemUiHelperImplBase(Activity activity, int level, int flags, + OnVisibilityChangeListener onVisibilityChangeListener) { + super(activity, level, flags, onVisibilityChangeListener); + + if ((mFlags & SystemUiHelper.FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES) != 0) { + mActivity.getWindow().addFlags( + WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); + } + } + + @Override + void show() { + if (mLevel > SystemUiHelper.LEVEL_LOW_PROFILE) { + mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + setIsShowing(true); + } + } + + @Override + void hide() { + if (mLevel > SystemUiHelper.LEVEL_LOW_PROFILE) { + mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + setIsShowing(false); + } + } + } + + private class HideRunnable implements Runnable { + @Override + public void run() { + hide(); + } + } + +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/systemui/SystemUiHelperImplHC.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/systemui/SystemUiHelperImplHC.java index 431f6d8..9c62667 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/systemui/SystemUiHelperImplHC.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/systemui/SystemUiHelperImplHC.java @@ -1,94 +1,94 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.util.systemui; - -import android.app.Activity; -import android.support.v7.app.ActionBar; -import android.support.v7.app.ActionBarActivity; -import android.view.View; -import android.view.WindowManager; - -class SystemUiHelperImplHC extends SystemUiHelper.SystemUiHelperImpl - implements View.OnSystemUiVisibilityChangeListener { - - final View mDecorView; - - SystemUiHelperImplHC(Activity activity, int level, int flags, - SystemUiHelper.OnVisibilityChangeListener onVisibilityChangeListener) { - super(activity, level, flags, onVisibilityChangeListener); - - mDecorView = activity.getWindow().getDecorView(); - mDecorView.setOnSystemUiVisibilityChangeListener(this); - } - - - @Override - void show() { - mDecorView.setSystemUiVisibility(createShowFlags()); - } - - @Override - void hide() { - mDecorView.setSystemUiVisibility(createHideFlags()); - } - - @Override - public final void onSystemUiVisibilityChange(int visibility) { - if ((visibility & createTestFlags()) != 0) { - onSystemUiHidden(); - } else { - onSystemUiShown(); - } - } - - protected void onSystemUiShown() { - ActionBar ab = ((ActionBarActivity) mActivity).getSupportActionBar(); - if (ab != null) { - ab.show(); - } - - mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); - - setIsShowing(true); - } - - protected void onSystemUiHidden() { - ActionBar ab = ((ActionBarActivity) mActivity).getSupportActionBar(); - if (ab != null) { - ab.hide(); - } - - mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); - - setIsShowing(false); - } - - protected int createShowFlags() { - return View.STATUS_BAR_VISIBLE; - } - - protected int createHideFlags() { - return View.STATUS_BAR_HIDDEN; - } - - protected int createTestFlags() { - return View.STATUS_BAR_HIDDEN; - } +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.util.systemui; + +import android.app.Activity; +import android.support.v7.app.ActionBar; +import android.support.v7.app.ActionBarActivity; +import android.view.View; +import android.view.WindowManager; + +class SystemUiHelperImplHC extends SystemUiHelper.SystemUiHelperImpl + implements View.OnSystemUiVisibilityChangeListener { + + final View mDecorView; + + SystemUiHelperImplHC(Activity activity, int level, int flags, + SystemUiHelper.OnVisibilityChangeListener onVisibilityChangeListener) { + super(activity, level, flags, onVisibilityChangeListener); + + mDecorView = activity.getWindow().getDecorView(); + mDecorView.setOnSystemUiVisibilityChangeListener(this); + } + + + @Override + void show() { + mDecorView.setSystemUiVisibility(createShowFlags()); + } + + @Override + void hide() { + mDecorView.setSystemUiVisibility(createHideFlags()); + } + + @Override + public final void onSystemUiVisibilityChange(int visibility) { + if ((visibility & createTestFlags()) != 0) { + onSystemUiHidden(); + } else { + onSystemUiShown(); + } + } + + protected void onSystemUiShown() { + ActionBar ab = ((ActionBarActivity) mActivity).getSupportActionBar(); + if (ab != null) { + ab.show(); + } + + mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + + setIsShowing(true); + } + + protected void onSystemUiHidden() { + ActionBar ab = ((ActionBarActivity) mActivity).getSupportActionBar(); + if (ab != null) { + ab.hide(); + } + + mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + + setIsShowing(false); + } + + protected int createShowFlags() { + return View.STATUS_BAR_VISIBLE; + } + + protected int createHideFlags() { + return View.STATUS_BAR_HIDDEN; + } + + protected int createTestFlags() { + return View.STATUS_BAR_HIDDEN; + } } \ No newline at end of file diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/systemui/SystemUiHelperImplICS.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/systemui/SystemUiHelperImplICS.java index 11d5829..ece98f2 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/systemui/SystemUiHelperImplICS.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/systemui/SystemUiHelperImplICS.java @@ -1,60 +1,60 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.util.systemui; - -import android.annotation.TargetApi; -import android.app.Activity; -import android.os.Build; -import android.view.View; - -@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) -class SystemUiHelperImplICS extends SystemUiHelperImplHC { - - SystemUiHelperImplICS(Activity activity, int level, int flags, - SystemUiHelper.OnVisibilityChangeListener onVisibilityChangeListener) { - super(activity, level, flags, onVisibilityChangeListener); - } - - @Override - protected int createShowFlags() { - return View.SYSTEM_UI_FLAG_VISIBLE; - } - - @Override - protected int createTestFlags() { - if (mLevel >= SystemUiHelper.LEVEL_LEAN_BACK) { - // Intentionally override test flags. - return View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; - } - - return View.SYSTEM_UI_FLAG_LOW_PROFILE; - } - - @Override - protected int createHideFlags() { - int flag = View.SYSTEM_UI_FLAG_LOW_PROFILE; - - if (mLevel >= SystemUiHelper.LEVEL_LEAN_BACK) { - flag |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; - } - - return flag; - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.util.systemui; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.os.Build; +import android.view.View; + +@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) +class SystemUiHelperImplICS extends SystemUiHelperImplHC { + + SystemUiHelperImplICS(Activity activity, int level, int flags, + SystemUiHelper.OnVisibilityChangeListener onVisibilityChangeListener) { + super(activity, level, flags, onVisibilityChangeListener); + } + + @Override + protected int createShowFlags() { + return View.SYSTEM_UI_FLAG_VISIBLE; + } + + @Override + protected int createTestFlags() { + if (mLevel >= SystemUiHelper.LEVEL_LEAN_BACK) { + // Intentionally override test flags. + return View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; + } + + return View.SYSTEM_UI_FLAG_LOW_PROFILE; + } + + @Override + protected int createHideFlags() { + int flag = View.SYSTEM_UI_FLAG_LOW_PROFILE; + + if (mLevel >= SystemUiHelper.LEVEL_LEAN_BACK) { + flag |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; + } + + return flag; + } +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/systemui/SystemUiHelperImplJB.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/systemui/SystemUiHelperImplJB.java index f19e7c7..34cd072 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/systemui/SystemUiHelperImplJB.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/systemui/SystemUiHelperImplJB.java @@ -1,93 +1,93 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.util.systemui; - -import android.annotation.TargetApi; -import android.app.ActionBar; -import android.app.Activity; -import android.os.Build; -import android.view.View; - -@TargetApi(Build.VERSION_CODES.JELLY_BEAN) -class SystemUiHelperImplJB extends SystemUiHelperImplICS { - - SystemUiHelperImplJB(Activity activity, int level, int flags, - SystemUiHelper.OnVisibilityChangeListener onVisibilityChangeListener) { - super(activity, level, flags, onVisibilityChangeListener); - } - - @Override - protected int createShowFlags() { - int flag = super.createShowFlags(); - - if (mLevel >= SystemUiHelper.LEVEL_HIDE_STATUS_BAR) { - flag |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; - - if (mLevel >= SystemUiHelper.LEVEL_LEAN_BACK) { - flag |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; - } - } - - return flag; - } - - @Override - protected int createHideFlags() { - int flag = super.createHideFlags(); - - if (mLevel >= SystemUiHelper.LEVEL_HIDE_STATUS_BAR) { - flag |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_FULLSCREEN; - - if (mLevel >= SystemUiHelper.LEVEL_LEAN_BACK) { - flag |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; - } - } - - return flag; - } - - @Override - protected void onSystemUiShown() { - if (mLevel == SystemUiHelper.LEVEL_LOW_PROFILE) { - // Manually show the action bar when in low profile mode. - ActionBar ab = mActivity.getActionBar(); - if (ab != null) { - ab.show(); - } - } - - setIsShowing(false); - } - - @Override - protected void onSystemUiHidden() { - if (mLevel == SystemUiHelper.LEVEL_LOW_PROFILE) { - // Manually hide the action bar when in low profile mode. - ActionBar ab = mActivity.getActionBar(); - if (ab != null) { - ab.hide(); - } - } - - setIsShowing(true); - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.util.systemui; + +import android.annotation.TargetApi; +import android.app.ActionBar; +import android.app.Activity; +import android.os.Build; +import android.view.View; + +@TargetApi(Build.VERSION_CODES.JELLY_BEAN) +class SystemUiHelperImplJB extends SystemUiHelperImplICS { + + SystemUiHelperImplJB(Activity activity, int level, int flags, + SystemUiHelper.OnVisibilityChangeListener onVisibilityChangeListener) { + super(activity, level, flags, onVisibilityChangeListener); + } + + @Override + protected int createShowFlags() { + int flag = super.createShowFlags(); + + if (mLevel >= SystemUiHelper.LEVEL_HIDE_STATUS_BAR) { + flag |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; + + if (mLevel >= SystemUiHelper.LEVEL_LEAN_BACK) { + flag |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; + } + } + + return flag; + } + + @Override + protected int createHideFlags() { + int flag = super.createHideFlags(); + + if (mLevel >= SystemUiHelper.LEVEL_HIDE_STATUS_BAR) { + flag |= View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_FULLSCREEN; + + if (mLevel >= SystemUiHelper.LEVEL_LEAN_BACK) { + flag |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; + } + } + + return flag; + } + + @Override + protected void onSystemUiShown() { + if (mLevel == SystemUiHelper.LEVEL_LOW_PROFILE) { + // Manually show the action bar when in low profile mode. + ActionBar ab = mActivity.getActionBar(); + if (ab != null) { + ab.show(); + } + } + + setIsShowing(false); + } + + @Override + protected void onSystemUiHidden() { + if (mLevel == SystemUiHelper.LEVEL_LOW_PROFILE) { + // Manually hide the action bar when in low profile mode. + ActionBar ab = mActivity.getActionBar(); + if (ab != null) { + ab.hide(); + } + } + + setIsShowing(true); + } +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/systemui/SystemUiHelperImplKK.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/systemui/SystemUiHelperImplKK.java index 988fc46..8ad73ed 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/systemui/SystemUiHelperImplKK.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/util/systemui/SystemUiHelperImplKK.java @@ -1,53 +1,53 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.util.systemui; - -import android.annotation.TargetApi; -import android.app.Activity; -import android.os.Build; -import android.view.View; - -@TargetApi(Build.VERSION_CODES.KITKAT) -class SystemUiHelperImplKK extends SystemUiHelperImplJB { - - SystemUiHelperImplKK(Activity activity, int level, int flags, - SystemUiHelper.OnVisibilityChangeListener onVisibilityChangeListener) { - super(activity, level, flags, onVisibilityChangeListener); - } - - @Override - protected int createHideFlags() { - int flag = super.createHideFlags(); - - if (mLevel == SystemUiHelper.LEVEL_IMMERSIVE) { - // If the client requested immersive mode, and we're on Android 4.4 - // or later, add relevant flags. Applying HIDE_NAVIGATION without - // IMMERSIVE prevents the activity from accepting all touch events, - // so we only do this on Android 4.4 and later (where IMMERSIVE is - // present). - flag |= ((mFlags & SystemUiHelper.FLAG_IMMERSIVE_STICKY) != 0) - ? View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY - : View.SYSTEM_UI_FLAG_IMMERSIVE; - } - - return flag; - } - -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.util.systemui; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.os.Build; +import android.view.View; + +@TargetApi(Build.VERSION_CODES.KITKAT) +class SystemUiHelperImplKK extends SystemUiHelperImplJB { + + SystemUiHelperImplKK(Activity activity, int level, int flags, + SystemUiHelper.OnVisibilityChangeListener onVisibilityChangeListener) { + super(activity, level, flags, onVisibilityChangeListener); + } + + @Override + protected int createHideFlags() { + int flag = super.createHideFlags(); + + if (mLevel == SystemUiHelper.LEVEL_IMMERSIVE) { + // If the client requested immersive mode, and we're on Android 4.4 + // or later, add relevant flags. Applying HIDE_NAVIGATION without + // IMMERSIVE prevents the activity from accepting all touch events, + // so we only do this on Android 4.4 and later (where IMMERSIVE is + // present). + flag |= ((mFlags & SystemUiHelper.FLAG_IMMERSIVE_STICKY) != 0) + ? View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + : View.SYSTEM_UI_FLAG_IMMERSIVE; + } + + return flag; + } + +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/views/CustomDrawerLayout.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/views/CustomDrawerLayout.java index 8308909..24ccc72 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/views/CustomDrawerLayout.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/views/CustomDrawerLayout.java @@ -1,49 +1,49 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.views; - -import android.content.Context; -import android.support.v4.widget.DrawerLayout; -import android.util.AttributeSet; -import android.view.KeyEvent; - -public class CustomDrawerLayout extends DrawerLayout{ - public CustomDrawerLayout(Context context) { - super(context); - } - - public CustomDrawerLayout(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public CustomDrawerLayout(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - return false; - } - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - return false; - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.views; + +import android.content.Context; +import android.support.v4.widget.DrawerLayout; +import android.util.AttributeSet; +import android.view.KeyEvent; + +public class CustomDrawerLayout extends DrawerLayout{ + public CustomDrawerLayout(Context context) { + super(context); + } + + public CustomDrawerLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public CustomDrawerLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + return false; + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + return false; + } +} diff --git a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/views/GoodScrollView.java b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/views/GoodScrollView.java index a1d9942..7dad030 100644 --- a/libraries/sharedCode/src/main/java/sharedcode/turboeditor/views/GoodScrollView.java +++ b/libraries/sharedCode/src/main/java/sharedcode/turboeditor/views/GoodScrollView.java @@ -1,84 +1,84 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 sharedcode.turboeditor.views; - -import android.content.Context; -import android.os.Handler; -import android.util.AttributeSet; -import android.view.View; -import android.widget.ScrollView; - -public class GoodScrollView extends ScrollView { - - public ScrollInterface scrollInterface; - int lastY; - boolean listenerEnabled = true; - - public GoodScrollView(Context context) { - super(context); - } - - public GoodScrollView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public GoodScrollView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - public void setScrollInterface(ScrollInterface scrollInterface) { - this.scrollInterface = scrollInterface; - } - - @Override - protected void onScrollChanged(int l, int t, int oldl, int oldt) { - super.onScrollChanged(l, t, oldl, oldt); - - if (scrollInterface == null || !listenerEnabled) return; - - if (Math.abs(lastY - t) > 100) { - lastY = t; - scrollInterface.onScrollChanged(l, t, oldl, oldt); - } - - } - - public boolean hasReachedBottom() { - View firstChild = getChildAt(getChildCount() - 1); - - int diff = (firstChild.getBottom() - (getHeight() + getScrollY() + firstChild.getTop()));// Calculate the scrolldiff - return diff <= 0; - } - - public void tempDisableListener(int mills) { - listenerEnabled = false; - new Handler().postDelayed(new Runnable() { - @Override - public void run() { - listenerEnabled = true; - } - }, mills); - } - - - public interface ScrollInterface { - public void onScrollChanged(int l, int t, int oldl, int oldt); - } -} +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 sharedcode.turboeditor.views; + +import android.content.Context; +import android.os.Handler; +import android.util.AttributeSet; +import android.view.View; +import android.widget.ScrollView; + +public class GoodScrollView extends ScrollView { + + public ScrollInterface scrollInterface; + int lastY; + boolean listenerEnabled = true; + + public GoodScrollView(Context context) { + super(context); + } + + public GoodScrollView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public GoodScrollView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public void setScrollInterface(ScrollInterface scrollInterface) { + this.scrollInterface = scrollInterface; + } + + @Override + protected void onScrollChanged(int l, int t, int oldl, int oldt) { + super.onScrollChanged(l, t, oldl, oldt); + + if (scrollInterface == null || !listenerEnabled) return; + + if (Math.abs(lastY - t) > 100) { + lastY = t; + scrollInterface.onScrollChanged(l, t, oldl, oldt); + } + + } + + public boolean hasReachedBottom() { + View firstChild = getChildAt(getChildCount() - 1); + + int diff = (firstChild.getBottom() - (getHeight() + getScrollY() + firstChild.getTop()));// Calculate the scrolldiff + return diff <= 0; + } + + public void tempDisableListener(int mills) { + listenerEnabled = false; + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + listenerEnabled = true; + } + }, mills); + } + + + public interface ScrollInterface { + public void onScrollChanged(int l, int t, int oldl, int oldt); + } +} diff --git a/libraries/sharedCode/src/main/res/layout/activity_home.xml b/libraries/sharedCode/src/main/res/layout/activity_home.xml index c18266f..1cb26e4 100644 --- a/libraries/sharedCode/src/main/res/layout/activity_home.xml +++ b/libraries/sharedCode/src/main/res/layout/activity_home.xml @@ -1,353 +1,353 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libraries/sharedCode/src/main/res/layout/activity_licenses.xml b/libraries/sharedCode/src/main/res/layout/activity_licenses.xml index 0086dde..86b6d10 100644 --- a/libraries/sharedCode/src/main/res/layout/activity_licenses.xml +++ b/libraries/sharedCode/src/main/res/layout/activity_licenses.xml @@ -1,31 +1,31 @@ - - - - + + + + diff --git a/libraries/sharedCode/src/main/res/layout/activity_select_file.xml b/libraries/sharedCode/src/main/res/layout/activity_select_file.xml index 284894d..9afcd02 100644 --- a/libraries/sharedCode/src/main/res/layout/activity_select_file.xml +++ b/libraries/sharedCode/src/main/res/layout/activity_select_file.xml @@ -1,60 +1,60 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/libraries/sharedCode/src/main/res/layout/demo_changelog_fragment_dialogstandard.xml b/libraries/sharedCode/src/main/res/layout/demo_changelog_fragment_dialogstandard.xml index 489bf3e..e8eb328 100644 --- a/libraries/sharedCode/src/main/res/layout/demo_changelog_fragment_dialogstandard.xml +++ b/libraries/sharedCode/src/main/res/layout/demo_changelog_fragment_dialogstandard.xml @@ -1,28 +1,28 @@ - - - - + + + \ No newline at end of file diff --git a/libraries/sharedCode/src/main/res/layout/dialog_encoding_list.xml b/libraries/sharedCode/src/main/res/layout/dialog_encoding_list.xml index bc6c6b6..860ac04 100644 --- a/libraries/sharedCode/src/main/res/layout/dialog_encoding_list.xml +++ b/libraries/sharedCode/src/main/res/layout/dialog_encoding_list.xml @@ -1,46 +1,46 @@ - - - - - - - - - - + + + + + + + + + + diff --git a/libraries/sharedCode/src/main/res/layout/dialog_fragment_edittext.xml b/libraries/sharedCode/src/main/res/layout/dialog_fragment_edittext.xml index c437740..745b6af 100644 --- a/libraries/sharedCode/src/main/res/layout/dialog_fragment_edittext.xml +++ b/libraries/sharedCode/src/main/res/layout/dialog_fragment_edittext.xml @@ -1,37 +1,37 @@ - - - - - - - + + + + + + + diff --git a/libraries/sharedCode/src/main/res/layout/dialog_fragment_file_info.xml b/libraries/sharedCode/src/main/res/layout/dialog_fragment_file_info.xml index a65cfa9..cfe85d5 100644 --- a/libraries/sharedCode/src/main/res/layout/dialog_fragment_file_info.xml +++ b/libraries/sharedCode/src/main/res/layout/dialog_fragment_file_info.xml @@ -1,32 +1,32 @@ - - - - - - - - + + + + + + + + diff --git a/libraries/sharedCode/src/main/res/layout/dialog_fragment_find_text.xml b/libraries/sharedCode/src/main/res/layout/dialog_fragment_find_text.xml index 12d9b31..e30b2a6 100644 --- a/libraries/sharedCode/src/main/res/layout/dialog_fragment_find_text.xml +++ b/libraries/sharedCode/src/main/res/layout/dialog_fragment_find_text.xml @@ -1,77 +1,77 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/libraries/sharedCode/src/main/res/layout/dialog_fragment_new_file_details.xml b/libraries/sharedCode/src/main/res/layout/dialog_fragment_new_file_details.xml index 3f42e91..1964a11 100644 --- a/libraries/sharedCode/src/main/res/layout/dialog_fragment_new_file_details.xml +++ b/libraries/sharedCode/src/main/res/layout/dialog_fragment_new_file_details.xml @@ -1,61 +1,61 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/libraries/sharedCode/src/main/res/layout/dialog_fragment_seekbar.xml b/libraries/sharedCode/src/main/res/layout/dialog_fragment_seekbar.xml index c09787d..6d01323 100644 --- a/libraries/sharedCode/src/main/res/layout/dialog_fragment_seekbar.xml +++ b/libraries/sharedCode/src/main/res/layout/dialog_fragment_seekbar.xml @@ -1,33 +1,33 @@ - - - - - - - - + + + + + + + + diff --git a/libraries/sharedCode/src/main/res/layout/fragment_reader_listview.xml b/libraries/sharedCode/src/main/res/layout/fragment_reader_listview.xml index 484f398..72d3bbd 100644 --- a/libraries/sharedCode/src/main/res/layout/fragment_reader_listview.xml +++ b/libraries/sharedCode/src/main/res/layout/fragment_reader_listview.xml @@ -1,27 +1,27 @@ - - - - - + + + + + diff --git a/libraries/sharedCode/src/main/res/layout/fragment_settings.xml b/libraries/sharedCode/src/main/res/layout/fragment_settings.xml index 6cfcffb..7e93fb1 100644 --- a/libraries/sharedCode/src/main/res/layout/fragment_settings.xml +++ b/libraries/sharedCode/src/main/res/layout/fragment_settings.xml @@ -1,267 +1,267 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libraries/sharedCode/src/main/res/layout/item_drawer_list.xml b/libraries/sharedCode/src/main/res/layout/item_drawer_list.xml index 42695d0..6b62f92 100644 --- a/libraries/sharedCode/src/main/res/layout/item_drawer_list.xml +++ b/libraries/sharedCode/src/main/res/layout/item_drawer_list.xml @@ -1,49 +1,49 @@ - - - - - - - - - - + + + + + + + + + + diff --git a/libraries/sharedCode/src/main/res/layout/item_file_list.xml b/libraries/sharedCode/src/main/res/layout/item_file_list.xml index b9b4720..22e5740 100644 --- a/libraries/sharedCode/src/main/res/layout/item_file_list.xml +++ b/libraries/sharedCode/src/main/res/layout/item_file_list.xml @@ -1,61 +1,61 @@ - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/libraries/sharedCode/src/main/res/layout/item_single_choice.xml b/libraries/sharedCode/src/main/res/layout/item_single_choice.xml index cb1e909..4ad4528 100644 --- a/libraries/sharedCode/src/main/res/layout/item_single_choice.xml +++ b/libraries/sharedCode/src/main/res/layout/item_single_choice.xml @@ -1,32 +1,32 @@ - - - - + + + + diff --git a/libraries/sharedCode/src/main/res/layout/item_two_lines.xml b/libraries/sharedCode/src/main/res/layout/item_two_lines.xml index ca36e4e..e47fcde 100644 --- a/libraries/sharedCode/src/main/res/layout/item_two_lines.xml +++ b/libraries/sharedCode/src/main/res/layout/item_two_lines.xml @@ -1,58 +1,58 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/libraries/sharedCode/src/main/res/layout/toolbar.xml b/libraries/sharedCode/src/main/res/layout/toolbar.xml index 07267f5..bf8a02d 100644 --- a/libraries/sharedCode/src/main/res/layout/toolbar.xml +++ b/libraries/sharedCode/src/main/res/layout/toolbar.xml @@ -1,31 +1,31 @@ - - - - - + + + + \ No newline at end of file diff --git a/libraries/sharedCode/src/main/res/menu/activity_select_file.xml b/libraries/sharedCode/src/main/res/menu/activity_select_file.xml index 5f947bd..e4e568c 100644 --- a/libraries/sharedCode/src/main/res/menu/activity_select_file.xml +++ b/libraries/sharedCode/src/main/res/menu/activity_select_file.xml @@ -1,47 +1,47 @@ - - - -

- - - - - - - - - + + + + + + + + + + + + + diff --git a/libraries/sharedCode/src/main/res/menu/fragment_editor.xml b/libraries/sharedCode/src/main/res/menu/fragment_editor.xml index fad5a8a..84f0a39 100644 --- a/libraries/sharedCode/src/main/res/menu/fragment_editor.xml +++ b/libraries/sharedCode/src/main/res/menu/fragment_editor.xml @@ -1,71 +1,71 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/libraries/sharedCode/src/main/res/menu/fragment_editor_search.xml b/libraries/sharedCode/src/main/res/menu/fragment_editor_search.xml index 2d1059a..266aebd 100644 --- a/libraries/sharedCode/src/main/res/menu/fragment_editor_search.xml +++ b/libraries/sharedCode/src/main/res/menu/fragment_editor_search.xml @@ -1,58 +1,58 @@ - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + diff --git a/libraries/sharedCode/src/main/res/menu/popup_new_file.xml b/libraries/sharedCode/src/main/res/menu/popup_new_file.xml index 4a1f10b..555d1e6 100644 --- a/libraries/sharedCode/src/main/res/menu/popup_new_file.xml +++ b/libraries/sharedCode/src/main/res/menu/popup_new_file.xml @@ -1,33 +1,33 @@ - - - - - - - - + + + + + + + + \ No newline at end of file diff --git a/libraries/sharedCode/src/main/res/raw/changelog.xml b/libraries/sharedCode/src/main/res/raw/changelog.xml index 958bb37..4e5cc9a 100644 --- a/libraries/sharedCode/src/main/res/raw/changelog.xml +++ b/libraries/sharedCode/src/main/res/raw/changelog.xml @@ -1,168 +1,168 @@ - - - - - - - Manly bug fixes - - - - Turbo Editor is a free and open source app. Now you can show your appreciation and support development by donating :) - New visual changes to make the app more "Material" - New save dialog when you are about to close a file - Many enchantments and fixes - Have feedback? Please use xda or the email. Thanks! - - - - Many enchantments and fixes - - - - [b]New! [/b]Important improvements to the syntax highlight - [b]New! [/b]Donation option in the about screen. Help to make Turbo Editor a better software! :) - [b]New! [/b]Setting to disable the "page system" - [b]New! [/b]Setting to ignore the back button - Fixed an issue related to cut/copy/past buttons - Other enchantments and fixes - - - - [b]New! [/b]Go To Line feature - Now the syntax highlight updates when scrolling - Other enchantments and fixs - - - - [b]New! [/b]Create empty files with just one click - [b]New! [/b]Read only mode - [b]New! [/b]Autosave function, to save files when the app loses focus - [b]New! [/b]Share the text from other apps to Turbo Editor - [b]New! [/b]Style changes - Other enchantments and fixs - - - - [b]New! [/b]Now you can open Very Big Files - [b]New! [/b]Better support for python and lua - Other enchantments and fixs - - - - [b]New! [/b]Find and Replace function - [b]New! [/b]Find with Regular Expression - [b]New! [/b]Right to Left support - [b]New! [/b]About screen - If a file was modified the app now asks to save the changes - Fixed an issue with the undo function - New animations - Other enchantments - - - - [b]New! [/b]Floating action button in the "Open a file" screen - [b]New! [/b]Set as working folder action in the "Open a file" screen - In the info/about screen added a link to the XDA thread - New menu icon for overflow - Updated the translations - Other enchantments - - - - [b]New! [/b]Added root features! - [b]New! [/b]Open hidden files - [b]New! [/b]GB2312 encoding - Other enchantments - - - - [b]New [/b] encoding dialog. There you can also enable/disable the auto encoding. - Added support to SQL and MATLAB - The font size dialog now shows the current font size - The background of the screens changes in relation to the selected theme - [b]Info[/b] The internet permission is used to show an Interstitial. The ad will be shown [b]at max once per day[/b]. - - - - [b]New![/b] Now you can search text inside the app! - - - - Now you can share the file! - Fixed many issues related to the keyboards - Added an option to activate suggestions and swipe functionality - - - - Fixed a Runtime Error, sorry :) - - - - Save, Undo and Redo actions are visible only when needed - [b]new![/b] Save with CTRL + S - More left space on the line numbers and changed their color - PHP variables now are highlight correctly - JS syntax highlight fixes - Fixed a bug with numbers inserted by certain keyboards - Updated translations - - - - [b]NOTE[/b] Syntax highlighting is updated when the cursor position changes - [b]SPECIAL NOTE[/b] If you like the app please rate it, but if you have some issue please tell me about them on the Google Plus Community, on the XDA thread or on Github :) - Now you can choose between light and dark theme - Fixed some issues with hard keyboards - Changed the size of line numbers to make them more readable - Increased file size limit - Optimized syntax color algorithm - Now you can create a new file without having to save it first - Bug fixes - - - - Now you can both wrap the text and view the line numbers - Optimized the line numbers algorithm - Clicking the X in the navigation drawer also closes the file - Menu titles corrected - Now list updates if a new folder is added - Resized the navigation drawer so it's not fullscreen on smaller devices - Now you can open files up to 50 kb. Be careful, you may experience crashes if you open files that are too big. - Minor fixes - - - - New Icons optimized for all the devices - Updated translations - Bug fixes - - - - Bug fixes - - - - Bug fixes - - - - First Version - - + + + + + + + Manly bug fixes + + + + Turbo Editor is a free and open source app. Now you can show your appreciation and support development by donating :) + New visual changes to make the app more "Material" + New save dialog when you are about to close a file + Many enchantments and fixes + Have feedback? Please use xda or the email. Thanks! + + + + Many enchantments and fixes + + + + [b]New! [/b]Important improvements to the syntax highlight + [b]New! [/b]Donation option in the about screen. Help to make Turbo Editor a better software! :) + [b]New! [/b]Setting to disable the "page system" + [b]New! [/b]Setting to ignore the back button + Fixed an issue related to cut/copy/past buttons + Other enchantments and fixes + + + + [b]New! [/b]Go To Line feature + Now the syntax highlight updates when scrolling + Other enchantments and fixs + + + + [b]New! [/b]Create empty files with just one click + [b]New! [/b]Read only mode + [b]New! [/b]Autosave function, to save files when the app loses focus + [b]New! [/b]Share the text from other apps to Turbo Editor + [b]New! [/b]Style changes + Other enchantments and fixs + + + + [b]New! [/b]Now you can open Very Big Files + [b]New! [/b]Better support for python and lua + Other enchantments and fixs + + + + [b]New! [/b]Find and Replace function + [b]New! [/b]Find with Regular Expression + [b]New! [/b]Right to Left support + [b]New! [/b]About screen + If a file was modified the app now asks to save the changes + Fixed an issue with the undo function + New animations + Other enchantments + + + + [b]New! [/b]Floating action button in the "Open a file" screen + [b]New! [/b]Set as working folder action in the "Open a file" screen + In the info/about screen added a link to the XDA thread + New menu icon for overflow + Updated the translations + Other enchantments + + + + [b]New! [/b]Added root features! + [b]New! [/b]Open hidden files + [b]New! [/b]GB2312 encoding + Other enchantments + + + + [b]New [/b] encoding dialog. There you can also enable/disable the auto encoding. + Added support to SQL and MATLAB + The font size dialog now shows the current font size + The background of the screens changes in relation to the selected theme + [b]Info[/b] The internet permission is used to show an Interstitial. The ad will be shown [b]at max once per day[/b]. + + + + [b]New![/b] Now you can search text inside the app! + + + + Now you can share the file! + Fixed many issues related to the keyboards + Added an option to activate suggestions and swipe functionality + + + + Fixed a Runtime Error, sorry :) + + + + Save, Undo and Redo actions are visible only when needed + [b]new![/b] Save with CTRL + S + More left space on the line numbers and changed their color + PHP variables now are highlight correctly + JS syntax highlight fixes + Fixed a bug with numbers inserted by certain keyboards + Updated translations + + + + [b]NOTE[/b] Syntax highlighting is updated when the cursor position changes + [b]SPECIAL NOTE[/b] If you like the app please rate it, but if you have some issue please tell me about them on the Google Plus Community, on the XDA thread or on Github :) + Now you can choose between light and dark theme + Fixed some issues with hard keyboards + Changed the size of line numbers to make them more readable + Increased file size limit + Optimized syntax color algorithm + Now you can create a new file without having to save it first + Bug fixes + + + + Now you can both wrap the text and view the line numbers + Optimized the line numbers algorithm + Clicking the X in the navigation drawer also closes the file + Menu titles corrected + Now list updates if a new folder is added + Resized the navigation drawer so it's not fullscreen on smaller devices + Now you can open files up to 50 kb. Be careful, you may experience crashes if you open files that are too big. + Minor fixes + + + + New Icons optimized for all the devices + Updated translations + Bug fixes + + + + Bug fixes + + + + Bug fixes + + + + First Version + + diff --git a/libraries/sharedCode/src/main/res/values-es-rMX/strings.xml b/libraries/sharedCode/src/main/res/values-es-rMX/strings.xml index d84567b..2e89f54 100644 --- a/libraries/sharedCode/src/main/res/values-es-rMX/strings.xml +++ b/libraries/sharedCode/src/main/res/values-es-rMX/strings.xml @@ -1,153 +1,153 @@ - - - - - - - Nueva cuenta - Activo - Borrar - Borrando archivos… - Subiendo… - Carpeta local actual - Llave privada - Claro - Codificación - Compartir - Nueva carpeta local - Nueva capeta remota - Nuevo archivo remoto - Nuevo archivo local - Desconectar - Carpeta local por defecto - Donde descargar? - Descargar - Descarga completa - Duplicar - Finalizado - Inicio - Servidor - Información - Local - Identificandote… - Editar - Mover - Oculto - Turbo Client - Turbo Editor - Nombre de usuario - Pasivo - Contraseña - Contraseña - Dejelo en blanco para rellenarlo en cada sesion - Puerto - Preferencias - Remoto - Para cambiar el tema, reinicie la aplicacion - Renombra - Carpeta remota predeterminada - Guardar - Oscuro - Seleccionar - Selecciona una cuenta - ¿Estás seguro? - Algo salio mal - No transferir el mismo archivo - Tema de la aplicación - Tipo de conexión - Tipo de protocolo - Otra carpeta - Usa una contraseña - Cargar - Subida finalizada - Que desea hacer? - Resalto de sintaxis - Deshacer - Rehacer - Sincronizar - Carpeta remota para sincronziar - Carpeta local para sincronizar - Vota - No se puede contactar con Google Play - Apoyar el desarrollo de otras características. - Actualizar a Premium - Actualizar a Premium y apoyar el desarrollo de Turbo Client! - Descargar version desbloqueada - ¿Que es Turbo Client? ¿Vale la pena? Fije su precio! - Actualice para desbloquear estas caracteristicas: - Poder para abrir y modificar cualquier tipo de archivo. - Servicio de copia de seguridad de copia de seguridad y restaurar sus datos de forma segura. - Desbloquear caracteristicas Premium - Le gusta esta aplicacion! - Me gusta esta aplicacion! - Copia de seguridad de las cuentas - Restaruar cuentas - Copia de seguridad y compartir las cuentas - Importortando cuentas… - Exportando las cuentas... - No se encontraron copias de seguridad - No se puede abrir el archivo - La carpeta temporal no existe - Se a producido un error - Interfaz de usuario - Remover - Fecha de modificacion - Nombre - Tamaño - Ordenar - Abrir - El archivo %1$s fue modificado, deseas subirlo? - El archivo %1$s se guardo correctamente! - Crear una cuenta nueva - Crear una nueva cuenta para comenzar. - Tipo - Enviar Comentarios - Copiar URL - Cortar - Pegar - Avanzado - Automatico - Bytes - Unidad de medida para el tamaño del archivo - Licencia de codigo abierto - Ver licencia de codigo abierto - Abrir un archivo - Abrir solo esta vez - Cambiar tipo de lista - Usar monoespacio - Archivos recientes - Tamaño de la fuente - Nombre de la conexion - Numero de linea - Wrap content - View it on the web - The file size is too big - Search - Add - File - Carpeta - Light Theme - Goto Line - Find - Find and Replace - Root Permission - Compartir - Text Suggestions - + + + + + + + Nueva cuenta + Activo + Borrar + Borrando archivos… + Subiendo… + Carpeta local actual + Llave privada + Claro + Codificación + Compartir + Nueva carpeta local + Nueva capeta remota + Nuevo archivo remoto + Nuevo archivo local + Desconectar + Carpeta local por defecto + Donde descargar? + Descargar + Descarga completa + Duplicar + Finalizado + Inicio + Servidor + Información + Local + Identificandote… + Editar + Mover + Oculto + Turbo Client + Turbo Editor + Nombre de usuario + Pasivo + Contraseña + Contraseña + Dejelo en blanco para rellenarlo en cada sesion + Puerto + Preferencias + Remoto + Para cambiar el tema, reinicie la aplicacion + Renombra + Carpeta remota predeterminada + Guardar + Oscuro + Seleccionar + Selecciona una cuenta + ¿Estás seguro? + Algo salio mal + No transferir el mismo archivo + Tema de la aplicación + Tipo de conexión + Tipo de protocolo + Otra carpeta + Usa una contraseña + Cargar + Subida finalizada + Que desea hacer? + Resalto de sintaxis + Deshacer + Rehacer + Sincronizar + Carpeta remota para sincronziar + Carpeta local para sincronizar + Vota + No se puede contactar con Google Play + Apoyar el desarrollo de otras características. + Actualizar a Premium + Actualizar a Premium y apoyar el desarrollo de Turbo Client! + Descargar version desbloqueada + ¿Que es Turbo Client? ¿Vale la pena? Fije su precio! + Actualice para desbloquear estas caracteristicas: + Poder para abrir y modificar cualquier tipo de archivo. + Servicio de copia de seguridad de copia de seguridad y restaurar sus datos de forma segura. + Desbloquear caracteristicas Premium + Le gusta esta aplicacion! + Me gusta esta aplicacion! + Copia de seguridad de las cuentas + Restaruar cuentas + Copia de seguridad y compartir las cuentas + Importortando cuentas… + Exportando las cuentas... + No se encontraron copias de seguridad + No se puede abrir el archivo + La carpeta temporal no existe + Se a producido un error + Interfaz de usuario + Remover + Fecha de modificacion + Nombre + Tamaño + Ordenar + Abrir + El archivo %1$s fue modificado, deseas subirlo? + El archivo %1$s se guardo correctamente! + Crear una cuenta nueva + Crear una nueva cuenta para comenzar. + Tipo + Enviar Comentarios + Copiar URL + Cortar + Pegar + Avanzado + Automatico + Bytes + Unidad de medida para el tamaño del archivo + Licencia de codigo abierto + Ver licencia de codigo abierto + Abrir un archivo + Abrir solo esta vez + Cambiar tipo de lista + Usar monoespacio + Archivos recientes + Tamaño de la fuente + Nombre de la conexion + Numero de linea + Wrap content + View it on the web + The file size is too big + Search + Add + File + Carpeta + Light Theme + Goto Line + Find + Find and Replace + Root Permission + Compartir + Text Suggestions + diff --git a/libraries/sharedCode/src/main/res/values-ms-rMY/strings.xml b/libraries/sharedCode/src/main/res/values-ms-rMY/strings.xml index a8cbb86..0972fb4 100644 --- a/libraries/sharedCode/src/main/res/values-ms-rMY/strings.xml +++ b/libraries/sharedCode/src/main/res/values-ms-rMY/strings.xml @@ -1,153 +1,153 @@ - - - - - - - Akaun Baru - Aktif - Buang - Menghapus fail... - Memproses... - Folder local sekarang - Kata kunci Private - Terang - Pengekodan - Kongsi - New local folder - New remote folder - New remote file - New local file - Putuskan sambungan - Folder local asal - Muat turun ke? - Muat turun - Muat turun selesai - Fail sudah wujud - Selesai - Home - Host - Maklumat - Local - Melog masuk... - Edit - Alih - Sembunyikan - Turbo Client - Turbo Editor - Kata Pengguna - Pasif - Kata Laluan - Kata Kunci - Biarkan kosong untuk ditanya setiap sesi - Port - Pilihan - Remote - Untuk mengubah tema, tutup dan buka semula aplikasi - Edit nama - root - Simpan - Gelap - Pilih - Pilih akaun - Anda pasti? - Something failed - Do not transfer same file - Tema aplikasi - Jenis sambungan - Jenis protokol - Folder lain - Guna kata laluan - Muat naik - Muat naik selesai - What do you want to do? - Syntax highlight - Batal edit - Buat semula edit - Sinkronisasi - Sinkronisasikan folder remote - Sinkronisasi folder local - Undi - Tidak dapat berhubung dengan Google Play - Sokong pembinaan ciri-ciri hebat yang lain. - Naik taraf kepada Premium - Menaiktaraf kepada Premium dan sokong perkembangan Turbo Client! - Download unlocked version - Apakah nilai Turbo Client pada anda? Berikan harga anda! - Naiktaraf untuk mendapat akses kepada ciri-ciri ini: - Membaca dan mengubahsuai apa-apa jenis fail. - Menyimpan dan mengambil semula data anda dengan selamat. - Akses ciri-ciri Premium - Saya sangat suka aplikasi ini! - Saya suka aplikasi ini! - Backup akaun - Kembalikan akaun - Backup dan kongsi akaun - Mengimport akaun… - Mengeksport akaun... - No backups found - Tidak dapat membuka fail - Temporary folder does not exist - An error occurred - antara muka pengguna - Hapus - Tarikh pengubahsuaian - Nama - Saiz - Susun - Buka - Fail %1$s telah berubah. Adakah anda mahu memuat naik? - The file %1$s was saved with success! - Cipta akaun baru - Create a new account to start. - Jenis - Send feedback - Copy URL - Cut - paste - Advanced - Auto - Bytes - Unit of measurement for file size - Open Source licenses - Show open source licenses - Open a file - Open this time only - Change the list type - Use monospace - Recent files - Font size - Connection Name - Line Numbers - Wrap content - View it on the web - The file size is too big - Search - Add - File - Folder - Light Theme - Goto Line - Find - Find and Replace - Root Permission - Kongsi - Text Suggestions - + + + + + + + Akaun Baru + Aktif + Buang + Menghapus fail... + Memproses... + Folder local sekarang + Kata kunci Private + Terang + Pengekodan + Kongsi + New local folder + New remote folder + New remote file + New local file + Putuskan sambungan + Folder local asal + Muat turun ke? + Muat turun + Muat turun selesai + Fail sudah wujud + Selesai + Home + Host + Maklumat + Local + Melog masuk... + Edit + Alih + Sembunyikan + Turbo Client + Turbo Editor + Kata Pengguna + Pasif + Kata Laluan + Kata Kunci + Biarkan kosong untuk ditanya setiap sesi + Port + Pilihan + Remote + Untuk mengubah tema, tutup dan buka semula aplikasi + Edit nama + root + Simpan + Gelap + Pilih + Pilih akaun + Anda pasti? + Something failed + Do not transfer same file + Tema aplikasi + Jenis sambungan + Jenis protokol + Folder lain + Guna kata laluan + Muat naik + Muat naik selesai + What do you want to do? + Syntax highlight + Batal edit + Buat semula edit + Sinkronisasi + Sinkronisasikan folder remote + Sinkronisasi folder local + Undi + Tidak dapat berhubung dengan Google Play + Sokong pembinaan ciri-ciri hebat yang lain. + Naik taraf kepada Premium + Menaiktaraf kepada Premium dan sokong perkembangan Turbo Client! + Download unlocked version + Apakah nilai Turbo Client pada anda? Berikan harga anda! + Naiktaraf untuk mendapat akses kepada ciri-ciri ini: + Membaca dan mengubahsuai apa-apa jenis fail. + Menyimpan dan mengambil semula data anda dengan selamat. + Akses ciri-ciri Premium + Saya sangat suka aplikasi ini! + Saya suka aplikasi ini! + Backup akaun + Kembalikan akaun + Backup dan kongsi akaun + Mengimport akaun… + Mengeksport akaun... + No backups found + Tidak dapat membuka fail + Temporary folder does not exist + An error occurred + antara muka pengguna + Hapus + Tarikh pengubahsuaian + Nama + Saiz + Susun + Buka + Fail %1$s telah berubah. Adakah anda mahu memuat naik? + The file %1$s was saved with success! + Cipta akaun baru + Create a new account to start. + Jenis + Send feedback + Copy URL + Cut + paste + Advanced + Auto + Bytes + Unit of measurement for file size + Open Source licenses + Show open source licenses + Open a file + Open this time only + Change the list type + Use monospace + Recent files + Font size + Connection Name + Line Numbers + Wrap content + View it on the web + The file size is too big + Search + Add + File + Folder + Light Theme + Goto Line + Find + Find and Replace + Root Permission + Kongsi + Text Suggestions + diff --git a/libraries/sharedCode/src/main/res/values-sk-rSK/strings.xml b/libraries/sharedCode/src/main/res/values-sk-rSK/strings.xml index 47542ec..7bb7364 100644 --- a/libraries/sharedCode/src/main/res/values-sk-rSK/strings.xml +++ b/libraries/sharedCode/src/main/res/values-sk-rSK/strings.xml @@ -1,153 +1,153 @@ - - - - - - - Nový účet - Aktívne - Vymazať - Mažem súbory… - Načítavanie… - Aktuálna miestna zložka - Súkromný kľúč - Svetlá - Kódovanie - Zdieľať - Nový lokálny súbor - Nový vzdialený priečinok - New remote file - New local file - Odpojiť sa - Predvol. miestna zložka - Kde oi chcete stiahnuť? - Stiahnuť - Sťahovanie dokončené - Duplikát - Hotovo - Domov - Hosť - Info - Lokálny - Prihlasujem sa… - Upraviť - Presunúť - Schovať - Turbo Klient - Turbo Editor - Užív. meno - Pasívny - Heslo - Heslo - Ponechajte prázdne, pre výzvu pri každej relácii - Port - Predvoľby - vzdialený - Pre zmenu témy, reštartujte aplikáciu - Premenovať - Root - Uložiť - Tmavá - Vybrať - Vyberte účet - Ste si istý? - Niečo zlyhalo - Nepresúvajte rovnaký súbor - Téma - Typ pripojenia - Typ protokolu - Ďalšia zložka - Napíš heslo - Nahrať - Nahrávanie dokončené - Čo chceš robiť? - Syntax highlight - Vrátiť späť - Prerobiť - Synchronizovať - Vzdial. zložka na synchronizáciu - Miestna zložka na synchronizáciu - Ohodnotiť - Nemožno kontaktovať Google Play - Podpor rozvoj ďalších skvelých funkcií. - Vylepšiť na Premium - Vylepši na Premium a podpor tým vývoj Turbo klienta! - Stiahnuť odomknutú verziu - Čo pre teba Turbo klient znamená? Stanov si vlastnú cenu! - Vylepši na Premium, pre odomknutie týchto funkcií: - Power to open and modify any type of file. - Backup service to backup and restore your data safely. - Unlock the Premium features - I really like this app! - I love this app! - Backup the accounts - Restore the accounts - Backup and share the accounts - Importing the accounts… - Exporting the accounts... - No backups found - Cannot open the file - Temporary folder does not exist - An error occurred - Ui - Remove - Modification date - Name - Size - Sort - Open - The file %1$s was modified, do you want to upload it? - The file %1$s was saved with success! - Create a new account - Create a new account to start. - Type - Send feedback - Copy URL - Cut - paste - Advanced - Auto - Bytes - Unit of measurement for file size - Open Source licenses - Show open source licenses - Open a file - Open this time only - Change the list type - Use monospace - Recent files - Font size - Connection Name - Line Numbers - Wrap content - View it on the web - The file size is too big - Search - Add - File - Folder - Light Theme - Goto Line - Find - Find and Replace - Root Permission - Zdieľať - Text Suggestions - + + + + + + + Nový účet + Aktívne + Vymazať + Mažem súbory… + Načítavanie… + Aktuálna miestna zložka + Súkromný kľúč + Svetlá + Kódovanie + Zdieľať + Nový lokálny súbor + Nový vzdialený priečinok + New remote file + New local file + Odpojiť sa + Predvol. miestna zložka + Kde oi chcete stiahnuť? + Stiahnuť + Sťahovanie dokončené + Duplikát + Hotovo + Domov + Hosť + Info + Lokálny + Prihlasujem sa… + Upraviť + Presunúť + Schovať + Turbo Klient + Turbo Editor + Užív. meno + Pasívny + Heslo + Heslo + Ponechajte prázdne, pre výzvu pri každej relácii + Port + Predvoľby + vzdialený + Pre zmenu témy, reštartujte aplikáciu + Premenovať + Root + Uložiť + Tmavá + Vybrať + Vyberte účet + Ste si istý? + Niečo zlyhalo + Nepresúvajte rovnaký súbor + Téma + Typ pripojenia + Typ protokolu + Ďalšia zložka + Napíš heslo + Nahrať + Nahrávanie dokončené + Čo chceš robiť? + Syntax highlight + Vrátiť späť + Prerobiť + Synchronizovať + Vzdial. zložka na synchronizáciu + Miestna zložka na synchronizáciu + Ohodnotiť + Nemožno kontaktovať Google Play + Podpor rozvoj ďalších skvelých funkcií. + Vylepšiť na Premium + Vylepši na Premium a podpor tým vývoj Turbo klienta! + Stiahnuť odomknutú verziu + Čo pre teba Turbo klient znamená? Stanov si vlastnú cenu! + Vylepši na Premium, pre odomknutie týchto funkcií: + Power to open and modify any type of file. + Backup service to backup and restore your data safely. + Unlock the Premium features + I really like this app! + I love this app! + Backup the accounts + Restore the accounts + Backup and share the accounts + Importing the accounts… + Exporting the accounts... + No backups found + Cannot open the file + Temporary folder does not exist + An error occurred + Ui + Remove + Modification date + Name + Size + Sort + Open + The file %1$s was modified, do you want to upload it? + The file %1$s was saved with success! + Create a new account + Create a new account to start. + Type + Send feedback + Copy URL + Cut + paste + Advanced + Auto + Bytes + Unit of measurement for file size + Open Source licenses + Show open source licenses + Open a file + Open this time only + Change the list type + Use monospace + Recent files + Font size + Connection Name + Line Numbers + Wrap content + View it on the web + The file size is too big + Search + Add + File + Folder + Light Theme + Goto Line + Find + Find and Replace + Root Permission + Zdieľať + Text Suggestions + diff --git a/libraries/sharedCode/src/main/res/values-w820dp/dimens.xml b/libraries/sharedCode/src/main/res/values-w820dp/dimens.xml index c91149f..74fcf1c 100644 --- a/libraries/sharedCode/src/main/res/values-w820dp/dimens.xml +++ b/libraries/sharedCode/src/main/res/values-w820dp/dimens.xml @@ -1,40 +1,40 @@ - - - - - 64dp - 64dp - - - 320dp - 10dp - 10dp - 1dp - 16dp - 72dp - 50dp - 10dp - 72dp - 2dp - 25sp - 16sp - 14sp - + + + + + 64dp + 64dp + + + 320dp + 10dp + 10dp + 1dp + 16dp + 72dp + 50dp + 10dp + 72dp + 2dp + 25sp + 16sp + 14sp + diff --git a/libraries/sharedCode/src/main/res/values-zh-rHK/strings.xml b/libraries/sharedCode/src/main/res/values-zh-rHK/strings.xml index 3ddcb8f..d52c4a2 100644 --- a/libraries/sharedCode/src/main/res/values-zh-rHK/strings.xml +++ b/libraries/sharedCode/src/main/res/values-zh-rHK/strings.xml @@ -1,153 +1,153 @@ - - - - - - - New account - 主動模式 - 刪除 - 刪除檔案中… - 載入中… - 本機資料夾 - 密鑰 - 明亮色系 - 編碼 - Share - New local folder - New remote folder - New remote file - New local file - 中斷連線 - 預設本機資料夾 - Where to download? - 下載 - Download completed - Duplicate - Done - Home - 主機 - 內容 - Local - 正在登入… - Edit - 移動 - Hide - Turbo Client - Turbo Editor - 帳戶 - 被動模式 - Passphrase - 密碼 - Leave it empty to prompt for it every session - 連接埠 - 選項 - Remote - To change the theme, restart the application - 重新命名 - 預設遠端資料夾 - 儲存 - 暗色系 - 選取 - 請選擇帳戶 - 確定繼續? - Something failed - Do not transfer same file - 佈景主題 - 連接類型 - 協定類型 - 另一個資料夾 - 使用 Passphrase - 上載 - Upload completed - What do you want to do? - Syntax highlight - 復原 - 重做 - 同步 - 同步的遠端資料夾 - 同步的本機資料夾 - 評分 - Cannot contact Google Play - Support the development of other great features. - Upgrade to Premium - Upgrade to Premium and support the development of Turbo Client! - Download unlocked version - What is Turbo Client worth to you? Set your price! - Upgrade to unlock this features: - Power to open and modify any type of file. - Backup service to backup and restore your data safely. - Unlock the Premium features - I really like this app! - I love this app! - Backup the accounts - Restore the accounts - Backup and share the accounts - Importing the accounts… - Exporting the accounts... - No backups found - Cannot open the file - Temporary folder does not exist - An error occurred - Ui - Remove - Modification date - Name - Size - Sort - Open - The file %1$s was modified, do you want to upload it? - The file %1$s was saved with success! - Create a new account - Create a new account to start. - Type - Send feedback - Copy URL - Cut - paste - Advanced - Auto - Bytes - Unit of measurement for file size - Open Source licenses - Show open source licenses - Open a file - Open this time only - Change the list type - Use monospace - Recent files - Font size - Connection Name - Line Numbers - Wrap content - View it on the web - The file size is too big - Search - Add - File - Folder - Light Theme - Goto Line - Find - Find and Replace - Root Permission - Share - Text Suggestions - + + + + + + + New account + 主動模式 + 刪除 + 刪除檔案中… + 載入中… + 本機資料夾 + 密鑰 + 明亮色系 + 編碼 + Share + New local folder + New remote folder + New remote file + New local file + 中斷連線 + 預設本機資料夾 + Where to download? + 下載 + Download completed + Duplicate + Done + Home + 主機 + 內容 + Local + 正在登入… + Edit + 移動 + Hide + Turbo Client + Turbo Editor + 帳戶 + 被動模式 + Passphrase + 密碼 + Leave it empty to prompt for it every session + 連接埠 + 選項 + Remote + To change the theme, restart the application + 重新命名 + 預設遠端資料夾 + 儲存 + 暗色系 + 選取 + 請選擇帳戶 + 確定繼續? + Something failed + Do not transfer same file + 佈景主題 + 連接類型 + 協定類型 + 另一個資料夾 + 使用 Passphrase + 上載 + Upload completed + What do you want to do? + Syntax highlight + 復原 + 重做 + 同步 + 同步的遠端資料夾 + 同步的本機資料夾 + 評分 + Cannot contact Google Play + Support the development of other great features. + Upgrade to Premium + Upgrade to Premium and support the development of Turbo Client! + Download unlocked version + What is Turbo Client worth to you? Set your price! + Upgrade to unlock this features: + Power to open and modify any type of file. + Backup service to backup and restore your data safely. + Unlock the Premium features + I really like this app! + I love this app! + Backup the accounts + Restore the accounts + Backup and share the accounts + Importing the accounts… + Exporting the accounts... + No backups found + Cannot open the file + Temporary folder does not exist + An error occurred + Ui + Remove + Modification date + Name + Size + Sort + Open + The file %1$s was modified, do you want to upload it? + The file %1$s was saved with success! + Create a new account + Create a new account to start. + Type + Send feedback + Copy URL + Cut + paste + Advanced + Auto + Bytes + Unit of measurement for file size + Open Source licenses + Show open source licenses + Open a file + Open this time only + Change the list type + Use monospace + Recent files + Font size + Connection Name + Line Numbers + Wrap content + View it on the web + The file size is too big + Search + Add + File + Folder + Light Theme + Goto Line + Find + Find and Replace + Root Permission + Share + Text Suggestions + diff --git a/libraries/sharedCode/src/main/res/values/arrays.xml b/libraries/sharedCode/src/main/res/values/arrays.xml index 0b71647..9de42dc 100644 --- a/libraries/sharedCode/src/main/res/values/arrays.xml +++ b/libraries/sharedCode/src/main/res/values/arrays.xml @@ -1,61 +1,61 @@ - - - - - - ChangeLog Library - EventBus - commons-io - RootCommands - Floating Action Button - - - - ISO-2022-JP - ISO-2022-CN - ISO-2022-KR - ISO-8859-5 - ISO-8859-7 - ISO-8859-8 - BIG5 - GB18030 - EUC-JP - EUC-KR - EUC-TW - SHIFT_JIS - IBM855 - IBM866 - KOI8-R - MACCYRILLIC - WINDOWS-1251 - WINDOWS-1252 - WINDOWS-1253 - WINDOWS-1255 - UTF-8 - UTF-16BE - UTF-16LE - UTF-32BE - UTF-32LE - GB2312 - HZ-GB-2312 - X-ISO-10646-UCS-4-3412 - X-ISO-10646-UCS-4-2143 - + + + + + + ChangeLog Library + EventBus + commons-io + RootCommands + Floating Action Button + + + + ISO-2022-JP + ISO-2022-CN + ISO-2022-KR + ISO-8859-5 + ISO-8859-7 + ISO-8859-8 + BIG5 + GB18030 + EUC-JP + EUC-KR + EUC-TW + SHIFT_JIS + IBM855 + IBM866 + KOI8-R + MACCYRILLIC + WINDOWS-1251 + WINDOWS-1252 + WINDOWS-1253 + WINDOWS-1255 + UTF-8 + UTF-16BE + UTF-16LE + UTF-32BE + UTF-32LE + GB2312 + HZ-GB-2312 + X-ISO-10646-UCS-4-3412 + X-ISO-10646-UCS-4-2143 + \ No newline at end of file diff --git a/libraries/sharedCode/src/main/res/values/attrs.xml b/libraries/sharedCode/src/main/res/values/attrs.xml index 573577d..75816f5 100644 --- a/libraries/sharedCode/src/main/res/values/attrs.xml +++ b/libraries/sharedCode/src/main/res/values/attrs.xml @@ -1,29 +1,29 @@ - - - - - - - - - - - + + + + + + + + + + + \ No newline at end of file diff --git a/libraries/sharedCode/src/main/res/values/colors.xml b/libraries/sharedCode/src/main/res/values/colors.xml index 49140d4..a19d67a 100644 --- a/libraries/sharedCode/src/main/res/values/colors.xml +++ b/libraries/sharedCode/src/main/res/values/colors.xml @@ -1,69 +1,69 @@ - - - - - - - - #212121 - #ffffff - #ffffffff - #ff212121 - #e5e5e5 - #ff2a2a2a - - #212121 - - #ffffff - #212121 - #f7f7f7 - #212121 - #666666 - #ffffffff - - #ffffff - #f7f7f7 - - #ffbb33 - #00a569 - #00a569 - #717171 - #aa66cc - #ff4444 - #33b5e5 - #ff00607d - - #ffff6600 - #ff2f6f9f - #ff4f9fcf - #ffd44950 - #ffd44950 - #ff009688 - #ff999999 - - #40969696 - #50000000 - #30000000 - - #b6cc45 - #DFED47 - - #607d8b + + + + + + + + #212121 + #ffffff + #ffffffff + #ff212121 + #e5e5e5 + #ff2a2a2a + + #212121 + + #ffffff + #212121 + #f7f7f7 + #212121 + #666666 + #ffffffff + + #ffffff + #f7f7f7 + + #ffbb33 + #00a569 + #00a569 + #717171 + #aa66cc + #ff4444 + #33b5e5 + #ff00607d + + #ffff6600 + #ff2f6f9f + #ff4f9fcf + #ffd44950 + #ffd44950 + #ff009688 + #ff999999 + + #40969696 + #50000000 + #30000000 + + #b6cc45 + #DFED47 + + #607d8b \ No newline at end of file diff --git a/libraries/sharedCode/src/main/res/values/dimens.xml b/libraries/sharedCode/src/main/res/values/dimens.xml index 2c2d8e9..62e9398 100644 --- a/libraries/sharedCode/src/main/res/values/dimens.xml +++ b/libraries/sharedCode/src/main/res/values/dimens.xml @@ -1,43 +1,43 @@ - - - - - 16dp - 16dp - 0dp - - - 300dp - 10dp - 10dp - 1dp - 16dp - 72dp - 50dp - 10dp - 72dp - 2dp - 25sp - 16sp - 14sp - - 56dp - + + + + + 16dp + 16dp + 0dp + + + 300dp + 10dp + 10dp + 1dp + 16dp + 72dp + 50dp + 10dp + 72dp + 2dp + 25sp + 16sp + 14sp + + 56dp + diff --git a/libraries/sharedCode/src/main/res/values/ids.xml b/libraries/sharedCode/src/main/res/values/ids.xml index 8f0858f..c549bfe 100644 --- a/libraries/sharedCode/src/main/res/values/ids.xml +++ b/libraries/sharedCode/src/main/res/values/ids.xml @@ -1,88 +1,88 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libraries/sharedCode/src/main/res/values/integers.xml b/libraries/sharedCode/src/main/res/values/integers.xml index 2789c9a..5768659 100644 --- a/libraries/sharedCode/src/main/res/values/integers.xml +++ b/libraries/sharedCode/src/main/res/values/integers.xml @@ -1,23 +1,23 @@ - - - - - 4194304 - + + + + + 4194304 + diff --git a/libraries/sharedCode/src/main/res/values/strings.xml b/libraries/sharedCode/src/main/res/values/strings.xml index 00bc02a..72871dc 100644 --- a/libraries/sharedCode/src/main/res/values/strings.xml +++ b/libraries/sharedCode/src/main/res/values/strings.xml @@ -1,75 +1,75 @@ - - - - - - Use monospace - Recent files - Font size - Connection Name - Line Numbers - Wrap content - View it on the web - File - Folder - Light Theme - Go to Line… - Go to Page… - Find - Replace - Share - Keyboard suggestions and Swipe - Auto-Encoding - Set as the working folder - This is the working folder - Do you want to save the changes to the file %s? - Regular Expression - Text to find - Text to replace - Next - Previous - Please wait… - %s occurrences was found - v%s - Translate - Changelog - Match case - Long click for more options - Auto save - Read only - Send error reports - Extra options - Split the text if too long - Ignore back button - Encoding - Share - Info - Turbo Editor - Preferences - Save - Syntax highlight - Undo - Redo - Open - The file %1$s was saved with success! - Open a file - No - New file - + + + + + + Use monospace + Recent files + Font size + Connection Name + Line Numbers + Wrap content + View it on the web + File + Folder + Light Theme + Go to Line… + Go to Page… + Find + Replace + Share + Keyboard suggestions and Swipe + Auto-Encoding + Set as the working folder + This is the working folder + Do you want to save the changes to the file %s? + Regular Expression + Text to find + Text to replace + Next + Previous + Please wait… + %s occurrences was found + v%s + Translate + Changelog + Match case + Long click for more options + Auto save + Read only + Send error reports + Extra options + Split the text if too long + Ignore back button + Encoding + Share + Info + Turbo Editor + Preferences + Save + Syntax highlight + Undo + Redo + Open + The file %1$s was saved with success! + Open a file + No + New file + diff --git a/libraries/sharedCode/src/main/res/values/styles.xml b/libraries/sharedCode/src/main/res/values/styles.xml index 097f9f0..004ff14 100644 --- a/libraries/sharedCode/src/main/res/values/styles.xml +++ b/libraries/sharedCode/src/main/res/values/styles.xml @@ -1,87 +1,87 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/libraries/sharedCode/src/main/res/xml/extra_options.xml b/libraries/sharedCode/src/main/res/xml/extra_options.xml index 393e25f..4b0c733 100644 --- a/libraries/sharedCode/src/main/res/xml/extra_options.xml +++ b/libraries/sharedCode/src/main/res/xml/extra_options.xml @@ -1,69 +1,69 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/settings.gradle b/settings.gradle index 61c83c4..5537738 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,20 +1,20 @@ -/* - * Copyright (C) 2014 Vlad Mihalachi - * - * This file is part of Turbo Editor. - * - * Turbo Editor 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. - * - * Turbo Editor 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 . - */ - -include ':app', ':libraries:RootCommands', ':libraries:FloatingActionButton', ':libraries:sharedCode', ':app-pro' +/* + * Copyright (C) 2014 Vlad Mihalachi + * + * This file is part of Turbo Editor. + * + * Turbo Editor 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. + * + * Turbo Editor 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 . + */ + +include ':app', ':libraries:RootCommands', ':libraries:FloatingActionButton', ':libraries:sharedCode', ':app-pro'