├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── ve
│ │ └── acpp
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── ve
│ │ │ ├── acpp
│ │ │ └── MainActivity.java
│ │ │ ├── lexer
│ │ │ ├── Language.java
│ │ │ ├── LanguageC.java
│ │ │ ├── LanguageLua.java
│ │ │ ├── Lexer.java
│ │ │ ├── LuaLexer.java
│ │ │ └── LuaTokenTypes.java
│ │ │ ├── utils
│ │ │ ├── AnimUtils.java
│ │ │ ├── DisplayUtils.java
│ │ │ ├── GraphUtils.java
│ │ │ ├── JsonUtils.java
│ │ │ ├── LightStatusBarUtils.java
│ │ │ ├── LocationUtils.java
│ │ │ ├── MD5.java
│ │ │ ├── RomUtils.java
│ │ │ └── SysUtils.java
│ │ │ └── view
│ │ │ ├── editor
│ │ │ ├── Base.java
│ │ │ ├── Caret.java
│ │ │ ├── CursorHandlerManager.java
│ │ │ ├── Editor.java
│ │ │ ├── EditorInputConnection.java
│ │ │ ├── EventManager.java
│ │ │ ├── ListenManager.java
│ │ │ ├── MeasureCache.java
│ │ │ ├── Painter.java
│ │ │ ├── SpanManager.java
│ │ │ ├── TextController.java
│ │ │ ├── out
│ │ │ │ ├── CaretInterface.java
│ │ │ │ ├── Config.java
│ │ │ │ ├── DisplayInterface.java
│ │ │ │ ├── SelectInterface.java
│ │ │ │ ├── Status.java
│ │ │ │ └── TextInterface.java
│ │ │ └── span
│ │ │ │ ├── SpanNode.java
│ │ │ │ ├── SpanType.java
│ │ │ │ └── TextSpanData.java
│ │ │ ├── ext
│ │ │ ├── AutoComplete.java
│ │ │ ├── AutoCompletePanel.java
│ │ │ ├── AutoIndent.java
│ │ │ ├── ClipboardPanel.java
│ │ │ └── ColorScheme.java
│ │ │ ├── listener
│ │ │ ├── RowListener.java
│ │ │ ├── SelectionListener.java
│ │ │ └── TextListener.java
│ │ │ ├── text
│ │ │ ├── cache
│ │ │ │ ├── Entry.java
│ │ │ │ └── TextCache.java
│ │ │ ├── document
│ │ │ │ ├── BaseDocument.java
│ │ │ │ ├── CommonLanguage.java
│ │ │ │ ├── Document.java
│ │ │ │ ├── DocumentEdit.java
│ │ │ │ ├── Gap.java
│ │ │ │ └── WordWrapable.java
│ │ │ └── undo
│ │ │ │ ├── Undo.java
│ │ │ │ ├── UndoManager.java
│ │ │ │ └── Undoable.java
│ │ │ └── utils
│ │ │ ├── CharRange.java
│ │ │ ├── EditorException.java
│ │ │ ├── Flag.java
│ │ │ ├── LinearSearchStrategy.java
│ │ │ ├── Pair.java
│ │ │ ├── Rectangle.java
│ │ │ ├── SearchStrategy.java
│ │ │ └── ZoomChecker.java
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ ├── cursor.png
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ └── activity_main.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── ve
│ └── acpp
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/libraries
5 | /.idea/modules.xml
6 | /.idea/workspace.xml
7 | .DS_Store
8 | /build
9 | /captures
10 | .externalNativeBuild
11 |
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CodeEditor
2 | 文本操作核心部分来自: https://github.com/brnogz/TextWarriorLibrary
3 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 | defaultConfig {
6 | applicationId "com.ve.acpp"
7 | minSdkVersion 21
8 | targetSdkVersion 28
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | compileOptions {
20 | targetCompatibility 1.8
21 | sourceCompatibility 1.8
22 | }
23 | }
24 |
25 | dependencies {
26 | implementation fileTree(dir: 'libs', include: ['*.jar'])
27 | implementation 'com.android.support:appcompat-v7:28.0.0'
28 | implementation 'com.android.support.constraint:constraint-layout:1.0.2'
29 | testImplementation 'junit:junit:4.12'
30 |
31 | androidTestImplementation 'com.android.support.test:runner:1.0.1'
32 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
33 | }
34 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/ve/acpp/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.ve.acpp;
2 |
3 | import android.content.Context;
4 | import android.graphics.Paint;
5 | import android.support.test.InstrumentationRegistry;
6 | import android.support.test.runner.AndroidJUnit4;
7 |
8 | import com.ve.view.editor.MeasureCache;
9 |
10 | import org.junit.Test;
11 | import org.junit.runner.RunWith;
12 |
13 | import static org.junit.Assert.*;
14 |
15 | /**
16 | * Instrumented test, which will execute on an Android device.
17 | *
18 | * @see Testing documentation
19 | */
20 | @RunWith(AndroidJUnit4.class)
21 | public class ExampleInstrumentedTest {
22 | @Test
23 | public void useAppContext() {
24 | // Context of the app under test.
25 | Context appContext = InstrumentationRegistry.getTargetContext();
26 |
27 | MeasureCache cache=new MeasureCache(new Paint());
28 | assertEquals(cache.getWidth('s'),cache.getWidth('s'),0.001);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/acpp/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.ve.acpp;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.view.View;
6 |
7 | import com.ve.view.editor.Editor;
8 | import com.ve.view.editor.out.Config;
9 | import com.ve.view.editor.out.TextInterface;
10 | import com.ve.view.ext.ClipboardPanel;
11 |
12 | import java.util.Random;
13 |
14 | public class MainActivity extends AppCompatActivity {
15 |
16 | Editor editor;
17 |
18 | ClipboardPanel clipboardPanel;
19 | @Override
20 | protected void onCreate(Bundle savedInstanceState) {
21 | super.onCreate(savedInstanceState);
22 | setContentView(R.layout.activity_main);
23 | editor = findViewById(R.id.activity_main_editor);
24 | Config config=new Config();
25 | config.showNonPrinting=true;
26 | config.wordWrap=false;
27 | editor.applyConfig(config);
28 | clipboardPanel=new ClipboardPanel(editor);
29 | editor.getOperator().setSelectionChangedListener((active, selStart, selEnd) -> {
30 | if (active){
31 | clipboardPanel.show();
32 | }else {
33 | clipboardPanel.hide();
34 | }
35 | });
36 |
37 | test();
38 | }
39 |
40 | private void test() {
41 | StringBuffer stringBuffer = new StringBuffer();
42 | Random random=new Random();
43 | for (int i = 0; i < 20; i++) {
44 | if (random.nextBoolean()) {
45 | stringBuffer.append(" asfgweilfuWEF72QWDFYFregargaergnwderghwaefwsefweg数的rnwd文档发给nueffgeriugiesgier\n");
46 | }
47 | stringBuffer.append(" asfgweilfuWEF72G\nQWDFYF、nwdwefgGAGDGSDFGHAWERGHWERHERREGRAGEsddnwd、nuef\nefgeriugiesgier\n");
48 |
49 | }
50 |
51 | TextInterface textInterface = editor.getTextInterface();
52 | textInterface.paste(stringBuffer.toString());
53 | textInterface.replaceText(0,textInterface.getLength()-1,"Good!");
54 | }
55 |
56 | public void onRedo(View view) {
57 | editor.getTextInterface().redo();
58 | }
59 |
60 | public void onUndo(View view) {
61 | editor.getTextInterface().undo();
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/lexer/Language.java:
--------------------------------------------------------------------------------
1 | package com.ve.lexer;
2 |
3 | import com.ve.view.text.document.CommonLanguage;
4 |
5 | import java.util.ArrayList;
6 | import java.util.HashMap;
7 |
8 |
9 | public abstract class Language extends CommonLanguage {
10 |
11 |
12 |
13 | private final static char[] BASIC_C_OPERATORS = {
14 | '(', ')', '{', '}', '.', ',', ';', '=', '+', '-',
15 | '/', '*', '&', '!', '|', ':', '[', ']', '<', '>',
16 | '?', '~', '%', '^'
17 | };
18 |
19 |
20 | protected HashMap _keywords = new HashMap(0);
21 | protected HashMap _names = new HashMap(0);
22 | protected HashMap _bases = new HashMap(0);
23 | protected HashMap _users = new HashMap(0);
24 | protected HashMap _operators = generateOperators(BASIC_C_OPERATORS);
25 |
26 | private ArrayList _ueserCache = new ArrayList();
27 | private String[] _userWords = new String[0];
28 | private String[] _keyword;
29 | private String[] _name;
30 |
31 | public void updateUserWord() {
32 | String[] uw = new String[_ueserCache.size()];
33 | _userWords = _ueserCache.toArray(uw);
34 | }
35 |
36 | public String[] getUserWord() {
37 | return _userWords;
38 | }
39 |
40 | public String[] getNames() {
41 | return _name;
42 | }
43 |
44 | public String[] getBasePackage(String name) {
45 | return _bases.get(name);
46 | }
47 |
48 | public String[] getKeywords() {
49 | return _keyword;
50 | }
51 |
52 | public void setKeywords(String[] keywords) {
53 | _keyword = keywords;
54 | _keywords = new HashMap(keywords.length);
55 | for (int i = 0; i < keywords.length; ++i) {
56 | _keywords.put(keywords[i], Lexer.KEYWORD);
57 | }
58 | }
59 |
60 | public void setNames(String[] names) {
61 | _name = names;
62 | ArrayList buf = new ArrayList();
63 | _names = new HashMap(names.length);
64 | for (int i = 0; i < names.length; ++i) {
65 | if (!buf.contains(names[i]))
66 | buf.add(names[i]);
67 | _names.put(names[i], Lexer.NAME);
68 | }
69 | _name = new String[buf.size()];
70 | buf.toArray(_name);
71 | }
72 |
73 | public void addBasePackage(String name, String[] names) {
74 | _bases.put(name, names);
75 | }
76 |
77 | public void clearUserWord() {
78 | _ueserCache.clear();
79 | _users.clear();
80 | }
81 |
82 | public void addUserWord(String name) {
83 | if (!_ueserCache.contains(name) && !_names.containsKey(name))
84 | _ueserCache.add(name);
85 | _users.put(name, Lexer.NAME);
86 | }
87 |
88 | protected void setOperators(char[] operators) {
89 | _operators = generateOperators(operators);
90 | }
91 |
92 | private HashMap generateOperators(char[] operators) {
93 | HashMap operatorsMap = new HashMap(operators.length);
94 | for (int i = 0; i < operators.length; ++i) {
95 | operatorsMap.put(operators[i], Lexer.OPERATOR);
96 | }
97 | return operatorsMap;
98 | }
99 |
100 | public final boolean isOperator(char c) {
101 | return _operators.containsKey(Character.valueOf(c));
102 | }
103 |
104 | public final boolean isKeyword(String s) {
105 | return _keywords.containsKey(s);
106 | }
107 |
108 | public final boolean isName(String s) {
109 | return _names.containsKey(s);
110 | }
111 |
112 | public final boolean isBasePackage(String s) {
113 | return _bases.containsKey(s);
114 | }
115 |
116 | public final boolean isBaseWord(String p, String s) {
117 | String[] pkg = _bases.get(p);
118 | for (String n : pkg) {
119 | if (n.equals(s))
120 | return true;
121 | }
122 | return false;
123 | }
124 |
125 | public final boolean isUserWord(String s) {
126 | return _users.containsKey(s);
127 | }
128 |
129 | private boolean contains(String[] a, String s) {
130 | for (String n : a) {
131 | if (n.equals(s))
132 | return true;
133 | }
134 | return false;
135 | }
136 |
137 | private boolean contains(ArrayList a, String s) {
138 | for (String n : a) {
139 | if (n.equals(s))
140 | return true;
141 | }
142 | return false;
143 | }
144 |
145 |
146 |
147 | public boolean isSentenceTerminator(char c) {
148 | return (c == '.');
149 | }
150 |
151 | public boolean isEscapeChar(char c) {
152 | return (c == '\\');
153 | }
154 |
155 | public boolean isProgLang() {
156 | return true;
157 | }
158 |
159 | public boolean isWordStart(char c) {
160 | return false;
161 | }
162 |
163 | public boolean isDelimiterA(char c) {
164 | return (c == '"');
165 | }
166 |
167 |
168 | public boolean isDelimiterB(char c) {
169 | return (c == '\'');
170 | }
171 |
172 |
173 | public boolean isLineAStart(char c) {
174 | return (c == '#');
175 | }
176 |
177 |
178 | public boolean isLineBStart(char c) {
179 | return false;
180 | }
181 |
182 | public boolean isLineStart(char c0, char c1) {
183 | return (c0 == '/' && c1 == '/');
184 | }
185 |
186 | public boolean isMultilineStartDelimiter(char c0, char c1) {
187 | return (c0 == '/' && c1 == '*');
188 | }
189 |
190 |
191 | public boolean isMultilineEndDelimiter(char c0, char c1) {
192 | return (c0 == '*' && c1 == '/');
193 | }
194 | }
195 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/lexer/LanguageC.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.ve.lexer;
10 |
11 | /**
12 | * Singleton class containing the symbols and operators of the C language
13 | */
14 | public class LanguageC extends Language{
15 | private static Language
16 | _theOne = null;
17 |
18 | private final static String[] keywords = {
19 | "char", "double", "float", "int", "long", "short", "void",
20 | "auto", "const", "extern", "register", "static", "volatile",
21 | "signed", "unsigned", "sizeof", "typedef",
22 | "enum", "struct", "union",
23 | "break", "case", "continue", "default", "do", "else", "for",
24 | "goto", "if", "return", "switch", "while"
25 | };
26 |
27 | public static Language getInstance(){
28 | if(_theOne == null){
29 | _theOne = new LanguageC();
30 | }
31 | return _theOne;
32 | }
33 |
34 | private LanguageC(){
35 | super.setKeywords(keywords);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/lexer/LanguageLua.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.ve.lexer;
10 |
11 | /**
12 | * Singleton class containing the symbols and operators of the Javascript language
13 | */
14 | public class LanguageLua extends Language {
15 | private static Language _theOne = null;
16 |
17 | //private final static String functionTarget = "_ENV|_G|_VERSION|assert|collectgarbage|coroutine|create|isyieldable|resume|running|status|wrap|yield|debug|gethook|getinfo|getlocal|getmetatable|getregistry|getupvalue|getuservalue|sethook|setlocal|setmetatable|setupvalue|setuservalue|traceback|upvalueid|upvaluejoin|dofile|error|getfenv|getmetatable|io|close|flush|input|lines|open|output|popen|read|stderr|stdin|stdout|tmpfile|type|write|ipairs|load|loadfile|loadstring|luajava|bindClass|clear|coding|createArray|createProxy|instanceof|loadLib|loaded|luapath|new|newInstance|package|math|abs|acos|asin|atan|atan2|ceil|cos|cosh|deg|exp|floor|fmod|frexp|huge|ldexp|log|log10|max|maxinteger|min|mininteger|modf|pi|pow|rad|random|randomseed|sin|sinh|sqrt|tan|tanh|tointeger|type|ult|module|next|os|clock|date|difftime|execute|exit|getenv|remove|rename|setlocale|time|tmpname|package|config|cpath|loaded|loaders|loadlib|path|preload|searchers|searchpath|seeall|pairs|pcall|print|rawequal|rawget|rawlen|rawset|require|select|setfenv|setmetatable|string|byte|char|dump|find|format|gfind|gmatch|gsub|len|lower|match|pack|packsize|rep|reverse|sub|unpack|upper|table|concat|foreach|foreachi|insert|maxn|move|pack|remove|sort|unpack|tonumber|tostring|type|unpack|char|charpattern|utf8|codepoint|codes|len|offset|xpcall";
18 | //private final static String functionTarget = "_ENV|_G|_VERSION|assert|collectgarbage|coroutine.create|coroutine.isyieldable|coroutine.resume|coroutine.running|coroutine.status|coroutine.wrap|coroutine.yield|debug.debug|debug.gethook|debug.getinfo|debug.getlocal|debug.getmetatable|debug.getregistry|debug.getupvalue|debug.getuservalue|debug.sethook|debug.setlocal|debug.setmetatable|debug.setupvalue|debug.setuservalue|debug.traceback|debug.upvalueid|debug.upvaluejoin|dofile|error|getfenv|getmetatable|io.close|io.flush|io.input|io.lines|io.open|io.output|io.popen|io.read|io.stderr|io.stdin|io.stdout|io.tmpfile|io.type|io.write|ipairs|load|loadfile|loadstring|luajava.bindClass|luajava.clear|luajava.coding|luajava.createArray|luajava.createProxy|luajava.instanceof|luajava.loadLib|luajava.loaded|luajava.luapath|luajava.new|luajava.newInstance|luajava.package|math.abs|math.acos|math.asin|math.atan|math.atan2|math.ceil|math.cos|math.cosh|math.deg|math.exp|math.floor|math.fmod|math.frexp|math.huge|math.ldexp|math.log|math.log10|math.max|math.maxinteger|math.min|math.mininteger|math.modf|math.pi|math.pow|math.rad|math.random|math.randomseed|math.sin|math.sinh|math.sqrt|math.tan|math.tanh|math.tointeger|math.type|math.ult|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.config|package.cpath|package.loaded|package.loaders|package.loadlib|package.path|package.preload|package.searchers|package.searchpath|package.seeall|pairs|pcall|print|rawequal|rawget|rawlen|rawset|require|select|setfenv|setmetatable|string.byte|string.char|string.dump|string.find|string.format|string.gfind|string.gmatch|string.gsub|string.len|string.lower|string.match|string.pack|string.packsize|string.rep|string.reverse|string.sub|string.unpack|string.upper|table.concat|table.foreach|table.foreachi|table.insert|table.maxn|table.move|table.pack|table.remove|table.sort|table.unpack|tonumber|tostring|type|unpack|utf8.char|utf8.charpattern|utf8.codepoint|utf8.codes|utf8.len|utf8.offset|xpcall";
19 |
20 | private final static String keywordTarget ="and|break|do|else|elseif|end|false|for|function|goto|if|in|local|nil|not|or|repeat|return|then|true|until|while";
21 | private final static String globalTarget="__add|__band|__bnot|__bor|__bxor|__call|__concat|__div|__eq|__idiv|__index|__le|__len|__lt|__mod|__mul|__newindex|__pow|__shl|__shr|__sub|__unm|_ENV|_G|assert|collectgarbage|dofile|error|findtable|getmetatable|ipairs|load|loadfile|loadstring|module|next|pairs|pcall|print|rawequal|rawget|rawlen|rawset|require|select|self|setmetatable|tointeger|tonumber|tostring|type|unpack|xpcall";
22 |
23 | private final static String packageName="coroutine|debug|io|luajava|math|os|package|string|table|utf8";
24 | private final static String package_coroutine = "create|isyieldable|resume|running|status|wrap|yield";
25 | private final static String package_debug = "debug|gethook|getinfo|getlocal|getmetatable|getregistry|getupvalue|getuservalue|sethook|setlocal|setmetatable|setupvalue|setuservalue|traceback|upvalueid|upvaluejoin";
26 | private final static String package_io = "close|flush|input|lines|open|output|popen|read|stderr|stdin|stdout|tmpfile|type|write";
27 | private final static String package_luajava = "astable|bindClass|clear|coding|createArray|createProxy|instanceof|loadLib|loaded|luapath|new|newInstance|package|tostring";
28 | private final static String package_math = "abs|acos|asin|atan|atan2|ceil|cos|cosh|deg|exp|floor|fmod|frexp|huge|ldexp|log|log10|max|maxinteger|min|mininteger|modf|pi|pow|rad|random|randomseed|sin|sinh|sqrt|tan|tanh|tointeger|type|ult";
29 | private final static String package_os = "clock|date|difftime|execute|exit|getenv|remove|rename|setlocale|time|tmpname";
30 | private final static String package_package = "config|cpath|loaded|loaders|loadlib|path|preload|searchers|searchpath|seeall";
31 | private final static String package_string = "byte|char|dump|find|format|gfind|gmatch|gsub|len|lower|match|pack|packsize|rep|reverse|sub|unpack|upper";
32 | private final static String package_table = "concat|foreach|foreachi|insert|maxn|move|pack|remove|sort|unpack";
33 | private final static String package_utf8 = "char|charpattern|codepoint|codes|len|offset";
34 | private final static String extFunctionTarget="activity|call|compile|dump|each|enum|import|loadbitmap|loadlayout|loadmenu|set|task|thread|timer";
35 | private final static String functionTarget = globalTarget+"|"+extFunctionTarget+"|"+packageName;;
36 | private final static String[] keywords = keywordTarget.split("\\|");
37 |
38 | private final static String[] names = functionTarget.split("\\|");
39 |
40 | private final static char[] LUA_OPERATORS = {
41 | '(', ')', '{', '}', ',', ';', '=', '+', '-',
42 | '/', '*', '&', '!', '|', ':', '[', ']', '<', '>',
43 | '?', '~', '%', '^'
44 | };
45 | public static Language getInstance(){
46 | if(_theOne == null){
47 | _theOne = new LanguageLua();
48 | }
49 | return _theOne;
50 | }
51 |
52 | private LanguageLua(){
53 | super.setOperators(LUA_OPERATORS);
54 | super.setKeywords(keywords);
55 | super.setNames(names);
56 | super.addBasePackage("io",package_io.split("\\|"));
57 | super.addBasePackage("string",package_string.split("\\|"));
58 | super.addBasePackage("luajava",package_luajava.split("\\|"));
59 | super.addBasePackage("os",package_os.split("\\|"));
60 | super.addBasePackage("table",package_table.split("\\|"));
61 | super.addBasePackage("math",package_math.split("\\|"));
62 | super.addBasePackage("utf8",package_utf8.split("\\|"));
63 | super.addBasePackage("coroutine",package_coroutine.split("\\|"));
64 | super.addBasePackage("package",package_package.split("\\|"));
65 | super.addBasePackage("debug",package_debug.split("\\|"));
66 | }
67 |
68 | /**
69 | * Whether the word after c is a token
70 | */
71 | public boolean isWordStart2(char c){
72 | return (c=='.');
73 | }
74 |
75 | public boolean isLineAStart(char c){
76 | return false;
77 | }
78 |
79 | /**
80 | * Whether c0c1L is a token, where L is a sequence of characters until the end of the line
81 | */
82 | public boolean isLineStart(char c0, char c1){
83 | return (c0 == '-' && c1 == '-');
84 | }
85 |
86 | /**
87 | * Whether c0c1 signifies the start of a multi-line token
88 | */
89 | public boolean isMultilineStartDelimiter(char c0, char c1){
90 | return (c0 == '[' && c1 == '[');
91 | }
92 |
93 | /**
94 | * Whether c0c1 signifies the end of a multi-line token
95 | */
96 | public boolean isMultilineEndDelimiter(char c0, char c1){
97 | return (c0 == ']' && c1 == ']');
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/lexer/LuaTokenTypes.java:
--------------------------------------------------------------------------------
1 | package com.ve.lexer;
2 |
3 | /**
4 | * Created by zy on 2015/8/26.
5 | */
6 | public enum LuaTokenTypes {
7 | WRONG,
8 | NL_BEFORE_LONGSTRING,
9 | WS,
10 | NEWLINE,
11 | SHEBANG,
12 | LONGCOMMENT,
13 | SHORTCOMMENT,
14 | LUADOC_COMMENT,
15 | LONGCOMMENT_BEGIN,
16 | LONGCOMMENT_END,
17 | NAME,
18 | NUMBER,
19 | STRING,
20 | LONGSTRING,
21 | LONGSTRING_BEGIN,
22 | LONGSTRING_END,
23 | UNTERMINATED_STRING,
24 | DIV,
25 | MULT,
26 | LPAREN,
27 | RPAREN,
28 | LBRACK,
29 | RBRACK,
30 | LCURLY,
31 | RCURLY,
32 | COLON,
33 | COMMA,
34 | DOT,
35 | ASSIGN,
36 | SEMI,
37 | EQ,
38 | NE,
39 | PLUS,
40 | MINUS,
41 | GE,
42 | GT,
43 | EXP,
44 | LE,
45 | LT,
46 | ELLIPSIS,
47 | CONCAT,
48 | GETN,
49 | MOD,
50 |
51 | IF,
52 | ELSE,
53 | ELSEIF,
54 | WHILE,
55 | WITH,
56 | THEN,
57 | FOR,
58 | IN,
59 | RETURN,
60 | BREAK,
61 | CONTINUE,
62 | TRUE,
63 | FALSE,
64 | NIL,
65 | FUNCTION,
66 | DO,
67 | NOT,
68 | AND,
69 | OR,
70 | LOCAL,
71 | REPEAT,
72 | UNTIL,
73 | END
74 | }
75 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/utils/AnimUtils.java:
--------------------------------------------------------------------------------
1 | package com.ve.utils;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.animation.ValueAnimator;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 |
9 | public class AnimUtils {
10 | private static final String TAG = AnimUtils.class.getSimpleName();
11 | public static void fadeOut(View view, boolean gone) {
12 | if (view != null) {
13 | view.animate().alpha(0).setDuration(400).setListener(new AnimatorListenerAdapter() {
14 | @Override
15 | public void onAnimationEnd(Animator animation) {
16 | view.setVisibility(gone ? View.GONE : View.INVISIBLE);
17 | }
18 | }).start();
19 | }
20 | }
21 |
22 | public static void fadeIn(View view) {
23 | if (view != null) {
24 | view.setVisibility(View.VISIBLE);
25 | view.animate().alpha(0).setDuration(400).start();
26 | }
27 | }
28 | public static void scaleUpOut(View view, boolean gone) {
29 | if (view != null) {
30 |
31 | ValueAnimator valueAnimator=ValueAnimator.ofInt(view.getHeight(),0);
32 | valueAnimator.addUpdateListener(animation -> {
33 | Integer value= (Integer) animation.getAnimatedValue();
34 | ViewGroup.LayoutParams mparams=view.getLayoutParams();
35 | mparams.height= value;
36 | view.setLayoutParams(mparams);
37 |
38 | });
39 | valueAnimator.addListener(new AnimatorListenerAdapter() {
40 | @Override
41 | public void onAnimationEnd(Animator animation) {
42 | view.setVisibility(gone ? View.GONE : View.INVISIBLE);
43 | }
44 | });
45 | valueAnimator.setDuration(600);
46 | valueAnimator.start();
47 | }
48 | }
49 |
50 | public static void scaleUpIn(View view) {
51 | if (view != null) {
52 | view.setVisibility(View.VISIBLE);
53 | ValueAnimator valueAnimator=ValueAnimator.ofInt(0,view.getMeasuredHeight());
54 | valueAnimator.addUpdateListener(animation -> {
55 | Integer value= (Integer) animation.getAnimatedValue();
56 | ViewGroup.LayoutParams mparams=view.getLayoutParams();
57 | mparams.height= value;
58 | view.setLayoutParams(mparams);
59 |
60 | });
61 | valueAnimator.setDuration(600);
62 | valueAnimator.start();
63 | }
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/utils/DisplayUtils.java:
--------------------------------------------------------------------------------
1 | package com.ve.utils;
2 |
3 | import android.content.Context;
4 |
5 |
6 | public class DisplayUtils {
7 |
8 | public static int px2dp(Context context, float pxValue) {
9 | final float scale = context.getResources().getDisplayMetrics().density;
10 | return (int) (pxValue / scale + 0.5f);
11 | }
12 |
13 | public static int dp2px(Context context, float dpValue) {
14 | float scale = context.getResources().getDisplayMetrics().density;
15 | return (int) (dpValue * scale + 0.5f);
16 | }
17 |
18 | public static int px2sp(Context context, float pxValue) {
19 | final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
20 | return (int) (pxValue / fontScale + 0.5f);
21 | }
22 |
23 | public static int sp2px(Context context, float spValue) {
24 | final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
25 | return (int) (spValue * fontScale + 0.5f);
26 | }
27 |
28 | /* public static int dp2px(float dpValue) {
29 | float scale = App.getApplication().getResources().getDisplayMetrics().density;
30 | return (int) (dpValue * scale + 0.5f);
31 |
32 | }
33 | public static int sp2px( float spValue) {
34 | final float fontScale = App.getApplication().getResources().getDisplayMetrics().scaledDensity;
35 | return (int) (spValue * fontScale + 0.5f);
36 | }*/
37 |
38 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/utils/GraphUtils.java:
--------------------------------------------------------------------------------
1 | package com.ve.utils;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Color;
5 | import android.graphics.Paint;
6 | import android.graphics.RectF;
7 |
8 | public class GraphUtils {
9 | public static int alphaColor(int alpha,int color){
10 | return Color.argb(alpha, Color.red(color),Color.green(color),Color.blue(color));
11 | }
12 | public static int alphaColor(float alpha,int color){
13 | return Color.argb((int)(alpha* Color.alpha(color)), Color.red(color),Color.green(color),Color.blue(color));
14 | }
15 | public static void drawCenterText(Canvas canvas, char c, RectF rect, Paint paint) {
16 | Paint.FontMetrics fontMetrics = paint.getFontMetrics();
17 | float top = fontMetrics.top;
18 | float bottom = fontMetrics.bottom;
19 | int baseLineY = (int) (rect.centerY() - top / 2 - bottom / 2);
20 | canvas.drawText(new char[]{c},0,1, rect.centerX(), baseLineY, paint);
21 | }
22 | public static void drawCenterText(Canvas canvas, String text, RectF rect, Paint paint) {
23 | Paint.FontMetrics fontMetrics = paint.getFontMetrics();
24 | float top = fontMetrics.top;
25 | float bottom = fontMetrics.bottom;
26 | int baseLineY = (int) (rect.centerY() - top / 2 - bottom / 2);
27 | canvas.drawText(text,0,text.length(), rect.centerX(), baseLineY, paint);
28 | }
29 |
30 | public static int grandColor(int start, int end, float per) {
31 | int red = (int) (Color.red(start) + (Color.red(end) - Color.red(start)) * per);
32 | int blue = (int) (Color.blue(start) + (Color.blue(end) - Color.blue(start)) * per);
33 | int green = (int) (Color.green(start) + (Color.green(end) - Color.green(start)) * per);
34 | return Color.rgb(red, green, blue);
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/utils/JsonUtils.java:
--------------------------------------------------------------------------------
1 | package com.ve.utils;
2 |
3 | public class JsonUtils {
4 |
5 | public static String formatJson(String jsonStr) {
6 | if (null == jsonStr || "".equals(jsonStr)) {
7 | return "";
8 | }
9 | StringBuilder sb = new StringBuilder();
10 | char last = '\0';
11 | char current = '\0';
12 | int indent = 0;
13 | boolean isInQuotationMarks = false;
14 | for (int i = 0; i < jsonStr.length(); i++) {
15 | last = current;
16 | current = jsonStr.charAt(i);
17 | switch (current) {
18 | case '"':
19 | if (last != '\\') {
20 | isInQuotationMarks = !isInQuotationMarks;
21 | }
22 | sb.append(current);
23 | break;
24 | case '{':
25 | case '[':
26 | sb.append(current);
27 | if (!isInQuotationMarks) {
28 | sb.append('\n');
29 | indent++;
30 | addIndentBlank(sb, indent);
31 | }
32 | break;
33 | case '}':
34 | case ']':
35 | if (!isInQuotationMarks) {
36 | sb.append('\n');
37 | indent--;
38 | addIndentBlank(sb, indent);
39 | }
40 | sb.append(current);
41 | break;
42 | case ',':
43 | sb.append(current);
44 | if (last != '\\' && !isInQuotationMarks) {
45 | sb.append('\n');
46 | addIndentBlank(sb, indent);
47 | }
48 | break;
49 | default:
50 | sb.append(current);
51 | }
52 | }
53 |
54 | return sb.toString();
55 | }
56 |
57 |
58 | private static void addIndentBlank(StringBuilder sb, int indent) {
59 | for (int i = 0; i < indent; i++) {
60 | sb.append('\t');
61 | }
62 | }
63 |
64 |
65 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/utils/LightStatusBarUtils.java:
--------------------------------------------------------------------------------
1 | package com.ve.utils;
2 |
3 | import android.app.Activity;
4 | import android.view.View;
5 | import android.view.Window;
6 | import android.view.WindowManager;
7 |
8 | import java.lang.reflect.Field;
9 | import java.lang.reflect.Method;
10 |
11 | public class LightStatusBarUtils {
12 |
13 | public static void setLightStatusBar(Activity activity, boolean dark) {
14 | switch (RomUtils.getLightStatausBarAvailableRomType()) {
15 | case RomUtils.AvailableRomType.MIUI:
16 | setMIUILightStatusBar(activity, dark);
17 | break;
18 |
19 | case RomUtils.AvailableRomType.FLYME:
20 | setFlymeLightStatusBar(activity, dark);
21 | break;
22 |
23 | case RomUtils.AvailableRomType.ANDROID_NATIVE:
24 | setAndroidNativeLightStatusBar(activity, dark);
25 | break;
26 |
27 | case RomUtils.AvailableRomType.NA:
28 | // N/A do nothing
29 | break;
30 | }
31 | }
32 |
33 | private static boolean setMIUILightStatusBar(Activity activity, boolean darkmode) {
34 | Class extends Window> clazz = activity.getWindow().getClass();
35 | try {
36 | int darkModeFlag = 0;
37 | Class> layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
38 | Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
39 | darkModeFlag = field.getInt(layoutParams);
40 | Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
41 | extraFlagField.invoke(activity.getWindow(), darkmode ? darkModeFlag : 0, darkModeFlag);
42 | return true;
43 | } catch (Exception e) {
44 | e.printStackTrace();
45 | }
46 | return false;
47 | }
48 |
49 | private static boolean setFlymeLightStatusBar(Activity activity, boolean dark) {
50 | boolean result = false;
51 | if (activity != null) {
52 | try {
53 | WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
54 | Field darkFlag = WindowManager.LayoutParams.class
55 | .getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON");
56 | Field meizuFlags = WindowManager.LayoutParams.class
57 | .getDeclaredField("meizuFlags");
58 | darkFlag.setAccessible(true);
59 | meizuFlags.setAccessible(true);
60 | int bit = darkFlag.getInt(null);
61 | int value = meizuFlags.getInt(lp);
62 | if (dark) {
63 | value |= bit;
64 | } else {
65 | value &= ~bit;
66 | }
67 | meizuFlags.setInt(lp, value);
68 | activity.getWindow().setAttributes(lp);
69 | result = true;
70 | } catch (Exception e) {
71 | }
72 | }
73 | return result;
74 | }
75 |
76 | private static void setAndroidNativeLightStatusBar(Activity activity, boolean dark) {
77 | View decor = activity.getWindow().getDecorView();
78 | if (dark) {
79 | decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
80 | } else {
81 | // We want to change tint color to white again.
82 | // You can also record the flags in advance so that you can turn UI back completely if
83 | // you have set other flags before, such as translucent or full screen.
84 | decor.setSystemUiVisibility(0);
85 | }
86 | }
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/utils/LocationUtils.java:
--------------------------------------------------------------------------------
1 | package com.ve.utils;
2 |
3 | import android.Manifest;
4 | import android.content.Context;
5 | import android.content.pm.PackageManager;
6 | import android.location.Location;
7 | import android.location.LocationListener;
8 | import android.location.LocationManager;
9 | import android.os.Build;
10 | import android.os.Bundle;
11 | import android.support.v4.app.ActivityCompat;
12 | import android.util.Log;
13 |
14 | import java.util.List;
15 |
16 | public class LocationUtils {
17 | private static final String TAG = LocationUtils.class.getSimpleName();
18 | private volatile static LocationUtils uniqueInstance;
19 | private LocationManager locationManager;
20 | private String locationProvider;
21 | private Location location;
22 | private Context mContext;
23 |
24 |
25 | private LocationUtils(Context context) {
26 | mContext = context;
27 | getLocation();
28 | }
29 |
30 | public static LocationUtils getInstance(Context context) {
31 | if (uniqueInstance == null) {
32 | synchronized (LocationUtils.class) {
33 | if (uniqueInstance == null) {
34 | uniqueInstance = new LocationUtils(context);
35 | }
36 | }
37 | }
38 | return uniqueInstance;
39 | }
40 |
41 |
42 | private void getLocation() {
43 | if (mContext==null){
44 | Log.w(TAG, "getLocation: mContext==null" );
45 | return;
46 | }
47 | locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
48 | List providers = locationManager.getProviders(true);
49 | if (providers.contains(LocationManager.NETWORK_PROVIDER)) {
50 | Log.d(TAG, "网络定位");
51 | locationProvider = LocationManager.NETWORK_PROVIDER;
52 | } else if (providers.contains(LocationManager.GPS_PROVIDER)) {
53 | Log.d(TAG, "GPS定位");
54 | locationProvider = LocationManager.GPS_PROVIDER;
55 | } else {
56 | Log.d(TAG, "没有可用的位置提供器");
57 | return;
58 | }
59 | if (Build.VERSION.SDK_INT >= 23 &&
60 | ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
61 | ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
62 | return;
63 | }
64 | if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
65 | return;
66 | }
67 | Location location = locationManager.getLastKnownLocation(locationProvider);
68 | if (location != null) {
69 | setLocation(location);
70 | }
71 | // 监视地理位置变化,第二个和第三个参数分别为更新的最短时间minTime和最短距离minDistace
72 | locationManager.requestLocationUpdates(locationProvider, 0, 0, locationListener);
73 | }
74 |
75 |
76 | private void setLocation(Location location) {
77 | this.location = location;
78 | String address = "纬度:" + location.getLatitude() + "经度:" + location.getLongitude();
79 | Log.d(TAG, address);
80 | }
81 |
82 |
83 | //获取经纬度
84 | public Location showLocation() {
85 | return location;
86 | }
87 |
88 | public String getLocationText() {
89 | if (location==null){
90 | return "0,0";
91 | }else {
92 | return location.getLatitude() + "," + location.getLongitude();
93 | }
94 | }
95 |
96 |
97 | // 移除定位监听
98 | public void removeLocationUpdatesListener() {
99 | if (Build.VERSION.SDK_INT >= 23 &&
100 | ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
101 | ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
102 | return;
103 | }
104 | if (locationManager != null) {
105 | uniqueInstance = null;
106 | locationManager.removeUpdates(locationListener);
107 | }
108 | }
109 |
110 | LocationListener locationListener = new LocationListener() {
111 | @Override
112 | public void onStatusChanged(String provider, int status, Bundle arg2) {
113 |
114 | }
115 |
116 | @Override
117 | public void onProviderEnabled(String provider) {
118 |
119 | }
120 |
121 | @Override
122 | public void onProviderDisabled(String provider) {
123 |
124 | }
125 | @Override
126 | public void onLocationChanged(Location location) {
127 | location.getAccuracy();
128 | setLocation(location);
129 | }
130 | };
131 |
132 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/utils/MD5.java:
--------------------------------------------------------------------------------
1 | package com.ve.utils;
2 |
3 | import java.security.MessageDigest;
4 |
5 | public class MD5 {
6 |
7 | private MD5() {}
8 |
9 | public final static String getMessageDigest(byte[] buffer) {
10 | char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
11 | try {
12 | MessageDigest mdTemp = MessageDigest.getInstance("MD5");
13 | mdTemp.update(buffer);
14 | byte[] md = mdTemp.digest();
15 | int j = md.length;
16 | char str[] = new char[j * 2];
17 | int k = 0;
18 | for (int i = 0; i < j; i++) {
19 | byte byte0 = md[i];
20 | str[k++] = hexDigits[byte0 >>> 4 & 0xf];
21 | str[k++] = hexDigits[byte0 & 0xf];
22 | }
23 | return new String(str);
24 | } catch (Exception e) {
25 | return null;
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/utils/RomUtils.java:
--------------------------------------------------------------------------------
1 | package com.ve.utils;
2 |
3 | import android.os.Build;
4 | import android.text.TextUtils;
5 |
6 | import java.io.BufferedReader;
7 | import java.io.IOException;
8 | import java.io.InputStreamReader;
9 |
10 | public class RomUtils {
11 |
12 | class AvailableRomType {
13 | public static final int MIUI = 1;
14 | public static final int FLYME = 2;
15 | public static final int ANDROID_NATIVE = 3;
16 | public static final int NA = 4;
17 | }
18 |
19 | public static boolean isLightStatusBarAvailable () {
20 | if (isMIUIV6OrAbove() || isFlymeV4OrAbove() || isAndroidMOrAbove()) {
21 | return true;
22 | }
23 | return false;
24 | }
25 |
26 | public static int getLightStatausBarAvailableRomType() {
27 | if (isMIUIV6OrAbove()) {
28 | return AvailableRomType.MIUI;
29 | }
30 |
31 | if (isFlymeV4OrAbove()) {
32 | return AvailableRomType.FLYME;
33 | }
34 |
35 | if (isAndroidMOrAbove()) {
36 | return AvailableRomType.ANDROID_NATIVE;
37 | }
38 |
39 | return AvailableRomType.NA;
40 | }
41 |
42 | //Flyme V4的displayId格式为 [Flyme OS 4.x.x.xA]
43 | //Flyme V5的displayId格式为 [Flyme 5.x.x.x beta]
44 | private static boolean isFlymeV4OrAbove() {
45 | String displayId = Build.DISPLAY;
46 | if (!TextUtils.isEmpty(displayId) && displayId.contains("Flyme")) {
47 | String[] displayIdArray = displayId.split(" ");
48 | for (String temp : displayIdArray) {
49 | //版本号4以上,形如4.x.
50 | if (temp.matches("^[4-9]\\.(\\d+\\.)+\\S*")) {
51 | return true;
52 | }
53 | }
54 | }
55 | return false;
56 | }
57 |
58 | //MIUI V6对应的versionCode是4
59 | //MIUI V7对应的versionCode是5
60 | private static boolean isMIUIV6OrAbove() {
61 | String miuiVersionCodeStr = getSystemProperty("ro.miui.ui.version.code");
62 | if (!TextUtils.isEmpty(miuiVersionCodeStr)) {
63 | try {
64 | int miuiVersionCode = Integer.parseInt(miuiVersionCodeStr);
65 | if (miuiVersionCode >= 4) {
66 | return true;
67 | }
68 | } catch (Exception e) {}
69 | }
70 | return false;
71 | }
72 |
73 | //Android Api 23以上
74 | private static boolean isAndroidMOrAbove() {
75 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
76 | return true;
77 | }
78 | return false;
79 | }
80 |
81 | private static String getSystemProperty(String propName) {
82 | String line;
83 | BufferedReader input = null;
84 | try {
85 | Process p = Runtime.getRuntime().exec("getprop " + propName);
86 | input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024);
87 | line = input.readLine();
88 | input.close();
89 | } catch (IOException ex) {
90 | return null;
91 | } finally {
92 | if (input != null) {
93 | try {
94 | input.close();
95 | } catch (IOException e) {
96 | }
97 | }
98 | }
99 | return line;
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/utils/SysUtils.java:
--------------------------------------------------------------------------------
1 | package com.ve.utils;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.Context;
5 | import android.net.ConnectivityManager;
6 | import android.net.NetworkInfo;
7 | import android.view.View;
8 | import android.view.inputmethod.InputMethodManager;
9 |
10 | public class SysUtils {
11 | public static void showInputMethod(View view, boolean show) {
12 | InputMethodManager im = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
13 | if (show) {
14 | im.showSoftInput(view, 0);
15 | im.restartInput(view);
16 | } else {
17 | im.hideSoftInputFromWindow(view.getWindowToken(), 0);
18 |
19 | }
20 | }
21 |
22 | public static boolean isInputMethodShow(View view) {
23 | InputMethodManager im = (InputMethodManager) view.getContext()
24 | .getSystemService(Context.INPUT_METHOD_SERVICE);
25 | return im.isActive();
26 | }
27 |
28 | public static boolean isNetworkAvailable(Context context) {
29 | ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
30 |
31 | if (connectivityManager == null) {
32 | return false;
33 | } else {
34 | @SuppressLint("MissingPermission") NetworkInfo[] networkInfo = connectivityManager.getAllNetworkInfo();
35 | if (networkInfo != null && networkInfo.length > 0) {
36 | for (int i = 0; i < networkInfo.length; i++) {
37 | // 判断当前网络状态是否为连接状态
38 | if (networkInfo[i].getState() == NetworkInfo.State.CONNECTED) {
39 | return true;
40 | }
41 | }
42 | }
43 | }
44 | return false;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/editor/Base.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.editor;
2 |
3 | import com.ve.view.editor.out.Config;
4 | import com.ve.view.text.document.Document;
5 |
6 | public class Base {
7 | protected Editor editor;
8 | protected TextController controller;
9 | protected EditorInputConnection inputConnection;
10 | protected Document document;
11 | protected Caret caret;
12 | protected Painter painter;
13 | protected ListenManager operator;
14 | protected SpanManager spanManager;
15 | protected Config config;
16 |
17 | public Base(Editor editor){
18 | this.editor=editor;
19 | }
20 | public void init(){
21 | document=editor.document;
22 | controller=editor.controller;
23 | inputConnection=editor.inputConnection;
24 | caret=editor.caret;
25 | painter =editor.painter;
26 | operator=editor.operator;
27 | spanManager=editor.spanManager;
28 | config=editor.config;
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/editor/Caret.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.editor;
2 |
3 | import android.view.MotionEvent;
4 |
5 | import com.ve.view.utils.EditorException;
6 | import com.ve.view.editor.out.CaretInterface;
7 |
8 | public class Caret extends Base implements CaretInterface{
9 | protected final static long SCROLL_PERIOD = 250;
10 |
11 | protected final static int SCROLL_UP = 0;
12 | protected final static int SCROLL_DOWN = 1;
13 | protected final static int SCROLL_LEFT = 2;
14 | protected final static int SCROLL_RIGHT = 3;
15 |
16 | protected static final int SCROLL_EDGE = 10;
17 |
18 | protected int position = 0, row = 0;
19 | protected int x, y;
20 |
21 | protected boolean touching;
22 |
23 | public Caret(Editor editor) {
24 | super(editor);
25 | }
26 |
27 |
28 | public boolean isTouching() {
29 | return touching;
30 | }
31 |
32 | public boolean autoScroll(int aspect) {
33 | boolean scrolled = false;
34 | switch (aspect) {
35 | case SCROLL_UP:
36 | editor.removeCallbacks(scrollUpTask);
37 | if ((!onFirstRow())) {
38 | editor.post(scrollUpTask);
39 | scrolled = true;
40 | }
41 | break;
42 | case SCROLL_DOWN:
43 | editor.removeCallbacks(scrollDownTask);
44 | if (!onLastRow()) {
45 | editor.post(scrollDownTask);
46 | scrolled = true;
47 | }
48 | break;
49 | case SCROLL_LEFT:
50 | editor.removeCallbacks(scrollLeftTask);
51 | if (!onStart() && row == document.gainRowOfPosition(position - 1)) {
52 | editor.post(scrollLeftTask);
53 | scrolled = true;
54 | }
55 | break;
56 | case SCROLL_RIGHT:
57 | editor.removeCallbacks(scrollRightTask);
58 | if (!onEnd() && row == document.gainRowOfPosition(position + 1)) {
59 | editor.post(scrollRightTask);
60 | scrolled = true;
61 | }
62 | break;
63 | default:
64 | EditorException.fail("Invalid scroll direction");
65 | break;
66 | }
67 | return scrolled;
68 | }
69 |
70 | public void stopAutoScroll() {
71 | editor.removeCallbacks(scrollDownTask);
72 | editor.removeCallbacks(scrollUpTask);
73 | editor.removeCallbacks(scrollLeftTask);
74 | editor.removeCallbacks(scrollRightTask);
75 | }
76 |
77 | public void stopAutoScroll(int aspect) {
78 | switch (aspect) {
79 | case SCROLL_UP:
80 | editor.removeCallbacks(scrollUpTask);
81 | break;
82 | case SCROLL_DOWN:
83 | editor.removeCallbacks(scrollDownTask);
84 | break;
85 | case SCROLL_LEFT:
86 | editor.removeCallbacks(scrollLeftTask);
87 | break;
88 | case SCROLL_RIGHT:
89 | editor.removeCallbacks(scrollRightTask);
90 | break;
91 | default:
92 | EditorException.fail("Invalid scroll direction");
93 | break;
94 | }
95 | }
96 |
97 |
98 | @Override
99 | public int getRow() {
100 | return row;
101 | }
102 |
103 | @Override
104 | public int getPosition() {
105 | return position;
106 | }
107 |
108 |
109 | @Override
110 | public boolean onFirstRow() {
111 | return (row == 0);
112 | }
113 |
114 | @Override
115 | public boolean onLastRow() {
116 | return (row == (document.getRowCount() - 1));
117 | }
118 | @Override
119 | public boolean onStart() {
120 | return position == 0;
121 | }
122 | @Override
123 | public boolean onEnd() {
124 | return (position == (document.length() - 1));
125 | }
126 |
127 | @Override
128 | public void moveLeft() {
129 | moveLeft(false);
130 | }
131 |
132 | @Override
133 | public void moveRight() {
134 | moveRight(false);
135 | }
136 |
137 |
138 | //TODO 待解决 按屏幕坐标移动
139 |
140 | @Override
141 | public void moveDown() {
142 | if (!onLastRow()) {
143 | int currCaret = position;
144 | int currRow = row;
145 | int newRow = currRow + 1;
146 | int currColumn = document.getColumn(currCaret);
147 | int currRowLength = document.gainRowLength(currRow);
148 | int newRowLength = document.gainRowLength(newRow);
149 |
150 | if (currColumn < newRowLength) {
151 | position += currRowLength;
152 | } else {
153 | position += currRowLength - currColumn + newRowLength - 1;
154 | }
155 | ++row;
156 |
157 | controller.updateSelectionRange(currCaret, position);
158 | if (!editor.displayInterface.scrollToPosition(position)) {
159 | editor.invalidateRows(currRow, newRow + 1);
160 | }
161 | operator.onRowChange(newRow);
162 | inputConnection.stopTextComposing();
163 | }
164 | }
165 |
166 | @Override
167 | public void moveUp() {
168 | if (!onFirstRow()) {
169 | int currCaret = position;
170 | int currRow = row;
171 | int newRow = currRow - 1;
172 | int currColumn = document.getColumn(currCaret);
173 | int newRowLength = document.gainRowLength(newRow);
174 |
175 | if (currColumn < newRowLength) {
176 | position -= newRowLength;
177 | } else {
178 | position -= (currColumn + 1);
179 | }
180 | --row;
181 |
182 | controller.updateSelectionRange(currCaret, position);
183 | if (!editor.displayInterface.scrollToPosition(position)) {
184 | editor.invalidateRows(newRow, currRow + 1);
185 | }
186 | operator.onRowChange(newRow);
187 | inputConnection.stopTextComposing();
188 | }
189 | }
190 |
191 | @Override
192 | public void moveRight(boolean isTyping) {
193 | if (!onEnd()) {
194 | int originalRow = row;
195 | ++position;
196 | updateRow();
197 | controller.updateSelectionRange(position - 1, position);
198 | if (!editor.displayInterface.scrollToPosition(position)) {
199 | editor.invalidateRows(originalRow, row + 1);
200 | }
201 | if (!isTyping) {
202 | inputConnection.stopTextComposing();
203 | }
204 | }
205 | }
206 |
207 |
208 | @Override
209 | public void moveLeft(boolean isTyping) {
210 | if (!onStart()) {
211 | int originalRow = row;
212 | --position;
213 | updateRow();
214 | controller.updateSelectionRange(position + 1, position);
215 | if (!editor.displayInterface.scrollToPosition(position)) {
216 | editor.invalidateRows(row, originalRow + 1);
217 | }
218 |
219 | if (!isTyping) {
220 | inputConnection.stopTextComposing();
221 | }
222 | }
223 | }
224 |
225 | public void moveToPosition(int position) {
226 | if (position < 0 || position >= document.length()) {
227 | EditorException.fail("Invalid caret position");
228 | return;
229 | }
230 |
231 | controller.updateSelectionRange(this.position, position);
232 | updatePosition(position);
233 | }
234 |
235 |
236 |
237 | //-------protected methods-------
238 |
239 |
240 | protected void updateRow() {
241 | int newRow = document.gainRowOfPosition(position);
242 | if (row != newRow) {
243 | row = newRow;
244 | operator.onRowChange(newRow);
245 | }
246 | }
247 |
248 |
249 | protected void reset() {
250 | position = 0;
251 | row = 0;
252 | }
253 |
254 | protected void setCoord(int x, int y) {
255 | this.x = x;
256 | this.y = y;
257 | }
258 |
259 | protected void setTouching(boolean touching) {
260 | this.touching = touching;
261 | }
262 |
263 |
264 | protected void updatePosition(int position) {
265 | this.position = position;
266 | int oldRow = row;
267 | updateRow();
268 | if (!editor.displayInterface.scrollToPosition(position)) {
269 | editor.invalidateRows(oldRow, oldRow + 1);
270 | editor.invalidateCaretRow();
271 | }
272 | inputConnection.stopTextComposing();
273 | }
274 |
275 | protected void onDrag(MotionEvent e) {
276 |
277 | int x = (int) e.getX() - editor.getPaddingLeft();
278 | int y = (int) e.getY() - editor.getPaddingTop();
279 | boolean scrolled = false;
280 |
281 |
282 | if (x < SCROLL_EDGE) {
283 | scrolled = caret.autoScroll(Caret.SCROLL_LEFT);
284 | } else if (x >= (editor.getContentWidth() - SCROLL_EDGE)) {
285 | scrolled = caret.autoScroll(Caret.SCROLL_RIGHT);
286 | } else if (y < SCROLL_EDGE) {
287 | scrolled = caret.autoScroll(Caret.SCROLL_UP);
288 | } else if (y >= (editor.getContentHeight() - SCROLL_EDGE)) {
289 | scrolled = caret.autoScroll(Caret.SCROLL_DOWN);
290 | }
291 |
292 | if (!scrolled) {
293 | caret.stopAutoScroll();
294 | int newPosition = editor.getPainter().getPositionByCoord(
295 | editor.displayInterface.screenToViewX((int) e.getX()),
296 | editor.displayInterface.screenToViewY((int) e.getY()), false
297 | );
298 | if (newPosition >= 0) {
299 | caret.moveToPosition(newPosition);
300 | }
301 | }
302 | }
303 |
304 | private final Runnable scrollDownTask = new Runnable() {
305 | @Override
306 | public void run() {
307 | moveDown();
308 | if (!onLastRow()) {
309 | editor.postDelayed(scrollDownTask, SCROLL_PERIOD);
310 | }
311 | }
312 | };
313 |
314 | private final Runnable scrollUpTask = new Runnable() {
315 | @Override
316 | public void run() {
317 | moveUp();
318 | if (!onFirstRow()) {
319 | editor.postDelayed(scrollUpTask, SCROLL_PERIOD);
320 | }
321 | }
322 | };
323 |
324 |
325 | private final Runnable scrollLeftTask = new Runnable() {
326 | @Override
327 | public void run() {
328 | moveLeft();
329 | if (!onStart() && row == document.gainRowOfPosition(position - 1)) {
330 | editor.postDelayed(scrollLeftTask, SCROLL_PERIOD);
331 | }
332 | }
333 | };
334 |
335 | private final Runnable scrollRightTask = new Runnable() {
336 | @Override
337 | public void run() {
338 | moveRight();
339 | if (!onEnd() && row == document.gainRowOfPosition(position + 1)) {
340 | editor.postDelayed(scrollRightTask, SCROLL_PERIOD);
341 | }
342 | }
343 | };
344 |
345 | }
346 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/editor/CursorHandlerManager.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.editor;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.BitmapFactory;
5 | import android.graphics.Canvas;
6 | import android.graphics.Color;
7 | import android.graphics.Paint;
8 | import android.graphics.Rect;
9 | import android.graphics.RectF;
10 | import android.graphics.drawable.Drawable;
11 | import android.view.MotionEvent;
12 |
13 | import com.ve.acpp.R;
14 | import com.ve.utils.DisplayUtils;
15 | import com.ve.view.utils.Rectangle;
16 |
17 | public class CursorHandlerManager {
18 | private Editor editor;
19 | private BaseHandle mid;
20 | private BaseHandle start;
21 | private BaseHandle end;
22 |
23 | private boolean showHandler;
24 |
25 | private int handlerSize;
26 | private Caret caret;
27 | private Drawable handleIcon;
28 |
29 | public CursorHandlerManager(Editor editor) {
30 | this.editor = editor;
31 |
32 | handleIcon = editor.getContext().getResources().getDrawable(R.drawable.cursor );
33 |
34 |
35 | handlerSize = DisplayUtils.dp2px(editor.getContext(), 24);
36 | mid = new CommonHandle();
37 | start = new SelectHandle();
38 | end = new SelectHandle();
39 | }
40 |
41 | public void init() {
42 | caret = editor.caret;
43 | }
44 |
45 | public void onDown(MotionEvent e) {
46 |
47 | if (!caret.isTouching()) {
48 | int x = (int) e.getX() + editor.getScrollX();
49 | int y = (int) e.getY() + editor.getScrollY();
50 |
51 | if (mid.onDown(x, y)) {
52 | showHandler = true;
53 | } else if (start.onDown(x, y)) {
54 | editor.displayInterface.scrollToSelectionStart();
55 | } else if (end.onDown(x, y)) {
56 | editor.displayInterface.scrollToSelectionEnd();
57 | }
58 | }
59 | }
60 |
61 | public void onUp(MotionEvent e) {
62 | mid.clearTouch();
63 | start.clearTouch();
64 | end.clearTouch();
65 | }
66 |
67 | public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
68 |
69 |
70 | if (mid.touched) {
71 | showHandler = true;
72 | mid.moveHandle(e2);
73 | return true;
74 | }
75 | if (start.touched) {
76 | start.moveHandle(e2);
77 | return true;
78 | }
79 | if (end.touched) {
80 | end.moveHandle(e2);
81 | return true;
82 | }
83 | return false;
84 |
85 | }
86 |
87 |
88 | public boolean onSingleTapUp(MotionEvent e) {
89 | int x = (int) e.getX() + editor.getScrollX();
90 | int y = (int) e.getY() + editor.getScrollY();
91 |
92 | if (mid.isInHandle(x, y) || start.isInHandle(x, y) || end.isInHandle(x, y)) {
93 | //忽略单次点击事件
94 | //拦截事件
95 | return true;
96 | } else {
97 | showHandler = true;
98 | return false;
99 | }
100 | }
101 |
102 | public boolean onDoubleTap(MotionEvent e) {
103 | int x = (int) e.getX() + editor.getScrollX();
104 | int y = (int) e.getY() + editor.getScrollY();
105 |
106 | if (mid.isInHandle(x, y)) {
107 | editor.getSelectInterface().select(true);
108 | return true;
109 | } else {
110 | return false;
111 | }
112 | }
113 |
114 |
115 | public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
116 | if (mid.isTouched() || start.isTouched() || end.isTouched()) {
117 | return true;
118 | } else {
119 | return false;
120 | }
121 | }
122 |
123 |
124 | public void draw(Canvas canvas) {
125 |
126 | if (!editor.getSelectInterface().isSelecting()) {
127 |
128 | if (!mid.isTouched()) {
129 | mid.attachToPosition(caret.getPosition());
130 | }
131 | if (showHandler) {
132 | mid.draw(canvas);
133 | }
134 | showHandler = false;
135 | } else {
136 | if (!(start.isTouched() && end.isTouched())) {
137 | start.attachToPosition(editor.getSelectInterface().getSelectionStart());
138 | end.attachToPosition(editor.getSelectInterface().getSelectionEnd());
139 | }
140 | start.draw(canvas);
141 | end.draw(canvas);
142 | }
143 | }
144 |
145 |
146 | private class BaseHandle {
147 | public Rectangle body = new Rectangle(0, 0, handlerSize, (int) (1f*handlerSize*handleIcon.getIntrinsicHeight()/handleIcon.getIntrinsicWidth()));
148 |
149 | private int anchorX = 0, anchorY = 0;
150 |
151 | private int touchX = 0, touchY = 0;//相对位置
152 |
153 | private final Paint paint;
154 |
155 |
156 | private boolean touched;
157 |
158 |
159 | public boolean isTouched() {
160 | return touched;
161 | }
162 |
163 | public BaseHandle() {
164 | paint = new Paint();
165 | paint.setColor(Color.BLUE);
166 | paint.setAntiAlias(true);
167 | }
168 |
169 | public void moveHandle(MotionEvent e) {
170 |
171 | int newPosition = gainNearestPosition((int) e.getX(), (int) e.getY());
172 |
173 | if (editor.textInterface.isValidPosition(newPosition)) {
174 | caret.moveToPosition(newPosition);
175 |
176 | moveToPosition(newPosition);
177 | }
178 |
179 | }
180 |
181 |
182 | public void draw(Canvas canvas) {
183 |
184 | handleIcon.setBounds(body.toRect());
185 | handleIcon.draw(canvas);
186 | /*canvas.drawLine(anchorX, anchorY,
187 | body.getCenterX(), body.getCenterY(), paint);
188 | canvas.drawArc(new RectF(anchorX - radius, anchorY - radius / 2 - offsetY(),
189 | body.x + radius * 2, body.y + radius / 2), 60, 60, true, paint);
190 | canvas.drawOval(body.toRectF(), paint);*/
191 |
192 |
193 | }
194 |
195 | private int offsetY() {
196 | return 0;
197 | }
198 |
199 |
200 | public void moveToPosition(int position) {
201 |
202 | Rect newCaretBounds = editor.painter.getBoundingBox(position);
203 | int newX = newCaretBounds.left + editor.getPaddingLeft();
204 | int newY = newCaretBounds.bottom + editor.getPaddingTop();
205 | setRestingCoord(newX, newY);
206 | editor.invalidate(body.toRect());
207 | }
208 |
209 |
210 | public void setRestingCoord(int x, int y) {
211 | anchorX = x;
212 | anchorY = y;
213 | body.setPosition(x - body.width / 2, y + offsetY());
214 |
215 | }
216 |
217 |
218 | public int gainNearestPosition(int x, int y) {
219 | int attachedLeft = editor.displayInterface.screenToViewX(x) - touchX + body.width / 2;
220 | int attachedBottom = editor.displayInterface.screenToViewY(y) - touchY - offsetY() - 2;
221 |
222 | return editor.getPainter().getPositionByCoord(attachedLeft, attachedBottom, false);
223 | }
224 |
225 | public void setTouch(int x, int y) {
226 | touchX = x - body.x;
227 | touchY = y - body.y;
228 | }
229 |
230 | public void clearTouch() {
231 | touched = false;
232 | touchX = 0;
233 | touchY = 0;
234 | }
235 |
236 |
237 | public boolean isInHandle(int x, int y) {
238 | return body.contains(x, y);
239 | }
240 |
241 | public boolean onDown(int x, int y) {
242 | touched = isInHandle(x, y);
243 | if (touched) {
244 | setTouch(x, y);
245 | editor.invalidate(body.toRect());
246 | }
247 | return touched;
248 | }
249 |
250 | public void attachToPosition(int position) {
251 | Rect boundingBox = editor.painter.getBoundingBox(position);
252 | int x = boundingBox.left + editor.getPaddingLeft();
253 | int y = boundingBox.bottom + editor.getPaddingTop();
254 | setRestingCoord(x, y);
255 | }
256 | }
257 |
258 | class CommonHandle extends BaseHandle {
259 | @Override
260 | public boolean isInHandle(int x, int y) {
261 | return super.isInHandle(x, y) && !editor.getSelectInterface().isSelecting();
262 | }
263 | }
264 |
265 | class SelectHandle extends BaseHandle {
266 | @Override
267 | public boolean isInHandle(int x, int y) {
268 | return super.isInHandle(x, y) && editor.getSelectInterface().isSelecting();
269 | }
270 | }
271 |
272 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/editor/EditorInputConnection.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.editor;
2 |
3 | import android.content.Context;
4 | import android.view.KeyEvent;
5 | import android.view.inputmethod.BaseInputConnection;
6 | import android.view.inputmethod.InputMethodManager;
7 |
8 | import com.ve.view.utils.EditorException;
9 | import com.ve.view.text.document.Document;
10 |
11 | public class EditorInputConnection extends BaseInputConnection {
12 |
13 | protected int composingLength = 0;
14 | protected Editor editor;
15 | protected Document document;
16 | protected Caret caret;
17 | protected TextController controller;
18 |
19 | public EditorInputConnection(Editor editor) {
20 | super(editor, true);
21 | this.editor = editor;
22 | }
23 |
24 | public void init() {
25 | this.document = editor.document;
26 | this.caret = editor.caret;
27 | this.controller = editor.controller;
28 | }
29 |
30 | public void reset() {
31 | composingLength = 0;
32 | document.endBatchEdit();
33 | }
34 |
35 | public boolean isComposing() {
36 | return composingLength == 0;
37 | }
38 |
39 | public void stopTextComposing() {
40 | InputMethodManager im = (InputMethodManager) editor.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
41 | im.restartInput(editor);
42 |
43 | if (isComposing()) {
44 | reset();
45 | }
46 | }
47 |
48 |
49 | @Override
50 | public boolean performContextMenuAction(int id) {
51 | switch (id) {
52 | case android.R.id.copy:
53 | editor.getTextInterface().copy();
54 | break;
55 | case android.R.id.cut:
56 | editor.getTextInterface().cut();
57 | break;
58 | case android.R.id.paste:
59 | editor.getTextInterface().paste();
60 | break;
61 | case android.R.id.startSelectingText:
62 | case android.R.id.stopSelectingText:
63 | case android.R.id.selectAll:
64 | editor.selectInterface.selectAll();
65 | break;
66 | }
67 |
68 | return false;
69 | }
70 |
71 | @Override
72 | public boolean sendKeyEvent(KeyEvent event) {
73 | switch (event.getKeyCode()) {
74 | case KeyEvent.KEYCODE_SHIFT_LEFT:
75 |
76 | editor.selectInterface.select(!editor.selectInterface.isSelecting());
77 | break;
78 | case KeyEvent.KEYCODE_DPAD_LEFT:
79 | caret.moveLeft();
80 | break;
81 | case KeyEvent.KEYCODE_DPAD_UP:
82 | caret.moveUp();
83 | break;
84 | case KeyEvent.KEYCODE_DPAD_RIGHT:
85 | caret.moveRight();
86 | break;
87 | case KeyEvent.KEYCODE_DPAD_DOWN:
88 | caret.moveDown();
89 | break;
90 | case KeyEvent.KEYCODE_MOVE_HOME:
91 | caret.moveToPosition(0);
92 | break;
93 | case KeyEvent.KEYCODE_MOVE_END:
94 | caret.moveToPosition(document.length() - 1);
95 | break;
96 | default:
97 | return super.sendKeyEvent(event);
98 | }
99 | return true;
100 | }
101 |
102 |
103 | @Override
104 | public boolean setComposingText(CharSequence text, int newCursorPosition) {
105 | if (!document.isBatchEdit()) {
106 | document.beginBatchEdit();
107 | }
108 |
109 | controller.replaceComposingText(caret.getPosition() - composingLength, composingLength, text.toString());
110 | composingLength = text.length();
111 |
112 | //TODO 减少重绘
113 | if (newCursorPosition > 1) {
114 | caret.moveToPosition(caret.position + newCursorPosition - 1);
115 | } else if (newCursorPosition <= 0) {
116 | caret.moveToPosition(caret.position - text.length() - newCursorPosition);
117 | }
118 | return true;
119 | }
120 |
121 | @Override
122 | public boolean commitText(CharSequence text, int newCursorPosition) {
123 | controller.replaceComposingText(caret.getPosition() - composingLength, composingLength, text.toString());
124 | composingLength = 0;
125 | document.endBatchEdit();
126 |
127 | //TODO 减少重绘
128 | if (newCursorPosition > 1) {
129 | caret.moveToPosition(caret.position + newCursorPosition - 1);
130 | } else if (newCursorPosition <= 0) {
131 | caret.moveToPosition(caret.position - text.length() - newCursorPosition);
132 | }
133 |
134 | return true;
135 | }
136 |
137 |
138 | @Override
139 | public boolean deleteSurroundingText(int leftLength, int rightLength) {
140 | EditorException.logIf(composingLength > 0, "deleteSurroundingText composingLength != 0");
141 |
142 | controller.deleteAroundComposingText(leftLength, rightLength);
143 | return true;
144 | }
145 |
146 | @Override
147 | public boolean finishComposingText() {
148 | reset();
149 | return true;
150 | }
151 |
152 |
153 | @Override
154 | public CharSequence getTextAfterCursor(int maxLen, int flags) {
155 | return controller.getTextAfterCursor(maxLen);
156 | }
157 |
158 | @Override
159 | public CharSequence getTextBeforeCursor(int maxLen, int flags) {
160 | return controller.getTextBeforeCursor(maxLen);
161 | }
162 |
163 | @Override
164 | public boolean setSelection(int start, int end) {
165 | if (start == end) {
166 | caret.moveToPosition(start);
167 | } else {
168 | controller.setSelectionRange(start, end - start, false);
169 | }
170 | return true;
171 | }
172 |
173 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/editor/EventManager.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.editor;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Rect;
5 | import android.view.GestureDetector;
6 | import android.view.HapticFeedbackConstants;
7 | import android.view.MotionEvent;
8 |
9 | import com.ve.utils.SysUtils;
10 | import com.ve.view.text.document.Document;
11 | import com.ve.view.utils.ZoomChecker;
12 |
13 |
14 | public class EventManager {
15 | protected Editor editor;
16 | protected Caret caret;
17 | protected GestureDetector gestureDetector;
18 | protected ZoomChecker zoomChecker;
19 | protected int fling;
20 | protected CursorHandlerManager cursorHandler;
21 | protected BaseListener baseListener;
22 | protected static int TOUCH_SLOP = 12;
23 |
24 | public EventManager(Editor editor) {
25 | this.editor = editor;
26 | baseListener = new BaseListener();
27 | gestureDetector = new GestureDetector(editor.getContext(), baseListener);
28 | gestureDetector.setIsLongpressEnabled(true);
29 | cursorHandler=new CursorHandlerManager(editor);
30 | zoomChecker = new ZoomChecker();
31 | zoomChecker.setListener(editor.getPainter());
32 | }
33 |
34 | public void init() {
35 | caret = editor.caret;
36 | cursorHandler.init();
37 | }
38 |
39 |
40 | public boolean onTouchEvent(MotionEvent event) {
41 | zoomChecker.checkZoom(event);
42 | boolean handled = gestureDetector.onTouchEvent(event);
43 | if (!handled && (event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {
44 | handled = baseListener.onUp(event);
45 | }
46 | return handled;
47 | }
48 |
49 |
50 | public boolean isNearChar(int x, int y, int charOffset) {
51 | Rect bounds = editor.painter.getBoundingBox(charOffset);
52 |
53 | return (y >= (bounds.top - TOUCH_SLOP)
54 | && y < (bounds.bottom + TOUCH_SLOP)
55 | && x >= (bounds.left - TOUCH_SLOP)
56 | && x < (bounds.right + TOUCH_SLOP)
57 | );
58 | }
59 |
60 | public void onTextDrawComplete(Canvas canvas) {
61 | cursorHandler.draw(canvas);
62 | }
63 |
64 |
65 | class BaseListener extends GestureDetector.SimpleOnGestureListener {
66 |
67 | public boolean onUp(MotionEvent e) {
68 | caret.stopAutoScroll();
69 | caret.setTouching(false);
70 | zoomChecker.reset();
71 | fling = 0;
72 | cursorHandler.onUp(e);
73 | return true;
74 | }
75 |
76 | @Override
77 | public boolean onDown(MotionEvent e) {
78 | int x = editor.displayInterface.screenToViewX((int) e.getX());
79 | int y = editor.displayInterface.screenToViewY((int) e.getY());
80 | caret.setTouching(isNearChar(x, y, caret.getPosition()));
81 |
82 | if (editor.displayInterface.isFlingScrolling()) {
83 | editor.displayInterface.stopFlingScrolling();
84 | } else if (editor.getSelectInterface().isSelecting()) {
85 | if (isNearChar(x, y, editor.getSelectInterface().getSelectionStart())) {
86 | editor.displayInterface.scrollToSelectionStart();
87 | editor.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
88 |
89 | caret.setTouching(true);
90 | } else if (isNearChar(x, y, editor.getSelectInterface().getSelectionEnd())) {
91 | editor.displayInterface.scrollToSelectionEnd();
92 | editor.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
93 | caret.setTouching(true);
94 | }
95 | }
96 |
97 | if (caret.isTouching()) {
98 | editor.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
99 | }
100 | cursorHandler.onDown(e);
101 |
102 | return true;
103 | }
104 |
105 | @Override
106 | public boolean onSingleTapUp(MotionEvent e) {
107 | if (cursorHandler.onSingleTapUp(e)){
108 | return true;
109 | }
110 |
111 | int x = editor.displayInterface.screenToViewX((int) e.getX());
112 | int y = editor.displayInterface.screenToViewY((int) e.getY());
113 | int position = editor.getPainter().getPositionByCoord(x, y, false);
114 |
115 | if (editor.getSelectInterface().isSelecting()) {
116 | int strictCharOffset = editor.getPainter().getPositionByCoord(x, y, true);
117 | if (editor.getSelectInterface().inSelectionRange(strictCharOffset) || isNearChar(x, y, editor.getSelectInterface().getSelectionStart()) || isNearChar(x, y, editor.getSelectInterface().getSelectionEnd())) {
118 | // do nothing
119 | } else {
120 | editor.getSelectInterface().cancelSelect();
121 | if (strictCharOffset >= 0) {
122 | caret.moveToPosition(position);
123 | }
124 | }
125 | } else {
126 | if (position >= 0) {
127 | caret.moveToPosition(position);
128 | }
129 | }
130 | SysUtils.showInputMethod(editor, true);
131 | return true;
132 | }
133 |
134 |
135 | @Override
136 | public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
137 | if (cursorHandler.onScroll(e1, e2, distanceX, distanceY)){
138 | return true;
139 | }
140 |
141 | if (caret.isTouching()) {
142 | caret.onDrag(e2);
143 | } else if (e2.getPointerCount() == 1) {
144 | if (fling == 0)
145 | if (Math.abs(distanceX) > Math.abs(distanceY))
146 | fling = 1;
147 | else
148 | fling = -1;
149 | if (fling == 1)
150 | distanceY = 0;
151 | else if (fling == -1)
152 | distanceX = 0;
153 |
154 | editor.displayInterface.scroll(distanceX, distanceY);
155 |
156 | }
157 | if ((e2.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {
158 | onUp(e2);
159 | }
160 | return true;
161 | }
162 |
163 |
164 | @Override
165 | public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
166 | if (cursorHandler.onFling(e1, e2, velocityX, velocityY)){
167 | return true;
168 | }
169 |
170 | if (!caret.isTouching()) {
171 | if (fling == 1) {
172 | velocityY = 0;
173 | } else if (fling == -1) {
174 | velocityX = 0;
175 | }
176 | editor.displayInterface.flingScroll((int) -velocityX, (int) -velocityY);
177 | }
178 | onUp(e2);
179 | return true;
180 | }
181 |
182 | @Override
183 | public void onLongPress(MotionEvent e) {
184 | onDoubleTap(e);
185 | }
186 |
187 | @Override
188 | public boolean onDoubleTap(MotionEvent e) {
189 | if (cursorHandler.onDoubleTap(e)){
190 | return true;
191 | }
192 |
193 | caret.setTouching(true);
194 | int x = editor.displayInterface.screenToViewX((int) e.getX());
195 | int y = editor.displayInterface.screenToViewY((int) e.getY());
196 | int charOffset = editor.getPainter().getPositionByCoord(x, y, false);
197 |
198 | if (charOffset >= 0) {
199 | caret.moveToPosition(charOffset);
200 | Document document = editor.document;
201 | int start;
202 | int end;
203 | for (start = charOffset; start >= 0; start--) {
204 | char c = document.charAt(start);
205 | if (!Character.isJavaIdentifierPart(c))
206 | break;
207 | }
208 | if (start != charOffset)
209 | start++;
210 | for (end = charOffset; end >= 0; end++) {
211 | char c = document.charAt(end);
212 | if (!Character.isJavaIdentifierPart(c))
213 | break;
214 | }
215 | //editor.getSelectInterface().select(true);
216 | editor.getSelectInterface().select(start, end - start);
217 | }
218 |
219 | return true;
220 | }
221 |
222 | }
223 |
224 |
225 | }
226 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/editor/ListenManager.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.editor;
2 |
3 | import com.ve.view.listener.RowListener;
4 | import com.ve.view.listener.SelectionListener;
5 | import com.ve.view.listener.TextListener;
6 |
7 | public class ListenManager extends Base implements RowListener, SelectionListener, TextListener {
8 | private RowListener rowListener;
9 | private SelectionListener selectionChangedListener;
10 | private TextListener textListener;
11 |
12 | public ListenManager(Editor editor) {
13 | super(editor);
14 | rowListener = new RowListener.RowAdapter();
15 | selectionChangedListener = new SelectionListener.SelectionAdapter();
16 | textListener = new TextListener.TextAdapter();
17 | }
18 |
19 | @Override
20 | public void onRowChange(int row) {
21 | if (rowListener != null) {
22 | rowListener.onRowChange(row);
23 | }
24 | }
25 |
26 | @Override
27 | public void onSelectionChanged(boolean select, int selectionStart, int selectionEnd) {
28 | selectionChangedListener.onSelectionChanged(select,selectionStart,selectionEnd);
29 | }
30 |
31 |
32 | @Override
33 | public void onNewLine( int caretPosition, int p2) {
34 | textListener.onNewLine( caretPosition, p2);
35 | }
36 |
37 | @Override
38 | public void onDelete(int caretPosition, int newCursorPosition) {
39 | textListener.onDelete( caretPosition, newCursorPosition);
40 | }
41 |
42 | @Override
43 | public void onAdd(CharSequence text, int caretPosition, int newCursorPosition) {
44 | textListener.onAdd(text, caretPosition, newCursorPosition);
45 | }
46 |
47 | public void setRowListener(RowListener rowListener) {
48 | this.rowListener = rowListener;
49 | }
50 |
51 | public void setSelectionChangedListener(SelectionListener selectionChangedListener) {
52 | this.selectionChangedListener = selectionChangedListener;
53 | }
54 |
55 | public void setTextListener(TextListener textListener) {
56 | this.textListener = textListener;
57 | }
58 |
59 |
60 |
61 |
62 |
63 | public int getCaretY() {
64 | return caret.y;
65 | }
66 |
67 | public int getCaretX() {
68 | return caret.x;
69 | }
70 |
71 |
72 |
73 |
74 |
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/editor/MeasureCache.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.editor;
2 |
3 | import android.graphics.Paint;
4 |
5 | import com.ve.view.editor.out.Config;
6 | import com.ve.view.text.document.CommonLanguage;
7 |
8 | import java.util.HashMap;
9 |
10 | public class MeasureCache {
11 | private HashMap cache;
12 | private Paint paint;
13 |
14 | private int rowHeight;
15 |
16 | private int spaceWidth;
17 | private int tabWidth;
18 | private int newlineWidth;
19 | private Config config;
20 |
21 | public MeasureCache(Paint paint) {
22 | this.paint = paint;
23 | this.cache = new HashMap<>();
24 | }
25 |
26 |
27 | public int getRowHeight() {
28 | return rowHeight;
29 | }
30 |
31 | public int getSpaceWidth() {
32 | return spaceWidth;
33 | }
34 |
35 | public int getTabWidth() {
36 | return tabWidth;
37 | }
38 |
39 | public int getNewlineWidth() {
40 | return newlineWidth;
41 | }
42 |
43 |
44 | public void setConfig(Config config) {
45 | this.config = config;
46 | invalidate();
47 | }
48 |
49 |
50 | private float measureFromCache(char c) {
51 |
52 | Float value = cache.get(c);
53 | if (value == null) {
54 | value = measure(c);
55 | cache.put(c, value);
56 | }
57 | return value;
58 | }
59 |
60 | private float measure(char c) {
61 | return paint.measureText(new char[]{c}, 0, 1);
62 | }
63 |
64 | private float measure(String s) {
65 | return paint.measureText(s, 0, s.length());
66 | }
67 |
68 | private float measure(char c1, char c2) {
69 | return paint.measureText(new char[]{c1, c2}, 0, 2);
70 | }
71 |
72 | public void invalidate() {
73 | cache.clear();
74 | reMeasure();
75 | }
76 |
77 | private void reMeasure() {
78 | if (config.showNonPrinting) {
79 | spaceWidth = (int) measure(CommonLanguage.GLYPH_SPACE);
80 | newlineWidth = (int) measure(CommonLanguage.GLYPH_NEWLINE);
81 | tabWidth = (int) measure(CommonLanguage.GLYPH_TAB);
82 |
83 | } else {
84 | spaceWidth = (int) measure(CommonLanguage.SPACE);
85 | newlineWidth = (int) measure(CommonLanguage.SPACE);
86 | tabWidth = spaceWidth *config. tabLength;
87 | }
88 | rowHeight = (int) (paint.descent()-paint.ascent());
89 | }
90 |
91 |
92 | public MeasureTool getNewMeasureTool() {
93 | return new MeasureTool();
94 | }
95 |
96 | class MeasureTool {
97 | private char emojiFlag;
98 | private boolean justEmoji;//上一个是Emoji
99 |
100 | public boolean isJustEmoji() {
101 | return justEmoji;
102 | }
103 |
104 | public MeasureTool() {
105 | reset();
106 | }
107 |
108 | public void reset() {
109 | emojiFlag = CommonLanguage.NULL_CHAR;
110 | justEmoji = false;
111 | }
112 |
113 | public int measure(char c) {
114 | int width;
115 |
116 | justEmoji = false;
117 | switch (c) {
118 | case CommonLanguage.EMOJI1:
119 | case CommonLanguage.EMOJI2:
120 | width = 0;
121 | emojiFlag = c;
122 | break;
123 | case CommonLanguage.SPACE:
124 | width = spaceWidth;
125 | break;
126 | case CommonLanguage.NEWLINE:
127 | case CommonLanguage.EOF:
128 | width = newlineWidth;
129 | break;
130 | case CommonLanguage.TAB:
131 | width = tabWidth;
132 | break;
133 | default:
134 | if (emojiFlag != CommonLanguage.NULL_CHAR) {
135 | width = (int) MeasureCache.this.measure(emojiFlag, c);
136 | justEmoji = true;
137 | emojiFlag = CommonLanguage.NULL_CHAR;
138 | } else {
139 | if (config.cacheEnable) {
140 | width = (int) MeasureCache.this.measureFromCache(c);
141 | } else {
142 | width = (int) MeasureCache.this.measure(c);
143 | }
144 |
145 | }
146 | break;
147 | }
148 |
149 | return width;
150 | }
151 |
152 |
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/editor/SpanManager.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.editor;
2 |
3 | import android.graphics.Color;
4 |
5 | import com.ve.lexer.Lexer;
6 | import com.ve.view.utils.Pair;
7 | import com.ve.view.editor.span.SpanType;
8 | import com.ve.view.editor.span.TextSpanData;
9 |
10 | public class SpanManager extends Base{
11 | protected final Lexer lexer = new Lexer(results -> editor.post(() -> {
12 | TextSpanData textSpanData = new TextSpanData();
13 | for (Pair result : results) {
14 | //System.err.printf("%d %d\n",result.getFirst(),result.getSecond());
15 | textSpanData.addSpan(result.getFirst(),new SpanType(result.getSecond()==0? Color.BLACK:Color.RED));
16 | }
17 | painter.setTextSpanData(textSpanData);
18 | editor.invalidate();
19 | }));
20 |
21 | public SpanManager(Editor editor) {
22 | super(editor);
23 | }
24 |
25 | public void startSpan() {
26 | lexer.tokenize(document);
27 | }
28 |
29 | public void stopSpan() {
30 | lexer.cancelTokenize();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/editor/out/CaretInterface.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.editor.out;
2 |
3 | public interface CaretInterface {
4 |
5 | int getRow();
6 |
7 | int getPosition();
8 |
9 | boolean onFirstRow();
10 |
11 | boolean onLastRow();
12 |
13 | boolean onStart();
14 |
15 | boolean onEnd();
16 |
17 | void moveLeft();
18 |
19 | void moveRight();
20 |
21 | void moveDown();
22 |
23 | void moveUp();
24 |
25 | void moveRight(boolean isTyping);
26 |
27 | void moveLeft(boolean isTyping);
28 | }
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/editor/out/Config.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.editor.out;
2 |
3 | import com.ve.view.ext.ColorScheme;
4 |
5 | public class Config {
6 | public boolean wordWrap = true;
7 | public boolean showNonPrinting = true;
8 | public int tabLength=4;
9 | public int textSize;
10 | public boolean cacheEnable = false;
11 | public boolean isAutoIndent = true;
12 |
13 | public int autoIndentWidth = 4;
14 | ColorScheme colorScheme;
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/editor/out/DisplayInterface.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.editor.out;
2 |
3 | public interface DisplayInterface {
4 | boolean scrollToPosition(int position);
5 |
6 | void scrollToSelectionStart();
7 |
8 | void scrollToSelectionEnd();
9 |
10 | void scroll(float dx, float dy);
11 |
12 | void smoothScrollTo(int x, int y);
13 |
14 | void flingScroll(int velocityX, int velocityY);
15 |
16 | boolean isFlingScrolling();
17 |
18 | void stopFlingScrolling();
19 |
20 | void smoothScroll(int dx, int dy);
21 |
22 | void scrollToCaret();
23 |
24 | int screenToViewX(int x);
25 |
26 | int screenToViewY(int y);
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/editor/out/SelectInterface.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.editor.out;
2 |
3 | public interface SelectInterface {
4 | boolean isSelecting();
5 |
6 | void cancelSelect();
7 |
8 | void select(boolean select);
9 |
10 | void selectAll();
11 |
12 | void select(int position, int count);
13 |
14 | boolean inSelectionRange(int position);
15 |
16 | int getSelectionStart();
17 |
18 | int getSelectionEnd();
19 | /* boolean isSelecting();
20 | int getSelectionStart();
21 | int getSelectionEnd();
22 | void setSelection(int start, int end);
23 | String getSelectionText();
24 | void cancelSelection();*/
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/editor/out/Status.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.editor.out;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 |
6 | import com.ve.view.editor.Editor;
7 |
8 | public class Status implements Parcelable {
9 | public int caretPosition;
10 | public int scrollX, scrollY;
11 | public boolean selectMode;
12 | public int selectBegin, selectEnd;
13 |
14 | @Override
15 | public int describeContents() {
16 | return 0;
17 | }
18 |
19 | public Status(Editor editor) {
20 | caretPosition = editor.getCaretInterface().getPosition();
21 | scrollX = editor.getScrollX();
22 | scrollY = editor.getScrollY();
23 | selectMode = editor.getSelectInterface().isSelecting();
24 | selectBegin = editor.getSelectInterface().getSelectionStart();
25 | selectEnd = editor.getSelectInterface().getSelectionEnd();
26 | }
27 |
28 | private Status(Parcel in) {
29 | caretPosition = in.readInt();
30 | scrollX = in.readInt();
31 | scrollY = in.readInt();
32 | selectMode = in.readInt() != 0;
33 | selectBegin = in.readInt();
34 | selectEnd = in.readInt();
35 | }
36 |
37 | @Override
38 | public void writeToParcel(Parcel out, int flags) {
39 | out.writeInt(caretPosition);
40 | out.writeInt(scrollX);
41 | out.writeInt(scrollY);
42 | out.writeInt(selectMode ? 1 : 0);
43 | out.writeInt(selectBegin);
44 | out.writeInt(selectEnd);
45 | }
46 |
47 | public static final Creator CREATOR = new Creator() {
48 | @Override
49 | public Status createFromParcel(Parcel in) {
50 | return new Status(in);
51 | }
52 |
53 | @Override
54 | public Status[] newArray(int size) {
55 | return new Status[size];
56 | }
57 | };
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/editor/out/TextInterface.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.editor.out;
2 |
3 | import com.ve.view.text.document.Document;
4 |
5 | public interface TextInterface {
6 |
7 |
8 | void replaceText(int position, int count, String text);
9 |
10 | int getLength();
11 |
12 | boolean isValidPosition(int position);
13 |
14 | int getRowCount();
15 |
16 | void setDocument(Document document);
17 |
18 | Document getDocument();
19 |
20 | void setEdited(boolean set);
21 |
22 | boolean isEdited();
23 |
24 | void undo();
25 |
26 | void redo();
27 |
28 | void cut();
29 |
30 | void copy();
31 |
32 | void paste();
33 |
34 | void paste(String text);
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/editor/span/SpanNode.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.editor.span;
2 |
3 | public class SpanNode {
4 | int length;
5 | SpanType type;
6 |
7 | public SpanNode(int length, SpanType type) {
8 | this.length = length;
9 | this.type = type;
10 | }
11 |
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/editor/span/SpanType.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.editor.span;
2 |
3 | import android.graphics.Paint;
4 | import android.graphics.Typeface;
5 | import android.support.annotation.ColorInt;
6 | import android.util.Log;
7 |
8 |
9 | public class SpanType {
10 | public static final Typeface TF_DEFAULT = Typeface.DEFAULT;
11 | public static final Typeface TF_BOLD = Typeface.DEFAULT_BOLD;
12 | public static final Typeface TF_ITALIC = Typeface.create(Typeface.DEFAULT, Typeface.ITALIC);
13 | public static final Typeface TF_BOLD_ITALIC = Typeface.create(Typeface.DEFAULT, Typeface.BOLD_ITALIC);
14 | @ColorInt
15 | private int color;
16 | private Typeface typeface;
17 |
18 | public SpanType(int color) {
19 | this.color = color;
20 | this.typeface = TF_DEFAULT;
21 | }
22 |
23 | public SpanType(int color, Typeface typeface) {
24 | this.color = color;
25 | this.typeface = typeface;
26 | }
27 |
28 | public void onSpan(Paint paint) {
29 | paint.setColor(color);
30 | paint.setTypeface(typeface);
31 | }
32 |
33 | @Override
34 | public String toString() {
35 | return String.format("#%h",color);
36 | }
37 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/editor/span/TextSpanData.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.editor.span;
2 |
3 | import android.graphics.Color;
4 | import android.graphics.Paint;
5 |
6 | import java.util.ArrayList;
7 | import java.util.List;
8 |
9 | public class TextSpanData {
10 |
11 |
12 | public static final SpanType NORMAL = new SpanType(Color.BLACK);
13 |
14 | private List spanNodes;
15 |
16 | public TextSpanData() {
17 | spanNodes = new ArrayList<>();
18 | addSpan(0, NORMAL);
19 | }
20 |
21 |
22 | public void addSpan(int position, SpanType type) {
23 | if (type == null) {
24 | type = NORMAL;
25 | }
26 | spanNodes.add(new SpanNode(position, type));
27 |
28 | }
29 |
30 |
31 | public void clear() {
32 | spanNodes.clear();
33 | addSpan(0, NORMAL);
34 | }
35 |
36 | public class SpanSeeker {
37 | private Paint paint;
38 | private SpanNode currentSpan, nextSpan;
39 | private int index = 0;
40 | private int spanPosition = 0;
41 |
42 | public SpanSeeker(Paint paint) {
43 | this.paint = paint;
44 | }
45 |
46 | public void begin(int currentPosition) {
47 |
48 | index = 0;
49 | spanPosition=0;
50 | nextSpan = spanNodes.get(index++);
51 | do {
52 | currentSpan = nextSpan;
53 | spanPosition += currentSpan.length;
54 | if (index < spanNodes.size()) {
55 | nextSpan = spanNodes.get(index++);
56 | } else {
57 | nextSpan = null;
58 | }
59 | } while (nextSpan != null && spanPosition <= currentPosition);
60 |
61 | currentSpan.type.onSpan(paint);
62 | }
63 |
64 | public boolean reachedNextSpan(int currentPosition, SpanNode span) {
65 | return span != null&¤tPosition>=spanPosition;
66 | }
67 |
68 | public void listenSpan(int currentPosition) {
69 | if (reachedNextSpan(currentPosition, nextSpan)) {
70 | currentSpan = nextSpan;
71 | spanPosition += currentSpan.length;
72 | currentSpan.type.onSpan(paint);
73 |
74 | if (index < spanNodes.size()) {
75 | nextSpan = spanNodes.get(index++);
76 | } else {
77 | nextSpan = null;
78 | }
79 |
80 | }
81 | }
82 |
83 | public SpanNode getCurrentSpan() {
84 | return currentSpan;
85 | }
86 | }
87 |
88 | public SpanSeeker getNewSeeker(Paint paint) {
89 | return new SpanSeeker(paint);
90 | }
91 |
92 |
93 | @Override
94 | public String toString() {
95 | StringBuilder stringBuilder = new StringBuilder();
96 | stringBuilder.append(getClass().getSimpleName() + "(" + spanNodes.size() + ")" + "{\n");
97 | for (SpanNode spanNode : spanNodes) {
98 | stringBuilder.append(String.format("[%d,%s]\n", spanNode.length, spanNode.type.toString()));
99 | }
100 |
101 | return stringBuilder.append("\n}\n").toString();
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/ext/AutoComplete.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.ext;
2 |
3 | import com.ve.lexer.LuaLexer;
4 | import com.ve.lexer.LuaTokenTypes;
5 |
6 | import java.io.IOException;
7 |
8 | public class AutoComplete {
9 | public static int createAutoIndent(CharSequence text) {
10 | LuaLexer lexer = new LuaLexer(text);
11 | int idt = 0;
12 | try {
13 | while (true) {
14 | LuaTokenTypes type = lexer.advance();
15 | if (type == null) {
16 | break;
17 | }
18 | idt += indent(type);
19 | }
20 | } catch (IOException e) {
21 | e.printStackTrace();
22 | }
23 | return idt;
24 | }
25 |
26 |
27 | private static int indent(LuaTokenTypes t) {
28 | switch (t) {
29 | case DO:
30 | case FUNCTION:
31 | case THEN:
32 | case REPEAT:
33 | case LCURLY:
34 | return 1;
35 | case UNTIL:
36 | case ELSEIF:
37 | case END:
38 | case RCURLY:
39 | return -1;
40 | default:
41 | return 0;
42 | }
43 | }
44 |
45 | public static CharSequence format(CharSequence text, int width) {
46 | StringBuilder builder = new StringBuilder();
47 | boolean isNewLine = true;
48 | LuaLexer lexer = new LuaLexer(text);
49 | try {
50 | int idt = 0;
51 |
52 | while (true) {
53 | LuaTokenTypes type = lexer.advance();
54 | if (type == null)
55 | break;
56 | if (type == LuaTokenTypes.NEWLINE) {
57 | isNewLine = true;
58 | builder.append('\n');
59 | idt = Math.max(0, idt);
60 |
61 | } else if (isNewLine) {
62 | if (type == LuaTokenTypes.WS) {
63 |
64 | } else if (type == LuaTokenTypes.ELSE) {
65 | idt--;
66 | builder.append(createIntdent(idt * width));
67 | builder.append(lexer.yytext());
68 | idt++;
69 | isNewLine = false;
70 | } else if (type == LuaTokenTypes.ELSEIF || type == LuaTokenTypes.END || type == LuaTokenTypes.UNTIL || type == LuaTokenTypes.RCURLY) {
71 | idt--;
72 | builder.append(createIntdent(idt * width));
73 | builder.append(lexer.yytext());
74 |
75 | isNewLine = false;
76 | } else {
77 | builder.append(createIntdent(idt * width));
78 | builder.append(lexer.yytext());
79 | idt += indent(type);
80 | isNewLine = false;
81 | }
82 | } else if (type == LuaTokenTypes.WS) {
83 | builder.append(' ');
84 | } else {
85 | builder.append(lexer.yytext());
86 | idt += indent(type);
87 | }
88 |
89 | }
90 | } catch (IOException e) {
91 | e.printStackTrace();
92 | }
93 |
94 | return builder;
95 | }
96 |
97 | private static char[] createIntdent(int n) {
98 | if (n < 0)
99 | return new char[0];
100 | char[] idts = new char[n];
101 | for (int i = 0; i < n; i++)
102 | idts[i] = ' ';
103 | return idts;
104 | }
105 |
106 | }
107 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/ext/AutoCompletePanel.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.ext;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.drawable.Drawable;
6 | import android.graphics.drawable.GradientDrawable;
7 | import android.util.DisplayMetrics;
8 | import android.util.TypedValue;
9 | import android.view.LayoutInflater;
10 | import android.view.View;
11 | import android.view.ViewGroup;
12 | import android.widget.AdapterView;
13 | import android.widget.AdapterView.OnItemClickListener;
14 | import android.widget.ArrayAdapter;
15 | import android.widget.Filter;
16 | import android.widget.Filterable;
17 | import android.widget.ListPopupWindow;
18 | import android.widget.TextView;
19 |
20 | import com.ve.lexer.Language;
21 | import com.ve.lexer.LanguageC;
22 | import com.ve.view.utils.Flag;
23 | import com.ve.view.editor.Editor;
24 |
25 | import java.util.ArrayList;
26 |
27 | public class AutoCompletePanel {
28 |
29 | private Editor editor;
30 | private Context context;
31 | private static Language language = LanguageC.getInstance();
32 | private ListPopupWindow _autoCompletePanel;
33 | private MyAdapter _adapter;
34 | private Filter _filter;
35 |
36 | private int _verticalOffset;
37 |
38 | private int _height;
39 |
40 | private int _horizontal;
41 |
42 | private CharSequence _constraint;
43 |
44 | private int _backgroundColor;
45 |
46 | private GradientDrawable gd;
47 |
48 | private int _textColor;
49 |
50 | public AutoCompletePanel(Editor textField) {
51 | editor = textField;
52 | context = textField.getContext();
53 | initAutoCompletePanel();
54 |
55 | }
56 |
57 | public void setTextColor(int color) {
58 | _textColor = color;
59 | gd.setStroke(1, color);
60 | _autoCompletePanel.setBackgroundDrawable(gd);
61 | }
62 |
63 |
64 | public void setBackgroundColor(int color) {
65 | _backgroundColor = color;
66 | gd.setColor(color);
67 | _autoCompletePanel.setBackgroundDrawable(gd);
68 | }
69 |
70 | public void setBackground(Drawable color) {
71 | _autoCompletePanel.setBackgroundDrawable(color);
72 | }
73 |
74 | private void initAutoCompletePanel() {
75 | _autoCompletePanel = new ListPopupWindow(context);
76 | _autoCompletePanel.setAnchorView(editor);
77 | _adapter = new MyAdapter(context, android.R.layout.simple_list_item_1);
78 | _autoCompletePanel.setAdapter(_adapter);
79 | //autoCompletePanel.setDropDownGravity(Gravity.BOTTOM | Gravity.LEFT);
80 | _filter = _adapter.getFilter();
81 | setHeight(300);
82 |
83 | TypedArray array = context.getTheme().obtainStyledAttributes(new int[]{
84 | android.R.attr.colorBackground,
85 | android.R.attr.textColorPrimary,
86 | });
87 | int backgroundColor = array.getColor(0, 0xFF00FF);
88 | int textColor = array.getColor(1, 0xFF00FF);
89 | array.recycle();
90 | gd = new GradientDrawable();
91 | gd.setColor(backgroundColor);
92 | gd.setCornerRadius(4);
93 | gd.setStroke(1, textColor);
94 | setTextColor(textColor);
95 | _autoCompletePanel.setBackgroundDrawable(gd);
96 | _autoCompletePanel.setOnItemClickListener(new OnItemClickListener() {
97 |
98 | @Override
99 | public void onItemClick(AdapterView> p1, View p2, int p3, long p4) {
100 | // TODO: Implement this method
101 | //editor.replaceText(editor.getCaret().getPosition() - _constraint.length(), _constraint.length(), ((TextView) p2).getText().toString());
102 | _adapter.abort();
103 | dismiss();
104 | }
105 | });
106 |
107 | }
108 |
109 | public void setWidth(int width) {
110 | // TODO: Implement this method
111 | _autoCompletePanel.setWidth(width);
112 | }
113 |
114 | private void setHeight(int height) {
115 | // TODO: Implement this method
116 |
117 | if (_height != height) {
118 | _height = height;
119 | _autoCompletePanel.setHeight(height);
120 | }
121 | }
122 |
123 | private void setHorizontalOffset(int horizontal) {
124 | // TODO: Implement this method
125 | horizontal = Math.min(horizontal, editor.getWidth() / 2);
126 | if (_horizontal != horizontal) {
127 | _horizontal = horizontal;
128 | _autoCompletePanel.setHorizontalOffset(horizontal);
129 | }
130 | }
131 |
132 |
133 | private void setVerticalOffset(int verticalOffset) {
134 | // TODO: Implement this method
135 | //verticalOffset=Math.min(verticalOffset,editor.getWidth()/2);
136 | int max = 0 - _autoCompletePanel.getHeight();
137 | if (verticalOffset > max) {
138 | editor.scrollBy(0, verticalOffset - max);
139 | verticalOffset = max;
140 | }
141 | if (_verticalOffset != verticalOffset) {
142 | _verticalOffset = verticalOffset;
143 | _autoCompletePanel.setVerticalOffset(verticalOffset);
144 | }
145 | }
146 |
147 | public void update(CharSequence constraint) {
148 | _adapter.restart();
149 | _filter.filter(constraint);
150 | }
151 |
152 | public void show() {
153 | if (!_autoCompletePanel.isShowing()) {
154 | _autoCompletePanel.show();
155 | }
156 | _autoCompletePanel.getListView().setFadingEdgeLength(0);
157 | }
158 |
159 | public void dismiss() {
160 | if (_autoCompletePanel.isShowing()) {
161 | _autoCompletePanel.dismiss();
162 | }
163 | }
164 |
165 | synchronized public static void setLanguage(Language lang) {
166 | language = lang;
167 | }
168 |
169 | synchronized public static Language getLanguage() {
170 | return language;
171 | }
172 |
173 | /**
174 | * Adapter定义
175 | */
176 | class MyAdapter extends ArrayAdapter implements Filterable {
177 |
178 | private int _h;
179 | private Flag _abort;
180 |
181 | private DisplayMetrics dm;
182 |
183 | public MyAdapter(Context context, int resource) {
184 | super(context, resource);
185 | _abort = new Flag();
186 | setNotifyOnChange(false);
187 | dm = context.getResources().getDisplayMetrics();
188 |
189 | }
190 |
191 | public void abort() {
192 | _abort.set();
193 | }
194 |
195 |
196 | private int dp(float n) {
197 | // TODO: Implement this method
198 | return (int) TypedValue.applyDimension(1, n, dm);
199 | }
200 |
201 | @Override
202 | public View getView(int position, View convertView, ViewGroup parent) {
203 | // TODO: Implement this method
204 | TextView view = (TextView) super.getView(position, convertView, parent);
205 | /*TextView view=null;
206 | if(convertView==null){
207 | view=new TextView(context);
208 | view.setScale(16);
209 | view.setPadding(dp(8),dp(3),dp(8),dp(3));
210 | }
211 | else{
212 | view=(TextView) convertView;
213 | }
214 | view.setText(getItem(position));*/
215 | view.setTextColor(_textColor);
216 | return view;
217 | }
218 |
219 |
220 | public void restart() {
221 | // TODO: Implement this method
222 | _abort.clear();
223 | }
224 |
225 | public int getItemHeight() {
226 | if (_h != 0) {
227 | return _h;
228 | }
229 |
230 | LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
231 | TextView item = (TextView) inflater.inflate(android.R.layout.simple_list_item_1, null);
232 | item.measure(0, 0);
233 | _h = item.getMeasuredHeight();
234 | return _h;
235 | }
236 |
237 | /**
238 | * 实现自动完成的过滤算法
239 | */
240 | @Override
241 | public Filter getFilter() {
242 | Filter filter = new Filter() {
243 |
244 | /**
245 | * 本方法在后台线程执行,定义过滤算法
246 | */
247 | @Override
248 | protected FilterResults performFiltering(CharSequence constraint) {
249 | /*int l=constraint.length();
250 | int i=l;
251 | for(;i>0;i--){
252 | if(constraint.charAt(l-1)=='.')
253 | break;
254 | }
255 | if(i>0){
256 | constraint=constraint.subSequence(i,l);
257 | }*/
258 |
259 | // 此处实现过滤
260 | // 过滤后利用FilterResults将过滤结果返回
261 | ArrayList buf = new ArrayList();
262 | String keyword = String.valueOf(constraint).toLowerCase();
263 | String[] ss = keyword.split("\\.");
264 | if (ss.length == 2) {
265 | String pkg = ss[0];
266 | keyword = ss[1];
267 | if (language.isBasePackage(pkg)) {
268 | String[] keywords = language.getBasePackage(pkg);
269 | for (String k : keywords) {
270 | if (k.toLowerCase().startsWith(keyword)) {
271 | buf.add(k);
272 | }
273 | }
274 | }
275 | } else if (ss.length == 1) {
276 | if (keyword.charAt(keyword.length() - 1) == '.') {
277 | String pkg = keyword.substring(0, keyword.length() - 1);
278 | keyword = "";
279 | if (language.isBasePackage(pkg)) {
280 | String[] keywords = language.getBasePackage(pkg);
281 | for (String k : keywords) {
282 | buf.add(k);
283 | }
284 | }
285 | } else {
286 | String[] keywords = language.getUserWord();
287 | for (String k : keywords) {
288 | if (k.toLowerCase().startsWith(keyword)) {
289 | buf.add(k);
290 | }
291 | }
292 | keywords = language.getKeywords();
293 | for (String k : keywords) {
294 | if (k.indexOf(keyword) == 0) {
295 | buf.add(k);
296 | }
297 | }
298 | keywords = language.getNames();
299 | for (String k : keywords) {
300 | if (k.toLowerCase().startsWith(keyword)) {
301 | buf.add(k);
302 | }
303 | }
304 | }
305 | }
306 | _constraint = keyword;
307 | FilterResults filterResults = new FilterResults();
308 | filterResults.values = buf; // results是上面的过滤结果
309 | filterResults.count = buf.size(); // 结果数量
310 | return filterResults;
311 | }
312 |
313 | /**
314 | * 本方法在UI线程执行,用于更新自动完成列表
315 | */
316 | @Override
317 | protected void publishResults(CharSequence constraint, FilterResults results) {
318 | if (results != null && results.count > 0 && !_abort.isSet()) {
319 | // 有过滤结果,显示自动完成列表
320 | MyAdapter.this.clear(); // 清空旧列表
321 | MyAdapter.this.addAll((ArrayList) results.values);
322 | //int y = editor.getPaintBaseline(editor.getRow()) - editor.getScrollY();
323 | int y = editor.getOperator().getCaretY() + editor.getPainter().rowHeight() / 2 - editor.getScrollY();
324 | setHeight(getItemHeight() * Math.min(2, results.count));
325 | //setHeight((int)(Math.min(editor.getContentHeight()*0.4,getItemHeight() * Math.min(6, results.count))));
326 |
327 | setHorizontalOffset(editor.getOperator().getCaretX() - editor.getScrollX());
328 | setVerticalOffset(y - editor.getHeight());//editor.getCaretY()-editor.getScrollY()-editor.getHeight());
329 | notifyDataSetChanged();
330 | show();
331 | } else {
332 | // 无过滤结果,关闭列表
333 | notifyDataSetInvalidated();
334 | }
335 | }
336 |
337 | };
338 | return filter;
339 | }
340 | }
341 | }
342 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/ext/AutoIndent.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.ext;
2 |
3 | import com.ve.lexer.LuaLexer;
4 | import com.ve.lexer.LuaTokenTypes;
5 |
6 | import java.io.IOException;
7 |
8 | public class AutoIndent {
9 | public static int createAutoIndent(CharSequence text) {
10 | LuaLexer lexer = new LuaLexer(text);
11 | int idt = 0;
12 | try {
13 | while (true) {
14 | LuaTokenTypes type = lexer.advance();
15 | if (type == null) {
16 | break;
17 | }
18 | idt += indent(type);
19 | }
20 | } catch (IOException e) {
21 | e.printStackTrace();
22 | }
23 | return idt;
24 | }
25 |
26 |
27 | private static int indent(LuaTokenTypes t) {
28 | switch (t) {
29 | case DO:
30 | case FUNCTION:
31 | case THEN:
32 | case REPEAT:
33 | case LCURLY:
34 | return 1;
35 | case UNTIL:
36 | case ELSEIF:
37 | case END:
38 | case RCURLY:
39 | return -1;
40 | default:
41 | return 0;
42 | }
43 | }
44 |
45 | public static CharSequence format(CharSequence text, int width) {
46 | StringBuilder builder = new StringBuilder();
47 | boolean isNewLine = true;
48 | LuaLexer lexer = new LuaLexer(text);
49 | try {
50 | int idt = 0;
51 |
52 | while (true) {
53 | LuaTokenTypes type = lexer.advance();
54 | if (type == null)
55 | break;
56 | if (type == LuaTokenTypes.NEWLINE) {
57 | isNewLine = true;
58 | builder.append('\n');
59 | idt = Math.max(0, idt);
60 |
61 | } else if (isNewLine) {
62 | if (type == LuaTokenTypes.WS) {
63 |
64 | } else if (type == LuaTokenTypes.ELSE) {
65 | idt--;
66 | builder.append(createIntdent(idt * width));
67 | builder.append(lexer.yytext());
68 | idt++;
69 | isNewLine = false;
70 | } else if (type == LuaTokenTypes.ELSEIF || type == LuaTokenTypes.END || type == LuaTokenTypes.UNTIL || type == LuaTokenTypes.RCURLY) {
71 | idt--;
72 | builder.append(createIntdent(idt * width));
73 | builder.append(lexer.yytext());
74 |
75 | isNewLine = false;
76 | } else {
77 | builder.append(createIntdent(idt * width));
78 | builder.append(lexer.yytext());
79 | idt += indent(type);
80 | isNewLine = false;
81 | }
82 | } else if (type == LuaTokenTypes.WS) {
83 | builder.append(' ');
84 | } else {
85 | builder.append(lexer.yytext());
86 | idt += indent(type);
87 | }
88 |
89 | }
90 | } catch (IOException e) {
91 | e.printStackTrace();
92 | }
93 |
94 | return builder;
95 | }
96 |
97 | private static char[] createIntdent(int n) {
98 | if (n < 0)
99 | return new char[0];
100 | char[] idts = new char[n];
101 | for (int i = 0; i < n; i++)
102 | idts[i] = ' ';
103 | return idts;
104 | }
105 |
106 | }
107 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/ext/ClipboardPanel.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.ext;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.view.ActionMode;
6 | import android.view.Menu;
7 | import android.view.MenuItem;
8 |
9 | import com.ve.view.editor.Editor;
10 |
11 | public class ClipboardPanel {
12 | protected Editor editor;
13 | private Context context;
14 | private ActionMode actionMode;
15 |
16 | public ClipboardPanel(Editor editor) {
17 | this.editor = editor;
18 | context = editor.getContext();
19 |
20 |
21 | }
22 |
23 |
24 | public void show() {
25 | startClipboardAction();
26 | }
27 |
28 | public void hide() {
29 | stopClipboardAction();
30 | }
31 |
32 | public void startClipboardAction() {
33 | // TODO: Implement this method
34 | if (actionMode == null)
35 | editor.startActionMode(new ActionMode.Callback() {
36 |
37 | @Override
38 | public boolean onCreateActionMode(ActionMode mode, Menu menu) {
39 |
40 |
41 | actionMode = mode;
42 | mode.setTitle(android.R.string.selectTextMode);
43 | TypedArray array = context.getTheme().obtainStyledAttributes(new int[]{
44 | android.R.attr.actionModeSelectAllDrawable,
45 | android.R.attr.actionModeCutDrawable,
46 | android.R.attr.actionModeCopyDrawable,
47 | android.R.attr.actionModePasteDrawable,
48 | });
49 | menu.add(0, 0, 0, context.getString(android.R.string.selectAll))
50 | .setShowAsActionFlags(2)
51 | .setAlphabeticShortcut('a')
52 | .setIcon(array.getDrawable(0));
53 |
54 | menu.add(0, 1, 0, context.getString(android.R.string.cut))
55 | .setShowAsActionFlags(2)
56 | .setAlphabeticShortcut('x')
57 | .setIcon(array.getDrawable(1));
58 |
59 | menu.add(0, 2, 0, context.getString(android.R.string.copy))
60 | .setShowAsActionFlags(2)
61 | .setAlphabeticShortcut('c')
62 | .setIcon(array.getDrawable(2));
63 |
64 | menu.add(0, 3, 0, context.getString(android.R.string.paste))
65 | .setShowAsActionFlags(2)
66 | .setAlphabeticShortcut('v')
67 | .setIcon(array.getDrawable(3));
68 | array.recycle();
69 | return true;
70 | }
71 |
72 | @Override
73 | public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
74 | // TODO: Implement this method
75 | return false;
76 | }
77 |
78 | @Override
79 | public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
80 | switch (item.getItemId()) {
81 | case 0:
82 | editor.getSelectInterface().selectAll();
83 | break;
84 | case 1:
85 | editor.getTextInterface().cut();
86 | mode.finish();
87 | break;
88 | case 2:
89 | editor.getTextInterface().copy();
90 | mode.finish();
91 | break;
92 | case 3:
93 | editor.getTextInterface().paste();
94 | mode.finish();
95 | }
96 | return false;
97 | }
98 |
99 | @Override
100 | public void onDestroyActionMode(ActionMode p1) {
101 | editor.getSelectInterface().cancelSelect();
102 | actionMode = null;
103 | }
104 | });
105 |
106 | }
107 |
108 | public void stopClipboardAction() {
109 | if (actionMode != null) {
110 | actionMode.finish();
111 | actionMode = null;
112 | }
113 | }
114 |
115 | }
116 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/ext/ColorScheme.java:
--------------------------------------------------------------------------------
1 |
2 | package com.ve.view.ext;
3 |
4 | import com.ve.lexer.Lexer;
5 | import com.ve.view.utils.EditorException;
6 |
7 | import java.util.HashMap;
8 |
9 | public class ColorScheme {
10 | public enum Colorable {
11 | FOREGROUND, BACKGROUND, SELECTION_FOREGROUND, SELECTION_BACKGROUND,
12 | CARET_FOREGROUND, CARET_BACKGROUND, CARET_DISABLED, LINE_HIGHLIGHT,
13 | NON_PRINTING_GLYPH, COMMENT, KEYWORD, NAME, LITERAL, STRING,
14 | SECONDARY
15 | }
16 |
17 | public ColorScheme() {
18 | setColor(Colorable.FOREGROUND, OFF_BLACK);
19 | setColor(Colorable.BACKGROUND, OFF_WHITE);
20 | setColor(Colorable.SELECTION_FOREGROUND, OFF_WHITE);
21 | setColor(Colorable.CARET_FOREGROUND, OFF_WHITE);
22 | }
23 |
24 | private static final int OFF_WHITE = 0xFFF0F0ED;
25 | private static final int OFF_BLACK = 0xFF333333;
26 |
27 |
28 | protected HashMap _colors = generateDefaultColors();
29 |
30 | public void setColor(Colorable colorable, int color) {
31 | _colors.put(colorable, color);
32 | }
33 |
34 | public int getColor(Colorable colorable) {
35 | Integer color = _colors.get(colorable);
36 | if (color == null) {
37 | EditorException.fail("Color not specified for " + colorable);
38 | return 0;
39 | }
40 | return color.intValue();
41 | }
42 |
43 | // Currently, color scheme is tightly coupled with semantics of the token types
44 | public int getTokenColor(int tokenType) {
45 | Colorable element;
46 | switch (tokenType) {
47 | case Lexer.NORMAL:
48 | element = Colorable.FOREGROUND;
49 | break;
50 | case Lexer.KEYWORD:
51 | element = Colorable.KEYWORD;
52 | break;
53 | case Lexer.NAME:
54 | element = Colorable.NAME;
55 | break;
56 | case Lexer.DOUBLE_SYMBOL_LINE: //fall-through
57 | case Lexer.DOUBLE_SYMBOL_DELIMITED_MULTILINE:
58 | case Lexer.SINGLE_SYMBOL_LINE_B:
59 | element = Colorable.COMMENT;
60 | break;
61 | case Lexer.SINGLE_SYMBOL_DELIMITED_A: //fall-through
62 | case Lexer.SINGLE_SYMBOL_DELIMITED_B:
63 | element = Colorable.STRING;
64 | break;
65 | case Lexer.LITERAL:
66 | element = Colorable.LITERAL;
67 | break;
68 | case Lexer.SINGLE_SYMBOL_LINE_A: //fall-through
69 | case Lexer.SINGLE_SYMBOL_WORD:
70 | case Lexer.OPERATOR:
71 | element = Colorable.SECONDARY;
72 | break;
73 | default:
74 | EditorException.fail("Invalid token type");
75 | element = Colorable.FOREGROUND;
76 | break;
77 | }
78 | return getColor(element);
79 | }
80 |
81 | /**
82 | * Whether this color scheme uses a dark background, like black or dark grey.
83 | */
84 | public boolean isDark() {
85 | return false;
86 | }
87 |
88 | private HashMap generateDefaultColors() {
89 | // High-contrast, black-on-white color scheme
90 | HashMap colors = new HashMap(Colorable.values().length);
91 | colors.put(Colorable.FOREGROUND, BLACK);
92 | colors.put(Colorable.BACKGROUND, WHITE);
93 | colors.put(Colorable.SELECTION_FOREGROUND, WHITE);
94 | colors.put(Colorable.SELECTION_BACKGROUND, 0xFF97C024);
95 | colors.put(Colorable.CARET_FOREGROUND, WHITE);
96 | colors.put(Colorable.CARET_BACKGROUND, LIGHT_BLUE2);
97 | colors.put(Colorable.CARET_DISABLED, GREY);
98 | colors.put(Colorable.LINE_HIGHLIGHT, 0x20888888);
99 |
100 | colors.put(Colorable.NON_PRINTING_GLYPH, LIGHT_GREY);
101 | colors.put(Colorable.COMMENT, OLIVE_GREEN); // Eclipse default color
102 | colors.put(Colorable.KEYWORD, DARK_BLUE); // Eclipse default color
103 | colors.put(Colorable.NAME, INDIGO); // Eclipse default color
104 | colors.put(Colorable.LITERAL, LIGHT_BLUE); // Eclipse default color
105 | colors.put(Colorable.STRING, PURPLE); // Eclipse default color
106 | colors.put(Colorable.SECONDARY, GREY);
107 | return colors;
108 | }
109 |
110 | // In ARGB format: 0xAARRGGBB
111 | private static final int BLACK = 0xFF000000;
112 | private static final int BLUE = 0xFF0000FF;
113 | private static final int DARK_RED = 0xFF8B0000;
114 | private static final int DARK_BLUE = 0xFFD040DD;
115 | private static final int GREY = 0xFF808080;
116 | private static final int LIGHT_GREY = 0xFFAAAAAA;
117 | private static final int MAROON = 0xFF800000;
118 | private static final int INDIGO = 0xFF2A40FF;
119 | private static final int OLIVE_GREEN = 0xFF3F7F5F;
120 | private static final int PURPLE = 0xFFDD4488;
121 | private static final int RED = 0x44FF0000;
122 | private static final int WHITE = 0xFFFFFFE0;
123 | private static final int PURPLE2 = 0xFFFF00FF;
124 | private static final int LIGHT_BLUE = 0xFF6080FF;
125 | private static final int LIGHT_BLUE2 = 0xFF40B0FF;
126 | private static final int GREEN = 0xFF88AA88;
127 | }
128 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/listener/RowListener.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.listener;
2 |
3 | public interface RowListener {
4 | void onRowChange(int newRowIndex);
5 |
6 | class RowAdapter implements RowListener {
7 | @Override
8 | public void onRowChange(int newRowIndex) {
9 |
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/listener/SelectionListener.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.listener;
2 |
3 | public interface SelectionListener {
4 | void onSelectionChanged(boolean active, int selStart, int selEnd);
5 | class SelectionAdapter implements SelectionListener {
6 | @Override
7 | public void onSelectionChanged(boolean active, int selStart, int selEnd) {
8 |
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/listener/TextListener.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.listener;
2 |
3 | public interface TextListener {
4 | void onNewLine(int caretPosition, int p2);
5 |
6 | void onDelete(int caretPosition, int newCursorPosition);
7 |
8 | void onAdd(CharSequence text, int caretPosition, int newCursorPosition);
9 |
10 | class TextAdapter implements TextListener {
11 | @Override
12 | public void onNewLine(int caretPosition, int p2) {
13 |
14 | }
15 |
16 | @Override
17 | public void onDelete(int caretPosition, int newCursorPosition) {
18 |
19 | }
20 |
21 | @Override
22 | public void onAdd(CharSequence text, int caretPosition, int newCursorPosition) {
23 |
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/text/cache/Entry.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.text.cache;
2 |
3 | public class Entry {
4 | private int line,position;
5 |
6 | public Entry(int line, int position) {
7 | this.line = line;
8 | this.position = position;
9 | }
10 |
11 | public int getLine() {
12 | return line;
13 | }
14 |
15 | public void setLine(int line) {
16 | this.line = line;
17 | }
18 |
19 | public int getPosition() {
20 | return position;
21 | }
22 |
23 | public void setPosition(int position) {
24 | this.position = position;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/text/cache/TextCache.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.text.cache;
2 |
3 | public class TextCache {
4 | private static final int CACHE_SIZE = 4; // minimum = 1
5 | private Entry[] entries = new Entry[CACHE_SIZE];
6 |
7 | public TextCache() {
8 | entries[0] = new Entry(0, 0);
9 | for (int i = 1; i < CACHE_SIZE; ++i) {
10 | entries[i] = new Entry(-1, -1);
11 | }
12 | }
13 |
14 | public Entry getNearestEntryByLine(int line) {
15 | int nearestMatch = 0;
16 | int nearestDistance = Integer.MAX_VALUE;
17 | for (int i = 0; i < CACHE_SIZE; ++i) {
18 | int distance = Math.abs(line - entries[i].getLine());
19 | if (distance < nearestDistance) {
20 | nearestDistance = distance;
21 | nearestMatch = i;
22 | }
23 | }
24 |
25 | Entry nearestEntry = entries[nearestMatch];
26 | makeHead(nearestMatch);
27 | return nearestEntry;
28 | }
29 |
30 | public Entry getNearestEntryByPosition(int position) {
31 | int nearestMatch = 0;
32 | int nearestDistance = Integer.MAX_VALUE;
33 | for (int i = 0; i < CACHE_SIZE; ++i) {
34 | int distance = Math.abs(position - entries[i].getPosition());
35 | if (distance < nearestDistance) {
36 | nearestDistance = distance;
37 | nearestMatch = i;
38 | }
39 | }
40 |
41 | Entry nearestEntry = entries[nearestMatch];
42 | makeHead(nearestMatch);
43 | return nearestEntry;
44 | }
45 |
46 |
47 | private void makeHead(int newHead) {
48 | if (newHead != 0) {
49 | Entry temp = entries[newHead];
50 | for (int i = newHead; i > 1; --i) {
51 | entries[i] = entries[i - 1];
52 | }
53 | entries[1] = temp;
54 | }
55 |
56 | }
57 |
58 | public void updateEntry(int line, int position) {
59 | if (line > 0) {
60 | //update
61 | for (int i = 1; i < CACHE_SIZE; ++i) {
62 | if (entries[i].getLine() == line) {
63 | entries[i].setPosition(position);
64 | return;
65 | }
66 | }
67 | //insert
68 | makeHead(CACHE_SIZE - 1);
69 | entries[1] = new Entry(line, position);
70 |
71 | }
72 |
73 | }
74 |
75 |
76 | public void invalidateEntriesFrom(int position) {
77 | for (int i = 1; i < CACHE_SIZE; ++i) {
78 | if (entries[i].getPosition() >= position) {
79 | entries[i] = new Entry(-1, -1);
80 | }
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/text/document/CommonLanguage.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.text.document;
2 |
3 | public class CommonLanguage {
4 | public final static char EOF = '\uFFFF';
5 | public final static char NULL_CHAR = '\u0000';
6 | public final static char NEWLINE = '\n';
7 | public final static char BACKSPACE = '\b';
8 | public static final char SPACE = ' ';
9 | public final static char TAB = '\t';
10 | public final static String GLYPH_NEWLINE = "\u21b5";
11 | public final static String GLYPH_SPACE = "\u00b7";
12 | public final static String GLYPH_TAB = "\u00bb";
13 | public final static char EMOJI1 = 0xd83c;
14 | public final static char EMOJI2 = 0xd83d;
15 |
16 | public static boolean isWhitespace(char c) {
17 | return c == SPACE || c == NEWLINE || c == TAB || c == '\r' || c == '\f' || c == EOF;
18 | }
19 | public static boolean isEmoji(char c) {
20 | return c == EMOJI1 || c == EMOJI2 ;
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/text/document/Document.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.text.document;
2 |
3 | import com.ve.view.utils.EditorException;
4 |
5 | import java.util.ArrayList;
6 |
7 | public class Document extends BaseDocument {
8 | private boolean isWordWrapEnable = false;
9 |
10 | private WordWrapable wordWrapable;
11 |
12 | private ArrayList rowTable;
13 |
14 | public Document() {
15 | super();
16 | resetRowTable();
17 | }
18 |
19 |
20 | private void resetRowTable() {
21 | ArrayList newRowTable = new ArrayList();
22 | newRowTable.add(0);
23 | rowTable = newRowTable;
24 | }
25 |
26 | public void setWordWrapable(WordWrapable wordWrapable) {
27 | this.wordWrapable = wordWrapable;
28 | }
29 |
30 | public void setWordWrapEnable(boolean enable) {
31 | if (enable && !isWordWrapEnable) {
32 | isWordWrapEnable = true;
33 | analyzeWordWrap();
34 | } else if (!enable && isWordWrapEnable) {
35 | isWordWrapEnable = false;
36 | analyzeWordWrap();
37 | }
38 | }
39 |
40 | public boolean isWordWrapEnable() {
41 | return isWordWrapEnable;
42 | }
43 |
44 |
45 | @Override
46 | public synchronized void delete(int charOffset, int totalChars, long timestamp, boolean undoable) {
47 | super.delete(charOffset, totalChars, timestamp, undoable);
48 |
49 | int startRow = gainRowOfPosition(charOffset);
50 | int analyzeEnd = findNextLineFrom(charOffset);
51 | updateWordWrapAfterEdit(startRow, analyzeEnd, -totalChars);
52 | }
53 |
54 | @Override
55 | public synchronized void insert(char[] c, int charOffset, long timestamp, boolean undoable) {
56 | super.insert(c, charOffset, timestamp, undoable);
57 |
58 | int startRow = gainRowOfPosition(charOffset);
59 | int analyzeEnd = findNextLineFrom(charOffset + c.length);
60 | updateWordWrapAfterEdit(startRow, analyzeEnd, c.length);
61 | }
62 |
63 | @Override
64 | public synchronized void shiftGapStart(int displacement) {
65 | super.shiftGapStart(displacement);
66 |
67 | if (displacement != 0) {
68 | int startOffset = (displacement > 0)
69 | ? gap.getStart() - displacement
70 | : gap.getStart();
71 | int startRow = gainRowOfPosition(startOffset);
72 | int analyzeEnd = findNextLineFrom(gap.getStart());
73 | updateWordWrapAfterEdit(startRow, analyzeEnd, displacement);
74 | }
75 | }
76 |
77 | //No error checking is done on parameters.
78 | private int findNextLineFrom(int charOffset) {
79 | int lineEnd = gap.position2index(charOffset);
80 |
81 | while (lineEnd < contents.length) {
82 | // skip the gap
83 | if (lineEnd == gap.getStart()) {
84 | lineEnd = gap.getEnd();
85 | }
86 |
87 | if (contents[lineEnd] == CommonLanguage.NEWLINE || contents[lineEnd] == CommonLanguage.EOF) {
88 | break;
89 | }
90 |
91 | ++lineEnd;
92 | }
93 |
94 | return gap.index2position(lineEnd) + 1;
95 | }
96 |
97 | private void updateWordWrapAfterEdit(int startRow, int analyzeEnd, int delta) {
98 | if (startRow > 0) {
99 | --startRow;
100 | }
101 | int analyzeStart = rowTable.get(startRow);
102 |
103 | //changes only affect the rowTable after startRow
104 | removeRowMetadata(startRow + 1, analyzeEnd - delta);
105 | adjustOffsetOfRowsFrom(startRow + 1, delta);
106 | analyzeWordWrap(startRow + 1, analyzeStart, analyzeEnd);
107 | }
108 |
109 | private void removeRowMetadata(int fromRow, int endOffset) {
110 | while (fromRow < rowTable.size() && rowTable.get(fromRow) <= endOffset) {
111 | rowTable.remove(fromRow);
112 | }
113 | }
114 |
115 | private void adjustOffsetOfRowsFrom(int fromRow, int offset) {
116 | for (int i = fromRow; i < rowTable.size(); ++i) {
117 | rowTable.set(i, rowTable.get(i) + offset);
118 | }
119 | }
120 |
121 | public void analyzeWordWrap() {
122 |
123 | resetRowTable();
124 |
125 | if (isWordWrapEnable && !hasMinimumWidthForWordWrap()) {
126 | if (wordWrapable.getEditorWidth() > 0) {
127 | EditorException.fail("Text field has non-zero width but still too small for word wrap");
128 | }
129 | //没有绘制之前getRowWidth() 可能是0
130 | return;
131 | }
132 |
133 | analyzeWordWrap(1, 0, length());
134 | }
135 |
136 | private boolean hasMinimumWidthForWordWrap() {
137 | final int maxWidth = wordWrapable.getEditorWidth();
138 | return (maxWidth >= 2 * wordWrapable.measure('M'));
139 | }
140 |
141 | private void analyzeWordWrap(int rowIndex, int startPosition, int endPosition) {
142 | if (!isWordWrapEnable) {
143 | int startIndex = gap.position2index(startPosition);
144 | int end = gap.position2index(endPosition);
145 | ArrayList rowTable = new ArrayList();
146 |
147 | while (startIndex < end) {
148 | if (startIndex == gap.getStart()) {
149 | startIndex = gap.getEnd();
150 | }
151 | char c = contents[startIndex];
152 | if (c == CommonLanguage.NEWLINE) {
153 | rowTable.add(gap.index2position(startIndex) + 1);
154 | }
155 | ++startIndex;
156 |
157 | }
158 | this.rowTable.addAll(rowIndex, rowTable);
159 | return;
160 | }
161 | if (!hasMinimumWidthForWordWrap()) {
162 | EditorException.fail("Not enough space to do word wrap");
163 | return;
164 | }
165 |
166 | ArrayList rowTable = new ArrayList();
167 | int offset = gap.position2index(startPosition);
168 | int end = gap.position2index(endPosition);
169 | int potentialBreakPoint = startPosition;
170 | int wordExtent = 0;
171 | final int maxWidth = wordWrapable.getEditorWidth();
172 | int remainingWidth = maxWidth;
173 |
174 | while (offset < end) {
175 | // skip the gap
176 | if (offset == gap.getStart()) {
177 | offset = gap.getEnd();
178 | }
179 |
180 | char c = contents[offset];
181 | wordExtent += wordWrapable.measure(c);
182 |
183 | if (CommonLanguage.isWhitespace(c)) {
184 | //full word obtained
185 | if (wordExtent <= remainingWidth) {
186 | remainingWidth -= wordExtent;
187 | } else if (wordExtent > maxWidth) {
188 | //handle a word too long to fit on one row
189 | int current = gap.position2index(potentialBreakPoint);
190 | remainingWidth = maxWidth;
191 |
192 | //start the word on a new row, if it isn't already
193 | if (potentialBreakPoint != startPosition && (rowTable.isEmpty() ||
194 | potentialBreakPoint != rowTable.get(rowTable.size() - 1))) {
195 | rowTable.add(potentialBreakPoint);
196 | }
197 |
198 | while (current <= offset) {
199 | // skip the gap
200 | if (current == gap.getStart()) {
201 | current = gap.getEnd();
202 | }
203 |
204 | int advance = wordWrapable.measure(contents[current]);
205 | if (advance > remainingWidth) {
206 | rowTable.add(gap.index2position(current));
207 | remainingWidth = maxWidth - advance;
208 | } else {
209 | remainingWidth -= advance;
210 | }
211 |
212 | ++current;
213 | }
214 | } else {
215 | //invariant: potentialBreakPoint != startOffset
216 | //put the word on a new row
217 | rowTable.add(potentialBreakPoint);
218 | remainingWidth = maxWidth - wordExtent;
219 | }
220 |
221 | wordExtent = 0;
222 | potentialBreakPoint = gap.index2position(offset) + 1;
223 | }
224 |
225 | if (c == CommonLanguage.NEWLINE) {
226 | //start a new row
227 | rowTable.add(potentialBreakPoint);
228 | remainingWidth = maxWidth;
229 | }
230 |
231 | ++offset;
232 | }
233 |
234 | //merge with existing row table
235 | this.rowTable.addAll(rowIndex, rowTable);
236 | }
237 |
238 | public String gainRowText(int rowNumber) {
239 |
240 | int rowSize = gainRowLength(rowNumber);
241 | if (rowSize == 0) {
242 | return new String();
243 | }
244 |
245 | int startIndex = rowTable.get(rowNumber);
246 | return subSequence(startIndex, rowSize).toString();
247 | }
248 |
249 | public int gainRowLength(int row) {
250 |
251 | if (!isValidRow(row)) {
252 | return 0;
253 | }
254 |
255 | if (row != (rowTable.size() - 1)) {
256 | return rowTable.get(row + 1) - rowTable.get(row);
257 | } else {
258 | return length() - rowTable.get(row);
259 | }
260 | }
261 |
262 | public int getRowCount() {
263 | return rowTable.size();
264 | }
265 |
266 | public int gainPositionOfRow(int row) {
267 | return isValidRow(row) ? rowTable.get(row) : -1;
268 | }
269 |
270 | public int gainRowOfPosition(int position) {
271 |
272 | if (!isValidPosition(position)) {
273 | return -1;
274 | }
275 |
276 | //binary search of rowTable
277 | int right = rowTable.size() - 1;
278 | int left = 0;
279 | while (right >= left) {
280 | int mid = (left + right) / 2;
281 | int nextLineOffset = ((mid + 1) < rowTable.size()) ? rowTable.get(mid + 1) : length();
282 | if (position >= rowTable.get(mid) && position < nextLineOffset) {
283 | return mid;
284 | }
285 |
286 | if (position >= nextLineOffset) {
287 | left = mid + 1;
288 | } else {
289 | right = mid - 1;
290 | }
291 | }
292 |
293 | //should not be here
294 | return -1;
295 | }
296 |
297 | public int getColumn(int position) {
298 | int row = gainRowOfPosition(position);
299 | EditorException.logIf(row < 0, "Invalid char offset given to getColumn");
300 | int firstCharOfRow = gainPositionOfRow(row);
301 | return position - firstCharOfRow;
302 | }
303 |
304 | protected boolean isValidRow(int row) {
305 | return row >= 0 && row < rowTable.size();
306 | }
307 |
308 |
309 |
310 | }
311 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/text/document/DocumentEdit.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.text.document;
2 |
3 | public interface DocumentEdit {
4 |
5 |
6 | void insertBefore(char c, int insertionPoint, long timestamp);
7 |
8 | void insertBefore(char[] cArray, int insertionPoint, long timestamp);
9 |
10 | void deleteAt(int deletionPoint, long timestamp);
11 |
12 | void deleteAt(int deletionPoint, int maxChars, long time);
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/text/document/Gap.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.text.document;
2 |
3 | public class Gap {
4 | protected final static int MIN_GAP_SIZE = 64;
5 |
6 | private int start, end;
7 | private char[] contents;
8 |
9 | public Gap(char[] contents) {
10 | this.contents=contents;
11 | start = 0;
12 | end = MIN_GAP_SIZE;
13 | }
14 |
15 | public void move(int distance) {
16 | start += distance;
17 | end += distance;
18 | }
19 |
20 | public void moveStart(int distance) {
21 | start += distance;
22 | }
23 |
24 | public void moveEnd(int distance) {
25 | end += distance;
26 | }
27 |
28 | public int getStart() {
29 | return start;
30 | }
31 |
32 | public void setStart(int start) {
33 | this.start = start;
34 | }
35 |
36 | public int getEnd() {
37 | return end;
38 | }
39 |
40 | public void setEnd(int end) {
41 | this.end = end;
42 | }
43 |
44 | protected final int size() {
45 | return end - start;
46 | }
47 |
48 | protected final int position2index(int position) {
49 | if (isBeforeGap(position)) {
50 | return position;
51 | } else {
52 | return position + size();
53 | }
54 | }
55 |
56 | protected final int index2position(int index) {
57 | if (isBeforeGap(index)) {
58 | return index;
59 | } else {
60 | return index - size();
61 | }
62 | }
63 |
64 | protected final boolean isBeforeGap(int index) {
65 | return index < start;
66 | }
67 |
68 |
69 | public void reset(int capacity) {
70 | start = 0;
71 | end = capacity;
72 | }
73 |
74 | final protected void shiftGapLeft(int newGapStart) {
75 | while (start > newGapStart) {
76 |
77 | start--;
78 | end--;
79 | contents[end] = contents[start];
80 | }
81 | }
82 |
83 | final protected void shiftGapRight(int newGapEnd) {
84 | while (end < newGapEnd) {
85 | contents[start] = contents[end];
86 | start++;
87 | end++;
88 | }
89 | }
90 |
91 | public void onContentChange(char[] contents) {
92 | this.contents=contents;
93 | }
94 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/text/document/WordWrapable.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.text.document;
2 |
3 | public interface WordWrapable {
4 | int measure(char c);
5 | int getEditorWidth();
6 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/text/undo/Undo.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.text.undo;
2 |
3 | public interface Undo {
4 | int undo();
5 |
6 | int redo();
7 |
8 | boolean canUndo();
9 |
10 | boolean canRedo();
11 |
12 | boolean isBatchEdit();
13 |
14 | void beginBatchEdit();
15 |
16 | void endBatchEdit();
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/text/undo/UndoManager.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.text.undo;
2 |
3 | import java.util.LinkedList;
4 |
5 | public class UndoManager implements Undo {
6 | private Undoable undoable;
7 | private LinkedList commands = new LinkedList();
8 | private boolean isBatchEdit = false;
9 | private int groupId = 0;
10 | private int top = 0;
11 | private long lastEditTime = -1;
12 |
13 | public UndoManager(Undoable undoable) {
14 | this.undoable = undoable;
15 | }
16 |
17 | //private
18 | private void push(Command c) {
19 | trimStack();
20 | ++top;
21 | commands.add(c);
22 | }
23 |
24 | private void trimStack() {
25 | while (commands.size() > top) {
26 | commands.removeLast();
27 | }
28 | }
29 |
30 | @Override
31 | public int undo() {
32 | if (canUndo()) {
33 | Command lastUndone = commands.get(top - 1);
34 | int group = lastUndone.group;
35 | do {
36 | Command c = commands.get(top - 1);
37 | if (c.group != group) {
38 | break;
39 | }
40 |
41 | lastUndone = c;
42 | c.undo();
43 | --top;
44 | } while (canUndo());
45 |
46 | return lastUndone.findUndoPosition();
47 | }
48 |
49 | return -1;
50 | }
51 |
52 | @Override
53 | public int redo() {
54 | if (canRedo()) {
55 | Command lastRedone = commands.get(top);
56 | int group = lastRedone.group;
57 | do {
58 | Command c = commands.get(top);
59 | if (c.group != group) {
60 | break;
61 | }
62 |
63 | lastRedone = c;
64 | c.redo();
65 | ++top;
66 | } while (canRedo());
67 |
68 | return lastRedone.findRedoPosition();
69 | }
70 |
71 | return -1;
72 | }
73 |
74 | @Override
75 | public final boolean canUndo() {
76 | return top > 0;
77 | }
78 |
79 | @Override
80 | public final boolean canRedo() {
81 | return top < commands.size();
82 | }
83 |
84 | @Override
85 | public boolean isBatchEdit() {
86 | return isBatchEdit;
87 | }
88 |
89 | @Override
90 | public void beginBatchEdit() {
91 | isBatchEdit = true;
92 | }
93 |
94 | @Override
95 | public void endBatchEdit() {
96 | isBatchEdit = false;
97 | groupId++;
98 | }
99 |
100 | public void captureInsert(int start, int length, long time) {
101 | boolean mergeSuccess = false;
102 |
103 | if (canUndo()) {
104 | Command c = commands.get(top - 1);
105 |
106 | if (c instanceof InsertCommand && c.merge(start, length, time)) {
107 | mergeSuccess = true;
108 | } else {
109 | c.recordData();
110 | }
111 | }
112 |
113 | if (!mergeSuccess) {
114 | push(new InsertCommand(start, length, groupId));
115 |
116 | if (!isBatchEdit) {
117 | groupId++;
118 | }
119 | }
120 |
121 | lastEditTime = time;
122 | }
123 |
124 | public void captureDelete(int start, int length, long time) {
125 | boolean mergeSuccess = false;
126 |
127 | if (canUndo()) {
128 | Command c = commands.get(top - 1);
129 |
130 | if (c instanceof DeleteCommand && c.merge(start, length, time)) {
131 | mergeSuccess = true;
132 | } else {
133 | c.recordData();
134 | }
135 | }
136 |
137 | if (!mergeSuccess) {
138 | push(new DeleteCommand(start, length, groupId));
139 |
140 | if (!isBatchEdit) {
141 | groupId++;
142 | }
143 | }
144 |
145 | lastEditTime = time;
146 | }
147 |
148 |
149 | private abstract class Command {
150 | public final static long MERGE_TIME = 1000000000; //750ms in nanoseconds
151 |
152 | public int start, length;
153 |
154 | public String data;
155 |
156 | public int group;
157 |
158 | public abstract void undo();
159 |
160 | public abstract void redo();
161 |
162 | public abstract void recordData();
163 |
164 | public abstract int findUndoPosition();
165 |
166 | public abstract int findRedoPosition();
167 |
168 | public abstract boolean merge(int start, int length, long time);
169 | }
170 |
171 | private class InsertCommand extends Command {
172 |
173 | public InsertCommand(int start, int length, int groupNumber) {
174 | this.start = start;
175 | this.length = length;
176 | group = groupNumber;
177 | }
178 |
179 | @Override
180 | public boolean merge(int newStart, int length, long time) {
181 | if (lastEditTime < 0) {
182 | return false;
183 | }
184 |
185 | if ((time - lastEditTime) < MERGE_TIME
186 | && newStart == start + this.length) {
187 | this.length += length;
188 | trimStack();
189 | return true;
190 | }
191 |
192 | return false;
193 | }
194 |
195 | @Override
196 | public void recordData() {
197 | data = undoable.subSequence(start, length).toString();
198 | }
199 |
200 | @Override
201 | public void undo() {
202 | if (data == null) {
203 | recordData();
204 | undoable.shiftGapStart(-length);
205 | } else {
206 | //dummy timestamp of 0
207 | undoable.delete(start, length, 0, false);
208 | }
209 | }
210 |
211 | @Override
212 | public void redo() {
213 | //dummy timestamp of 0
214 | undoable.insert(data.toCharArray(), start, 0, false);
215 | }
216 |
217 | @Override
218 | public int findRedoPosition() {
219 | return start + length;
220 | }
221 |
222 | @Override
223 | public int findUndoPosition() {
224 | return start;
225 | }
226 | }
227 |
228 |
229 | private class DeleteCommand extends Command {
230 |
231 | public DeleteCommand(int start, int length, int seqNumber) {
232 | this.start = start;
233 | this.length = length;
234 | group = seqNumber;
235 | }
236 |
237 | @Override
238 | public boolean merge(int newStart, int length, long time) {
239 | if (lastEditTime < 0) {
240 | return false;
241 | }
242 |
243 | if ((time - lastEditTime) < MERGE_TIME
244 | && newStart == start - this.length - length + 1) {
245 | start = newStart;
246 | this.length += length;
247 | trimStack();
248 | return true;
249 | }
250 |
251 | return false;
252 | }
253 |
254 | @Override
255 | public void recordData() {
256 | data = new String(undoable.subEditingSequence(length));
257 | }
258 |
259 | @Override
260 | public void undo() {
261 | if (data == null) {
262 | recordData();
263 | undoable.shiftGapStart(length);
264 | } else {
265 | //dummy timestamp of 0
266 | undoable.insert(data.toCharArray(), start, 0, false);
267 | }
268 | }
269 |
270 | @Override
271 | public void redo() {
272 | //dummy timestamp of 0
273 | undoable.delete(start, length, 0, false);
274 | }
275 |
276 | @Override
277 | public int findRedoPosition() {
278 | return start;
279 | }
280 |
281 | @Override
282 | public int findUndoPosition() {
283 | return start + length;
284 | }
285 | }
286 | }
287 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/text/undo/Undoable.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.text.undo;
2 |
3 | public interface Undoable {
4 | String subSequence(int start, int length);
5 |
6 | void shiftGapStart(int distance);
7 |
8 | void delete(int start, int length, long timestamp, boolean undoable);
9 |
10 | void insert(char[] chars, int start, long timestamp, boolean undoable);
11 |
12 | char[] subEditingSequence(int length);
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/utils/CharRange.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.utils;
2 |
3 | public class CharRange {
4 | int left,right;
5 |
6 | public CharRange(int left, int right) {
7 | this.left = left;
8 | this.right = right;
9 | }
10 |
11 | public int getLeft() {
12 | return left;
13 | }
14 |
15 | public void setLeft(int left) {
16 | this.left = left;
17 | }
18 |
19 | public int getRight() {
20 | return right;
21 | }
22 |
23 | public void setRight(int right) {
24 | this.right = right;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/utils/EditorException.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.utils;
2 |
3 | import android.util.Log;
4 |
5 | public class EditorException {
6 | private static final String TAG = EditorException.class.getSimpleName();
7 | private static final boolean DEBUG = true;
8 |
9 |
10 | static public void fail(final String details) {
11 | if (DEBUG) {
12 | Log.e(TAG, "fail: " + details);
13 |
14 | }
15 | }
16 |
17 | static public void logIf(boolean condition, final String details) {
18 |
19 | if (condition) {
20 | fail(details);
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/utils/Flag.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.ve.view.utils;
10 |
11 | public class Flag {
12 | private boolean state = false;
13 |
14 | synchronized public final void set(){
15 | state = true;
16 | }
17 |
18 | synchronized public final void clear(){
19 | state = false;
20 | }
21 |
22 | synchronized public final boolean isSet(){
23 | return state;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/utils/LinearSearchStrategy.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.ve.view.utils;
10 |
11 |
12 | import com.ve.view.text.document.CommonLanguage;
13 | import com.ve.view.text.document.Document;
14 |
15 | public class LinearSearchStrategy implements SearchStrategy{
16 | private int _unitsDone = 0;
17 |
18 | @Override
19 | // only applicable to replaceAll operation
20 | public int getProgress(){
21 | return _unitsDone;
22 | }
23 |
24 | @Override
25 | public int wrappedFind(Document src, String target, int start,
26 | boolean isCaseSensitive, boolean isWholeWord){
27 |
28 | // search towards end of doc first...
29 | int foundOffset = find(src, target, start, src.length(),
30 | isCaseSensitive, isWholeWord);
31 | // ...then from beginning of doc
32 | if(foundOffset < 0){
33 | foundOffset = find(src, target, 0, start,
34 | isCaseSensitive, isWholeWord);
35 | }
36 |
37 | return foundOffset;
38 | }
39 |
40 | @Override
41 | public int find(Document src, String target, int start, int end,
42 | boolean isCaseSensitive, boolean isWholeWord) {
43 | if(target.length() == 0){
44 | return -1;
45 | }
46 | if(start < 0){
47 | EditorException.fail("BaseDocument.find: Invalid start position");
48 | start = 0;
49 | }
50 | if(end > src.length()){
51 | EditorException.fail("BaseDocument.find: Invalid end position");
52 | end = src.length();
53 | }
54 |
55 | end = Math.min(end, src.length() - target.length() + 1);
56 | int offset = start;
57 | while(offset < end){
58 | if(equals(src, target, offset, isCaseSensitive) &&
59 | (!isWholeWord || isSandwichedByWhitespace(src, offset, target.length())) ){
60 | break;
61 | }
62 |
63 | ++offset;
64 | ++_unitsDone;
65 | }
66 |
67 | if (offset < end){
68 | return offset;
69 | }
70 | else{
71 | return -1;
72 | }
73 | }
74 |
75 | @Override
76 | public int wrappedFindBackwards(Document src, String target, int start,
77 | boolean isCaseSensitive, boolean isWholeWord){
78 |
79 | // search towards beginning of doc first...
80 | int foundOffset = findBackwards(src, target, start, -1,
81 | isCaseSensitive, isWholeWord);
82 | // ...then from end of doc
83 | if(foundOffset < 0){
84 | foundOffset = findBackwards(src, target, src.length()-1, start,
85 | isCaseSensitive, isWholeWord);
86 | }
87 |
88 | return foundOffset;
89 | }
90 |
91 |
92 | @Override
93 | public int findBackwards(Document src, String target, int start, int end,
94 | boolean isCaseSensitive, boolean isWholeWord) {
95 | if(target.length() == 0){
96 | return -1;
97 | }
98 | if(start >= src.length()){
99 | EditorException.fail("Invalid start position given to BaseDocument.find");
100 | start = src.length() - 1;
101 | }
102 | if(end < -1){
103 | EditorException.fail("Invalid end position given to BaseDocument.find");
104 | end = -1;
105 | }
106 | int offset = Math.min(start, src.length()-target.length());
107 | while(offset > end){
108 | if(equals(src, target, offset, isCaseSensitive) &&
109 | (!isWholeWord || isSandwichedByWhitespace(src, offset, target.length()) )){
110 | break;
111 | }
112 |
113 | --offset;
114 | }
115 |
116 | if (offset > end){
117 | return offset;
118 | }
119 | else{
120 | return -1;
121 | }
122 | }
123 |
124 | @Override
125 | public Pair replaceAll(Document src, String searchText,
126 | String replacementText, int mark,
127 | boolean isCaseSensitive, boolean isWholeWord){
128 | int replacementCount = 0;
129 | int anchor = mark;
130 | _unitsDone = 0;
131 |
132 | final char[] replacement = replacementText.toCharArray();
133 | int foundIndex = find(src, searchText, 0, src.length(),
134 | isCaseSensitive, isWholeWord);
135 | long timestamp = System.nanoTime();
136 |
137 | src.beginBatchEdit();
138 | while (foundIndex != -1){
139 | src.deleteAt(foundIndex, searchText.length(), timestamp);
140 | src.insertBefore(replacement, foundIndex, timestamp);
141 | if(foundIndex < anchor){
142 | // adjust anchor because of differences in doc length
143 | // after word replacement
144 | anchor += replacementText.length() - searchText.length();
145 | }
146 | ++replacementCount;
147 | _unitsDone += searchText.length(); //skip replaced chars
148 | foundIndex = find(
149 | src,
150 | searchText,
151 | foundIndex + replacementText.length(),
152 | src.length(),
153 | isCaseSensitive,
154 | isWholeWord);
155 | }
156 | src.endBatchEdit();
157 |
158 | return new Pair(replacementCount, Math.max(anchor, 0));
159 | }
160 |
161 |
162 | protected boolean equals(Document src, String target,
163 | int srcOffset, boolean isCaseSensitive){
164 | if((src.length() - srcOffset) < target.length()){
165 | //compared range in src must at least be as long as target
166 | return false;
167 | }
168 |
169 | int i;
170 | for(i = 0; i < target.length(); ++i){
171 | if (isCaseSensitive &&
172 | target.charAt(i) != src.charAt(i + srcOffset)){
173 | return false;
174 | }
175 | // for case-insensitive search, compare both strings in lower case
176 | if (!isCaseSensitive &&
177 | Character.toLowerCase(target.charAt(i)) !=
178 | Character.toLowerCase(src.charAt(i + srcOffset))){
179 | return false;
180 | }
181 |
182 | }
183 |
184 | return true;
185 | }
186 |
187 | /**
188 | * Checks if a word starting at startPosition with size length is bounded
189 | * by whitespace.
190 | */
191 | protected boolean isSandwichedByWhitespace(Document src,
192 | int start, int length){
193 | boolean startWithWhitespace = (start == 0)
194 | ? true
195 | : CommonLanguage.isWhitespace(src.charAt(start - 1));
196 |
197 | int end = start + length;
198 | boolean endWithWhitespace = (end == src.length())
199 | ? true
200 | : CommonLanguage.isWhitespace(src.charAt(end));
201 |
202 | return (startWithWhitespace && endWithWhitespace);
203 | }
204 |
205 | }
206 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/utils/Pair.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.ve.view.utils;
10 |
11 | public final class Pair {
12 | private int _first;
13 | private int _second;
14 |
15 | public Pair(int x, int y){
16 | _first = x;
17 | _second = y;
18 | }
19 |
20 | public final int getFirst(){
21 | return _first;
22 | }
23 |
24 | public final int getSecond(){
25 | return _second;
26 | }
27 |
28 | public final void setFirst(int value){
29 | _first = value;
30 | }
31 |
32 | public final void setSecond(int value){
33 | _second = value;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/utils/Rectangle.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.utils;
2 |
3 | import android.graphics.Rect;
4 | import android.graphics.RectF;
5 |
6 | public class Rectangle {
7 | public int x;//left
8 | public int y;//top
9 | public int width;
10 | public int height;
11 |
12 | public Rectangle() {
13 | }
14 | public static void main(String[] args) {
15 | Rectangle rectangle=new Rectangle(3,4,8,8);
16 | System.out.println(rectangle);
17 | System.out.println(rectangle.right());
18 | System.out.println(rectangle.bottom());
19 | }
20 |
21 | public Rectangle(int x, int y, int width, int height) {
22 | this.x = x;
23 | this.y = y;
24 | this.width = width;
25 | this.height = height;
26 | }
27 |
28 | public void setBounds(int x, int y, int width, int height) {
29 | this.x = x;
30 | this.y = y;
31 | this.width = width;
32 | this.height = height;
33 | }
34 |
35 | public int right() {
36 | return x + width;
37 | }
38 |
39 | public int bottom() {
40 | return y + height;
41 | }
42 |
43 |
44 | public RectF toRectF() {
45 | return new RectF(x, y, right(), bottom());
46 | }
47 |
48 | public Rect toRect() {
49 | return new Rect(x, y, right(), bottom());
50 | }
51 |
52 | public boolean intersects(Rectangle bounds) {
53 | return this.x >= bounds.x && this.x < bounds.x + bounds.width && this.y >= bounds.y && this.y < bounds.y + bounds.height;
54 | }
55 |
56 | public boolean contains(int x, int y) {
57 | return x >= this.x && x < this.x + this.width && y >= this.y && y < this.y + this.height;
58 | }
59 |
60 | public int getCenterX() {
61 | return this.x + this.width / 2;
62 | }
63 |
64 | public int getCenterY() {
65 | return this.y + this.height / 2;
66 | }
67 |
68 | public void setPosition(int x, int y) {
69 | this.x = x;
70 | this.y = y;
71 | }
72 |
73 | @Override
74 | public String toString() {
75 | return String.format("(%d,%d,%d,%d)\n",x,y,width,height);
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/utils/SearchStrategy.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013 Tah Wei Hoon.
3 | * All rights reserved. This program and the accompanying materials
4 | * are made available under the terms of the Apache License Version 2.0,
5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html
6 | *
7 | * This software is provided "as is". Use at your own risk.
8 | */
9 | package com.ve.view.utils;
10 |
11 | import com.ve.view.text.document.Document;
12 |
13 | public interface SearchStrategy {
14 | /**
15 | * Searches for target, starting from start (inclusive),
16 | * and stopping at end (exclusive).
17 | *
18 | * @return charOffset of found string; -1 if not found
19 | */
20 | public int find(Document src, String target, int start, int end,
21 | boolean isCaseSensitive, boolean isWholeWord);
22 |
23 | /**
24 | * Searches for target, starting from start (inclusive),
25 | * wrapping around to the beginning of document and
26 | * stopping at start (exclusive).
27 | *
28 | * @return charOffset of found string; -1 if not found
29 | */
30 | public int wrappedFind(Document src, String target, int start,
31 | boolean isCaseSensitive, boolean isWholeWord);
32 |
33 | /**
34 | * Searches backwards from startCharOffset (inclusive),
35 | * and stopping at end (exclusive).
36 | *
37 | * @return charOffset of found string; -1 if not found
38 | */
39 | public int findBackwards(Document src, String target, int start, int end,
40 | boolean isCaseSensitive, boolean isWholeWord);
41 |
42 | /**
43 | * Searches backwards from start (inclusive), wrapping around to
44 | * the end of document and stopping at start (exclusive).
45 | *
46 | * @return charOffset of found string; -1 if not found
47 | */
48 | public int wrappedFindBackwards(Document src, String target, int start,
49 | boolean isCaseSensitive, boolean isWholeWord);
50 |
51 | /**
52 | * Replace all matches of searchText in src with replacementText.
53 | *
54 | * @param mark Optional. A position in src that can be tracked for changes.
55 | * After replacements are made, the position may be shifted because of
56 | * insertion/deletion of text before it. The new position of mark is
57 | * returned in Pair.second. If mark is an invalid position, Pair.second
58 | * is undefined.
59 | *
60 | * @return Pair.first is the number of replacements made.
61 | * Pair.second is new position of mark after replacements are made.
62 | */
63 | public Pair replaceAll(Document src, String searchText,
64 | String replacementText, int mark,
65 | boolean isCaseSensitive, boolean isWholeWord);
66 |
67 |
68 | /**
69 | * The number of characters that have been examined by the current find
70 | * operation. This method is not synchronized, and the value returned
71 | * may be outdated.
72 | *
73 | * @return The number of characters searched so far
74 | */
75 | public int getProgress();
76 | }
77 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ve/view/utils/ZoomChecker.java:
--------------------------------------------------------------------------------
1 | package com.ve.view.utils;
2 |
3 | import android.view.MotionEvent;
4 |
5 | public class ZoomChecker {
6 |
7 | private ZoomListener listener;
8 | private double distance;
9 | private double scaleFrom = 1, scaleCurrent = 1;
10 |
11 |
12 | private static double calculateDistance(MotionEvent event) {
13 | float dx = event.getX(0) - event.getX(1);
14 | float dy = event.getY(0) - event.getY(1);
15 | return Math.sqrt(dx * dx + dy * dy);
16 | }
17 |
18 | public boolean checkZoom(MotionEvent event) {
19 | if (event.getAction() != MotionEvent.ACTION_MOVE || event.getPointerCount() != 2) {
20 | reset();
21 | return false;
22 | }
23 |
24 | double newDistance = calculateDistance(event);
25 | if (!isZooming()) {
26 | distance = newDistance;
27 | //listener.onZoomStart();
28 | scaleCurrent = scaleFrom;
29 |
30 | } else {
31 | if (listener != null) {
32 | double zoom = newDistance / distance;
33 | scaleCurrent = zoom * scaleFrom;
34 | listener.onZoom(zoom,scaleCurrent);
35 | }
36 | }
37 | return true;
38 |
39 | }
40 |
41 | public void reset() {
42 | distance = -1;
43 | scaleFrom = scaleCurrent;
44 | //listener.onZoomEnd();
45 |
46 | }
47 |
48 | public void setListener(ZoomListener listener) {
49 | this.listener = listener;
50 | }
51 |
52 | public boolean isZooming() {
53 | return distance > 0;
54 | }
55 |
56 | public interface ZoomListener {
57 | void onZoom(double scale,double scaleAll);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/cursor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ve3344/CodeEditor/5e785d20085ebfe92bf01bbbb599ab5326862c0a/app/src/main/res/drawable/cursor.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
16 |
20 |
21 |
28 |
29 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ve3344/CodeEditor/5e785d20085ebfe92bf01bbbb599ab5326862c0a/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ve3344/CodeEditor/5e785d20085ebfe92bf01bbbb599ab5326862c0a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ve3344/CodeEditor/5e785d20085ebfe92bf01bbbb599ab5326862c0a/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ve3344/CodeEditor/5e785d20085ebfe92bf01bbbb599ab5326862c0a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ve3344/CodeEditor/5e785d20085ebfe92bf01bbbb599ab5326862c0a/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ve3344/CodeEditor/5e785d20085ebfe92bf01bbbb599ab5326862c0a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ve3344/CodeEditor/5e785d20085ebfe92bf01bbbb599ab5326862c0a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ve3344/CodeEditor/5e785d20085ebfe92bf01bbbb599ab5326862c0a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ve3344/CodeEditor/5e785d20085ebfe92bf01bbbb599ab5326862c0a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ve3344/CodeEditor/5e785d20085ebfe92bf01bbbb599ab5326862c0a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #667bf1
4 | #667bf1
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Acpp
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/ve/acpp/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.ve.acpp;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 |
5 | repositories {
6 | google()
7 | jcenter()
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:3.1.3'
11 |
12 |
13 | // NOTE: Do not place your application dependencies here; they belong
14 | // in the individual module build.gradle files
15 | }
16 | }
17 |
18 | allprojects {
19 | repositories {
20 | google()
21 | jcenter()
22 | }
23 | }
24 |
25 | task clean(type: Delete) {
26 | delete rootProject.buildDir
27 | }
28 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ve3344/CodeEditor/5e785d20085ebfe92bf01bbbb599ab5326862c0a/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Nov 18 16:27:58 CST 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------